@openclaw/codex 2026.8.1-beta.2 → 2026.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/api.js +146 -0
  2. package/dist/{attempt-client-cleanup-DMLXUfa8.js → attempt-client-cleanup-CVPvp9dL.js} +47 -5
  3. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  4. package/dist/{binding-connection-I5Bo-AsX.js → binding-connection-CCQFs8lf.js} +15 -3
  5. package/dist/{bounded-turn-qgZmzy_4.js → bounded-turn-rq5tiSXk.js} +27 -26
  6. package/dist/{capabilities-CybGI1oz.js → capabilities-CGm_94eL.js} +1 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-B1AEz8ZA.js → command-formatters-C54Yg3eA.js} +2 -2
  9. package/dist/{command-handlers-Db8pu_ds.js → command-handlers-BvOAHTrA.js} +111 -67
  10. package/dist/{compact-LspQaGfl.js → compact-Hd7SAc0N.js} +35 -30
  11. package/dist/{session-binding-DPxIvMgE.js → config-Cxw3Cl3k.js} +361 -1161
  12. package/dist/context-compaction-activity-BOeJf7yo.js +58 -0
  13. package/dist/doctor-contract-api.js +128 -26
  14. package/dist/{dynamic-tools-BVdyWkUD.js → dynamic-tools-BFoUcRdx.js} +1341 -1464
  15. package/dist/{effective-mcp-catalog-DIIhAtOZ.js → effective-mcp-catalog-BTJHnDSx.js} +2 -2
  16. package/dist/elicitation-input-DMkeki8d.js +71 -0
  17. package/dist/{event-projector-assistant-message-BwxV4d6N.js → event-projector-assistant-message-C5-Ds8mA.js} +18 -1
  18. package/dist/{event-projector-usage-e5huIQWu.js → event-projector-usage-D3PHFFXh.js} +18 -18
  19. package/dist/harness-DVfUQBlQ.js +248 -0
  20. package/dist/harness.js +2 -226
  21. package/dist/index.js +405 -299
  22. package/dist/{isolated-completion-C58NbL40.js → isolated-completion-Cbbx_5Ed.js} +4 -4
  23. package/dist/launch-args-sXOAg0SX.js +84 -0
  24. package/dist/managed-binary-ZkGodW19.js +248 -0
  25. package/dist/media-understanding-provider.js +2 -2
  26. package/dist/model-catalog-o6Cgjoss.js +101 -0
  27. package/dist/{models-BFlpn1Ex.js → models-DgFWSuxA.js} +25 -28
  28. package/dist/{native-subagent-monitor-DyTGmTUx.js → native-subagent-monitor-CwFyoylz.js} +385 -94
  29. package/dist/node-exec-server.runtime-DXrqpw1c.js +195 -0
  30. package/dist/plugin-approval-roundtrip-D80UZiJ_.js +95 -0
  31. package/dist/{plugin-marketplace-discovery-CzrGJdCR.js → plugin-marketplace-discovery-mdmScYOj.js} +60 -55
  32. package/dist/{plugin-metadata-cache-CNJPj4_b.js → plugin-metadata-cache-CulOABwu.js} +164 -134
  33. package/dist/{protocol-validators-B4F7L8Rf.js → protocol-validators-Bg5rpNxk.js} +203 -7
  34. package/dist/{rate-limits-DZXmOL8-.js → rate-limits-hFtzt2Up.js} +1 -1
  35. package/dist/{sandbox-guard-Cmwc9eIk.js → request-CCm4LaED.js} +207 -20
  36. package/dist/{run-attempt-P8BdxC1I.js → run-attempt-BwNQDH2u.js} +1506 -836
  37. package/dist/{runtime-artifact-BVSPdd6g.js → runtime-artifact-DeBvleJw.js} +4 -7
  38. package/dist/session-binding-q2WSTVmC.js +947 -0
  39. package/dist/{session-catalog-xwNi_SDM.js → session-catalog-C2Tae4rI.js} +1698 -1346
  40. package/dist/session-catalog-home-id-DAKUrm3C.js +112 -0
  41. package/dist/{session-cli-7hgXZDV7.js → session-cli-BuDjm5Ou.js} +20 -7
  42. package/dist/session-permission-policy-gLm40Vtq.js +160 -0
  43. package/dist/session-retirement-CdkI0bvb.js +90 -0
  44. package/dist/{session-upstream-marker-CrIFX06J.js → session-upstream-marker-Bcy33kam.js} +2 -3
  45. package/dist/{settled-turn-finalizer-BRWBO4MO.js → settled-turn-finalizer-B8JJcxCi.js} +27 -22
  46. package/dist/{shared-client-kIBbGjk4.js → shared-client-kNkljY-R.js} +2023 -607
  47. package/dist/{side-question-9HAL26l9.js → side-question-GwD73tX6.js} +109 -57
  48. package/dist/{thread-lifecycle-Bu1BTDXr.js → thread-lifecycle-Ss-uwnC4.js} +1208 -497
  49. package/dist/{thread-ownership-6kOQHlyC.js → thread-ownership-CeYBqg_4.js} +18 -8
  50. package/dist/{plugin-app-cache-key-C3H2CVp6.js → thread-resume-DF54hagX.js} +64 -6
  51. package/dist/{transcript-mirror-B9gGgnJS.js → transcript-mirror-CkEQIna-.js} +158 -41
  52. package/dist/transport-stdio-C7-s9TLG.js +708 -0
  53. package/dist/{upstream-prompt-provenance-ClRBije0.js → upstream-prompt-provenance-CFmsKwr9.js} +12 -14
  54. package/dist/{upstream-session-fork-CE-_LdnP.js → upstream-session-fork-BACGlOFa.js} +86 -88
  55. package/dist/{usage-CHlV0e-o.js → usage-C2l8Mils.js} +3 -3
  56. package/dist/{web-search-provider.runtime-DRw2Vl42.js → web-search-provider.runtime-B2XgRdFv.js} +3 -3
  57. package/openclaw.plugin.json +50 -2
  58. package/package.json +9 -8
  59. package/dist/app-server-policy-zcBDc1JU.js +0 -49
  60. package/dist/incognito-session-ET_ufDH-.js +0 -2
  61. package/dist/request-BRl5_UfX.js +0 -157
  62. package/dist/session-retirement-DoMMJFKm.js +0 -36
  63. package/dist/thread-resume-CPrJNQVM.js +0 -30
  64. package/dist/transport-stdio-DIImwDVS.js +0 -99
@@ -1,42 +1,51 @@
1
- import { C as isCodexRemoteExecPlacementSandbox, E as resolveCodexPluginsPolicy, M as isCodexAppServerApprovalPolicyAllowedByRequirements, N as resolveOpenClawExecPolicyForCodexAppServer, S as withMcpElicitationsApprovalPolicy, T as readCodexPluginConfig, c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, h as resolveCodexAppServerHomeScope, i as createCodexSessionGenerationSupersededError, j as resolveCodexModelBackedReviewerPolicyContext, l as resolveCodexRunSessionBindingAuthority, u as scopeCodexRunBindingStore, v as resolveCodexComputerUseConfig, w as isCodexSandboxExecServerEnabled, x as shouldAutoApproveCodexAppServerApprovals } from "./session-binding-DPxIvMgE.js";
2
- import { $ as resolveCodexAppServerAuthProfileId, A as isCodexAppServerApprovalRequest, C as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, D as resolveCodexTurnTerminalIdleTimeoutMs, E as resolveCodexTurnCompletionIdleTimeoutMs, G as protectCodexAppServerLiveThread, H as ensureCodexAppServerClientRuntime, I as isCodexAppServerRequestTimeoutError, M as isCodexAppServerConnectionClosedError, N as isCodexAppServerIndeterminateRequestCancellationError, O as withCodexStartupTimeout, P as isCodexAppServerIndeterminateTransportError, Q as resolveCodexAppServerAuthAccountCacheKey, R as resolveCodexAppServerClientInstanceId, T as resolveCodexTurnAssistantCompletionIdleTimeoutMs, X as readRecentCodexRateLimits, Y as readCodexRateLimitsRevision, Z as rememberCodexRateLimitsRead, a as getLeasedSharedCodexAppServerClient, b as CodexAppServerStartupError, ct as isJsonObject, et as resolveCodexAppServerAuthProfileIdForAgent, f as retainSharedCodexAppServerClientIfCurrent, g as withAbortableTimeout, j as isCodexAppServerBrokenPipeError, k as getCodexAppServerClientInstanceId, l as releaseLeasedSharedCodexAppServerClient, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, nt as resolveCodexAppServerPreparedApiKeyCacheKey, o as getSharedCodexAppServerClient, p as retireSharedCodexAppServerClientIfCurrent, q as retainCodexAppServerLiveThread, rt as resolveCodexAppServerPreparedAuthHandoff, s as isCodexAppServerStartSelectionChangedError, st as flattenCodexDynamicToolFunctions, t as clearSharedCodexAppServerClientIfCurrent, tt as resolveCodexAppServerFallbackApiKeyCacheKey, ut as resolveCodexAppServerHomeDir, w as resolveCodexStartupTimeoutMs, x as isCodexAppServerStartupError, y as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, z as CodexAppServerRpcError } from "./shared-client-kIBbGjk4.js";
3
- 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-DMLXUfa8.js";
4
- 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-e5huIQWu.js";
5
- import { l as readCodexTurn, o as assertCodexTurnStartResponse, s as readCodexDynamicToolCallParams, u as readCodexTurnCompletedNotification } from "./protocol-validators-B4F7L8Rf.js";
6
- import { i as defaultCodexAppInventoryCache, n as buildCodexAppServerRuntimeFingerprint, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-C3H2CVp6.js";
7
- import { A as resolveCodexDynamicToolsLoadingForRuntime, B as buildCodexPluginThreadConfigInputFingerprint, C as codexLegacyDynamicToolsFingerprint, D as isSystemAgentOnlyCodexDynamicToolAllowlist, F as projectContextEngineAssemblyForCodex, G as shouldBuildCodexPluginThreadConfig, I as resolveCodexContextEngineProjectionMaxChars, L as resolveCodexContextEngineProjectionReserveTokens, M as isContextEngineBindingCompatible, N as fitCodexProjectedContextForTurnStart, P as neutralizeCodexExplicitMentionSigils, R as buildCodexPluginAppsConfigPatchFromPolicyContext, S as codexDynamicToolsFingerprint, U as buildPluginAppPolicyContext, V as buildCodexPluginThreadConfigTimeoutFallback, W as mergeCodexThreadConfigs, Z as resolveRecoverableCodexPluginConfigKeys, _ as resolveCodexAppServerThreadModelSelection, a as isCodexAppServerProfilerEnabled, f as resolveCodexWebSearchPlan, i as startOrResumeThread, j as buildContextEngineBinding, n as buildTurnStartParams, p as buildDeveloperInstructions, r as buildCodexUserInput, t as buildTurnCollaborationMode, w as filterCodexDynamicTools, x as areCodexDynamicToolFingerprintsCompatible, z as buildCodexPluginThreadConfig } from "./thread-lifecycle-Bu1BTDXr.js";
8
- import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
9
- import { i as readUpstreamUserText, n as attachUpstreamUserText, r as readMirrorIdentity, t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-ClRBije0.js";
10
- 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-B9gGgnJS.js";
11
- import { t as CODEX_CONTROL_METHODS } from "./capabilities-CybGI1oz.js";
12
- import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-DZXmOL8-.js";
13
- import { r as formatCodexDisplayText } from "./command-formatters-B1AEz8ZA.js";
14
- import { n as resolveCodexAppServerForOpenClawToolPolicy, t as resolveCodexAppServerForModelProvider } from "./app-server-policy-zcBDc1JU.js";
15
- import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-I5Bo-AsX.js";
16
- import { n as prepareCodexAppServerAuthBinding } from "./auth-binding-B4FfbcCg.js";
17
- import { t as resolveCodexLocalRuntimeAttribution } from "./local-runtime-attribution-B_2mnqhb.js";
18
- import { n as createAssistantMessage, r as createAssistantMirrorMessage, t as createAssistantCommentaryMessage } from "./event-projector-assistant-message-BwxV4d6N.js";
19
- import { a as CodexToolProgressProjection, c as isCodexCommandBearingToolCall, d as sanitizeCodexToolResponse, i as readCodexMirroredSessionHistoryMessages, l as resolveCodexToolProgressDetailMode, n as CodexEventProjection, o as shouldEmitTranscriptToolProgress, r as CodexToolTranscriptProjection, s as inferCodexDynamicToolMeta, t as codexNativeSubagentMonitorRuntime, u as sanitizeCodexToolArguments } from "./native-subagent-monitor-DyTGmTUx.js";
20
- import { A as buildCodexNativeHookRelayDisabledConfig, B as resolveDynamicToolCallTimeoutMs, C as shouldWarnCodexDynamicToolBuildStageSummary, E as CodexNativeToolLifecycleProjector, F as scheduleCodexNativeHookRelayUnregister, G as toCodexDynamicToolProtocolResponse, H as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult, I as handleDynamicToolCallWithTimeout, K as resolveCodexToolAbortTerminalReason, L as hasPendingDynamicToolTerminalDiagnostic, M as emitCodexNativePreToolUseFailureDiagnostic, N as resolveCodexNativeHookRelayEvents, O as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, P as resolveCodexNativeHookRelayTtlMs, R as isDynamicToolTerminalDiagnosticEvent, S as shouldRequireCodexSandboxExecServerEnvironment, T as readBoundedCodexRemoteWorkspaceFile, U as shouldReleaseTurnAfterTerminalDynamicTool, V as resolveTerminalDynamicToolBatchAction, W as toCodexDynamicToolProgressResponse, _ as resolveCodexAppServerHookChannelId, a as emitDynamicToolStartedDiagnostic, b as resolveCodexSandboxEnvironmentSelection, c as resolveCodexProviderWebSearchSupport, d as releaseCodexSandboxExecServerEnvironment, f as buildDynamicTools, g as resolveCodexAppServerExecutionCwd, h as formatCodexDynamicToolBuildStageSummary, i as emitDynamicToolErrorDiagnostic, j as createCodexNativeHookRelay, k as buildCodexNativeHookRelayConfig, m as disableCodexPluginThreadConfig, n as projectCodexExecutableDynamicTools, o as emitDynamicToolTerminalDiagnostic, p as createCodexDynamicToolBuildStageTracker, r as handleCodexAppServerElicitationRequest, s as handleCodexAppServerApprovalRequest, t as createCodexDynamicToolBridge, u as ensureCodexSandboxExecServerEnvironment, v as resolveCodexExternalSandboxPolicyForOpenClawSandbox, x as shouldEnableCodexAppServerNativeToolSurface, y as resolveCodexMessageToolProvider, z as isMatchingDynamicToolTerminalDiagnostic } from "./dynamic-tools-BVdyWkUD.js";
21
- import { a as runCodexComputerUseLiveTest, n as ensureCodexComputerUse, o as killStaleComputerUseMcpChildren, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-CNJPj4_b.js";
1
+ import { C as isCodexRemoteExecPlacementSandbox, D as resolveOpenClawExecPolicyForCodexAppServer, E as resolveCodexPluginsPolicy, P as resolveCodexAppServerHomeDir, S as isCodexPairedNodeRemoteExecPlacementSandbox, T as readCodexPluginConfig, d as withMcpElicitationsApprovalPolicy, m as resolveCodexModelBackedReviewerPolicyContext, o as resolveCodexComputerUseConfig, p as canUseCodexModelBackedApprovalsReviewerForModel, r as resolveCodexAppServerHomeScope, u as shouldAutoApproveCodexAppServerApprovals, v as readCodexRequirementsToml, w as isCodexSandboxExecServerEnabled } from "./config-Cxw3Cl3k.js";
2
+ import { $ as CodexAppServerRpcError, A as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, B as resolveCodexTurnTerminalIdleTimeoutMs, Dt as readCodexRateLimitsRevision, Ft as resolveCodexAppServerFallbackApiKeyCacheKey, G as isCodexAppServerConnectionClosedError, H as getCodexAppServerClientInstanceId, Ht as isJsonObject, I as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, It as resolveCodexAppServerPreparedApiKeyCacheKey, J as isCodexAppServerOverloadError, K as isCodexAppServerIndeterminateRequestCancellationError, L as resolveCodexStartupTimeoutMs, Lt as resolveCodexAppServerPreparedAuthHandoff, M as TURN_FINALIZE_DRAIN_ABORT_GRACE_MS, Mt as resolveCodexAppServerAuthProfileId, N as TURN_TERMINAL_SETTLEMENT_IDLE_TIMEOUT_MS, Nt as resolveCodexAppServerAuthProfileIdForAgent, Ot as readRecentCodexRateLimits, P as isCodexAppServerStartupError, Q as resolveCodexAppServerClientInstanceId, R as resolveCodexTurnAssistantCompletionIdleTimeoutMs, Tt as withAbortableTimeout, U as isCodexAppServerApprovalRequest, V as withCodexStartupTimeout, Vt as flattenCodexDynamicToolFunctions, W as isCodexAppServerBrokenPipeError, X as isCodexAppServerRequestTimeoutError, _t as ensureCodexAppServerClientRuntime, a as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, at as resolveDynamicToolCallTimeoutMs, c as getLeasedSharedCodexAppServerClient, ct as shouldReleaseTurnAfterTerminalDynamicTool, dt as resolveCodexToolAbortTerminalReason, et as createCodexElicitationResponse, f as readCodexAppServerClientDesktopGenerationFingerprint, h as releaseLeasedSharedCodexAppServerClient, i as clearSharedCodexAppServerClientIfCurrent, it as isMatchingDynamicToolTerminalDiagnostic, j as CodexAppServerStartupError, jt as resolveCodexAppServerAuthAccountCacheKey, kt as rememberCodexRateLimitsRead, l as getSharedCodexAppServerClient, lt as toCodexDynamicToolProgressResponse, nt as hasPendingDynamicToolTerminalDiagnostic, ot as resolveTerminalDynamicToolBatchAction, q as isCodexAppServerIndeterminateTransportError, rt as isDynamicToolTerminalDiagnosticEvent, s as createIsolatedCodexAppServerClient, st as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult, tt as handleDynamicToolCallWithTimeout, u as isCodexAppServerStartSelectionChangedError, ut as toCodexDynamicToolProtocolResponse, v as retainSharedCodexAppServerClientIfCurrent, xt as protectCodexAppServerLiveThread, y as retireSharedCodexAppServerClientIfCurrent, z as resolveCodexTurnCompletionIdleTimeoutMs } from "./shared-client-kNkljY-R.js";
3
+ import { c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, l as resolveCodexRunSessionBindingAuthority, u as scopeCodexRunBindingStore } from "./session-binding-q2WSTVmC.js";
4
+ import { c as assertCodexTurnStartResponse, d as readCodexTurn, f as readCodexTurnCompletedNotification, l as readCodexDynamicToolCallParams } from "./protocol-validators-Bg5rpNxk.js";
5
+ import { B as readNonNegativeInteger, D as itemStatus, E as itemName, H as splitPlanText, I as readItem, L as readItemString, M as extractRawAssistantText, P as readCodexErrorNotificationMessage, V as readNullableString, _ as readCodexNotificationThreadId, a as closeCodexStartupClientBestEffort, d as terminateCodexBackgroundTerminals, f as unsubscribeCodexThreadBestEffort, g as isCodexNotificationForTurn, h as CodexProjectionDiagnostics, j as shouldSynthesizeToolProgressForItem, k as shouldClearTerminalPresentationForNativeItem, l as retireCodexAppServerClientAfterTimedOutTurn, m as getCodexAppServerTurnRouter, o as interruptCodexTurnAndWaitBestEffort, p as CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS, r as CodexAppServerUnsafeSubscriptionError, t as CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS, u as retireUnsafeCodexTurnClientBestEffort } from "./attempt-client-cleanup-CVPvp9dL.js";
6
+ import { C as readRawResponseToolCallId, E as updateActiveTurnItemIds, S as readNotificationItemId, T as updateActiveCompletionBlockerItemIds, _ as isReasoningItemCompletionNotification, a as codexExecutionToolName, b as isTerminalTurnStatus, c as isAssistantCompletionReleaseNotification, d as isNativeToolProgressNotification, f as isPendingOpenClawDynamicToolCompletionNotification, g as isRawToolOutputCompletionNotification, h as isRawReasoningCompletionNotification, l as isCodexTurnAbortMarkerNotification, m as isRawFunctionToolOutputCompletionNotification, o as describeNotificationActivity, p as isRawAssistantProgressNotification, r as projectCodexThreadUsageUpdate, s as isAssistantCommentaryCompletionNotification, t as CodexResponseCompletionProjection, u as isFileChangePatchUpdatedNotification, v as isReasoningProgressNotification, w as shouldDisarmAssistantCompletionIdleWatch, x as readCodexNotificationItem, y as isRetryableErrorNotification } from "./event-projector-usage-D3PHFFXh.js";
7
+ import { i as buildCodexPluginAppCacheKey, r as buildCodexAppServerRuntimeFingerprint, s as defaultCodexAppInventoryCache } from "./thread-resume-DF54hagX.js";
8
+ import { $ as resolveCodexContinuityProjectionMaxChars, B as isMessageOnlyCodexSourceReply, D as resolveCodexAppServerThreadModelSelection, F as codexDynamicToolsFingerprint, G as buildContextEngineBinding, I as codexLegacyDynamicToolsFingerprint, J as fitCodexProjectedContextForTurnStart, K as isContextEngineBindingCompatible, L as filterCodexDynamicTools, P as areCodexDynamicToolFingerprintsCompatible, Q as resolveCodexContextEngineProjectionReserveTokens, S as buildDeveloperInstructions, V as isSystemAgentOnlyCodexDynamicToolAllowlist, W as resolveCodexDynamicToolsLoadingForRuntime, X as projectContextEngineAssemblyForCodex, Y as neutralizeCodexExplicitMentionSigils, Z as resolveCodexContextEngineProjectionMaxChars, a as isCodexAppServerProfilerEnabled, at as buildPluginAppPolicyContext, c as buildCodexNativeHookRelayConfig, d as emitCodexNativePreToolUseFailureDiagnostic, et as buildCodexPluginAppsConfigPatchFromPolicyContext, f as resolveCodexNativeHookRelayEvents, i as startOrResumeThread, l as buildCodexNativeHookRelayDisabledConfig, m as scheduleCodexNativeHookRelayUnregister, n as buildTurnStartParams, nt as buildCodexPluginThreadConfigInputFingerprint, ot as mergeCodexThreadConfigs, p as resolveCodexNativeHookRelayTtlMs, pt as resolveRecoverableCodexPluginConfigKeys, q as buildCodexContinuityCalibration, r as buildCodexUserInput, rt as buildCodexPluginThreadConfigTimeoutFallback, s as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, st as shouldBuildCodexPluginThreadConfig, t as buildTurnCollaborationMode, tt as buildCodexPluginThreadConfig, u as createCodexNativeHookRelay, x as resolveCodexWebSearchPlan } from "./thread-lifecycle-Ss-uwnC4.js";
9
+ import { l as isIncognitoSessionKey, r as retainCodexAppServerBindingSubscription } from "./thread-ownership-CeYBqg_4.js";
10
+ import { i as readUpstreamUserText, n as attachUpstreamUserText, r as readMirrorIdentity, t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-CFmsKwr9.js";
11
+ import { a as buildCodexUserPromptMessage, i as mirrorPromptAtTurnStartBestEffort, l as readCodexMirrorSourceFingerprint, n as createCodexAppServerUserMessagePersistenceNotifier, o as promptSnapshot, s as applyCodexTranscriptTaint, t as codexTranscriptMirrorRuntime, u as serializeCodexMirrorSourceEvidence } from "./transcript-mirror-CkEQIna-.js";
12
+ import { t as CODEX_CONTROL_METHODS } from "./capabilities-CGm_94eL.js";
13
+ import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-hFtzt2Up.js";
14
+ import { r as formatCodexDisplayText } from "./command-formatters-C54Yg3eA.js";
15
+ import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, r as resolveCodexEffectiveSessionPermissionPolicy } from "./session-permission-policy-gLm40Vtq.js";
16
+ import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-CCQFs8lf.js";
17
+ import { n as prepareCodexAppServerAuthBinding } from "./auth-binding-Ds_QVm-X.js";
18
+ 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-CwFyoylz.js";
21
+ import { C as shouldRequireCodexSandboxExecServerEnvironment, D as CodexNativeToolLifecycleProjector, E as readBoundedCodexRemoteWorkspaceFile, S as shouldEnableCodexAppServerNativeToolSurface, _ as resolveCodexAppServerHookChannelId, a as emitDynamicToolStartedDiagnostic, c as resolveCodexProviderWebSearchSupport, d as releaseCodexSandboxExecServerEnvironment, f as buildDynamicTools, g as resolveCodexAppServerExecutionCwd, h as formatCodexDynamicToolBuildStageSummary, i as emitDynamicToolErrorDiagnostic, m as disableCodexPluginThreadConfig, n as projectCodexExecutableDynamicTools, o as emitDynamicToolTerminalDiagnostic, p as createCodexDynamicToolBuildStageTracker, r as routeCodexAppServerElicitationRequest, s as handleCodexAppServerApprovalRequest, t as createCodexDynamicToolBridge, u as ensureCodexSandboxExecServerEnvironment, v as resolveCodexExternalSandboxPolicyForOpenClawSandbox, w as shouldWarnCodexDynamicToolBuildStageSummary, x as resolveCodexSandboxEnvironmentSelection, y as resolveCodexMessageToolProvider } from "./dynamic-tools-BFoUcRdx.js";
22
+ import { a as runCodexComputerUseLiveTest, n as ensureCodexComputerUse, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-CulOABwu.js";
22
23
  import { asBoolean, asFiniteNumber, asOptionalRecord, hasNonEmptyString, normalizeOptionalString, readNonBlankString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
23
24
  import fs from "node:fs/promises";
24
25
  import path from "node:path";
25
26
  import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
26
27
  import { asDateTimestampMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
27
- import { AgentHarnessPreflightError, CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, agentHarnessAttemptTerminal, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildAgentHarnessUserInputAnswers, buildBootstrapContextForFiles, buildHarnessContextEngineRuntimeContext, buildWatchedSessionsHarnessContext, callGatewayTool, cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, deliverAgentHarnessUserInputPrompt, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, getBeforeToolCallPolicyDiagnosticState, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, prepareHarnessNativeMcpAppPreview, projectAgentHarnessTranscriptMessageForDisplay, resolveAgentDir, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveBootstrapFilesForRun, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveSessionAgentIds, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessAfterCompactionHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessGatewayQuestion, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
28
+ import { AgentHarnessPreflightError, CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, agentHarnessAttemptTerminal, agentHarnessStructuredInput, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildBootstrapContextForFiles, buildEmbeddedForegroundPromptContext, buildHarnessContextEngineRuntimeContext, buildWatchedSessionsHarnessContext, callGatewayTool, cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, detectAndLoadAgentHarnessPromptImages, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, hasBeforeToolCallPolicy, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, prepareHarnessNativeMcpAppPreview, projectAgentHarnessTranscriptMessageForDisplay, resolveAgentDir, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveAttemptFsWorkspaceOnly, resolveBootstrapFilesForRun, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessAfterCompactionHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
28
29
  import crypto, { createHash } from "node:crypto";
29
30
  import { markAuthProfileBlockedUntil, resolveAgentDir as resolveAgentDir$1, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
31
+ import { resolveAgentConfig as resolveAgentConfig$1, resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
30
32
  import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
31
33
  import { parseSqliteSessionFileMarker } from "openclaw/plugin-sdk/session-store-runtime";
32
- import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
33
- import { captureFinalCodexCronCreatorToolAllowlist, materializeStaticMcpToolsForScheduledHarnessRun, resolveCodexMcpToolOverridesForAgent } from "openclaw/plugin-sdk/codex-mcp-projection";
34
+ import { addAbortListener } from "node:events";
35
+ import { truncateUtf16Safe, truncateUtf8Prefix } from "openclaw/plugin-sdk/text-utility-runtime";
34
36
  import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
37
+ import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
38
+ import { captureFinalCodexCronCreatorToolAllowlist, materializeStaticMcpToolsForScheduledHarnessRun, resolveCodexMcpToolOverridesForAgent } from "openclaw/plugin-sdk/codex-mcp-projection";
39
+ import { hasPromptImageInput } from "openclaw/plugin-sdk/session-transcript-runtime";
40
+ import { Buffer as Buffer$1 } from "node:buffer";
41
+ import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
35
42
  import { prepareMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";
43
+ import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-local-roots";
44
+ import { isSilentReplyPayloadText } from "openclaw/plugin-sdk/reply-chunking";
36
45
  import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
37
46
  import { resolveGeneratedMediaMaxBytes } from "openclaw/plugin-sdk/media-generation-runtime";
38
47
  import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
39
- import { root } from "openclaw/plugin-sdk/file-access-runtime";
48
+ import { stripInvisibleUnicode } from "openclaw/plugin-sdk/web-content-extractor";
40
49
  import { MESSAGE_TOOL_DELIVERY_HINTS } from "openclaw/plugin-sdk/message-tool-delivery-hints";
41
50
  //#region extensions/codex/src/app-server/attempt-steering.ts
42
51
  /**
@@ -63,6 +72,12 @@ function createCodexSteeringQueue(params) {
63
72
  let sendChain = Promise.resolve();
64
73
  let sealedError;
65
74
  let closedError;
75
+ const assertActive = () => {
76
+ const unavailableError = closedError ?? sealedError;
77
+ if (unavailableError) throw unavailableError;
78
+ params.signal.throwIfAborted();
79
+ params.assertActive();
80
+ };
66
81
  const clearBatchTimer = () => {
67
82
  if (batchTimer) {
68
83
  clearTimeout(batchTimer);
@@ -115,19 +130,20 @@ function createCodexSteeringQueue(params) {
115
130
  const sendBatch = async (items) => {
116
131
  const liveItems = items.filter((item) => !item.settled);
117
132
  if (liveItems.length === 0) return;
118
- const unavailableError = closedError ?? sealedError ?? (params.signal.aborted ? /* @__PURE__ */ new Error("codex app-server steering queue aborted") : void 0);
119
- if (unavailableError) {
120
- for (const item of liveItems) rejectItem(item, unavailableError);
121
- throw unavailableError;
122
- }
123
- const clientUserMessageId = `openclaw:${params.turnId}:steer:${++batchSequence}`;
124
- const batch = { items: liveItems };
125
- dispatchedBatches.set(clientUserMessageId, batch);
133
+ let clientUserMessageId;
126
134
  try {
135
+ assertActive();
136
+ const input = [];
137
+ for (const item of liveItems) {
138
+ input.push(...await params.prepareMessage(item.text, item));
139
+ assertActive();
140
+ }
141
+ clientUserMessageId = `openclaw:${params.turnId}:steer:${++batchSequence}`;
142
+ dispatchedBatches.set(clientUserMessageId, { items: liveItems });
127
143
  await params.client.request("turn/steer", {
128
144
  threadId: params.threadId,
129
145
  expectedTurnId: params.turnId,
130
- input: liveItems.flatMap((item) => buildCodexUserInput(item.text, item.images)),
146
+ input,
131
147
  clientUserMessageId
132
148
  }, {
133
149
  timeoutMs: params.requestTimeoutMs,
@@ -135,8 +151,8 @@ function createCodexSteeringQueue(params) {
135
151
  });
136
152
  for (const item of liveItems) reportItemAcceptance(item, true);
137
153
  } catch (error) {
138
- dispatchedBatches.delete(clientUserMessageId);
139
- const acceptedUnconfirmed = isCodexAppServerIndeterminateRequestCancellationError(error) || isCodexAppServerIndeterminateTransportError(error);
154
+ if (clientUserMessageId) dispatchedBatches.delete(clientUserMessageId);
155
+ const acceptedUnconfirmed = clientUserMessageId !== void 0 && (isCodexAppServerIndeterminateRequestCancellationError(error) || isCodexAppServerIndeterminateTransportError(error));
140
156
  for (const item of liveItems) {
141
157
  if (acceptedUnconfirmed) reportItemAcceptance(item, true);
142
158
  rejectItem(item, error);
@@ -170,10 +186,9 @@ function createCodexSteeringQueue(params) {
170
186
  rejectDelivery = reject;
171
187
  });
172
188
  const item = {
189
+ ...options,
173
190
  acceptance: "open",
174
191
  text,
175
- images: options?.images,
176
- onQueueAccepted: options?.onQueueAccepted,
177
192
  resolve: resolveDelivery,
178
193
  reject: rejectDelivery,
179
194
  settled: false
@@ -188,23 +203,11 @@ function createCodexSteeringQueue(params) {
188
203
  if (params.signal.aborted) abortQueue();
189
204
  return {
190
205
  async queue(text, options) {
191
- const unavailableError = closedError ?? sealedError ?? (params.signal.aborted ? /* @__PURE__ */ new Error("codex app-server steering queue aborted") : void 0);
192
- if (unavailableError) {
206
+ try {
207
+ assertActive();
208
+ } catch (error) {
193
209
  options?.onQueueAccepted?.(false);
194
- throw unavailableError;
195
- }
196
- const pendingUserInput = options?.isInboundUserMessage === true ? params.claimPendingUserInput() : void 0;
197
- if (pendingUserInput) {
198
- if (!options?.images?.length) {
199
- const answered = pendingUserInput.answer(text);
200
- options?.onQueueAccepted?.(answered);
201
- if (!answered) throw new Error("codex pending user input rejected the answer");
202
- return;
203
- }
204
- flushBatch().catch(() => void 0);
205
- const { item, delivery } = createPendingMessage(text, options);
206
- await Promise.all([enqueueSend([item]).finally(() => pendingUserInput.cancel()), delivery]);
207
- return;
210
+ throw error;
208
211
  }
209
212
  const { item, delivery } = createPendingMessage(text, options);
210
213
  batchedMessages.push(item);
@@ -221,8 +224,21 @@ function createCodexSteeringQueue(params) {
221
224
  const batch = dispatchedBatches.get(clientUserMessageId);
222
225
  if (!batch) return false;
223
226
  dispatchedBatches.delete(clientUserMessageId);
224
- for (const item of batch.items) resolveItem(item);
225
- return true;
227
+ for (const item of batch.items) reportItemAcceptance(item, true);
228
+ const resolveBatch = () => {
229
+ for (const item of batch.items) resolveItem(item);
230
+ return true;
231
+ };
232
+ const rejectBatch = (error) => {
233
+ for (const item of batch.items) rejectItem(item, error);
234
+ return true;
235
+ };
236
+ if (!params.beforeConfirmConsumed) return resolveBatch();
237
+ try {
238
+ return params.beforeConfirmConsumed(batch.items).then(resolveBatch, rejectBatch);
239
+ } catch (error) {
240
+ return rejectBatch(error);
241
+ }
226
242
  },
227
243
  sealAdmission: sealQueueAdmission,
228
244
  cancel: cancelQueue
@@ -235,15 +251,17 @@ function normalizeCodexSteerDebounceMs(value) {
235
251
  //#endregion
236
252
  //#region extensions/codex/src/app-server/event-projector-assistant.ts
237
253
  var CodexAssistantProjection = class {
238
- constructor(params, emitAgentEvent, matchesToolProgressEcho, nextTranscriptTimestamp) {
254
+ constructor(params, emitAgentEvent, matchesToolProgressEcho, nextTranscriptTimestamp, checkpointCommentary) {
239
255
  this.params = params;
240
256
  this.emitAgentEvent = emitAgentEvent;
241
257
  this.matchesToolProgressEcho = matchesToolProgressEcho;
242
258
  this.nextTranscriptTimestamp = nextTranscriptTimestamp;
259
+ this.checkpointCommentary = checkpointCommentary;
243
260
  this.assistantTextByItem = /* @__PURE__ */ new Map();
244
261
  this.assistantItemOrder = [];
245
262
  this.assistantTimestampByItem = /* @__PURE__ */ new Map();
246
263
  this.assistantPhaseByItem = /* @__PURE__ */ new Map();
264
+ this.assistantDeliveryByItem = /* @__PURE__ */ new Map();
247
265
  this.latestTerminalAssistantCandidateSuperseded = false;
248
266
  this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
249
267
  this.terminalAssistantCandidateEarlierActiveItemIds = /* @__PURE__ */ new Set();
@@ -253,6 +271,10 @@ var CodexAssistantProjection = class {
253
271
  this.rawPromotedAssistantItemIds = /* @__PURE__ */ new Set();
254
272
  this.assistantStarted = false;
255
273
  this.streamedPartialAssistantItemReplaceable = false;
274
+ this.persistableAssistantBarrier = 0;
275
+ this.persistedAssistantBoundary = false;
276
+ this.persistableAssistantBarrierItemIds = /* @__PURE__ */ new Set();
277
+ this.completedAssistantItemIds = /* @__PURE__ */ new Set();
256
278
  }
257
279
  hasCompletedTerminalAssistantText(completedItemIds) {
258
280
  const latestCompletedItemId = this.latestCompletedTerminalAssistantItemId;
@@ -284,7 +306,8 @@ var CodexAssistantProjection = class {
284
306
  if (!delta) return;
285
307
  if (itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
286
308
  const isCommentary = this.isCommentaryAssistantItem(itemId);
287
- if (!isCommentary && itemId !== this.latestTerminalAssistantCandidateItemId) this.markTerminalAssistantCandidateSupersededBy();
309
+ const isAsync = this.isAsyncAssistantItem(itemId);
310
+ if (!isCommentary && !isAsync && itemId !== this.latestTerminalAssistantCandidateItemId) this.markTerminalAssistantCandidateSupersededBy();
288
311
  if (!this.assistantStarted) {
289
312
  this.assistantStarted = true;
290
313
  await this.params.onAssistantMessageStart?.();
@@ -292,6 +315,7 @@ var CodexAssistantProjection = class {
292
315
  this.rememberAssistantItem(itemId);
293
316
  const text = `${this.assistantTextByItem.get(itemId) ?? ""}${delta}`;
294
317
  this.assistantTextByItem.set(itemId, text);
318
+ if (isAsync) return;
295
319
  if (isCommentary) {
296
320
  this.emitCommentaryProgress({
297
321
  itemId,
@@ -322,23 +346,26 @@ var CodexAssistantProjection = class {
322
346
  if (knownFinalAnswer && !replaceable) await this.params.onPartialReply?.(streamPayload);
323
347
  }
324
348
  recordItemStarted(item, itemId) {
325
- if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
349
+ this.noteNativeWorkBarrier(item);
326
350
  this.rememberAssistantPhase(item);
351
+ if (item?.type === "agentMessage" && itemId && !this.isNonTerminalAssistantItem(itemId) && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
327
352
  if (item?.type === "agentMessage" && itemId) this.rememberAssistantItem(itemId);
328
- if (itemId && itemId !== this.latestTerminalAssistantCandidateItemId) {
353
+ if (itemId && !this.isNonTerminalAssistantItem(itemId) && itemId !== this.latestTerminalAssistantCandidateItemId) {
329
354
  this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
330
355
  if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
331
356
  }
332
357
  }
333
358
  recordItemCompleted(item, itemId, activeItemIds) {
334
- if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
335
- if (itemId) this.latestCompletedItemId = itemId;
359
+ if (itemId && item?.type === "agentMessage") this.completedAssistantItemIds.add(itemId);
360
+ this.noteNativeWorkBarrier(item);
336
361
  this.rememberAssistantPhase(item);
337
- if (item?.type === "agentMessage" && !this.isCommentaryAssistantItem(item.id)) {
362
+ if (item?.type === "agentMessage" && itemId && !this.isNonTerminalAssistantItem(itemId) && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
363
+ if (itemId && !this.isNonTerminalAssistantItem(itemId)) this.latestCompletedItemId = itemId;
364
+ if (item?.type === "agentMessage" && !this.isNonTerminalAssistantItem(item.id)) {
338
365
  this.latestCompletedTerminalAssistantItemId = item.id;
339
366
  this.markLatestTerminalAssistantCandidate(item.id, activeItemIds);
340
367
  this.pendingRawTerminalAssistantEchoItemId = item.id;
341
- } else if (itemId) {
368
+ } else if (itemId && !this.isNonTerminalAssistantItem(itemId)) {
342
369
  this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
343
370
  if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
344
371
  }
@@ -351,14 +378,17 @@ var CodexAssistantProjection = class {
351
378
  text: item.text
352
379
  });
353
380
  this.pendingRawCommentaryEchoes += 1;
354
- } else if (item.text && this.isFinalAnswerAssistantItem(item.id)) this.emitAnswerCandidate(item.id, "candidate");
381
+ } else if (item.text && !this.isAsyncAssistantItem(item.id) && this.isFinalAnswerAssistantItem(item.id)) this.emitAnswerCandidate(item.id, "candidate");
382
+ return this.createAsyncDelivery(item.id);
355
383
  }
356
384
  }
357
385
  recordSnapshotItem(item) {
386
+ if (item.type === "agentMessage") this.completedAssistantItemIds.add(item.id);
358
387
  this.rememberAssistantPhase(item);
359
388
  if (item.type === "agentMessage" && typeof item.text === "string") {
360
389
  this.rememberAssistantItem(item.id);
361
390
  this.assistantTextByItem.set(item.id, item.text);
391
+ return this.createAsyncDelivery(item.id);
362
392
  }
363
393
  }
364
394
  handleRawResponseItemCompleted(item, activeItemIds) {
@@ -392,6 +422,7 @@ var CodexAssistantProjection = class {
392
422
  const isIdlessTerminalAssistantAfterCompletedWork = candidateWasSupersededBeforeRaw && rawItemId === void 0 && pendingTerminalAssistantEchoItemId === void 0 && activeItemIds.size === 0;
393
423
  if (text && phase !== "commentary" && candidateWasSupersededBeforeRaw && itemId !== this.streamedPartialAssistantItemId && !isIdlessTerminalAssistantAfterCompletedWork) return;
394
424
  if (phase) this.assistantPhaseByItem.set(itemId, phase);
425
+ this.completedAssistantItemIds.add(itemId);
395
426
  this.rememberAssistantItem(itemId);
396
427
  this.assistantTextByItem.set(itemId, text);
397
428
  if (!text) return;
@@ -403,21 +434,64 @@ var CodexAssistantProjection = class {
403
434
  else this.markLatestTerminalAssistantCandidate(itemId, activeItemIds, { canReleaseAfterToolHandoff: isIdlessTerminalAssistantAfterCompletedWork });
404
435
  }
405
436
  collectAssistantTexts() {
406
- const finalText = this.resolveFinalAssistantTextItem()?.text;
407
- return finalText ? [finalText] : [];
437
+ const afterHandoff = this.collectPersistableAssistantTexts(this.persistableAssistantBarrier);
438
+ const audibleAfterHandoff = afterHandoff.filter((text) => !isSilentReplyPayloadText(text));
439
+ if (audibleAfterHandoff.length > 0) return audibleAfterHandoff;
440
+ if (afterHandoff.length > 0) return afterHandoff.slice(-1);
441
+ if (this.persistedAssistantBoundary) return [];
442
+ const recoveredAudible = this.collectPersistableAssistantTexts(0).filter((text) => !isSilentReplyPayloadText(text));
443
+ if (recoveredAudible.length > 0) return recoveredAudible.slice(-1);
444
+ const recovered = this.resolveFinalAssistantTextItem()?.text;
445
+ return recovered ? [recovered] : [];
408
446
  }
409
447
  collectCommentaryMessages() {
410
448
  return this.assistantItemOrder.flatMap((itemId) => {
411
- if (!this.isCommentaryAssistantItem(itemId)) return [];
449
+ const message = this.readCommentaryMessage(itemId);
450
+ return message ? [{
451
+ itemId,
452
+ message
453
+ }] : [];
454
+ });
455
+ }
456
+ readCommentaryMessage(itemId) {
457
+ const text = this.assistantTextByItem.get(itemId)?.trim();
458
+ const timestamp = this.assistantTimestampByItem.get(itemId);
459
+ return this.isCommentaryAssistantItem(itemId) && text && timestamp !== void 0 ? createAssistantCommentaryMessage(this.params, text, itemId, timestamp) : void 0;
460
+ }
461
+ collectAsyncMessages() {
462
+ return this.assistantItemOrder.flatMap((itemId) => {
463
+ if (!this.isAsyncAssistantItem(itemId)) return [];
412
464
  const text = this.assistantTextByItem.get(itemId)?.trim();
413
465
  const timestamp = this.assistantTimestampByItem.get(itemId);
414
466
  if (!text || timestamp === void 0) return [];
415
467
  return [{
416
468
  itemId,
417
- message: createAssistantCommentaryMessage(this.params, text, itemId, timestamp)
469
+ message: createAssistantAsyncMessage(this.params, text, itemId, timestamp)
418
470
  }];
419
471
  });
420
472
  }
473
+ collectCompletedAssistantMessages(completedItemIds, options) {
474
+ return this.assistantItemOrder.flatMap((itemId) => {
475
+ const text = this.assistantTextByItem.get(itemId)?.trim();
476
+ if (!completedItemIds.has(itemId) || this.isNonTerminalAssistantItem(itemId) || !text || isSilentReplyPayloadText(text) || this.isToolProgressEchoText(itemId, text)) return [];
477
+ const message = this.createAssistantMessage(text, options);
478
+ const timestamp = this.assistantTimestampByItem.get(itemId) ?? message.timestamp;
479
+ return [{
480
+ itemId,
481
+ message: {
482
+ ...message,
483
+ timestamp
484
+ }
485
+ }];
486
+ });
487
+ }
488
+ markAssistantBoundaryPersisted(itemId) {
489
+ const index = this.assistantItemOrder.indexOf(itemId);
490
+ if (index >= 0) {
491
+ this.persistableAssistantBarrier = Math.max(this.persistableAssistantBarrier, index + 1);
492
+ this.persistedAssistantBoundary = true;
493
+ }
494
+ }
421
495
  finalizeAnswerCandidate(turn) {
422
496
  if (turn.status !== "completed") {
423
497
  this.supersedeVisibleAnswerCandidate();
@@ -427,7 +501,7 @@ var CodexAssistantProjection = class {
427
501
  const authoritativeIndex = turnItems.findLastIndex((item) => {
428
502
  if (item.type !== "agentMessage" || typeof item.text !== "string" || item.text.trim().length === 0) return false;
429
503
  const phase = readItemString(item, "phase");
430
- return phase === "final_answer" || phase === void 0;
504
+ return readItemString(item, "delivery") !== "async" && (phase === "final_answer" || phase === void 0);
431
505
  });
432
506
  const authoritative = authoritativeIndex >= 0 ? turnItems[authoritativeIndex] : void 0;
433
507
  if (turnItems.slice(authoritativeIndex + 1).some(shouldClearTerminalPresentationForNativeItem) || authoritative?.id === this.latestTerminalAssistantCandidateItemId && this.latestTerminalAssistantCandidateSuperseded) {
@@ -445,7 +519,7 @@ var CodexAssistantProjection = class {
445
519
  hasAssistantItemTextForSynthesis() {
446
520
  for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
447
521
  const itemId = this.assistantItemOrder[i];
448
- if (!itemId || this.assistantPhaseByItem.get(itemId) === "commentary") continue;
522
+ if (!itemId || this.isNonTerminalAssistantItem(itemId)) continue;
449
523
  const text = this.assistantTextByItem.get(itemId);
450
524
  if (text && text.length > 0) return true;
451
525
  }
@@ -454,7 +528,7 @@ var CodexAssistantProjection = class {
454
528
  createCurrentAttemptAssistantMessage(options) {
455
529
  for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
456
530
  const itemId = this.assistantItemOrder[i];
457
- if (!itemId || this.isCommentaryAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
531
+ if (!itemId || this.isNonTerminalAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
458
532
  const text = this.assistantTextByItem.get(itemId) ?? "";
459
533
  const normalizedText = text.trim();
460
534
  if (normalizedText && this.isToolProgressEchoText(itemId, normalizedText)) continue;
@@ -475,10 +549,18 @@ var CodexAssistantProjection = class {
475
549
  if (item?.type !== "agentMessage") return;
476
550
  const phase = readItemString(item, "phase");
477
551
  if (phase) this.assistantPhaseByItem.set(item.id, phase);
552
+ const delivery = readItemString(item, "delivery");
553
+ if (delivery) this.assistantDeliveryByItem.set(item.id, delivery);
478
554
  }
479
555
  isCommentaryAssistantItem(itemId) {
480
556
  return this.assistantPhaseByItem.get(itemId) === "commentary";
481
557
  }
558
+ isAsyncAssistantItem(itemId) {
559
+ return this.assistantDeliveryByItem.get(itemId) === "async";
560
+ }
561
+ isNonTerminalAssistantItem(itemId) {
562
+ return this.isCommentaryAssistantItem(itemId) || this.isAsyncAssistantItem(itemId);
563
+ }
482
564
  isFinalAnswerAssistantItem(itemId) {
483
565
  return this.assistantPhaseByItem.get(itemId) === "final_answer";
484
566
  }
@@ -486,7 +568,7 @@ var CodexAssistantProjection = class {
486
568
  return this.assistantPhaseByItem.get(itemId) === "final_answer";
487
569
  }
488
570
  emitCommentaryProgress(params) {
489
- const progressText = params.text.replace(/\s+/g, " ").trim();
571
+ const progressText = params.text.trim();
490
572
  if (!progressText || this.lastCommentaryProgressTextByItem.get(params.itemId) === progressText) return;
491
573
  this.lastCommentaryProgressTextByItem.set(params.itemId, progressText);
492
574
  this.emitAgentEvent({
@@ -553,22 +635,104 @@ var CodexAssistantProjection = class {
553
635
  const itemId = this.assistantItemOrder[i];
554
636
  if (!itemId) continue;
555
637
  const text = this.assistantTextByItem.get(itemId)?.trim();
556
- if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
638
+ if (this.isNonTerminalAssistantItem(itemId)) continue;
557
639
  if (text && !this.isToolProgressEchoText(itemId, text)) return {
558
640
  itemId,
559
641
  text
560
642
  };
561
643
  }
562
644
  }
645
+ collectPersistableAssistantTexts(minIndex) {
646
+ let texts = [];
647
+ let replaceable = false;
648
+ for (let index = minIndex; index < this.assistantItemOrder.length; index += 1) {
649
+ const itemId = this.assistantItemOrder[index];
650
+ if (!itemId || this.isNonTerminalAssistantItem(itemId)) continue;
651
+ const text = this.assistantTextByItem.get(itemId)?.trim();
652
+ if (!text || this.isToolProgressEchoText(itemId, text)) continue;
653
+ if (isSilentReplyPayloadText(text)) {
654
+ texts.push(text);
655
+ continue;
656
+ }
657
+ const isTerminalFinal = this.assistantPhaseByItem.get(itemId) === "final_answer";
658
+ if (!isTerminalFinal || replaceable) {
659
+ texts = [text];
660
+ replaceable = !isTerminalFinal;
661
+ continue;
662
+ }
663
+ texts.push(text);
664
+ }
665
+ return texts;
666
+ }
667
+ noteNativeWorkBarrier(item) {
668
+ if (!item || !shouldAdvancePersistableAssistantBarrier(item)) return;
669
+ if (item.id && this.persistableAssistantBarrierItemIds.has(item.id)) return;
670
+ if (item.id) this.persistableAssistantBarrierItemIds.add(item.id);
671
+ this.persistableAssistantBarrier = this.assistantItemOrder.length;
672
+ }
563
673
  rememberAssistantItem(itemId) {
564
- if (!itemId || this.assistantItemOrder.includes(itemId)) return;
565
- this.assistantItemOrder.push(itemId);
566
- this.assistantTimestampByItem.set(itemId, this.nextTranscriptTimestamp());
674
+ if (!itemId) return;
675
+ if (!this.assistantTimestampByItem.has(itemId)) {
676
+ this.assistantItemOrder.push(itemId);
677
+ this.assistantTimestampByItem.set(itemId, this.nextTranscriptTimestamp());
678
+ }
679
+ if (this.isCommentaryAssistantItem(itemId)) this.checkpointCommentary?.(itemId, {
680
+ read: () => this.readCommentaryMessage(itemId),
681
+ ready: () => this.completedAssistantItemIds.has(itemId)
682
+ });
683
+ }
684
+ createAsyncDelivery(itemId) {
685
+ if (!this.isAsyncAssistantItem(itemId)) return;
686
+ const text = this.assistantTextByItem.get(itemId);
687
+ const timestamp = this.assistantTimestampByItem.get(itemId);
688
+ if (!text?.trim() || timestamp === void 0) return;
689
+ return {
690
+ itemId,
691
+ message: createAssistantAsyncMessage(this.params, text, itemId, timestamp),
692
+ text
693
+ };
567
694
  }
568
695
  isToolProgressEchoText(itemId, text) {
569
696
  return this.rawPromotedAssistantItemIds.has(itemId) && this.matchesToolProgressEcho(text);
570
697
  }
571
698
  };
699
+ function shouldAdvancePersistableAssistantBarrier(item) {
700
+ return shouldClearTerminalPresentationForNativeItem(item) || item.type === "dynamicToolCall" || item.type === "sleep";
701
+ }
702
+ //#endregion
703
+ //#region extensions/codex/src/app-server/event-projector-async-delivery.ts
704
+ /** Owns authorization, exactly-once settlement, and terminal retry for async user messages. */
705
+ var CodexAsyncDeliveryProjection = class {
706
+ constructor(params, threadId, turnId, options) {
707
+ this.params = params;
708
+ this.threadId = threadId;
709
+ this.turnId = turnId;
710
+ this.options = options;
711
+ this.settledItemIds = /* @__PURE__ */ new Set();
712
+ this.pendingDeliveries = /* @__PURE__ */ new Map();
713
+ }
714
+ allows(item, reportUnauthorized = false) {
715
+ if (item?.type !== "agentMessage" || item.delivery !== "async") return true;
716
+ const allowed = this.params.disableTools !== true && (this.options.asyncUserMessageAllowed ?? (this.params.toolsAllow === void 0 || this.params.toolsAllow.some((name) => name === "*" || name === "message")));
717
+ if (!allowed && reportUnauthorized) embeddedAgentLog.warn("blocked unauthorized codex async user message", {
718
+ itemId: item.id,
719
+ threadId: this.threadId,
720
+ turnId: this.turnId
721
+ });
722
+ return allowed;
723
+ }
724
+ pending() {
725
+ return [...this.pendingDeliveries.values()];
726
+ }
727
+ async deliver(delivery) {
728
+ if (this.settledItemIds.has(delivery.itemId)) return;
729
+ const settlement = await this.options.onAsyncDelivery?.(delivery);
730
+ if (settlement === "settled") {
731
+ this.settledItemIds.add(delivery.itemId);
732
+ this.pendingDeliveries.delete(delivery.itemId);
733
+ } else if (settlement === "retry") this.pendingDeliveries.set(delivery.itemId, delivery);
734
+ }
735
+ };
572
736
  //#endregion
573
737
  //#region extensions/codex/src/app-server/event-projector-media.ts
574
738
  const GENERATED_IMAGE_MEDIA_SUBDIR = "tool-image-generation";
@@ -739,9 +903,10 @@ function isBase64WhitespaceCode(code) {
739
903
  //#endregion
740
904
  //#region extensions/codex/src/app-server/event-projector-reasoning.ts
741
905
  var CodexReasoningProjection = class {
742
- constructor(params, emitAgentEvent) {
906
+ constructor(params, emitAgentEvent, onNativePlanUpdate) {
743
907
  this.params = params;
744
908
  this.emitAgentEvent = emitAgentEvent;
909
+ this.onNativePlanUpdate = onNativePlanUpdate;
745
910
  this.reasoningTextByGroup = /* @__PURE__ */ new Map();
746
911
  this.reasoningItemOrder = /* @__PURE__ */ new Map();
747
912
  this.planTextByItem = /* @__PURE__ */ new Map();
@@ -782,7 +947,7 @@ var CodexReasoningProjection = class {
782
947
  }))
783
948
  });
784
949
  }
785
- handleTurnPlanUpdated(params, source = "codex-app-server") {
950
+ async handleTurnPlanUpdated(params, source = "codex-app-server") {
786
951
  const explanation = readNullableString(params, "explanation");
787
952
  const plan = Array.isArray(params.plan) ? params.plan.flatMap((entry) => {
788
953
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
@@ -796,6 +961,10 @@ var CodexReasoningProjection = class {
796
961
  }) : void 0;
797
962
  const planText = [explanation, ...(plan ?? []).map(({ step, status }) => `- [${status}] ${step}`)].filter((part) => Boolean(part)).join("\n");
798
963
  if (planText) this.turnPlanText = planText;
964
+ if (source === "codex-app-server" && plan) await this.onNativePlanUpdate?.({
965
+ ...typeof explanation === "string" ? { markdown: explanation } : {},
966
+ steps: plan
967
+ });
799
968
  this.emitPlanUpdate({
800
969
  explanation,
801
970
  steps: plan
@@ -825,7 +994,7 @@ var CodexReasoningProjection = class {
825
994
  return this.turnPlanText ?? [...this.planTextByItem.values()].filter((text) => text.trim().length > 0).join("\n\n");
826
995
  }
827
996
  emitPlanUpdate(params, source = "codex-app-server") {
828
- if (!params.explanation && (!params.steps || params.steps.length === 0)) return;
997
+ if (!params.explanation && params.steps === void 0) return;
829
998
  this.emitAgentEvent({
830
999
  stream: "plan",
831
1000
  data: {
@@ -833,7 +1002,7 @@ var CodexReasoningProjection = class {
833
1002
  title: "Plan updated",
834
1003
  source,
835
1004
  ...params.explanation ? { explanation: params.explanation } : {},
836
- ...params.steps && params.steps.length > 0 ? { steps: params.steps } : {}
1005
+ ...params.steps ? { steps: params.steps } : {}
837
1006
  }
838
1007
  });
839
1008
  }
@@ -858,50 +1027,64 @@ function reasoningMethodOrder(method) {
858
1027
  const attemptTerminal = agentHarnessAttemptTerminal;
859
1028
  //#endregion
860
1029
  //#region extensions/codex/src/app-server/event-projector-snapshot.ts
861
- function readTurnTaintMetadata(message) {
862
- const metadata = message["__openclaw"];
863
- return metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata : void 0;
864
- }
865
- function applyStickyTurnTaint(messages) {
866
- let tainted = false;
867
- return messages.map((message) => {
868
- if (message.role === "user") {
869
- tainted = false;
870
- return message;
871
- }
872
- const metadata = readTurnTaintMetadata(message);
873
- tainted ||= metadata?.turnTainted === true || metadata?.resultContentSource === "network";
874
- return message.role === "assistant" && tainted ? {
875
- ...message,
876
- __openclaw: {
877
- ...metadata,
878
- turnTainted: true
879
- }
880
- } : message;
881
- });
882
- }
883
1030
  function buildCodexMessagesSnapshot(params) {
884
1031
  const messages = promptSnapshot(params.runParams, params.turnId, params.upstreamUserText);
885
1032
  if (params.reasoningText) messages.push(attachCodexMirrorIdentity(params.createAssistantMirrorMessage("Codex reasoning", params.reasoningText), `${params.turnId}:reasoning`));
886
- if (params.planText) messages.push(attachCodexMirrorIdentity(params.createAssistantMirrorMessage("Codex plan", params.planText), `${params.turnId}:plan`));
887
- const visibleWorkMessages = [...params.runParams.config?.ui?.prefs?.chatPersistCommentary === false ? [] : params.commentaryMessages.map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:commentary:${itemId}`)), ...params.toolMessages].toSorted((left, right) => (asDateTimestampMs(left.timestamp) ?? 0) - (asDateTimestampMs(right.timestamp) ?? 0));
1033
+ const commentaryMessages = params.runParams.config?.ui?.prefs?.chatPersistCommentary === false ? [] : params.commentaryMessages.map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:commentary:${itemId}`));
1034
+ const asyncMessages = params.asyncMessages.map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:async:${itemId}`));
1035
+ const visibleWorkMessages = [
1036
+ ...commentaryMessages,
1037
+ ...asyncMessages,
1038
+ ...(params.assistantMessages ?? []).map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:assistant:${itemId}`)),
1039
+ ...params.toolMessages
1040
+ ].toSorted((left, right) => (asDateTimestampMs(left.timestamp) ?? 0) - (asDateTimestampMs(right.timestamp) ?? 0));
888
1041
  messages.push(...visibleWorkMessages);
889
1042
  if (params.lastAssistant) messages.push(attachCodexMirrorIdentity(params.lastAssistant, `${params.turnId}:assistant`));
890
- return applyStickyTurnTaint(messages).map((message) => projectAgentHarnessTranscriptMessageForDisplay({
1043
+ const taint = { tainted: false };
1044
+ return messages.map((message) => projectAgentHarnessTranscriptMessageForDisplay({
891
1045
  hidden: params.runParams.trigger === "memory",
892
- message
1046
+ message: applyCodexTranscriptTaint(message, taint)
893
1047
  }));
894
1048
  }
1049
+ function buildCodexSteeringMessagesSnapshot(params) {
1050
+ const asyncMessages = params.assistantProjection.collectAsyncMessages().filter(({ itemId }) => params.completedItemIds.has(itemId));
1051
+ const commentaryMessages = params.assistantProjection.collectCommentaryMessages().filter(({ itemId }) => params.completedItemIds.has(itemId));
1052
+ const assistantMessages = params.assistantProjection.collectCompletedAssistantMessages(params.completedItemIds, {
1053
+ tokenUsage: void 0,
1054
+ aborted: false,
1055
+ promptError: void 0
1056
+ });
1057
+ return {
1058
+ messages: buildCodexMessagesSnapshot({
1059
+ runParams: params.runParams,
1060
+ turnId: params.turnId,
1061
+ upstreamUserText: params.upstreamUserText,
1062
+ reasoningText: void 0,
1063
+ asyncMessages,
1064
+ commentaryMessages,
1065
+ assistantMessages,
1066
+ toolMessages: params.toolMessages,
1067
+ lastAssistant: void 0,
1068
+ createAssistantMirrorMessage: (title, text) => params.assistantProjection.createAssistantMirrorMessage(title, text)
1069
+ }).filter((message) => message.role !== "user"),
1070
+ assistantBoundaryItemId: assistantMessages.at(-1)?.itemId
1071
+ };
1072
+ }
895
1073
  //#endregion
896
1074
  //#region extensions/codex/src/app-server/event-projector-result.ts
897
1075
  function buildCodexAttemptResult(input) {
898
1076
  input.nativeToolLifecycleProjection.finalizeActive();
899
1077
  const assistantTexts = input.assistantProjection.collectAssistantTexts();
1078
+ const asyncMessages = input.assistantProjection.collectAsyncMessages();
900
1079
  const commentaryMessages = input.assistantProjection.collectCommentaryMessages();
901
1080
  const reasoningText = input.reasoningProjection.reasoningText();
902
1081
  const planText = input.reasoningProjection.planText();
903
- const completedUsage = input.responseCompletions.usage ?? input.tokenUsage;
904
- const projectedUsage = input.aborted ? input.tokenUsage : completedUsage;
1082
+ const unavailableThreadUsage = input.tokenUsage ? {
1083
+ ...input.tokenUsage,
1084
+ contextUsage: { state: "unavailable" }
1085
+ } : void 0;
1086
+ const completedUsage = input.responseCompletions.usage ?? (input.responseCompletions.modelIterations > 0 ? unavailableThreadUsage : input.tokenUsage);
1087
+ const projectedUsage = input.aborted ? unavailableThreadUsage : completedUsage;
905
1088
  const hasAssistantItemText = input.assistantProjection.hasAssistantItemTextForSynthesis();
906
1089
  const legacyFailClosed = !input.completedTurn || input.completedTurn.status !== "completed" || hasAssistantItemText;
907
1090
  const hasDeliverableAssistantOnCompletedTurn = input.completedTurn?.status === "completed" && assistantTexts.some((text) => text.trim().length > 0);
@@ -927,7 +1110,7 @@ function buildCodexAttemptResult(input) {
927
1110
  turnId: input.turnId,
928
1111
  upstreamUserText: input.upstreamUserText,
929
1112
  reasoningText,
930
- planText,
1113
+ asyncMessages,
931
1114
  commentaryMessages,
932
1115
  toolMessages: input.toolTranscriptProjection.transcriptMessages,
933
1116
  lastAssistant,
@@ -976,6 +1159,7 @@ function buildCodexAttemptResult(input) {
976
1159
  acceptedSessionSpawns: input.toolTelemetry.acceptedSessionSpawns,
977
1160
  cloudCodeAssistFormatError: false,
978
1161
  contextTokens: input.contextTokens,
1162
+ contextTokensSource: input.contextTokensSource,
979
1163
  attemptUsage: projectedUsage,
980
1164
  ...input.completedCompactionCount > 0 ? { compactionCount: input.completedCompactionCount } : {},
981
1165
  replayMetadata: {
@@ -992,6 +1176,78 @@ function buildCodexAttemptResult(input) {
992
1176
  };
993
1177
  }
994
1178
  //#endregion
1179
+ //#region extensions/codex/src/app-server/transcript-checkpoint.ts
1180
+ /** Commits completed work in receipt order without waiting for the enclosing turn. */
1181
+ var CodexTranscriptCheckpoint = class {
1182
+ constructor(params, threadId, turnId) {
1183
+ this.params = params;
1184
+ this.threadId = threadId;
1185
+ this.turnId = turnId;
1186
+ this.pending = [];
1187
+ this.commentaryItemIds = /* @__PURE__ */ new Set();
1188
+ this.lastTimestamp = 0;
1189
+ this.writing = Promise.resolve();
1190
+ this.tainted = false;
1191
+ this.closed = false;
1192
+ this.nextTimestamp = () => {
1193
+ this.lastTimestamp = Math.max(Date.now(), this.lastTimestamp + 1);
1194
+ return this.lastTimestamp;
1195
+ };
1196
+ this.enqueueCommentary = (itemId, entry) => {
1197
+ if (this.params.config?.ui?.prefs?.chatPersistCommentary === false || this.commentaryItemIds.has(itemId)) return;
1198
+ this.commentaryItemIds.add(itemId);
1199
+ this.enqueue({
1200
+ ...entry,
1201
+ read: () => {
1202
+ const message = entry.read();
1203
+ return message ? attachCodexMirrorIdentity(message, `${this.turnId}:commentary:${itemId}`) : void 0;
1204
+ }
1205
+ });
1206
+ };
1207
+ this.enqueue = (entry) => {
1208
+ if (this.params.sessionTarget && !this.closed) this.pending.push(entry);
1209
+ };
1210
+ }
1211
+ flush(close = false) {
1212
+ if (this.closed) return this.writing;
1213
+ this.closed = close;
1214
+ this.writing = this.writing.then(async () => {
1215
+ const blocked = close ? -1 : this.pending.findIndex((entry) => entry.ready?.() === false);
1216
+ const count = blocked < 0 ? this.pending.length : blocked;
1217
+ if (count === 0) return;
1218
+ const taint = { tainted: this.tainted };
1219
+ const messages = this.pending.slice(0, count).flatMap((entry) => {
1220
+ const message = entry.read();
1221
+ return message ? [projectAgentHarnessTranscriptMessageForDisplay({
1222
+ hidden: this.params.trigger === "memory",
1223
+ message: applyCodexTranscriptTaint(message, taint)
1224
+ })] : [];
1225
+ });
1226
+ try {
1227
+ await codexTranscriptMirrorRuntime.mirror({
1228
+ ...this.params.sessionTarget,
1229
+ sessionId: this.params.sessionId,
1230
+ cwd: this.params.workspaceDir,
1231
+ messages,
1232
+ idempotencyScope: `codex-app-server:${this.threadId}`,
1233
+ runId: this.params.runId,
1234
+ runMirrorIdentityPrefix: `${this.turnId}:`,
1235
+ config: this.params.config
1236
+ });
1237
+ this.pending.splice(0, count);
1238
+ this.tainted = taint.tainted;
1239
+ } catch (error) {
1240
+ embeddedAgentLog.warn("failed to checkpoint codex app-server transcript", {
1241
+ runId: this.params.runId,
1242
+ sessionId: this.params.sessionId,
1243
+ error: formatErrorMessage(error)
1244
+ });
1245
+ }
1246
+ });
1247
+ return this.writing;
1248
+ }
1249
+ };
1250
+ //#endregion
995
1251
  //#region extensions/codex/src/app-server/usage-limit-error.ts
996
1252
  /**
997
1253
  * Enriches Codex usage-limit failures with current rate-limit information and
@@ -1146,8 +1402,10 @@ var CodexAppServerEventProjector = class {
1146
1402
  this.aborted = false;
1147
1403
  this.responseCompletions = new CodexResponseCompletionProjection();
1148
1404
  this.completedCompactionCount = 0;
1149
- this.lastTranscriptTimestamp = 0;
1405
+ this.transcriptCheckpoint = new CodexTranscriptCheckpoint(params, threadId, turnId);
1406
+ this.asyncDeliveryProjection = new CodexAsyncDeliveryProjection(params, threadId, turnId, options);
1150
1407
  this.contextTokens = options.initialContextTokens;
1408
+ this.contextTokensSource = options.initialContextTokens === void 0 ? void 0 : "resolved";
1151
1409
  this.diagnostics = new CodexProjectionDiagnostics(threadId, turnId);
1152
1410
  this.nativeToolLifecycleProjector = new CodexNativeToolLifecycleProjector(params, threadId, turnId, { runAbortSignal: options.runAbortSignal });
1153
1411
  this.generatedMediaProjection = new CodexGeneratedMediaProjection(params.config, {
@@ -1157,22 +1415,38 @@ var CodexAppServerEventProjector = class {
1157
1415
  signal: options.runAbortSignal
1158
1416
  });
1159
1417
  this.toolProgressProjection = new CodexToolProgressProjection(params);
1160
- this.toolTranscriptProjection = new CodexToolTranscriptProjection(params, threadId, turnId, this.toolProgressProjection, () => this.nextTranscriptTimestamp(), {
1418
+ this.toolTranscriptProjection = new CodexToolTranscriptProjection(params, threadId, turnId, this.toolProgressProjection, this.transcriptCheckpoint.nextTimestamp, {
1161
1419
  nativePostToolUseRelayEnabled: options.nativePostToolUseRelayEnabled,
1162
1420
  prepareNativeMcpAppResultDetails: options.prepareNativeMcpAppResultDetails,
1163
- trajectoryRecorder: options.trajectoryRecorder
1421
+ trajectoryRecorder: options.trajectoryRecorder,
1422
+ checkpointMessage: this.transcriptCheckpoint.enqueue
1164
1423
  });
1165
1424
  this.eventProjection = new CodexEventProjection(threadId, turnId, (event) => this.emitAgentEvent(event), this.toolProgressProjection, this.toolTranscriptProjection, options.onNativeToolResultRecorded);
1166
- this.assistantProjection = new CodexAssistantProjection(params, (event) => this.emitAgentEvent(event), (text) => this.toolProgressProjection.matchesEcho(text), () => this.nextTranscriptTimestamp());
1167
- this.reasoningProjection = new CodexReasoningProjection(params, (event) => this.emitAgentEvent(event));
1168
- }
1169
- nextTranscriptTimestamp() {
1170
- this.lastTranscriptTimestamp = Math.max(Date.now(), this.lastTranscriptTimestamp + 1);
1171
- return this.lastTranscriptTimestamp;
1425
+ this.assistantProjection = new CodexAssistantProjection(params, (event) => this.emitAgentEvent(event), (text) => this.toolProgressProjection.matchesEcho(text), this.transcriptCheckpoint.nextTimestamp, this.transcriptCheckpoint.enqueueCommentary);
1426
+ this.reasoningProjection = new CodexReasoningProjection(params, (event) => this.emitAgentEvent(event), options.onNativePlanUpdate);
1172
1427
  }
1173
1428
  getCompletedTurnStatus() {
1174
1429
  return this.completedTurn?.status;
1175
1430
  }
1431
+ buildSteeringTranscriptPrefix() {
1432
+ const snapshot = buildCodexSteeringMessagesSnapshot({
1433
+ runParams: this.params,
1434
+ turnId: this.turnId,
1435
+ upstreamUserText: this.options.upstreamUserText,
1436
+ completedItemIds: this.completedItemIds,
1437
+ assistantProjection: this.assistantProjection,
1438
+ toolMessages: this.toolTranscriptProjection.transcriptMessages
1439
+ });
1440
+ this.pendingSteeringAssistantBoundaryItemId = snapshot.assistantBoundaryItemId;
1441
+ return snapshot.messages;
1442
+ }
1443
+ markSteeringTranscriptPersisted() {
1444
+ const itemId = this.pendingSteeringAssistantBoundaryItemId;
1445
+ if (itemId) {
1446
+ this.assistantProjection.markAssistantBoundaryPersisted(itemId);
1447
+ this.pendingSteeringAssistantBoundaryItemId = void 0;
1448
+ }
1449
+ }
1176
1450
  hasCompletedTerminalAssistantText() {
1177
1451
  return this.assistantProjection.hasCompletedTerminalAssistantText(this.completedItemIds);
1178
1452
  }
@@ -1199,8 +1473,9 @@ var CodexAppServerEventProjector = class {
1199
1473
  const ordinal = this.params.allocateToolOutcomeOrdinal?.(item.id);
1200
1474
  if (ordinal !== void 0) this.nativeToolOutcomeOrdinals.set(item.id, ordinal);
1201
1475
  }
1202
- recordNativeToolApprovalFailure(toolCallId, disposition) {
1476
+ recordNativeToolApprovalFailure(toolCallId, disposition, approvalKind) {
1203
1477
  this.nativeToolLifecycleProjector.recordApprovalFailureDisposition(toolCallId, disposition);
1478
+ if (disposition === "timed_out" && approvalKind) this.toolProgressProjection.approvalTimeoutKinds.set(toolCallId, approvalKind);
1204
1479
  }
1205
1480
  recordNativeToolPreToolUseFailure(failure) {
1206
1481
  this.nativeToolLifecycleProjector.recordPreToolUseFailure(failure);
@@ -1210,9 +1485,11 @@ var CodexAppServerEventProjector = class {
1210
1485
  if (!params) return;
1211
1486
  if (notification.method === "hook/started" || notification.method === "hook/completed") {
1212
1487
  if (!this.isHookNotificationForCurrentThread(params)) return;
1213
- } else if (notification.method === "guardianWarning") {
1214
- if (readCodexNotificationThreadId(params) !== this.threadId) return;
1488
+ } else if (notification.method === "guardianWarning" || notification.method === "warning" || notification.method === "configWarning") {
1489
+ const warningThreadId = readCodexNotificationThreadId(params);
1490
+ if (notification.method === "guardianWarning" && warningThreadId !== this.threadId || warningThreadId && warningThreadId !== this.threadId) return;
1215
1491
  } else if (!isCodexNotificationForTurn(params, this.threadId, this.turnId)) return;
1492
+ if (notification.method !== "guardianWarning" && notification.method !== "item/autoApprovalReview/started" && notification.method !== "item/autoApprovalReview/completed") this.eventProjection.flushPendingGuardianWarning();
1216
1493
  this.nativeToolLifecycleProjector.handleNotification(notification);
1217
1494
  this.assistantProjection.handleNotification(notification.method, params);
1218
1495
  switch (notification.method) {
@@ -1227,13 +1504,15 @@ var CodexAppServerEventProjector = class {
1227
1504
  this.reasoningProjection.handlePlanDelta(params);
1228
1505
  break;
1229
1506
  case "turn/plan/updated":
1230
- this.reasoningProjection.handleTurnPlanUpdated(params);
1507
+ await this.reasoningProjection.handleTurnPlanUpdated(params);
1231
1508
  break;
1232
1509
  case "item/started":
1233
1510
  await this.handleItemStarted(params);
1511
+ await this.transcriptCheckpoint.flush();
1234
1512
  break;
1235
1513
  case "item/completed":
1236
1514
  await this.handleItemCompleted(params);
1515
+ await this.transcriptCheckpoint.flush();
1237
1516
  break;
1238
1517
  case "item/commandExecution/outputDelta":
1239
1518
  this.toolProgressProjection.handleOutputDelta(params, "bash");
@@ -1242,18 +1521,28 @@ var CodexAppServerEventProjector = class {
1242
1521
  case "item/autoApprovalReview/completed":
1243
1522
  this.eventProjection.handleGuardianReview(notification.method, params);
1244
1523
  break;
1524
+ case "autoApprovalReview/strictReviewRequired":
1525
+ this.eventProjection.handleStrictReviewRequired(params);
1526
+ break;
1245
1527
  case "guardianWarning":
1246
1528
  this.eventProjection.handleGuardianWarning(params);
1247
1529
  break;
1530
+ case "warning":
1531
+ case "configWarning":
1532
+ this.eventProjection.handleWarning(params);
1533
+ break;
1248
1534
  case "hook/started":
1249
1535
  case "hook/completed":
1250
1536
  this.eventProjection.handleHook(notification.method, params);
1251
1537
  break;
1252
1538
  case "thread/tokenUsage/updated":
1253
1539
  projectCodexThreadUsageUpdate(params, this.tokenUsage, (usage) => this.tokenUsage = usage, (data) => {
1254
- this.contextTokens = data.modelContextWindow ?? this.contextTokens;
1540
+ if (data.modelContextWindow !== void 0) {
1541
+ this.contextTokens = data.modelContextWindow;
1542
+ this.contextTokensSource = this.params.authoredContextTokenCap === void 0 ? "runtime" : "runtime-configured";
1543
+ }
1255
1544
  this.emitAgentEvent({
1256
- stream: "codex_app_server.usage",
1545
+ stream: "usage",
1257
1546
  data
1258
1547
  });
1259
1548
  });
@@ -1266,14 +1555,21 @@ var CodexAppServerEventProjector = class {
1266
1555
  break;
1267
1556
  case "rawResponseItem/completed":
1268
1557
  await this.handleRawResponseItemCompleted(params);
1558
+ await this.transcriptCheckpoint.flush();
1559
+ break;
1560
+ case "model/rerouted":
1561
+ this.eventProjection.handleModelRerouted(params);
1269
1562
  break;
1270
- case "error":
1563
+ case "error": {
1271
1564
  this.responseCompletions.clear();
1272
1565
  if (params.willRetry === true) break;
1273
- this.settledTurnFailureFinalizationAllowed = (isJsonObject(params.error) ? params.error.codexErrorInfo : void 0) === "serverOverloaded";
1566
+ const codexErrorInfo = isJsonObject(params.error) ? params.error.codexErrorInfo : void 0;
1567
+ const compactionFailure = codexErrorInfo === "other" && this.isCompacting();
1568
+ this.settledTurnFailureFinalizationAllowed = codexErrorInfo === "serverOverloaded" || compactionFailure;
1274
1569
  this.promptError = this.formatCodexErrorMessage(params) ?? "codex app-server error";
1275
- this.promptErrorSource = "prompt";
1570
+ this.promptErrorSource = compactionFailure ? "compaction" : "prompt";
1276
1571
  break;
1572
+ }
1277
1573
  case "thread/compacted":
1278
1574
  case "turn/started":
1279
1575
  case "turn/diff/updated":
@@ -1282,7 +1578,6 @@ var CodexAppServerEventProjector = class {
1282
1578
  case "item/fileChange/outputDelta":
1283
1579
  case "item/fileChange/patchUpdated":
1284
1580
  case "item/mcpToolCall/progress":
1285
- case "model/rerouted":
1286
1581
  case "model/verification":
1287
1582
  case "turn/moderationMetadata":
1288
1583
  case "model/safetyBuffering/updated": break;
@@ -1292,6 +1587,7 @@ var CodexAppServerEventProjector = class {
1292
1587
  }
1293
1588
  }
1294
1589
  buildResult(toolTelemetry, options) {
1590
+ this.eventProjection.flushPendingGuardianWarning();
1295
1591
  return buildCodexAttemptResult({
1296
1592
  runParams: this.params,
1297
1593
  turnId: this.turnId,
@@ -1307,6 +1603,7 @@ var CodexAppServerEventProjector = class {
1307
1603
  aborted: this.aborted,
1308
1604
  tokenUsage: this.tokenUsage,
1309
1605
  contextTokens: this.contextTokens,
1606
+ contextTokensSource: this.contextTokensSource,
1310
1607
  completedCompactionCount: this.completedCompactionCount,
1311
1608
  activeItemCount: this.activeItemIds.size,
1312
1609
  completedItemCount: this.completedItemIds.size,
@@ -1325,9 +1622,12 @@ var CodexAppServerEventProjector = class {
1325
1622
  recordDynamicToolCall(params) {
1326
1623
  this.toolTranscriptProjection.recordDynamicToolCall(params);
1327
1624
  }
1328
- /** Projects a successful OpenClaw update_plan call through the native plan stream. */
1329
- recordDynamicPlanUpdate(params) {
1330
- if (isJsonObject(params)) this.reasoningProjection.handleTurnPlanUpdated(params, "openclaw");
1625
+ /** Projects a successful OpenClaw progress_card call through the native plan stream. */
1626
+ async recordDynamicProgressCardUpdate(params) {
1627
+ if (isJsonObject(params)) {
1628
+ const projected = { plan: Array.isArray(params.plan) ? params.plan : [] };
1629
+ await this.reasoningProjection.handleTurnPlanUpdated(projected, "openclaw");
1630
+ }
1331
1631
  }
1332
1632
  recordDynamicToolResult(params) {
1333
1633
  this.toolProgressProjection.recordDynamicToolResult(params);
@@ -1409,13 +1709,15 @@ var CodexAppServerEventProjector = class {
1409
1709
  this.activeItemIds.delete(itemId);
1410
1710
  this.completedItemIds.add(itemId);
1411
1711
  }
1412
- this.assistantProjection.recordItemCompleted(item, itemId, this.activeItemIds);
1712
+ if (!this.asyncDeliveryProjection.allows(item, true)) return;
1713
+ const asyncMessage = this.assistantProjection.recordItemCompleted(item, itemId, this.activeItemIds);
1714
+ if (asyncMessage) await this.asyncDeliveryProjection.deliver(asyncMessage);
1413
1715
  this.reasoningProjection.recordItem(item);
1414
1716
  await this.generatedMediaProjection.recordNative(item);
1415
1717
  if (item?.type === "contextCompaction" && itemId) {
1416
1718
  this.activeCompactionItemIds.delete(itemId);
1417
1719
  this.completedCompactionCount += 1;
1418
- this.options.onContextCompacted?.();
1720
+ await this.options.onContextCompacted?.();
1419
1721
  await runAgentHarnessAfterCompactionHook({
1420
1722
  sessionFile: this.params.sessionFile,
1421
1723
  messages: await this.toolTranscriptProjection.readMirroredSessionMessages(),
@@ -1431,17 +1733,17 @@ var CodexAppServerEventProjector = class {
1431
1733
  channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
1432
1734
  }
1433
1735
  });
1434
- this.emitAgentEvent({
1435
- stream: "compaction",
1436
- data: {
1437
- phase: "end",
1438
- backend: "codex-app-server",
1439
- completed: true,
1440
- threadId: this.threadId,
1441
- turnId: this.turnId,
1442
- itemId
1443
- }
1736
+ await persistCodexContextCompactionActivity({
1737
+ sessionTarget: this.params.sessionTarget,
1738
+ config: this.params.config,
1739
+ cwd: this.params.workspaceDir,
1740
+ runId: this.params.runId,
1741
+ threadId: this.threadId,
1742
+ turnId: this.turnId,
1743
+ itemId,
1744
+ timestamp: this.transcriptCheckpoint.nextTimestamp()
1444
1745
  });
1746
+ this.eventProjection.emitCompactionEnd(itemId, true);
1445
1747
  }
1446
1748
  this.toolProgressProjection.recordToolMeta(item);
1447
1749
  this.toolProgressProjection.rememberCommandAggregateOutputEcho(item);
@@ -1470,7 +1772,8 @@ var CodexAppServerEventProjector = class {
1470
1772
  const turn = readCodexTurn(params.turn);
1471
1773
  if (!turn || turn.id !== this.turnId) return;
1472
1774
  this.completedTurn = turn;
1473
- this.settledTurnFailureFinalizationAllowed = turn.status === "failed" && turn.error?.codexErrorInfo === "serverOverloaded";
1775
+ const compactionFailure = turn.status === "failed" && (this.promptErrorSource === "compaction" || turn.error?.codexErrorInfo === "other" && this.isCompacting());
1776
+ this.settledTurnFailureFinalizationAllowed = turn.status === "failed" && (turn.error?.codexErrorInfo === "serverOverloaded" || compactionFailure);
1474
1777
  if (turn.status !== "completed") this.responseCompletions.clear();
1475
1778
  if (turn.status === "failed") {
1476
1779
  const usageLimitMessage = formatCodexUsageLimitErrorMessage({
@@ -1479,9 +1782,18 @@ var CodexAppServerEventProjector = class {
1479
1782
  rateLimits: this.options.readRecentRateLimits?.()
1480
1783
  });
1481
1784
  this.promptError = usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : turn.error?.message ?? "codex app-server turn failed";
1482
- this.promptErrorSource = "prompt";
1785
+ this.promptErrorSource = compactionFailure ? "compaction" : "prompt";
1786
+ }
1787
+ if (compactionFailure) {
1788
+ const failedCompactionItemIds = [...this.activeCompactionItemIds];
1789
+ for (const itemId of failedCompactionItemIds) {
1790
+ this.activeItemIds.delete(itemId);
1791
+ this.activeCompactionItemIds.delete(itemId);
1792
+ this.eventProjection.emitCompactionEnd(itemId, false);
1793
+ }
1483
1794
  }
1484
1795
  const turnItems = turn.items ?? [];
1796
+ const unsettledAsyncDeliveries = this.asyncDeliveryProjection.pending();
1485
1797
  for (let index = turnItems.length - 1; index >= 0; index -= 1) {
1486
1798
  const item = turnItems[index];
1487
1799
  if (!item || !this.isCurrentTurnSnapshotItem(item)) continue;
@@ -1492,8 +1804,10 @@ var CodexAppServerEventProjector = class {
1492
1804
  }
1493
1805
  }
1494
1806
  for (const item of turnItems) {
1807
+ if (!this.asyncDeliveryProjection.allows(item)) continue;
1495
1808
  this.diagnostics.warnUnknownItemStatus(item);
1496
- this.assistantProjection.recordSnapshotItem(item);
1809
+ const asyncMessage = this.assistantProjection.recordSnapshotItem(item);
1810
+ if (asyncMessage) await this.asyncDeliveryProjection.deliver(asyncMessage);
1497
1811
  this.reasoningProjection.recordItem(item);
1498
1812
  await this.generatedMediaProjection.recordNative(item);
1499
1813
  this.toolProgressProjection.recordToolMeta(item);
@@ -1505,6 +1819,8 @@ var CodexAppServerEventProjector = class {
1505
1819
  this.toolProgressProjection.emitToolResultSummary(item);
1506
1820
  this.toolProgressProjection.emitToolResultOutput(item);
1507
1821
  }
1822
+ this.toolProgressProjection.approvalTimeoutKinds.clear();
1823
+ for (const delivery of unsettledAsyncDeliveries) await this.asyncDeliveryProjection.deliver(delivery);
1508
1824
  this.assistantProjection.finalizeAnswerCandidate(turn);
1509
1825
  this.activeCompactionItemIds.clear();
1510
1826
  await this.reasoningProjection.maybeEndReasoning();
@@ -1566,24 +1882,7 @@ var CodexAppServerEventProjector = class {
1566
1882
  return usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : readCodexErrorNotificationMessage(params);
1567
1883
  }
1568
1884
  emitAgentEvent(event) {
1569
- try {
1570
- emitAgentEvent({
1571
- runId: this.params.runId,
1572
- stream: event.stream,
1573
- data: event.data,
1574
- ...this.params.sessionKey ? { sessionKey: this.params.sessionKey } : {}
1575
- });
1576
- } catch (error) {
1577
- embeddedAgentLog.debug("codex app-server global agent event emit failed", { error });
1578
- }
1579
- try {
1580
- const maybePromise = this.params.onAgentEvent?.(event);
1581
- Promise.resolve(maybePromise).catch((error) => {
1582
- embeddedAgentLog.debug("codex app-server agent event handler rejected", { error });
1583
- });
1584
- } catch (error) {
1585
- embeddedAgentLog.debug("codex app-server agent event handler threw", { error });
1586
- }
1885
+ emitCodexAgentEvent(this.params, event);
1587
1886
  }
1588
1887
  isHookNotificationForCurrentThread(params) {
1589
1888
  const threadId = readStringField(params, "threadId");
@@ -1592,11 +1891,28 @@ var CodexAppServerEventProjector = class {
1592
1891
  }
1593
1892
  };
1594
1893
  //#endregion
1894
+ //#region extensions/codex/src/app-server/mcp-tool-metadata.ts
1895
+ /** Hosted app ownership is authoritative only on metadata supplied by Codex. */
1896
+ function readCodexMcpToolConnectorId(tool) {
1897
+ const metadata = asOptionalRecord(asOptionalRecord(tool)?.["_meta"]);
1898
+ return normalizeOptionalString(metadata?.connector_id) ?? normalizeOptionalString(metadata?.connectorId);
1899
+ }
1900
+ /** Preserve MCP App visibility so model-only tools cannot become widget authority. */
1901
+ function readCodexMcpToolUiVisibility(tool) {
1902
+ const visibility = asOptionalRecord(asOptionalRecord(asOptionalRecord(tool)?.["_meta"])?.ui)?.visibility;
1903
+ if (!Array.isArray(visibility)) return;
1904
+ return [...new Set(visibility.filter((value) => value === "app" || value === "model"))].toSorted();
1905
+ }
1906
+ //#endregion
1595
1907
  //#region extensions/codex/src/app-server/native-mcp-app.ts
1908
+ const CODEX_APPS_MCP_SERVER$1 = "codex_apps";
1596
1909
  function readMcpAppResourceUri(item) {
1597
1910
  const uri = normalizeOptionalString(asOptionalRecord(item.appContext)?.resourceUri) ?? normalizeOptionalString(item.mcpAppResourceUri);
1598
1911
  return uri?.startsWith("ui://") ? uri : void 0;
1599
1912
  }
1913
+ function readMcpAppConnectorId(item) {
1914
+ return normalizeOptionalString(asOptionalRecord(item.appContext)?.connectorId);
1915
+ }
1600
1916
  function readMcpToolResult(item) {
1601
1917
  const result = asOptionalRecord(item.result);
1602
1918
  if (!result || !Array.isArray(result.content)) return;
@@ -1634,13 +1950,16 @@ function createNativeMcpRuntime(params) {
1634
1950
  launchSummary: "Codex native MCP connection",
1635
1951
  toolCount: Object.keys(status.tools).length
1636
1952
  }])),
1637
- tools: loaded.flatMap((status) => statusTools(status).map((tool) => ({
1638
- serverName: status.name,
1639
- safeServerName: status.name,
1640
- toolName: String(tool.name),
1641
- inputSchema: asOptionalRecord(tool.inputSchema) ?? { type: "object" },
1642
- fallbackDescription: normalizeOptionalString(tool.description) ?? String(tool.name)
1643
- })))
1953
+ tools: loaded.flatMap((status) => statusTools(status).map((tool) => {
1954
+ const uiVisibility = readCodexMcpToolUiVisibility(tool);
1955
+ return Object.assign({
1956
+ serverName: status.name,
1957
+ safeServerName: status.name,
1958
+ toolName: String(tool.name),
1959
+ inputSchema: asOptionalRecord(tool.inputSchema) ?? { type: "object" },
1960
+ fallbackDescription: normalizeOptionalString(tool.description) ?? String(tool.name)
1961
+ }, uiVisibility ? { uiVisibility } : {});
1962
+ }))
1644
1963
  };
1645
1964
  return catalog;
1646
1965
  };
@@ -1668,11 +1987,18 @@ function createNativeMcpRuntime(params) {
1668
1987
  const status = (await loadStatuses()).find((entry) => entry.name === serverName);
1669
1988
  return { tools: status ? statusTools(status) : [] };
1670
1989
  },
1671
- readResource: async (serverName, uri) => await params.client.request("mcpServer/resource/read", {
1672
- threadId: params.threadId,
1673
- server: serverName,
1674
- uri
1675
- }),
1990
+ readResource: async (serverName, uri) => {
1991
+ const isCodexAppsServer = serverName === CODEX_APPS_MCP_SERVER$1;
1992
+ const response = await params.client.request("mcpServer/resource/read", {
1993
+ threadId: params.threadId,
1994
+ ...isCodexAppsServer ? { originCallId: params.originCallId } : {},
1995
+ server: serverName,
1996
+ uri,
1997
+ ...params.connectorId ? { connectorId: params.connectorId } : {}
1998
+ });
1999
+ if (isCodexAppsServer && response.originCallId !== params.originCallId) throw new Error(`Codex MCP resource response originCallId mismatch: expected ${params.originCallId}, received ${response.originCallId}`);
2000
+ return response;
2001
+ },
1676
2002
  listResources: async (serverName) => {
1677
2003
  return { resources: (await loadStatuses()).find((entry) => entry.name === serverName)?.resources ?? [] };
1678
2004
  },
@@ -1685,15 +2011,25 @@ function createNativeMcpRuntime(params) {
1685
2011
  }
1686
2012
  function createCodexNativeMcpAppResultDetailsPreparer(params) {
1687
2013
  if (params.attempt.config?.mcp?.apps?.enabled !== true) return;
1688
- const runtime = createNativeMcpRuntime(params);
1689
2014
  return async (item) => {
1690
2015
  const serverName = normalizeOptionalString(item.server);
1691
2016
  const toolName = normalizeOptionalString(item.tool);
1692
2017
  const uiResourceUri = readMcpAppResourceUri(item);
2018
+ const connectorId = readMcpAppConnectorId(item);
1693
2019
  const toolResult = readMcpToolResult(item);
1694
2020
  if (!serverName || !toolName || !uiResourceUri || !toolResult) return;
1695
- const allowedAppToolNames = new Set((await runtime.getCatalog()).tools.filter((tool) => tool.serverName === serverName).map((tool) => tool.toolName));
1696
- if (allowedAppToolNames.size === 0) return;
2021
+ if (serverName === CODEX_APPS_MCP_SERVER$1 && !connectorId) return;
2022
+ const runtime = createNativeMcpRuntime({
2023
+ ...params,
2024
+ originCallId: item.id,
2025
+ ...connectorId ? { connectorId } : {}
2026
+ });
2027
+ const tools = (await runtime.listTools?.(serverName))?.tools ?? [];
2028
+ const allowedAppToolNames = new Set(tools.filter((tool) => {
2029
+ const uiVisibility = readCodexMcpToolUiVisibility(tool);
2030
+ return (uiVisibility === void 0 || uiVisibility.includes("app")) && (serverName !== CODEX_APPS_MCP_SERVER$1 || readCodexMcpToolConnectorId(tool) === connectorId);
2031
+ }).map((tool) => tool.name));
2032
+ if (!allowedAppToolNames.has(toolName)) return;
1697
2033
  return await prepareHarnessNativeMcpAppPreview({
1698
2034
  runtime,
1699
2035
  serverName,
@@ -1708,184 +2044,347 @@ function createCodexNativeMcpAppResultDetailsPreparer(params) {
1708
2044
  };
1709
2045
  }
1710
2046
  //#endregion
2047
+ //#region extensions/codex/src/app-server/plan-compaction-state.ts
2048
+ 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.";
2049
+ const PROGRESS_CARD_MAX_STEPS = 50;
2050
+ const PROGRESS_CARD_MAX_STEP_UTF8_BYTES = 512;
2051
+ const PROGRESS_CARD_MAX_UTF8_BYTES = 8 * 1024;
2052
+ const RESTORED_PLAN_MAX_MARKDOWN_BYTES = 2 * 1024;
2053
+ const RESTORED_PLAN_MAX_PAYLOAD_BYTES = 32 * 1024;
2054
+ const RESTORED_PLAN_TRUNCATION_SUFFIX = "…";
2055
+ function canonicalizeNativeProgressCardInput(input) {
2056
+ const markdown = input.markdown === void 0 ? void 0 : truncateUtf8(stripInvisibleUnicode(input.markdown), PROGRESS_CARD_MAX_UTF8_BYTES);
2057
+ const plan = [];
2058
+ let hasInProgressStep = false;
2059
+ for (const step of input.steps.slice(0, PROGRESS_CARD_MAX_STEPS)) {
2060
+ const text = truncateUtf8(stripInvisibleUnicode(step.step), PROGRESS_CARD_MAX_STEP_UTF8_BYTES);
2061
+ if (!text.trim()) continue;
2062
+ let status = step.status;
2063
+ if (status === "in_progress") if (hasInProgressStep) status = "pending";
2064
+ else hasInProgressStep = true;
2065
+ plan.push({
2066
+ step: text,
2067
+ status
2068
+ });
2069
+ }
2070
+ return {
2071
+ ...markdown !== void 0 ? { markdown } : {},
2072
+ plan
2073
+ };
2074
+ }
2075
+ /** Retains the latest projected plan so Codex compaction cannot discard it. */
2076
+ var CodexCompactionPlanState = class {
2077
+ record(event) {
2078
+ if (event.stream !== "plan") return;
2079
+ const plan = readBoundedPlan(event.data.explanation, event.data.steps);
2080
+ this.latestPlan = plan;
2081
+ }
2082
+ /** Retains markdown that migration-only plan events intentionally omit. */
2083
+ recordProgressCardInput(value) {
2084
+ if (!isJsonObject(value)) return;
2085
+ this.latestPlan = readBoundedPlan(value.markdown, value.plan);
2086
+ }
2087
+ async restore(params) {
2088
+ if (!this.latestPlan) return;
2089
+ await params.client.request("thread/inject_items", {
2090
+ threadId: params.threadId,
2091
+ items: [{
2092
+ type: "message",
2093
+ role: "user",
2094
+ content: [{
2095
+ type: "input_text",
2096
+ text: `${RESTORED_PLAN_PREAMBLE}\n${serializePlan(this.latestPlan)}`
2097
+ }]
2098
+ }]
2099
+ }, {
2100
+ timeoutMs: params.timeoutMs,
2101
+ signal: params.signal
2102
+ });
2103
+ }
2104
+ };
2105
+ function readBoundedPlan(markdownValue, stepsValue) {
2106
+ const canonical = canonicalizeNativeProgressCardInput({
2107
+ ...typeof markdownValue === "string" ? { markdown: markdownValue } : {},
2108
+ steps: readPlanSteps(stepsValue)
2109
+ });
2110
+ const markdown = canonical.markdown === void 0 ? void 0 : truncateUtf8(canonical.markdown, RESTORED_PLAN_MAX_MARKDOWN_BYTES);
2111
+ const steps = [];
2112
+ for (const step of canonical.plan) {
2113
+ const candidate = {
2114
+ ...markdown?.trim() ? { markdown } : {},
2115
+ steps: [...steps, step]
2116
+ };
2117
+ if (Buffer$1.byteLength(serializePlan(candidate), "utf8") > RESTORED_PLAN_MAX_PAYLOAD_BYTES) break;
2118
+ steps.push(step);
2119
+ }
2120
+ return markdown?.trim() || steps.length > 0 ? {
2121
+ ...markdown?.trim() ? { markdown } : {},
2122
+ steps
2123
+ } : void 0;
2124
+ }
2125
+ function serializePlan(plan) {
2126
+ return JSON.stringify({
2127
+ markdown: plan.markdown,
2128
+ plan: plan.steps
2129
+ });
2130
+ }
2131
+ function truncateUtf8(value, maxBytes) {
2132
+ if (Buffer$1.byteLength(value, "utf8") <= maxBytes) return value;
2133
+ return `${truncateUtf8Prefix(value, maxBytes - Buffer$1.byteLength(RESTORED_PLAN_TRUNCATION_SUFFIX, "utf8"))}${RESTORED_PLAN_TRUNCATION_SUFFIX}`;
2134
+ }
2135
+ function readPlanSteps(value) {
2136
+ if (!Array.isArray(value)) return [];
2137
+ return value.flatMap((entry) => {
2138
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
2139
+ const { step, status } = entry;
2140
+ if (typeof step !== "string" || !isPlanStatus(status)) return [];
2141
+ return [{
2142
+ step,
2143
+ status
2144
+ }];
2145
+ });
2146
+ }
2147
+ function isPlanStatus(value) {
2148
+ return value === "pending" || value === "in_progress" || value === "completed";
2149
+ }
2150
+ //#endregion
1711
2151
  //#region extensions/codex/src/app-server/user-input-bridge.ts
1712
- /** Bridges Codex request_user_input calls to gateway questions and secret text prompts. */
2152
+ /** Owns per-turn Codex request_user_input and ordinary MCP elicitation lifecycles. */
1713
2153
  const DEFAULT_USER_INPUT_TIMEOUT_MS = 15 * 6e4;
1714
2154
  const NONBLOCKING_USER_INPUT_TIMEOUT_MS = 12e4;
1715
- /** Creates a per-turn bridge for pending Codex user-input requests. */
2155
+ const MAX_USER_INPUT_QUESTIONS = 3;
2156
+ const MAX_USER_INPUT_OPTIONS = 4;
2157
+ const MAX_USER_INPUT_ID = 256;
2158
+ const MAX_USER_INPUT_HEADER = 64;
2159
+ const MAX_USER_INPUT_TEXT = 4096;
2160
+ /** Creates the single per-turn owner for Codex operator input. */
1716
2161
  function createCodexUserInputBridge(params) {
1717
- let sensitiveInput;
1718
- let pendingGateway;
1719
2162
  const gatewayCall = params.gatewayCall ?? callGatewayTool;
1720
- const resolveSecret = (value) => {
1721
- const current = sensitiveInput;
1722
- if (!current) return;
1723
- sensitiveInput = void 0;
1724
- current.cleanup();
1725
- current.resolve(value);
1726
- };
1727
- const resolveSecretIfCurrent = (current, value) => {
1728
- if (sensitiveInput !== current) return false;
1729
- resolveSecret(value);
1730
- return true;
2163
+ const jobs = [];
2164
+ let activeCompletion;
2165
+ const inputSessionKey = params.paramsForRun.sessionKey ?? params.paramsForRun.sessionId;
2166
+ const pump = () => {
2167
+ const job = jobs[0];
2168
+ if (activeCompletion || !job) return;
2169
+ activeCompletion = job.run(job.abort.signal).catch((error) => {
2170
+ embeddedAgentLog.warn("failed to bridge codex operator input", { error });
2171
+ return job.abort.signal.aborted ? job.cancelValue : job.failureValue;
2172
+ }).then((value) => {
2173
+ job.resolve(job.abort.signal.aborted ? job.cancelValue : value ?? job.failureValue);
2174
+ }).finally(() => {
2175
+ if (jobs[0] === job) jobs.shift();
2176
+ activeCompletion = void 0;
2177
+ pump();
2178
+ });
1731
2179
  };
1732
- const cancelGateway = () => {
1733
- pendingGateway?.abort.abort(/* @__PURE__ */ new Error("Codex user input request cancelled"));
2180
+ const enqueue = (definition) => {
2181
+ if (params.signal?.aborted) return Promise.resolve(definition.cancelValue);
2182
+ return new Promise((resolve) => {
2183
+ jobs.push({
2184
+ ...definition,
2185
+ resolve
2186
+ });
2187
+ pump();
2188
+ });
1734
2189
  };
2190
+ const cancelJob = (job) => {
2191
+ const index = jobs.indexOf(job);
2192
+ if (index < 0) return;
2193
+ if (index === 0) {
2194
+ job.abort.abort(/* @__PURE__ */ new Error("Codex operator input request cancelled"));
2195
+ return;
2196
+ }
2197
+ jobs.splice(index, 1);
2198
+ job.resolve(job.cancelValue);
2199
+ };
2200
+ const cancelPending = async () => {
2201
+ const pendingCompletion = activeCompletion;
2202
+ const queuedJobs = jobs.splice(1);
2203
+ for (const job of queuedJobs) job.resolve(job.cancelValue);
2204
+ jobs[0]?.abort.abort(/* @__PURE__ */ new Error("Codex operator input request cancelled"));
2205
+ await pendingCompletion;
2206
+ };
2207
+ const execute = (input, timeoutMs, signal) => agentHarnessStructuredInput.run({
2208
+ input,
2209
+ sessionKey: inputSessionKey,
2210
+ agentId: params.paramsForRun.agentId,
2211
+ runId: params.paramsForRun.runId,
2212
+ timeoutMs,
2213
+ gatewayCall,
2214
+ delivery: params.paramsForRun,
2215
+ signal,
2216
+ promptOptions: {
2217
+ formatText: formatCodexDisplayText,
2218
+ unsupportedIntro: "Codex input request could not be shown:",
2219
+ urlIntro: "Codex needs confirmation:"
2220
+ }
2221
+ });
2222
+ params.signal?.addEventListener("abort", () => void cancelPending(), { once: true });
1735
2223
  return {
1736
2224
  async handleRequest(request) {
1737
2225
  const requestParams = readUserInputParams(request.params);
1738
- if (!requestParams) return;
1739
- if (requestParams.threadId !== params.threadId || requestParams.turnId !== params.turnId) return;
2226
+ if (!requestParams || requestParams.threadId !== params.threadId || requestParams.turnId !== params.turnId) return;
1740
2227
  if (requestParams.questions.length === 0) return emptyUserInputResponse();
1741
- resolveSecret(emptyUserInputResponse());
1742
- cancelGateway();
1743
- if (requestParams.questions.some((question) => question.isSecret)) return new Promise((resolve) => {
1744
- const abortListener = () => resolveSecret(emptyUserInputResponse());
1745
- const timeout = requestParams.isBlocking ? void 0 : setTimeout(() => resolveSecret(emptyUserInputResponse()), NONBLOCKING_USER_INPUT_TIMEOUT_MS);
1746
- timeout?.unref?.();
1747
- const cleanup = () => {
1748
- params.signal?.removeEventListener("abort", abortListener);
1749
- if (timeout) clearTimeout(timeout);
1750
- };
1751
- const current = {
1752
- requestId: request.id,
1753
- threadId: requestParams.threadId,
1754
- questions: requestParams.questions,
1755
- claimed: false,
1756
- resolve,
1757
- cleanup
1758
- };
1759
- sensitiveInput = current;
1760
- params.signal?.addEventListener("abort", abortListener, { once: true });
1761
- if (params.signal?.aborted) {
1762
- resolveSecret(emptyUserInputResponse());
1763
- return;
2228
+ const timeoutMs = requestParams.isBlocking ? params.paramsForRun.timeoutMs ?? DEFAULT_USER_INPUT_TIMEOUT_MS : NONBLOCKING_USER_INPUT_TIMEOUT_MS;
2229
+ const input = compileUserInputQuestions(requestParams.questions);
2230
+ const cancelValue = emptyUserInputResponse();
2231
+ return await enqueue({
2232
+ requestId: request.id,
2233
+ abort: new AbortController(),
2234
+ cancelValue,
2235
+ failureValue: cancelValue,
2236
+ run: async (signal) => {
2237
+ const result = await execute(input, timeoutMs, signal);
2238
+ return result.status === "answered" ? gatewayAnswersToCodexResponse(result.answers) : cancelValue;
1764
2239
  }
1765
- deliverAgentHarnessUserInputPrompt(params.paramsForRun, requestParams.questions, {
1766
- formatText: formatCodexDisplayText,
1767
- intro: "Codex needs input:"
1768
- }).catch((error) => {
1769
- embeddedAgentLog.warn("failed to deliver secret codex user input prompt", { error });
1770
- resolveSecretIfCurrent(current, emptyUserInputResponse());
1771
- });
1772
2240
  });
1773
- const abort = new AbortController();
1774
- const abortFromRun = () => abort.abort(params.signal?.reason);
1775
- params.signal?.addEventListener("abort", abortFromRun, { once: true });
1776
- if (params.signal?.aborted) abortFromRun();
1777
- pendingGateway = {
1778
- requestId: request.id,
1779
- threadId: requestParams.threadId,
1780
- abort
1781
- };
1782
- try {
1783
- const result = await runAgentHarnessGatewayQuestion({
1784
- questions: requestParams.questions,
1785
- sessionKey: params.paramsForRun.sessionKey ?? params.paramsForRun.sessionId,
1786
- agentId: params.paramsForRun.agentId,
1787
- runId: params.paramsForRun.runId,
1788
- timeoutMs: requestParams.isBlocking ? params.paramsForRun.timeoutMs ?? DEFAULT_USER_INPUT_TIMEOUT_MS : NONBLOCKING_USER_INPUT_TIMEOUT_MS,
1789
- gatewayCall,
1790
- delivery: params.paramsForRun,
1791
- promptOptions: {
1792
- formatText: formatCodexDisplayText,
1793
- intro: "Codex needs input:"
1794
- },
1795
- signal: abort.signal
2241
+ },
2242
+ async handleElicitationRequest(request) {
2243
+ if (readRawOwnString(request.params, "threadId") !== params.threadId) return;
2244
+ const requestSnapshot = agentHarnessStructuredInput.snapshot(request.params);
2245
+ if (requestSnapshot === void 0) {
2246
+ const cancelValue = createCodexElicitationResponse("cancel");
2247
+ return await enqueue({
2248
+ requestId: request.id,
2249
+ abort: new AbortController(),
2250
+ cancelValue,
2251
+ failureValue: declineElicitation("OpenClaw could not handle this elicitation."),
2252
+ run: async (signal) => {
2253
+ const result = await execute({
2254
+ kind: "unsupported",
2255
+ message: "OpenClaw declined a malformed or over-limit MCP elicitation request."
2256
+ }, params.paramsForRun.timeoutMs ?? DEFAULT_USER_INPUT_TIMEOUT_MS, signal);
2257
+ return result.status === "unsupported" ? declineElicitation(result.message) : cancelValue;
2258
+ }
1796
2259
  });
1797
- return result.status === "answered" ? gatewayAnswersToCodexResponse(result.answers.answers) : emptyUserInputResponse();
1798
- } catch (error) {
1799
- embeddedAgentLog.warn("failed to bridge codex user input through gateway", { error });
1800
- return emptyUserInputResponse();
1801
- } finally {
1802
- params.signal?.removeEventListener("abort", abortFromRun);
1803
- if (pendingGateway?.abort === abort) pendingGateway = void 0;
1804
2260
  }
1805
- },
1806
- claimPendingRequest() {
1807
- const current = sensitiveInput;
1808
- if (!current || current.claimed) return;
1809
- current.claimed = true;
1810
- return {
1811
- answer: (text) => resolveSecretIfCurrent(current, buildUserInputResponse(current.questions, text)),
1812
- cancel: () => resolveSecretIfCurrent(current, emptyUserInputResponse())
1813
- };
2261
+ const { compileCodexOrdinaryElicitation } = await import("./elicitation-input-DMkeki8d.js");
2262
+ const compiled = compileCodexOrdinaryElicitation({
2263
+ value: requestSnapshot,
2264
+ threadId: params.threadId,
2265
+ turnId: params.turnId
2266
+ });
2267
+ if (compiled.kind === "ignored") return;
2268
+ const cancelValue = createCodexElicitationResponse("cancel");
2269
+ const timeoutMs = params.paramsForRun.timeoutMs ?? DEFAULT_USER_INPUT_TIMEOUT_MS;
2270
+ return await enqueue({
2271
+ requestId: request.id,
2272
+ abort: new AbortController(),
2273
+ cancelValue,
2274
+ failureValue: declineElicitation("OpenClaw could not handle this elicitation."),
2275
+ run: async (signal) => {
2276
+ const result = await execute(compiled.input, timeoutMs, signal);
2277
+ if (result.status === "answered") return createCodexElicitationResponse("accept", compiled.input.kind === "ready" && compiled.input.plan.kind === "url" ? null : result.content);
2278
+ if (result.status === "declined") return declineElicitation(result.message);
2279
+ if (result.status === "unsupported") return declineElicitation(result.message);
2280
+ return cancelValue;
2281
+ }
2282
+ });
1814
2283
  },
1815
2284
  handleNotification(notification) {
1816
- if (notification.method !== "serverRequest/resolved") return;
1817
- const notificationParams = isJsonObject(notification.params) ? notification.params : void 0;
1818
- const requestId = notificationParams ? readRequestId(notificationParams) : void 0;
1819
- if (!notificationParams || requestId === void 0) return;
1820
- if (sensitiveInput && readStringField(notificationParams, "threadId") === sensitiveInput.threadId && String(requestId) === String(sensitiveInput.requestId)) resolveSecret(emptyUserInputResponse());
1821
- if (pendingGateway && readStringField(notificationParams, "threadId") === pendingGateway.threadId && String(requestId) === String(pendingGateway.requestId)) pendingGateway.abort.abort(/* @__PURE__ */ new Error("Codex server request resolved"));
2285
+ if (notification.method !== "serverRequest/resolved" || !isJsonObject(notification.params)) return;
2286
+ const requestId = readRequestId(notification.params);
2287
+ if (requestId === void 0 || readOwnString(notification.params, "threadId") !== params.threadId) return;
2288
+ const job = jobs.find((candidate) => typeof candidate.requestId === typeof requestId && candidate.requestId === requestId);
2289
+ if (job) cancelJob(job);
1822
2290
  },
1823
- cancelPending() {
1824
- resolveSecret(emptyUserInputResponse());
1825
- cancelGateway();
1826
- }
2291
+ cancelPending
1827
2292
  };
1828
2293
  }
2294
+ function compileUserInputQuestions(questions) {
2295
+ return agentHarnessStructuredInput.compileQuestions({
2296
+ questions,
2297
+ intro: "Codex needs input:"
2298
+ });
2299
+ }
1829
2300
  function readUserInputParams(value) {
1830
- if (!isJsonObject(value)) return;
1831
- const threadId = readStringField(value, "threadId");
1832
- const turnId = readStringField(value, "turnId");
1833
- const itemId = readStringField(value, "itemId");
1834
- const questionsRaw = value.questions;
1835
- if (!threadId || !turnId || !itemId || !Array.isArray(questionsRaw)) return;
2301
+ const snapshot = agentHarnessStructuredInput.snapshot(value);
2302
+ if (!agentHarnessStructuredInput.isRecord(snapshot)) return;
2303
+ const threadId = readBoundedUserInputText(snapshot, "threadId", MAX_USER_INPUT_ID);
2304
+ const turnId = readBoundedUserInputText(snapshot, "turnId", MAX_USER_INPUT_ID);
2305
+ const itemId = readBoundedUserInputText(snapshot, "itemId", MAX_USER_INPUT_ID);
2306
+ const questions = readArray(snapshot, "questions", MAX_USER_INPUT_QUESTIONS);
2307
+ if (!threadId || !turnId || !itemId || !questions) return;
2308
+ const parsed = [];
2309
+ for (const questionValue of questions) {
2310
+ const question = readQuestion(questionValue);
2311
+ if (!question) return;
2312
+ parsed.push(question);
2313
+ }
1836
2314
  return {
1837
2315
  threadId,
1838
2316
  turnId,
1839
- itemId,
1840
- questions: questionsRaw.map((rawQuestion) => {
1841
- const question = readQuestion(rawQuestion);
1842
- if (question && isJsonObject(rawQuestion) && rawQuestion.multiSelect === true) question.multiSelect = true;
1843
- return question;
1844
- }).filter((question) => Boolean(question)),
1845
- isBlocking: value.isBlocking !== false
2317
+ questions: parsed,
2318
+ isBlocking: readValue(snapshot, "isBlocking") !== false
1846
2319
  };
1847
2320
  }
1848
2321
  function readQuestion(value) {
1849
- if (!isJsonObject(value)) return;
1850
- const id = readStringField(value, "id");
1851
- const header = readStringField(value, "header");
1852
- const question = readStringField(value, "question");
2322
+ if (!agentHarnessStructuredInput.isRecord(value)) return;
2323
+ const id = readBoundedUserInputText(value, "id", MAX_USER_INPUT_ID);
2324
+ const header = readBoundedUserInputText(value, "header", MAX_USER_INPUT_HEADER);
2325
+ const question = readBoundedUserInputText(value, "question", MAX_USER_INPUT_TEXT);
1853
2326
  if (!id || !header || !question) return;
2327
+ const options = readOptions(readValue(value, "options"));
2328
+ if (options === void 0) return;
1854
2329
  return {
1855
2330
  id,
1856
2331
  header,
1857
2332
  question,
1858
- isOther: value.isOther === true,
1859
- isSecret: value.isSecret === true,
1860
- options: readOptions(value.options)
2333
+ isOther: readValue(value, "isOther") === true,
2334
+ isSecret: readValue(value, "isSecret") === true,
2335
+ ...readValue(value, "multiSelect") === true ? { multiSelect: true } : {},
2336
+ options
1861
2337
  };
1862
2338
  }
1863
2339
  function readOptions(value) {
1864
- if (!Array.isArray(value)) return null;
1865
- const options = value.map(readOption).filter((option) => Boolean(option));
1866
- return options.length > 0 ? options : null;
1867
- }
1868
- function readOption(value) {
1869
- if (!isJsonObject(value)) return;
1870
- const label = readStringField(value, "label");
1871
- const description = readStringField(value, "description") ?? "";
1872
- return label ? {
1873
- label,
1874
- description
1875
- } : void 0;
2340
+ if (value === void 0 || value === null) return null;
2341
+ if (!Array.isArray(value) || value.length > MAX_USER_INPUT_OPTIONS) return;
2342
+ const options = [];
2343
+ for (const entry of value) {
2344
+ if (!agentHarnessStructuredInput.isRecord(entry)) return;
2345
+ const label = readBoundedUserInputText(entry, "label", MAX_USER_INPUT_ID);
2346
+ const description = readBoundedUserInputText(entry, "description", MAX_USER_INPUT_TEXT, true);
2347
+ if (!label) return;
2348
+ options.push({
2349
+ label,
2350
+ ...description ? { description } : {}
2351
+ });
2352
+ }
2353
+ return options;
2354
+ }
2355
+ function readValue(record, key) {
2356
+ return Object.hasOwn(record, key) ? record[key] : void 0;
2357
+ }
2358
+ function readBoundedUserInputText(record, key, maximum, allowEmpty = false) {
2359
+ const value = readValue(record, key);
2360
+ return typeof value === "string" && value.length <= maximum && (allowEmpty || value.length > 0) ? value : void 0;
2361
+ }
2362
+ function readArray(record, key, maximum) {
2363
+ const value = readValue(record, key);
2364
+ return Array.isArray(value) && value.length <= maximum ? value : void 0;
1876
2365
  }
1877
- function buildUserInputResponse(questions, inputText) {
1878
- return buildAgentHarnessUserInputAnswers(questions, inputText);
2366
+ function readOwnString(record, key) {
2367
+ const descriptor = Object.getOwnPropertyDescriptor(record, key);
2368
+ return descriptor && "value" in descriptor && typeof descriptor.value === "string" ? descriptor.value : void 0;
2369
+ }
2370
+ function readRawOwnString(value, key) {
2371
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
2372
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
2373
+ return descriptor && "value" in descriptor && typeof descriptor.value === "string" ? descriptor.value : void 0;
2374
+ }
2375
+ function readRequestId(record) {
2376
+ const descriptor = Object.getOwnPropertyDescriptor(record, "requestId");
2377
+ const value = descriptor && "value" in descriptor ? descriptor.value : void 0;
2378
+ return typeof value === "string" || typeof value === "number" ? value : void 0;
1879
2379
  }
1880
2380
  function gatewayAnswersToCodexResponse(answers) {
1881
2381
  return { answers: Object.fromEntries(Object.entries(answers).map(([questionId, values]) => [questionId, { answers: values }])) };
1882
2382
  }
1883
2383
  function emptyUserInputResponse() {
1884
- return emptyAgentHarnessUserInputAnswers();
2384
+ return { ...emptyAgentHarnessUserInputAnswers() };
1885
2385
  }
1886
- function readRequestId(record) {
1887
- const value = record.requestId;
1888
- return typeof value === "string" || typeof value === "number" ? value : void 0;
2386
+ function declineElicitation(message) {
2387
+ return createCodexElicitationResponse("decline", null, message ? { message } : null);
1889
2388
  }
1890
2389
  //#endregion
1891
2390
  //#region extensions/codex/src/app-server/run-attempt-active-turn.ts
@@ -1894,12 +2393,14 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
1894
2393
  const { context, turnState } = prompt;
1895
2394
  const { runtime, attemptTools } = context;
1896
2395
  const { connection } = runtime;
1897
- const { params, runAbortController, terminalState, abortExplicitly, abortFromUpstream, bindingStore, bindingIdentity, sessionAgentId, sandboxSessionKey, effectiveCwd } = connection;
1898
- const { dynamicToolParams, computerContextEpoch, toolBridge } = attemptTools;
2396
+ const { params, runAbortController, terminalState, abortExplicitly, abortFromUpstream, bindingStore, bindingIdentity, sessionAgentId, sandboxSessionKey, contextSessionKey, effectiveCwd } = connection;
2397
+ const { dynamicToolParams, compactionPlanState, computerContextEpoch, toolBridge } = attemptTools;
1899
2398
  const { state, userInputBridgeRef, steeringQueueRef, turnWatches, completeTurn, interruptTurn } = turnRuntime;
1900
2399
  const { emitExecutionPhaseOnce, emitLifecycleStart, maybeAnnounceFastModeAutoOff } = lifecycle;
1901
2400
  const { enqueueNotification } = notifications;
1902
2401
  const activeTurnId = turn.turn.id;
2402
+ const progressCardTool = toolBridge.availableTools.find((tool) => tool.name === "progress_card");
2403
+ let nativePlanUpdateOrdinal = 0;
1903
2404
  const prepareNativeMcpAppResultDetails = createCodexNativeMcpAppResultDetailsPreparer({
1904
2405
  client: resourceState.client,
1905
2406
  threadId: resourceState.thread.threadId,
@@ -1934,6 +2435,16 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
1934
2435
  }, resourceState.thread.threadId, activeTurnId, {
1935
2436
  initialContextTokens: connection.mutable.startupContextTokens,
1936
2437
  nativePostToolUseRelayEnabled: resourceState.nativeHookRelay?.allowedEvents.includes("post_tool_use") === true && resourceState.nativeHookRelay.shouldRelayEvent("post_tool_use"),
2438
+ asyncUserMessageAllowed: params.disableTools !== true && (params.toolsAllow === void 0 || toolBridge.availableTools.some((tool) => tool.name === "message")),
2439
+ onAsyncDelivery: async (delivery) => {
2440
+ return await codexTranscriptMirrorRuntime.deliverAsyncMessageBestEffort({
2441
+ params: dynamicToolParams,
2442
+ cwd: effectiveCwd,
2443
+ threadId: resourceState.thread.threadId,
2444
+ turnId: activeTurnId,
2445
+ ...delivery
2446
+ });
2447
+ },
1937
2448
  readRecentRateLimits: () => readRecentCodexRateLimits(resourceState.client),
1938
2449
  runAbortSignal: runAbortController.signal,
1939
2450
  remoteWorkspaceRoot: connection.appServer.remoteWorkspaceRoot,
@@ -1948,12 +2459,39 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
1948
2459
  trajectoryRecorder,
1949
2460
  resolveDynamicToolResultContentSource: toolBridge.resultContentSourceForTool,
1950
2461
  onNativeToolResultRecorded: maybeAnnounceFastModeAutoOff,
2462
+ ...progressCardTool ? { onNativePlanUpdate: async (update) => {
2463
+ nativePlanUpdateOrdinal += 1;
2464
+ try {
2465
+ const input = canonicalizeNativeProgressCardInput(update);
2466
+ await progressCardTool.execute(`codex-native-plan:${activeTurnId}:${nativePlanUpdateOrdinal}`, input, runAbortController.signal);
2467
+ } catch (error) {
2468
+ embeddedAgentLog.warn("failed to persist native Codex plan to progress card", {
2469
+ runId: params.runId,
2470
+ threadId: resourceState.thread.threadId,
2471
+ error: formatErrorMessage(error)
2472
+ });
2473
+ }
2474
+ } } : {},
1951
2475
  ...prepareNativeMcpAppResultDetails ? { prepareNativeMcpAppResultDetails } : {},
1952
2476
  upstreamUserText: turnState.codexTurnPromptText,
1953
- onContextCompacted: () => {
2477
+ onContextCompacted: async () => {
1954
2478
  computerContextEpoch.value += 1;
1955
2479
  delete computerContextEpoch.frameToolCallId;
1956
2480
  delete computerContextEpoch.frameImageIdentity;
2481
+ try {
2482
+ await compactionPlanState.restore({
2483
+ client: resourceState.client,
2484
+ threadId: resourceState.thread.threadId,
2485
+ timeoutMs: connection.appServer.requestTimeoutMs,
2486
+ signal: runAbortController.signal
2487
+ });
2488
+ } catch (error) {
2489
+ embeddedAgentLog.warn("failed to restore Codex plan state after compaction", {
2490
+ runId: params.runId,
2491
+ threadId: resourceState.thread.threadId,
2492
+ error: formatErrorMessage(error)
2493
+ });
2494
+ }
1957
2495
  }
1958
2496
  });
1959
2497
  if (isTerminalTurnStatus(turn.turn.status)) state.terminalTurnNotificationQueued = true;
@@ -1964,6 +2502,18 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
1964
2502
  turnWatches.armAttemptIdleWatch();
1965
2503
  turnWatches.touchActivity("turn:start", { attemptProgress: true });
1966
2504
  for (const failure of pendingNativePreToolUseFailures.splice(0)) activeProjector.recordNativeToolPreToolUseFailure(failure);
2505
+ const notifyUserMessagePersisted = createCodexAppServerUserMessagePersistenceNotifier(params);
2506
+ const promptMirrorPromise = mirrorPromptAtTurnStartBestEffort({
2507
+ params,
2508
+ agentId: sessionAgentId,
2509
+ notifyUserMessagePersisted,
2510
+ sessionKey: sandboxSessionKey,
2511
+ cwd: effectiveCwd,
2512
+ threadId: resourceState.thread.threadId,
2513
+ turnId: activeTurnId,
2514
+ upstreamUserText: turnState.codexTurnPromptText
2515
+ });
2516
+ await promptMirrorPromise;
1967
2517
  if (resourceState.turnRoute) try {
1968
2518
  await resourceState.turnRoute.bindTurn(activeTurnId);
1969
2519
  } catch (error) {
@@ -1974,28 +2524,91 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
1974
2524
  throw error;
1975
2525
  }
1976
2526
  }
1977
- if (!state.completed && isTerminalTurnStatus(turn.turn.status)) await enqueueNotification({
1978
- method: "turn/completed",
1979
- params: {
2527
+ if (!state.completed && isTerminalTurnStatus(turn.turn.status)) {
2528
+ if (!isJsonObject(turn.turn)) throw new Error("Codex turn completion payload is not a JSON object");
2529
+ await enqueueNotification({
2530
+ method: "turn/completed",
2531
+ params: {
2532
+ threadId: resourceState.thread.threadId,
2533
+ turnId: activeTurnId,
2534
+ turn: turn.turn
2535
+ }
2536
+ }, {
1980
2537
  threadId: resourceState.thread.threadId,
1981
- turnId: activeTurnId,
1982
- turn: turn.turn
1983
- }
1984
- }, {
1985
- threadId: resourceState.thread.threadId,
1986
- turnId: activeTurnId
2538
+ turnId: activeTurnId
2539
+ });
2540
+ }
2541
+ const assertSteeringActive = () => {
2542
+ params.hostCapabilities.assertActive();
2543
+ runAbortController.signal.throwIfAborted();
2544
+ if (state.completed || state.terminalTurnNotificationQueued || state.timedOut) throw new Error("codex app-server turn is no longer accepting steering");
2545
+ };
2546
+ const workspaceOnly = resolveAttemptFsWorkspaceOnly({
2547
+ config: params.config,
2548
+ sessionAgentId
1987
2549
  });
2550
+ const imageContext = {
2551
+ workspaceDir: connection.effectiveWorkspace,
2552
+ model: params.model,
2553
+ config: params.config,
2554
+ workspaceOnly,
2555
+ localRoots: workspaceOnly ? void 0 : getAgentScopedMediaLocalRoots(params.config ?? {}, sessionAgentId),
2556
+ sandbox: connection.sandbox?.enabled && connection.sandbox.fsBridge ? {
2557
+ root: connection.sandbox.workspaceDir,
2558
+ bridge: connection.sandbox.fsBridge
2559
+ } : void 0
2560
+ };
1988
2561
  const activeSteeringQueue = createCodexSteeringQueue({
1989
2562
  client: resourceState.client,
1990
2563
  threadId: resourceState.thread.threadId,
1991
2564
  turnId: activeTurnId,
1992
2565
  requestTimeoutMs: connection.appServer.requestTimeoutMs,
1993
- claimPendingUserInput: () => userInputBridgeRef.current?.claimPendingRequest(),
1994
- signal: runAbortController.signal
2566
+ signal: runAbortController.signal,
2567
+ assertActive: assertSteeringActive,
2568
+ prepareMessage: async (text, options) => {
2569
+ const result = await detectAndLoadAgentHarnessPromptImages({
2570
+ ...imageContext,
2571
+ prompt: text,
2572
+ existingImages: options.images,
2573
+ imageOrder: options.imageOrder,
2574
+ media: options.media,
2575
+ userTurnTranscriptRecorder: options.userTurnTranscriptRecorder
2576
+ });
2577
+ if (result.failedMediaCount) throw new Error(`failed to hydrate ${result.failedMediaCount} structured image attachment(s) for Codex steering`);
2578
+ return buildCodexUserInput(text, result.images);
2579
+ },
2580
+ beforeConfirmConsumed: async (items) => {
2581
+ const inboundItems = items.filter((item) => item.isInboundUserMessage === true);
2582
+ if (inboundItems.length === 0) return;
2583
+ await promptMirrorPromise;
2584
+ const messages = activeProjector.buildSteeringTranscriptPrefix();
2585
+ if (params.sessionTarget && messages.length > 0) {
2586
+ await codexTranscriptMirrorRuntime.mirror({
2587
+ agentId: sessionAgentId,
2588
+ sessionKey: contextSessionKey,
2589
+ sessionId: params.sessionId,
2590
+ storePath: params.sessionTarget.storePath,
2591
+ cwd: effectiveCwd,
2592
+ messages,
2593
+ idempotencyScope: `codex-app-server:${resourceState.thread.threadId}`,
2594
+ runId: params.runId,
2595
+ runMirrorIdentityPrefix: `${activeTurnId}:`,
2596
+ config: params.config
2597
+ });
2598
+ activeProjector.markSteeringTranscriptPersisted();
2599
+ }
2600
+ for (const item of inboundItems) {
2601
+ const recorder = item.userTurnTranscriptRecorder;
2602
+ if (!recorder) continue;
2603
+ await recorder.persistApproved();
2604
+ if (!recorder.hasPersisted()) throw new Error("Codex consumed steering before its user turn was persisted");
2605
+ }
2606
+ }
1995
2607
  });
1996
2608
  steeringQueueRef.current = activeSteeringQueue;
1997
2609
  const claimPendingUserInputAnswer = async (text, optionsLocal) => {
1998
- if (optionsLocal?.isInboundUserMessage !== true || optionsLocal.images?.length) return false;
2610
+ if (optionsLocal?.isInboundUserMessage !== true || hasPromptImageInput(optionsLocal)) return false;
2611
+ assertSteeringActive();
1999
2612
  return await claimPendingAgentQuestionAnswer({
2000
2613
  sessionKey: params.sessionKey ?? params.sessionId,
2001
2614
  text,
@@ -2009,14 +2622,17 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2009
2622
  resolvedBy
2010
2623
  });
2011
2624
  const queueMessage = async (text, optionsLocal) => {
2012
- const isInboundUserMessage = optionsLocal?.isInboundUserMessage === true;
2013
2625
  if (await claimPendingUserInputAnswer(text, optionsLocal)) {
2014
2626
  optionsLocal?.onQueueAccepted?.(true);
2015
2627
  return;
2016
- } else if (isInboundUserMessage && optionsLocal?.images?.length) try {
2017
- await cancelPendingUserInput("image-reply");
2018
- } catch (error) {
2019
- embeddedAgentLog.warn("failed to cancel codex gateway question before image steering", { error });
2628
+ }
2629
+ if (optionsLocal?.isInboundUserMessage === true && hasPromptImageInput(optionsLocal)) {
2630
+ assertSteeringActive();
2631
+ try {
2632
+ await cancelPendingUserInput("image-reply");
2633
+ } catch (error) {
2634
+ embeddedAgentLog.warn("failed to cancel codex gateway question before image steering", { error });
2635
+ }
2020
2636
  }
2021
2637
  try {
2022
2638
  await activeSteeringQueue.queue(text, optionsLocal);
@@ -2031,6 +2647,7 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2031
2647
  const handle = {
2032
2648
  kind: "embedded",
2033
2649
  runId: params.runId,
2650
+ startedAtMs: params.startedAtMs,
2034
2651
  toolAuthorityFingerprint: params.toolAuthorityFingerprint,
2035
2652
  claimPendingUserInputAnswer,
2036
2653
  cancelPendingUserInput,
@@ -2045,7 +2662,7 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2045
2662
  isAbortable: () => !terminalState.terminalOutcomeFrozen || terminalState.sharedAbortAllowedAfterTerminalOutcome,
2046
2663
  isCompacting: () => projectorRef.current?.isCompacting() ?? false,
2047
2664
  supportsTranscriptCommitWait: true,
2048
- supportsQueueMessageImages: true,
2665
+ supportsQueueMessageImages: params.model.input.includes("image"),
2049
2666
  sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
2050
2667
  taskSuggestionDeliveryMode: params.taskSuggestionDeliveryMode,
2051
2668
  cancel: () => abortExplicitly("cancelled"),
@@ -2058,17 +2675,6 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2058
2675
  terminalState.terminalOutcomeFrozen = true;
2059
2676
  params.abortSignal?.removeEventListener("abort", abortFromUpstream);
2060
2677
  };
2061
- const notifyUserMessagePersisted = createCodexAppServerUserMessagePersistenceNotifier(params);
2062
- mirrorPromptAtTurnStartBestEffort({
2063
- params,
2064
- agentId: sessionAgentId,
2065
- notifyUserMessagePersisted,
2066
- sessionKey: sandboxSessionKey,
2067
- cwd: effectiveCwd,
2068
- threadId: resourceState.thread.threadId,
2069
- turnId: activeTurnId,
2070
- upstreamUserText: turnState.codexTurnPromptText
2071
- });
2072
2678
  const abortListener = () => {
2073
2679
  if (state.timedOut) {
2074
2680
  (async () => {
@@ -2085,7 +2691,15 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2085
2691
  })().finally(completeTurn);
2086
2692
  return;
2087
2693
  }
2088
- interruptTurn(activeTurnId).finally(completeTurn);
2694
+ const interrupted = interruptTurn(activeTurnId);
2695
+ if (terminalState.explicitCancellationObserved) state.abortCleanup = interrupted.then(async (confirmed) => {
2696
+ if (!confirmed) throw new Error("Codex cancellation could not confirm the turn stopped; background terminals may still be running.");
2697
+ await terminateCodexBackgroundTerminals(resourceState.client, resourceState.thread.threadId);
2698
+ });
2699
+ (terminalState.explicitCancellationObserved ? state.abortCleanup : interrupted).then(completeTurn, (error) => {
2700
+ embeddedAgentLog.warn("codex app-server cancellation cleanup failed", { error });
2701
+ completeTurn();
2702
+ });
2089
2703
  };
2090
2704
  runAbortController.signal.addEventListener("abort", abortListener, { once: true });
2091
2705
  if (runAbortController.signal.aborted) abortListener();
@@ -2110,6 +2724,8 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2110
2724
  const { codexModelCallDiagnostics } = requestRuntime;
2111
2725
  const { activeTurnId, abortListener, handle, freezeRunTerminalOutcome } = activeTurn;
2112
2726
  prompt.context.attemptTools.scheduledAppAuthoritySourceRef.current = void 0;
2727
+ freezeRunTerminalOutcome();
2728
+ await state.abortCleanup.catch(() => void 0);
2113
2729
  try {
2114
2730
  steeringQueueRef.current?.cancel();
2115
2731
  if (params.isFinalFallbackAttempt !== false) await maybeEmitFastModeAutoResetBestEffort();
@@ -2133,15 +2749,11 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2133
2749
  const retainLiveIncognitoThread = terminalState.turnSucceeded && isIncognitoSessionKey(params.sessionKey);
2134
2750
  const retainedPersistentThread = terminalState.turnSucceeded && !isIncognitoSessionKey(params.sessionKey) && params.cleanupBundleMcpOnRunEnd !== true && resourceState.thread.liveThreadConfigFingerprint !== void 0 && resourceState.thread.clientId === resolveCodexAppServerClientInstanceId(resourceState.client) && resourceState.thread.preserveNativeModel !== true && resourceState.thread.connectionScope !== "supervision" && !resourceState.thread.ringZeroConfigFingerprint ? (await bindingStore.read(bindingIdentity))?.threadId === resourceState.thread.threadId && await bindingStore.withLease(bindingIdentity, async () => {
2135
2751
  if ((await bindingStore.read(bindingIdentity))?.threadId !== resourceState.thread.threadId) return false;
2136
- return await retainCodexAppServerLiveThread(resourceState.client, resourceState.thread.threadId, resourceState.thread.liveThreadOwnership?.release ?? (async (threadId) => {
2137
- if (!await unsubscribeCodexThreadBestEffort(resourceState.client, {
2138
- threadId,
2139
- timeoutMs: 5e3
2140
- })) {
2141
- await closeCodexStartupClientBestEffort(resourceState.client);
2142
- throw new CodexAppServerUnsafeSubscriptionError(`Codex retained thread subscription could not be released: ${threadId}`);
2143
- }
2144
- }), resourceState.thread.liveThreadConfigFingerprint, connection.mutable.pluginAppServer.serviceTier);
2752
+ return await retainCodexAppServerBindingSubscription(resourceState.client, resourceState.thread.threadId, {
2753
+ release: resourceState.thread.liveThreadOwnership?.release,
2754
+ configFingerprint: resourceState.thread.liveThreadConfigFingerprint,
2755
+ serviceTier: connection.mutable.pluginAppServer.serviceTier
2756
+ });
2145
2757
  }) : false;
2146
2758
  const retainLiveThread = retainLiveIncognitoThread || retainedPersistentThread;
2147
2759
  const bindingReleased = isIncognitoSessionKey(params.sessionKey) && !retainLiveIncognitoThread ? await bindingStore.mutate(bindingIdentity, {
@@ -2159,7 +2771,13 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2159
2771
  } finally {
2160
2772
  await runCleanupStep("codex-user-input-cancel", () => userInputBridgeRef.current?.cancelPending());
2161
2773
  await runCleanupStep("codex-turn-watch-clear", () => turnWatches.clearAllTimers());
2774
+ await runCleanupStep("codex-dynamic-tool-cleanup", async () => {
2775
+ const cleanupReason = terminalState.turnSucceeded ? "completion" : state.timedOut ? "timeout" : runAbortController.signal.aborted ? "cancel" : "error";
2776
+ const cleanups = prompt.context.attemptTools.runCleanups.splice(0);
2777
+ await Promise.allSettled(cleanups.map(async (cleanup) => await cleanup(cleanupReason)));
2778
+ });
2162
2779
  await runCleanupStep("codex-route-release", releaseCurrentRoute);
2780
+ await runCleanupStep("codex-transcript-checkpoint", () => activeTurn.activeProjector.transcriptCheckpoint.flush(true));
2163
2781
  await runCleanupStep("codex-shared-client-release", releaseSharedClientLeaseAndRetireOneShotClient);
2164
2782
  const nativeHookRelay = resourceState.nativeHookRelay;
2165
2783
  resourceState.nativeHookRelay = void 0;
@@ -2178,12 +2796,12 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2178
2796
  runAbortController.signal.removeEventListener("abort", abortListener);
2179
2797
  });
2180
2798
  await runCleanupStep("codex-steering-cancel", () => steeringQueueRef.current?.cancel());
2181
- await runCleanupStep("codex-terminal-freeze", freezeRunTerminalOutcome);
2182
2799
  await runCleanupStep("codex-reply-backend-detach", () => params.replyOperation?.detachBackend(handle));
2183
2800
  await runCleanupStep("codex-active-run-clear", () => {
2184
2801
  clearActiveEmbeddedRun(params.sessionId, handle, params.sessionKey, params.sessionFile);
2185
2802
  });
2186
2803
  }
2804
+ await state.abortCleanup;
2187
2805
  }
2188
2806
  //#endregion
2189
2807
  //#region extensions/codex/src/app-server/workspace-dir-cache.ts
@@ -2295,10 +2913,7 @@ async function listCodexAppServerRolloutFilesForThread(agentDir, threadId, codex
2295
2913
  path.join(resolvedAgentDir, "agent", "codex-home", "sessions"),
2296
2914
  path.join(path.dirname(resolvedAgentDir), "codex-home", "sessions")
2297
2915
  ];
2298
- const rolloutRoot = rolloutPath ? roots.find((root) => {
2299
- const relativePath = path.relative(root, rolloutPath);
2300
- return relativePath !== "" && relativePath !== ".." && !relativePath.startsWith(`..${path.sep}`) && !path.isAbsolute(relativePath);
2301
- }) : void 0;
2916
+ const rolloutRoot = rolloutPath ? roots.find((root) => isPathStrictlyInside(root, rolloutPath)) : void 0;
2302
2917
  if (rolloutPath && rolloutRoot && path.isAbsolute(rolloutPath) && path.extname(rolloutPath) === ".jsonl" && path.basename(rolloutPath).includes(threadId)) try {
2303
2918
  const opened = await (await root(rolloutRoot, {
2304
2919
  hardlinks: "reject",
@@ -2555,14 +3170,6 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
2555
3170
  }
2556
3171
  //#endregion
2557
3172
  //#region extensions/codex/src/app-server/run-attempt-connection.ts
2558
- function applyStoredBindingPermissions(params) {
2559
- if (params.execPolicyTouched || params.binding?.connectionScope === "supervision") return params.appServer;
2560
- return {
2561
- ...params.appServer,
2562
- approvalPolicy: params.binding?.approvalPolicy ?? params.appServer.approvalPolicy,
2563
- sandbox: params.binding?.sandbox ?? params.appServer.sandbox
2564
- };
2565
- }
2566
3173
  async function prepareCodexAttemptConnection({ params, options }) {
2567
3174
  const attemptStartedAt = Date.now();
2568
3175
  const profilerEnabled = isCodexAppServerProfilerEnabled(params.config);
@@ -2575,16 +3182,15 @@ async function prepareCodexAttemptConnection({ params, options }) {
2575
3182
  resetAnnounced: false
2576
3183
  };
2577
3184
  const preDynamicStartupStages = createCodexDynamicToolBuildStageTracker({ enabled: profilerEnabled });
2578
- const attemptClientFactory = options.clientFactory ?? getLeasedSharedCodexAppServerClient;
2579
3185
  const runtimeArtifactRequest = params.captureRuntimeArtifact || params.expectedRuntimeArtifact ? params.expectedRuntimeArtifact ? { expected: params.expectedRuntimeArtifact } : {} : void 0;
2580
3186
  const pluginConfig = readCodexPluginConfig(options.pluginConfig);
3187
+ const requirementsToml = readCodexRequirementsToml({});
2581
3188
  const computerUseConfig = resolveCodexComputerUseConfig({ pluginConfig });
2582
- const { sessionAgentId } = resolveSessionAgentIds({
3189
+ const { sessionAgentId } = resolveSessionAgentIdsStrict({
2583
3190
  sessionKey: params.sessionKey,
2584
3191
  config: params.config,
2585
3192
  agentId: params.agentId
2586
3193
  });
2587
- const beforeToolCallPolicy = getBeforeToolCallPolicyDiagnosticState();
2588
3194
  preDynamicStartupStages.mark("config");
2589
3195
  const resolvedWorkspace = resolveUserPath(params.workspaceDir);
2590
3196
  await ensureCodexWorkspaceDirOnce(resolvedWorkspace);
@@ -2596,14 +3202,36 @@ async function prepareCodexAttemptConnection({ params, options }) {
2596
3202
  sessionKey: sandboxSessionKey,
2597
3203
  workspaceDir: resolvedWorkspace
2598
3204
  });
3205
+ const attemptClientFactory = options.clientFactory ?? (isCodexPairedNodeRemoteExecPlacementSandbox(sandbox) ? createIsolatedCodexAppServerClient : getLeasedSharedCodexAppServerClient);
2599
3206
  preDynamicStartupStages.mark("sandbox");
2600
3207
  const execPolicy = resolveOpenClawExecPolicyForCodexAppServer({
3208
+ permissionMode: params.permissionMode,
2601
3209
  execOverrides: params.execOverrides,
2602
- approvals: loadExecApprovals(),
3210
+ approvals: params.permissionMode === "full" ? void 0 : loadExecApprovals(),
2603
3211
  config: params.config,
2604
3212
  agentId: sessionAgentId
2605
3213
  });
2606
3214
  const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, sessionAgentId);
3215
+ const preparedEnvironment = params.hostCapabilities.preparedEnvironment?.();
3216
+ const remoteExec = isCodexRemoteExecPlacementSandbox(sandbox);
3217
+ const preparedShellEnvironment = preparedEnvironment ? {
3218
+ ...preparedEnvironment.credentialScrubEnv,
3219
+ ...!remoteExec ? preparedEnvironment.localIdentityEnv : void 0
3220
+ } : void 0;
3221
+ const shellEnvironment = preparedShellEnvironment && Object.keys(preparedShellEnvironment).length > 0 ? preparedShellEnvironment : void 0;
3222
+ const disableLoginShell = remoteExec || preparedEnvironment?.managedLocalIdentity === true || preparedEnvironment !== void 0 && Object.keys(preparedEnvironment.credentialScrubEnv).length > 0;
3223
+ const withPreparedProcessEnv = (appServer) => {
3224
+ return shellEnvironment ? {
3225
+ ...appServer,
3226
+ start: {
3227
+ ...appServer.start,
3228
+ env: {
3229
+ ...appServer.start.env,
3230
+ ...shellEnvironment
3231
+ }
3232
+ }
3233
+ } : appServer;
3234
+ };
2607
3235
  let bindingIdentity = sessionBindingIdentity({
2608
3236
  sessionId: params.sessionId,
2609
3237
  sessionKey: params.sessionKey,
@@ -2650,21 +3278,20 @@ async function prepareCodexAttemptConnection({ params, options }) {
2650
3278
  const usesSupervisionConnection = startupBinding?.connectionScope === "supervision";
2651
3279
  if (usesSupervisionConnection) activeContextEngine = void 0;
2652
3280
  if (usesSupervisionConnection && pluginConfig.supervision?.enabled !== true) throw new Error("Codex supervision is disabled; refusing to open a native user-home supervised session");
2653
- const resolveRuntimeOptionsForBinding = (selection) => applyStoredBindingPermissions({
2654
- appServer: resolveCodexBindingAppServerConnection({
2655
- binding: startupBinding,
2656
- pluginConfig,
2657
- execPolicy,
2658
- modelProvider: selection.modelProvider,
2659
- model: selection.model,
2660
- config: params.config,
2661
- agentDir,
2662
- openClawSandboxActive: sandbox?.enabled === true
2663
- }).appServer,
2664
- binding: startupBinding,
2665
- execPolicyTouched: execPolicy.touched
2666
- });
3281
+ const resolveRuntimeOptionsForBinding = (binding, selection) => resolveCodexBindingAppServerConnection({
3282
+ binding,
3283
+ pluginConfig,
3284
+ execPolicy,
3285
+ modelProvider: selection.modelProvider,
3286
+ model: selection.model,
3287
+ config: params.config,
3288
+ agentDir,
3289
+ requirementsToml,
3290
+ openClawSandboxActive: sandbox?.enabled === true,
3291
+ sessionPermissionMode: params.permissionMode
3292
+ }).appServer;
2667
3293
  const initialStartupBindingHadInactiveThreadBootstrap = isInactiveThreadBootstrapBinding(startupBinding);
3294
+ const appServerHomeScope = resolveCodexAppServerHomeScope({ appServer: pluginConfig.appServer });
2668
3295
  const preparedAuthRoute = usesSupervisionConnection ? void 0 : params.runtimePlan?.auth.modelRoute;
2669
3296
  const startupAuthProfileCandidate = usesSupervisionConnection ? void 0 : preparedAuthRoute ? params.runtimePlan?.auth.forwardedAuthProfileId : params.runtimePlan?.auth.forwardedAuthProfileId ?? params.authProfileId ?? startupBinding?.authProfileId;
2670
3297
  const resolvedStartupAuthProfileId = usesSupervisionConnection ? void 0 : preparedAuthRoute ? startupAuthProfileCandidate : params.authProfileStore ? resolveCodexAppServerAuthProfileId({
@@ -2686,13 +3313,13 @@ async function prepareCodexAttemptConnection({ params, options }) {
2686
3313
  authProfileId: resolvedStartupAuthProfileId,
2687
3314
  authProfileStore: params.authProfileStore,
2688
3315
  agentDir,
2689
- homeScope: resolveCodexAppServerHomeScope({ appServer: pluginConfig.appServer }),
3316
+ homeScope: appServerHomeScope,
2690
3317
  requirePreparedAuth: isCodexRemoteExecPlacementSandbox(sandbox),
2691
3318
  config: params.config,
2692
3319
  subscriptionProfileRequiredError: "Prepared Codex subscription route requires a forwarded OpenAI OAuth or token profile.",
2693
3320
  subscriptionProfileUnusableError: "Prepared Codex subscription auth profile is unusable."
2694
3321
  });
2695
- const startupClientAuthProfileId = usesSupervisionConnection || startupPreparedAuth?.kind === "api-key" ? null : startupAuthProfileId;
3322
+ const startupClientAuthProfileId = usesSupervisionConnection || appServerHomeScope === "user" || startupPreparedAuth?.kind === "api-key" ? null : startupAuthProfileId;
2696
3323
  const resolveReviewerPolicyContext = (binding) => {
2697
3324
  const nativeModelOwned = binding?.preserveNativeModel === true;
2698
3325
  return resolveCodexModelBackedReviewerPolicyContext({
@@ -2705,41 +3332,67 @@ async function prepareCodexAttemptConnection({ params, options }) {
2705
3332
  };
2706
3333
  let reviewerPolicyContext = resolveReviewerPolicyContext(startupBinding);
2707
3334
  preDynamicStartupStages.mark("auth-profile");
2708
- let configuredAppServer = resolveRuntimeOptionsForBinding({
3335
+ let configuredAppServer = resolveRuntimeOptionsForBinding(startupBinding, {
2709
3336
  modelProvider: reviewerPolicyContext.modelProvider,
2710
3337
  model: reviewerPolicyContext.model
2711
3338
  });
2712
3339
  const effectiveWorkspace = sandbox?.enabled ? sandbox.workspaceAccess === "rw" ? resolvedWorkspace : sandbox.workspaceDir : resolvedWorkspace;
2713
3340
  const requestedCwd = params.cwd ? resolveUserPath(params.cwd) : void 0;
2714
3341
  if (sandbox?.enabled && requestedCwd && requestedCwd !== resolvedWorkspace) throw new Error("cwd override is not supported for sandboxed Codex app-server runs; omit cwd or use the agent workspace as cwd");
2715
- const effectiveCwd = sandbox?.enabled ? effectiveWorkspace : requestedCwd ?? effectiveWorkspace;
3342
+ const sessionPermissionCwd = resolveCodexSessionPermissionCwd({
3343
+ permissionMode: params.permissionMode,
3344
+ sessionRoot: params.sessionRoot,
3345
+ defaultRoot: effectiveWorkspace,
3346
+ requestedCwd,
3347
+ fallbackCwd: effectiveWorkspace
3348
+ });
3349
+ const effectiveCwd = sandbox?.enabled ? effectiveWorkspace : sessionPermissionCwd;
2716
3350
  if (effectiveWorkspace !== resolvedWorkspace) await ensureCodexWorkspaceDirOnce(effectiveWorkspace);
2717
3351
  preDynamicStartupStages.mark("effective-workspace");
2718
- const shouldPromoteApprovalPolicy = beforeToolCallPolicy.hasBeforeToolCallHook || beforeToolCallPolicy.trustedToolPolicies.length > 0;
2719
- const resolvePolicyAppServer = () => resolveCodexAppServerForOpenClawToolPolicy({
2720
- appServer: configuredAppServer,
3352
+ const applySessionPermissionPolicy = (appServer, selection) => applyCodexSessionPermissionPolicy({
3353
+ appServer,
3354
+ permissionMode: params.permissionMode,
3355
+ sessionRoot: params.sessionRoot,
3356
+ defaultRoot: effectiveWorkspace,
2721
3357
  pluginConfig,
2722
- env: process.env,
2723
- shouldPromote: shouldPromoteApprovalPolicy,
2724
- execPolicy,
2725
- canUseUntrustedApprovalPolicy: shouldPromoteApprovalPolicy && configuredAppServer.approvalPolicy === "never" && (configuredAppServer.start.transport !== "stdio" || isCodexAppServerApprovalPolicyAllowedByRequirements("untrusted"))
2726
- });
2727
- let policyAppServer = resolvePolicyAppServer();
2728
- let appServer = resolveCodexAppServerForModelProvider({
2729
- appServer: policyAppServer,
2730
- provider: reviewerPolicyContext.modelProvider,
2731
- model: reviewerPolicyContext.model,
2732
- config: params.config,
2733
- env: process.env,
2734
- agentDir
2735
- });
2736
- let approvalPolicyPromotedForOpenClawToolPolicy = configuredAppServer.approvalPolicy === "never" && appServer.approvalPolicy === "untrusted";
2737
- if (approvalPolicyPromotedForOpenClawToolPolicy) embeddedAgentLog.info("codex app-server approval policy promoted for OpenClaw tool policy", {
2738
- from: "never",
2739
- to: "untrusted",
2740
- beforeToolCallHook: beforeToolCallPolicy.hasBeforeToolCallHook,
2741
- trustedToolPolicies: beforeToolCallPolicy.trustedToolPolicies
3358
+ canUseAutoReview: canUseCodexModelBackedApprovalsReviewerForModel({
3359
+ modelProvider: selection.modelProvider,
3360
+ model: selection.model,
3361
+ config: params.config,
3362
+ env: {
3363
+ ...process.env,
3364
+ ...appServer.start.env,
3365
+ ...shellEnvironment
3366
+ },
3367
+ agentDir,
3368
+ homeScope: appServer.start.homeScope,
3369
+ codexArgs: appServer.start.args
3370
+ }),
3371
+ requirementsToml,
3372
+ policyLocked: startupBinding?.connectionScope === "supervision",
3373
+ execMode: execPolicy.mode
2742
3374
  });
3375
+ const resolveFinalAppServer = (configured, selection) => {
3376
+ const session = applySessionPermissionPolicy(configured, selection);
3377
+ const trusted = resolveCodexAppServerForModelProvider({
3378
+ appServer: session,
3379
+ provider: selection.modelProvider,
3380
+ model: selection.model,
3381
+ config: params.config,
3382
+ env: {
3383
+ ...process.env,
3384
+ ...session.start.env,
3385
+ ...shellEnvironment
3386
+ },
3387
+ agentDir
3388
+ });
3389
+ return {
3390
+ session,
3391
+ appServer: withPreparedProcessEnv(trusted)
3392
+ };
3393
+ };
3394
+ let resolvedAppServer = resolveFinalAppServer(configuredAppServer, reviewerPolicyContext);
3395
+ let appServer = resolvedAppServer.appServer;
2743
3396
  preDynamicStartupStages.mark("app-server-policy");
2744
3397
  preDynamicStartupStages.mark("native-hook-relay");
2745
3398
  const terminalState = {
@@ -2787,20 +3440,23 @@ async function prepareCodexAttemptConnection({ params, options }) {
2787
3440
  preDynamicStartupStages.mark("rotate-binding");
2788
3441
  if (startupBinding !== startupBindingBeforeRotation) {
2789
3442
  reviewerPolicyContext = resolveReviewerPolicyContext(startupBinding);
2790
- configuredAppServer = resolveRuntimeOptionsForBinding({
3443
+ configuredAppServer = resolveRuntimeOptionsForBinding(startupBinding, {
2791
3444
  modelProvider: reviewerPolicyContext.modelProvider,
2792
3445
  model: reviewerPolicyContext.model
2793
3446
  });
2794
- policyAppServer = resolvePolicyAppServer();
2795
- appServer = resolveCodexAppServerForModelProvider({
2796
- appServer: policyAppServer,
2797
- provider: reviewerPolicyContext.modelProvider,
2798
- model: reviewerPolicyContext.model,
2799
- config: params.config,
2800
- env: process.env,
2801
- agentDir
2802
- });
2803
- approvalPolicyPromotedForOpenClawToolPolicy = configuredAppServer.approvalPolicy === "never" && appServer.approvalPolicy === "untrusted";
3447
+ resolvedAppServer = resolveFinalAppServer(configuredAppServer, reviewerPolicyContext);
3448
+ appServer = resolvedAppServer.appServer;
3449
+ }
3450
+ const sessionPermissionPolicy = resolveCodexEffectiveSessionPermissionPolicy({
3451
+ appServer,
3452
+ permissionMode: params.permissionMode,
3453
+ sessionRoot: params.sessionRoot,
3454
+ defaultRoot: effectiveWorkspace
3455
+ });
3456
+ if (sessionPermissionPolicy) {
3457
+ params.permissionMode = sessionPermissionPolicy.mode;
3458
+ params.sessionRoot = sessionPermissionPolicy.root;
3459
+ (params.execOverrides ??= {}).mode = sessionPermissionPolicy.execMode;
2804
3460
  }
2805
3461
  const nativeHookRelayEvents = resolveCodexNativeHookRelayEvents({
2806
3462
  configuredEvents: options.nativeHookRelay?.events,
@@ -2809,22 +3465,10 @@ async function prepareCodexAttemptConnection({ params, options }) {
2809
3465
  const mutable = {
2810
3466
  startupBinding,
2811
3467
  startupContextTokens: startupBindingResolution.startupContextTokens,
2812
- pluginAppServer: appServer
3468
+ pluginAppServer: appServer,
3469
+ continuityCalibration: startupBindingBeforeRotation?.continuityCalibration
2813
3470
  };
2814
- const resolveRuntimeOptionsForCurrentBinding = (selection) => applyStoredBindingPermissions({
2815
- appServer: resolveCodexBindingAppServerConnection({
2816
- binding: mutable.startupBinding,
2817
- pluginConfig,
2818
- execPolicy,
2819
- modelProvider: selection.modelProvider,
2820
- model: selection.model,
2821
- config: params.config,
2822
- agentDir,
2823
- openClawSandboxActive: sandbox?.enabled === true
2824
- }).appServer,
2825
- binding: mutable.startupBinding,
2826
- execPolicyTouched: execPolicy.touched
2827
- });
3471
+ const resolveRuntimeOptionsForCurrentBinding = (selection) => resolveFinalAppServer(resolveRuntimeOptionsForBinding(mutable.startupBinding, selection), selection).appServer;
2828
3472
  return {
2829
3473
  params,
2830
3474
  options,
@@ -2846,6 +3490,8 @@ async function prepareCodexAttemptConnection({ params, options }) {
2846
3490
  contextSessionKey,
2847
3491
  sandbox,
2848
3492
  agentDir,
3493
+ shellEnvironment,
3494
+ disableLoginShell,
2849
3495
  bindingIdentity,
2850
3496
  bindingStore,
2851
3497
  activeContextEngine,
@@ -2858,7 +3504,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
2858
3504
  effectiveWorkspace,
2859
3505
  effectiveCwd,
2860
3506
  appServer,
2861
- approvalPolicyPromotedForOpenClawToolPolicy,
3507
+ sessionPermissionPolicy,
2862
3508
  nativeHookRelayEvents,
2863
3509
  runAbortController,
2864
3510
  terminalState,
@@ -2951,10 +3597,12 @@ function resolveContextEngineBootstrapProjectionDecision(params) {
2951
3597
  */
2952
3598
  async function buildCodexWorkspaceBootstrapContext(params) {
2953
3599
  try {
3600
+ const inheritsAgentWorkspace = (params.executionWorkspace ?? params.resolvedWorkspace) !== params.resolvedWorkspace;
3601
+ const promptWorkspace = inheritsAgentWorkspace ? params.resolvedWorkspace : params.effectiveWorkspace;
2954
3602
  const memoryToolsAvailable = params.memoryToolNames.length > 0 && canRouteCodexWorkspaceMemoryThroughTools({
2955
3603
  config: params.params.config,
2956
3604
  agentId: params.params.agentId ?? params.sessionAgentId,
2957
- workspaceDir: params.effectiveWorkspace
3605
+ workspaceDir: inheritsAgentWorkspace ? params.resolvedWorkspace : params.effectiveWorkspace
2958
3606
  });
2959
3607
  const bootstrapFiles = await resolveBootstrapFilesForRun({
2960
3608
  workspaceDir: params.resolvedWorkspace,
@@ -2974,7 +3622,7 @@ async function buildCodexWorkspaceBootstrapContext(params) {
2974
3622
  const memoryReferenceFiles = memoryToolRoutedBootstrapFiles.map((file) => remapCodexContextFilePath({
2975
3623
  file: toCodexEmbeddedContextFile(file),
2976
3624
  sourceWorkspaceDir: params.resolvedWorkspace,
2977
- targetWorkspaceDir: params.effectiveWorkspace
3625
+ targetWorkspaceDir: promptWorkspace
2978
3626
  }));
2979
3627
  const contextFiles = buildBootstrapContextForFiles(memoryToolsAvailable ? bootstrapFiles.filter((file) => !isCodexWorkspaceRootMemoryBootstrapFile({
2980
3628
  file,
@@ -2986,23 +3634,33 @@ async function buildCodexWorkspaceBootstrapContext(params) {
2986
3634
  }).map((file) => remapCodexContextFilePath({
2987
3635
  file,
2988
3636
  sourceWorkspaceDir: params.resolvedWorkspace,
2989
- targetWorkspaceDir: params.effectiveWorkspace
3637
+ targetWorkspaceDir: promptWorkspace
2990
3638
  }));
2991
3639
  const promptContextFiles = selectCodexWorkspacePromptContextFiles(contextFiles, {
2992
3640
  excludeMemory: memoryToolsAvailable,
2993
3641
  memoryWorkspaceDir: params.effectiveWorkspace
2994
3642
  });
2995
- const turnScopedDeveloperInstructionFiles = shouldInjectCodexOpenClawPromptContext(params.params) ? selectCodexWorkspaceTurnScopedDeveloperInstructionFiles(contextFiles) : [];
3643
+ const injectOpenClawContext = shouldInjectCodexOpenClawPromptContext(params.params);
3644
+ const restrictedProjectDocNeedsOpenClawCarrier = params.params.pluginHarnessToolPolicyRestricted === true && !params.params.disableTools && !isMessageOnlyCodexSourceReply(params.params) && params.params.bootstrapContextMode !== "lightweight";
3645
+ const threadDeveloperInstructionFiles = injectOpenClawContext && !params.ringZeroActive && (inheritsAgentWorkspace || restrictedProjectDocNeedsOpenClawCarrier) ? selectCodexWorkspaceAgentProjectInstructionFiles(contextFiles, params.resolvedWorkspace) : [];
3646
+ const turnScopedDeveloperInstructionFiles = injectOpenClawContext ? selectCodexWorkspaceTurnScopedDeveloperInstructionFiles(contextFiles) : [];
2996
3647
  return {
2997
3648
  bootstrapFiles,
2998
3649
  contextFiles,
3650
+ inheritsAgentWorkspace,
2999
3651
  promptContextFiles,
3652
+ threadDeveloperInstructionFiles,
3000
3653
  turnScopedDeveloperInstructionFiles,
3001
3654
  memoryReferenceFiles,
3002
3655
  memoryToolRoutedBootstrapFiles,
3003
3656
  memoryToolNames: [...params.memoryToolNames],
3004
3657
  memoryToolRouted: memoryToolsAvailable,
3005
3658
  promptContext: renderCodexWorkspaceBootstrapPromptContext(promptContextFiles),
3659
+ threadDeveloperInstructions: renderCodexWorkspaceDeveloperInstructions({
3660
+ files: threadDeveloperInstructionFiles,
3661
+ header: "## OpenClaw Agent Workspace Instructions",
3662
+ preamble: "OpenClaw loaded this bounded snapshot from the configured agent workspace."
3663
+ }),
3006
3664
  turnScopedDeveloperInstructions: renderCodexWorkspaceCollaborationDeveloperInstructions(turnScopedDeveloperInstructionFiles),
3007
3665
  memoryCollaborationInstructions: shouldInjectCodexOpenClawPromptContext(params.params) ? await renderCodexWorkspaceMemoryCollaborationInstructions({
3008
3666
  files: memoryReferenceFiles,
@@ -3018,7 +3676,8 @@ async function buildCodexWorkspaceBootstrapContext(params) {
3018
3676
  embeddedAgentLog.warn("failed to load codex workspace bootstrap instructions", { error });
3019
3677
  return {
3020
3678
  bootstrapFiles: [],
3021
- contextFiles: []
3679
+ contextFiles: [],
3680
+ inheritsAgentWorkspace: false
3022
3681
  };
3023
3682
  }
3024
3683
  }
@@ -3051,7 +3710,7 @@ function buildCodexSystemPromptReport(params) {
3051
3710
  injectedWorkspaceFiles: buildCodexBootstrapInjectionStats({
3052
3711
  bootstrapFiles: params.workspaceBootstrapContext.bootstrapFiles,
3053
3712
  injectedFiles: params.workspaceBootstrapContext.promptContextFiles ?? [],
3054
- developerInstructionFiles: params.workspaceBootstrapContext.turnScopedDeveloperInstructionFiles ?? [],
3713
+ developerInstructionFiles: [...params.workspaceBootstrapContext.threadDeveloperInstructionFiles ?? [], ...params.workspaceBootstrapContext.turnScopedDeveloperInstructionFiles ?? []],
3055
3714
  memoryToolRoutedBootstrapFiles: params.workspaceBootstrapContext.memoryToolRoutedBootstrapFiles ?? [],
3056
3715
  memoryToolRouted: params.workspaceBootstrapContext.memoryToolRouted === true
3057
3716
  }),
@@ -3132,12 +3791,17 @@ function buildCodexBootstrapInjectionStats(params) {
3132
3791
  const rawChars = file.missing ? 0 : (file.content ?? "").trimEnd().length;
3133
3792
  const memoryToolRoutedFile = baseName === CODEX_MEMORY_CONTEXT_BASENAME && params.memoryToolRouted === true && memoryToolRoutedPaths.has(normalizeCodexContextFilePath(pathValue));
3134
3793
  const injected = memoryToolRoutedFile ? void 0 : readCodexIndexedContextFileContent(injectedIndex, pathValue, fileName) ?? readCodexIndexedContextFileContent(developerInstructionIndex, pathValue, fileName);
3135
- let injectedChars = memoryToolRoutedFile ? 0 : injected?.length ?? 0;
3136
- let truncated = memoryToolRoutedFile ? false : !file.missing && injectedChars < rawChars;
3137
- if (injected === void 0 && CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName)) {
3138
- injectedChars = rawChars;
3139
- truncated = false;
3140
- }
3794
+ if (!file.missing && injected === void 0 && CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName)) return {
3795
+ name: displayName,
3796
+ path: pathValue,
3797
+ missing: false,
3798
+ rawChars,
3799
+ injectionStatus: "native_unverified",
3800
+ injectedChars: null,
3801
+ truncated: null
3802
+ };
3803
+ const injectedChars = memoryToolRoutedFile ? 0 : injected?.length ?? 0;
3804
+ const truncated = memoryToolRoutedFile ? false : !file.missing && injectedChars < rawChars;
3141
3805
  return {
3142
3806
  name: displayName,
3143
3807
  path: pathValue,
@@ -3272,7 +3936,7 @@ function renderCodexWorkspaceBootstrapPromptContext(contextFiles) {
3272
3936
  const files = contextFiles;
3273
3937
  if (files.length === 0) return;
3274
3938
  const lines = [
3275
- "OpenClaw loaded these user-editable workspace files for the current turn. Codex loads AGENTS.md natively. SOUL.md, IDENTITY.md, and USER.md are provided as turn-scoped collaboration instructions so native Codex subagents do not inherit them. Those files are not repeated here.",
3939
+ "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.",
3276
3940
  "",
3277
3941
  "# Project Context",
3278
3942
  "",
@@ -3295,6 +3959,10 @@ function selectCodexWorkspacePromptContextFiles(contextFiles, options = {}) {
3295
3959
  function selectCodexWorkspaceTurnScopedDeveloperInstructionFiles(contextFiles) {
3296
3960
  return selectCodexWorkspaceDeveloperInstructionFiles(contextFiles, CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES);
3297
3961
  }
3962
+ function selectCodexWorkspaceAgentProjectInstructionFiles(contextFiles, agentWorkspaceDir) {
3963
+ const agentProjectDocPath = path.join(path.resolve(agentWorkspaceDir), "AGENTS.md");
3964
+ return selectCodexWorkspaceDeveloperInstructionFiles(contextFiles, CODEX_NATIVE_PROJECT_DOC_BASENAMES).filter((file) => path.resolve(file.path) === agentProjectDocPath);
3965
+ }
3298
3966
  function selectCodexWorkspaceDeveloperInstructionFiles(contextFiles, basenames) {
3299
3967
  return contextFiles.filter((file) => {
3300
3968
  const baseName = getCodexContextFileBasename(file.path);
@@ -3439,23 +4107,120 @@ function compareCodexContextFiles(left, right) {
3439
4107
  if (leftBase !== rightBase) return leftBase.localeCompare(rightBase);
3440
4108
  return leftPath.localeCompare(rightPath);
3441
4109
  }
3442
- function compareCodexBootstrapFiles(left, right) {
3443
- return compareCodexContextFiles(toCodexEmbeddedContextFile(left), toCodexEmbeddedContextFile(right));
4110
+ function compareCodexBootstrapFiles(left, right) {
4111
+ return compareCodexContextFiles(toCodexEmbeddedContextFile(left), toCodexEmbeddedContextFile(right));
4112
+ }
4113
+ function normalizeCodexContextFilePath(filePath) {
4114
+ return filePath.trim().replaceAll("\\", "/").toLowerCase();
4115
+ }
4116
+ function getCodexContextFileDisplayBasename(filePath) {
4117
+ return filePath.trim().replaceAll("\\", "/").split("/").pop()?.trim() ?? "";
4118
+ }
4119
+ function getCodexContextFileBasename(filePath) {
4120
+ return normalizeCodexContextFilePath(filePath).split("/").pop() ?? "";
4121
+ }
4122
+ function normalizeCodexDynamicToolName(name) {
4123
+ return name.trim().toLowerCase();
4124
+ }
4125
+ function isNonEmptyString$1(value) {
4126
+ return typeof value === "string" && value.length > 0;
4127
+ }
4128
+ //#endregion
4129
+ //#region extensions/codex/src/app-server/run-attempt-state.ts
4130
+ async function clearCodexBindingAfterInvalidImagePayload(bindingStore, identity, fields) {
4131
+ const currentBinding = await bindingStore.read(identity);
4132
+ const expectedThreadId = fields.threadId ?? currentBinding?.threadId;
4133
+ if (!expectedThreadId) return;
4134
+ if (currentBinding && currentBinding.threadId !== expectedThreadId) {
4135
+ embeddedAgentLog.warn("codex app-server image payload error detected for unbound thread; preserving thread binding", {
4136
+ ...fields,
4137
+ boundThreadId: currentBinding.threadId
4138
+ });
4139
+ return;
4140
+ }
4141
+ if (currentBinding?.connectionScope === "supervision") {
4142
+ embeddedAgentLog.warn("codex app-server image payload error detected for supervised thread; preserving native binding", fields);
4143
+ return;
4144
+ }
4145
+ embeddedAgentLog.warn("codex app-server image payload error detected; clearing thread binding", fields);
4146
+ await bindingStore.mutate(identity, {
4147
+ kind: "clear",
4148
+ threadId: expectedThreadId
4149
+ });
4150
+ }
4151
+ async function markCodexAppServerBindingCoveredThroughTurn(params) {
4152
+ await params.bindingStore.mutate(params.identity, {
4153
+ kind: "patch",
4154
+ threadId: params.threadId,
4155
+ patch: {
4156
+ historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString(),
4157
+ ...params.continuityCalibration ? { continuityCalibration: params.continuityCalibration } : {}
4158
+ }
4159
+ });
4160
+ }
4161
+ function isNonEmptyString(value) {
4162
+ return typeof value === "string" && value.length > 0;
3444
4163
  }
3445
- function normalizeCodexContextFilePath(filePath) {
3446
- return filePath.trim().replaceAll("\\", "/").toLowerCase();
4164
+ function shouldUseFreshCodexThreadAfterContextEngineOverflow(params) {
4165
+ if (!params.contextEngineActive || params.thread.lifecycle.action !== "resumed") return false;
4166
+ const message = formatErrorMessage(params.error);
4167
+ return /ran out of room in the model'?s context window/iu.test(message) || /context window/iu.test(message) || /context length/iu.test(message) || /maximum context/iu.test(message) || /too many tokens/iu.test(message);
3447
4168
  }
3448
- function getCodexContextFileDisplayBasename(filePath) {
3449
- return filePath.trim().replaceAll("\\", "/").split("/").pop()?.trim() ?? "";
4169
+ function isCodexActiveCompactTurnError(error) {
4170
+ if (!(error instanceof CodexAppServerRpcError)) return false;
4171
+ const data = isJsonObject(error.data) ? error.data : void 0;
4172
+ const codexErrorInfo = isJsonObject(data?.codexErrorInfo) ? data.codexErrorInfo : void 0;
4173
+ return (isJsonObject(codexErrorInfo?.activeTurnNotSteerable) ? codexErrorInfo.activeTurnNotSteerable : void 0)?.turnKind === "compact";
3450
4174
  }
3451
- function getCodexContextFileBasename(filePath) {
3452
- return normalizeCodexContextFilePath(filePath).split("/").pop() ?? "";
4175
+ function readCodexFinalizationHookNotification(notification, threadId, turnId) {
4176
+ if (notification.method !== "hook/started" && notification.method !== "hook/completed") return;
4177
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
4178
+ const run = params && isJsonObject(params.run) ? params.run : void 0;
4179
+ if (params?.threadId !== threadId || params.turnId !== turnId || run?.eventName !== "stop" && run?.eventName !== "subagentStop" || typeof run.id !== "string" || !run.id) return;
4180
+ if (notification.method === "hook/started") return {
4181
+ phase: "started",
4182
+ runId: run.id
4183
+ };
4184
+ return {
4185
+ phase: "completed",
4186
+ runId: run.id,
4187
+ status: typeof run.status === "string" ? run.status : void 0
4188
+ };
3453
4189
  }
3454
- function normalizeCodexDynamicToolName(name) {
3455
- return name.trim().toLowerCase();
4190
+ function joinPresentSections(...sections) {
4191
+ return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
3456
4192
  }
3457
- function isNonEmptyString$1(value) {
3458
- return typeof value === "string" && value.length > 0;
4193
+ function prependCurrentInboundContext(prompt, context) {
4194
+ const text = context?.text.trim();
4195
+ return text ? [neutralizeCodexExplicitMentionSigils(text), prompt].filter(Boolean).join("\n\n") : prompt;
4196
+ }
4197
+ function buildCodexAppServerTimeoutDiagnostics(params) {
4198
+ const readNonBlankDetailString = (key) => {
4199
+ const value = params.details?.[key];
4200
+ return hasNonEmptyString(value) ? value : void 0;
4201
+ };
4202
+ const activeAppServerTurnRequests = asFiniteNumber(params.details?.activeAppServerTurnRequests);
4203
+ const activeTurnItemCount = asFiniteNumber(params.details?.activeTurnItemCount);
4204
+ const terminalTurnNotificationQueued = asBoolean(params.details?.terminalTurnNotificationQueued);
4205
+ const completionIdleWatchArmed = asBoolean(params.details?.completionIdleWatchArmed);
4206
+ const assistantCompletionIdleWatchArmed = asBoolean(params.details?.assistantCompletionIdleWatchArmed);
4207
+ const terminalIdleWatchArmed = asBoolean(params.details?.terminalIdleWatchArmed);
4208
+ return {
4209
+ ...params.idleMs !== void 0 ? { idleMs: params.idleMs } : {},
4210
+ ...params.timeoutMs !== void 0 ? { timeoutMs: params.timeoutMs } : {},
4211
+ ...params.lastActivityReason ? { lastActivityReason: params.lastActivityReason } : {},
4212
+ ...readNonBlankDetailString("lastNotificationMethod") ? { lastNotificationMethod: readNonBlankDetailString("lastNotificationMethod") } : {},
4213
+ ...readNonBlankDetailString("lastNotificationItemId") ? { lastNotificationItemId: readNonBlankDetailString("lastNotificationItemId") } : {},
4214
+ ...readNonBlankDetailString("lastNotificationItemType") ? { lastNotificationItemType: readNonBlankDetailString("lastNotificationItemType") } : {},
4215
+ ...readNonBlankDetailString("lastNotificationItemRole") ? { lastNotificationItemRole: readNonBlankDetailString("lastNotificationItemRole") } : {},
4216
+ ...readNonBlankDetailString("lastAssistantTextPreview") ? { lastAssistantTextPreview: readNonBlankDetailString("lastAssistantTextPreview") } : {},
4217
+ ...activeAppServerTurnRequests !== void 0 ? { activeAppServerTurnRequests } : {},
4218
+ ...activeTurnItemCount !== void 0 ? { activeTurnItemCount } : {},
4219
+ ...terminalTurnNotificationQueued !== void 0 ? { terminalTurnNotificationQueued } : {},
4220
+ ...completionIdleWatchArmed !== void 0 ? { completionIdleWatchArmed } : {},
4221
+ ...assistantCompletionIdleWatchArmed !== void 0 ? { assistantCompletionIdleWatchArmed } : {},
4222
+ ...terminalIdleWatchArmed !== void 0 ? { terminalIdleWatchArmed } : {}
4223
+ };
3459
4224
  }
3460
4225
  //#endregion
3461
4226
  //#region extensions/codex/src/app-server/run-attempt-context.ts
@@ -3527,16 +4292,20 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
3527
4292
  });
3528
4293
  historyState.messages = await readFencedHistory() ?? historyState.messages;
3529
4294
  }
4295
+ const memoryToolNames = getCodexWorkspaceMemoryToolNames(toolBridge.availableSpecs);
3530
4296
  const workspaceBootstrapContext = await buildCodexWorkspaceBootstrapContext({
3531
4297
  params: runtimeParams,
3532
- resolvedWorkspace,
4298
+ resolvedWorkspace: runtimeParams.bootstrapWorkspaceDir ?? resolvedWorkspace,
4299
+ executionWorkspace: resolvedWorkspace,
3533
4300
  effectiveWorkspace,
3534
4301
  sessionKey: contextSessionKey,
3535
4302
  sessionAgentId,
3536
- memoryToolNames: getCodexWorkspaceMemoryToolNames(toolBridge.availableSpecs),
4303
+ memoryToolNames,
4304
+ ringZeroActive: isHostScopedAgentToolActive("openclaw") && isSystemAgentOnlyCodexDynamicToolAllowlist(runtimeParams.toolsAllow),
3537
4305
  sandboxed: sandbox?.enabled === true
3538
4306
  });
3539
- const baseDeveloperInstructions = buildDeveloperInstructions(runtimeParams, { dynamicTools: toolBridge.availableSpecs });
4307
+ const agentWorkspaceDeveloperInstructions = workspaceBootstrapContext.threadDeveloperInstructions ? connection.mutable.startupBinding?.agentWorkspaceDeveloperInstructions ?? workspaceBootstrapContext.threadDeveloperInstructions : void 0;
4308
+ const baseDeveloperInstructions = joinPresentSections(buildDeveloperInstructions(runtimeParams, { dynamicTools: toolBridge.availableSpecs }), agentWorkspaceDeveloperInstructions);
3540
4309
  return {
3541
4310
  runtime,
3542
4311
  attemptTools,
@@ -3547,6 +4316,7 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
3547
4316
  hookRunner,
3548
4317
  buildActiveContextEngineRuntimeContext,
3549
4318
  workspaceBootstrapContext,
4319
+ agentWorkspaceDeveloperInstructions,
3550
4320
  baseDeveloperInstructions,
3551
4321
  openClawPromptContext: buildCodexOpenClawPromptContext({
3552
4322
  params: runtimeParams,
@@ -3570,11 +4340,16 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
3570
4340
  contextEngineProjection: void 0,
3571
4341
  precomputedStaleBindingContinuityProjectionApplied: false,
3572
4342
  staleBindingContinuityForcedFreshStart: false,
4343
+ noEngineContinuityProjectionApplied: false,
3573
4344
  inactiveThreadBootstrapBindingForcedFreshStart: initialInactiveThreadBootstrapBindingForcedFreshStart
3574
4345
  },
3575
4346
  codexContextProjectionMaxChars: resolveCodexContextEngineProjectionMaxChars({
3576
4347
  contextTokenBudget: effectiveContextTokenBudget,
3577
4348
  reserveTokens: resolveCodexContextEngineProjectionReserveTokens()
4349
+ }),
4350
+ codexContinuityProjectionMaxChars: resolveCodexContinuityProjectionMaxChars({
4351
+ contextTokenBudget: effectiveContextTokenBudget,
4352
+ calibration: connection.mutable.continuityCalibration
3578
4353
  })
3579
4354
  };
3580
4355
  }
@@ -3785,105 +4560,6 @@ function isInvalidCodexImagePayloadError(message) {
3785
4560
  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);
3786
4561
  }
3787
4562
  //#endregion
3788
- //#region extensions/codex/src/app-server/run-attempt-state.ts
3789
- async function clearCodexBindingAfterInvalidImagePayload(bindingStore, identity, fields) {
3790
- const currentBinding = await bindingStore.read(identity);
3791
- const expectedThreadId = fields.threadId ?? currentBinding?.threadId;
3792
- if (!expectedThreadId) return;
3793
- if (currentBinding && currentBinding.threadId !== expectedThreadId) {
3794
- embeddedAgentLog.warn("codex app-server image payload error detected for unbound thread; preserving thread binding", {
3795
- ...fields,
3796
- boundThreadId: currentBinding.threadId
3797
- });
3798
- return;
3799
- }
3800
- if (currentBinding?.connectionScope === "supervision") {
3801
- embeddedAgentLog.warn("codex app-server image payload error detected for supervised thread; preserving native binding", fields);
3802
- return;
3803
- }
3804
- embeddedAgentLog.warn("codex app-server image payload error detected; clearing thread binding", fields);
3805
- await bindingStore.mutate(identity, {
3806
- kind: "clear",
3807
- threadId: expectedThreadId
3808
- });
3809
- }
3810
- async function markCodexAppServerBindingCoveredThroughTurn(params) {
3811
- await params.bindingStore.mutate(params.identity, {
3812
- kind: "patch",
3813
- threadId: params.threadId,
3814
- patch: { historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString() }
3815
- });
3816
- }
3817
- function isNonEmptyString(value) {
3818
- return typeof value === "string" && value.length > 0;
3819
- }
3820
- function shouldUseFreshCodexThreadAfterContextEngineOverflow(params) {
3821
- if (!params.contextEngineActive || params.thread.lifecycle.action !== "resumed") return false;
3822
- const message = formatErrorMessage(params.error);
3823
- return /ran out of room in the model'?s context window/iu.test(message) || /context window/iu.test(message) || /context length/iu.test(message) || /maximum context/iu.test(message) || /too many tokens/iu.test(message);
3824
- }
3825
- function isCodexActiveCompactTurnError(error) {
3826
- if (!(error instanceof CodexAppServerRpcError)) return false;
3827
- const data = isJsonObject(error.data) ? error.data : void 0;
3828
- const codexErrorInfo = isJsonObject(data?.codexErrorInfo) ? data.codexErrorInfo : void 0;
3829
- return (isJsonObject(codexErrorInfo?.activeTurnNotSteerable) ? codexErrorInfo.activeTurnNotSteerable : void 0)?.turnKind === "compact";
3830
- }
3831
- function readCodexFinalizationHookNotification(notification, threadId, turnId) {
3832
- if (notification.method !== "hook/started" && notification.method !== "hook/completed") return;
3833
- const params = isJsonObject(notification.params) ? notification.params : void 0;
3834
- const run = params && isJsonObject(params.run) ? params.run : void 0;
3835
- if (params?.threadId !== threadId || params.turnId !== turnId || run?.eventName !== "stop" && run?.eventName !== "subagentStop" || typeof run.id !== "string" || !run.id) return;
3836
- if (notification.method === "hook/started") return {
3837
- phase: "started",
3838
- runId: run.id
3839
- };
3840
- return {
3841
- phase: "completed",
3842
- runId: run.id,
3843
- status: typeof run.status === "string" ? run.status : void 0
3844
- };
3845
- }
3846
- function joinPresentSections(...sections) {
3847
- return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
3848
- }
3849
- function prependCurrentInboundContext(prompt, context) {
3850
- const text = context?.text.trim();
3851
- return text ? [neutralizeCodexExplicitMentionSigils(text), prompt].filter(Boolean).join("\n\n") : prompt;
3852
- }
3853
- function waitForCodexNotificationDispatchTurn() {
3854
- return new Promise((resolve) => {
3855
- setImmediate(resolve);
3856
- });
3857
- }
3858
- function buildCodexAppServerTimeoutDiagnostics(params) {
3859
- const readNonBlankDetailString = (key) => {
3860
- const value = params.details?.[key];
3861
- return hasNonEmptyString(value) ? value : void 0;
3862
- };
3863
- const activeAppServerTurnRequests = asFiniteNumber(params.details?.activeAppServerTurnRequests);
3864
- const activeTurnItemCount = asFiniteNumber(params.details?.activeTurnItemCount);
3865
- const terminalTurnNotificationQueued = asBoolean(params.details?.terminalTurnNotificationQueued);
3866
- const completionIdleWatchArmed = asBoolean(params.details?.completionIdleWatchArmed);
3867
- const assistantCompletionIdleWatchArmed = asBoolean(params.details?.assistantCompletionIdleWatchArmed);
3868
- const terminalIdleWatchArmed = asBoolean(params.details?.terminalIdleWatchArmed);
3869
- return {
3870
- ...params.idleMs !== void 0 ? { idleMs: params.idleMs } : {},
3871
- ...params.timeoutMs !== void 0 ? { timeoutMs: params.timeoutMs } : {},
3872
- ...params.lastActivityReason ? { lastActivityReason: params.lastActivityReason } : {},
3873
- ...readNonBlankDetailString("lastNotificationMethod") ? { lastNotificationMethod: readNonBlankDetailString("lastNotificationMethod") } : {},
3874
- ...readNonBlankDetailString("lastNotificationItemId") ? { lastNotificationItemId: readNonBlankDetailString("lastNotificationItemId") } : {},
3875
- ...readNonBlankDetailString("lastNotificationItemType") ? { lastNotificationItemType: readNonBlankDetailString("lastNotificationItemType") } : {},
3876
- ...readNonBlankDetailString("lastNotificationItemRole") ? { lastNotificationItemRole: readNonBlankDetailString("lastNotificationItemRole") } : {},
3877
- ...readNonBlankDetailString("lastAssistantTextPreview") ? { lastAssistantTextPreview: readNonBlankDetailString("lastAssistantTextPreview") } : {},
3878
- ...activeAppServerTurnRequests !== void 0 ? { activeAppServerTurnRequests } : {},
3879
- ...activeTurnItemCount !== void 0 ? { activeTurnItemCount } : {},
3880
- ...terminalTurnNotificationQueued !== void 0 ? { terminalTurnNotificationQueued } : {},
3881
- ...completionIdleWatchArmed !== void 0 ? { completionIdleWatchArmed } : {},
3882
- ...assistantCompletionIdleWatchArmed !== void 0 ? { assistantCompletionIdleWatchArmed } : {},
3883
- ...terminalIdleWatchArmed !== void 0 ? { terminalIdleWatchArmed } : {}
3884
- };
3885
- }
3886
- //#endregion
3887
4563
  //#region extensions/codex/src/app-server/settled-turn-context.ts
3888
4564
  function collectUniqueMessageIdentities(messages) {
3889
4565
  const identities = /* @__PURE__ */ new Map();
@@ -3973,100 +4649,17 @@ async function captureCodexSettledTurnFinalizationContext(params) {
3973
4649
  }
3974
4650
  //#endregion
3975
4651
  //#region extensions/codex/src/app-server/trajectory.ts
3976
- const SENSITIVE_FIELD_RE = /(?:authorization|cookie|credential|key|password|passwd|secret|token)/iu;
3977
- const PRIVATE_PAYLOAD_FIELD_RE = /(?:image|screenshot|attachment|fileData|dataUri)/iu;
3978
- const AUTHORIZATION_VALUE_RE = /\b(Bearer|Basic)\s+[A-Za-z0-9+/._~=-]{8,}/giu;
3979
- const JWT_VALUE_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/gu;
3980
- const COOKIE_PAIR_RE = /\b([A-Za-z][A-Za-z0-9_.-]{1,64})=([A-Za-z0-9+/._~%=-]{16,})(?=;|\s|$)/gu;
3981
- const TRAJECTORY_RUNTIME_EVENT_MAX_BYTES = 256 * 1024;
3982
- const TRAJECTORY_RUNTIME_OVERSIZE_PRESERVED_DATA_KEYS = ["usage", "promptCache"];
3983
- function boundedTrajectoryEvent(event) {
3984
- const line = JSON.stringify(event);
3985
- const bytes = Buffer.byteLength(line, "utf8");
3986
- if (bytes <= TRAJECTORY_RUNTIME_EVENT_MAX_BYTES) return event;
3987
- const originalData = event.data && typeof event.data === "object" && !Array.isArray(event.data) ? event.data : {};
3988
- const originalDataKeys = Object.keys(originalData);
3989
- const preservedDataKeys = /* @__PURE__ */ new Set();
3990
- const baseData = {
3991
- truncated: true,
3992
- originalBytes: bytes,
3993
- limitBytes: TRAJECTORY_RUNTIME_EVENT_MAX_BYTES,
3994
- reason: "trajectory-event-size-limit"
3995
- };
3996
- const buildTruncatedEvent = (includeDroppedFields) => {
3997
- const data = { ...baseData };
3998
- for (const key of TRAJECTORY_RUNTIME_OVERSIZE_PRESERVED_DATA_KEYS) if (preservedDataKeys.has(key)) data[key] = originalData[key];
3999
- if (includeDroppedFields) {
4000
- const droppedFields = originalDataKeys.filter((key) => !preservedDataKeys.has(key));
4001
- if (droppedFields.length > 0) data.droppedFields = droppedFields;
4002
- }
4003
- const truncatedEvent = {
4004
- ...event,
4005
- data
4006
- };
4007
- const truncated = JSON.stringify(truncatedEvent);
4008
- if (Buffer.byteLength(truncated, "utf8") <= TRAJECTORY_RUNTIME_EVENT_MAX_BYTES) return truncatedEvent;
4009
- };
4010
- let best = buildTruncatedEvent(true) ?? buildTruncatedEvent(false);
4011
- if (!best) return;
4012
- for (const key of TRAJECTORY_RUNTIME_OVERSIZE_PRESERVED_DATA_KEYS) {
4013
- if (!Object.hasOwn(originalData, key)) continue;
4014
- preservedDataKeys.add(key);
4015
- const next = buildTruncatedEvent(true) ?? buildTruncatedEvent(false);
4016
- if (next) {
4017
- best = next;
4018
- continue;
4019
- }
4020
- preservedDataKeys.delete(key);
4021
- }
4022
- return best;
4023
- }
4024
- function createCodexHostTrajectorySink(params) {
4025
- return {
4026
- write: (event) => {
4027
- params.recorder.recordEvent(event.type, event.data);
4028
- },
4029
- flush: async () => {
4030
- await params.recorder.flush();
4031
- }
4032
- };
4033
- }
4034
- /** Creates a trajectory recorder when trajectory capture is enabled for the environment. */
4652
+ /** Creates a trajectory recorder when the host exposes its capture capability. */
4035
4653
  function createCodexTrajectoryRecorder(params) {
4036
- if (!parseTrajectoryEnabled(params.env ?? process.env)) return null;
4037
- if (!params.trajectoryRecorder) {
4038
- params.warn?.("codex trajectory capture requires the SQLite host recorder", {
4039
- sessionId: params.attempt.sessionId,
4040
- reason: "sqlite-recorder-unavailable"
4041
- });
4042
- return null;
4043
- }
4044
- const sink = createCodexHostTrajectorySink({ recorder: params.trajectoryRecorder });
4045
- let seq = 0;
4046
- const attribution = resolveCodexLocalRuntimeAttribution(params.attempt);
4654
+ if (!params.trajectory) return null;
4655
+ const trajectory = params.trajectory;
4047
4656
  return {
4048
4657
  recordEvent: (type, data) => {
4049
- const event = boundedTrajectoryEvent({
4050
- traceSchema: "openclaw-trajectory",
4051
- schemaVersion: 1,
4052
- traceId: params.attempt.sessionId,
4053
- source: "runtime",
4054
- type,
4055
- ts: (/* @__PURE__ */ new Date()).toISOString(),
4056
- seq: seq += 1,
4057
- sourceSeq: seq,
4058
- sessionId: params.attempt.sessionId,
4059
- sessionKey: params.attempt.sessionKey,
4060
- runId: params.attempt.runId,
4061
- workspaceDir: params.cwd,
4062
- provider: attribution.provider,
4063
- modelId: params.attempt.modelId,
4064
- modelApi: attribution.api,
4065
- data: data ? sanitizeValue(data) : void 0
4066
- });
4067
- if (event) sink.write(event);
4658
+ try {
4659
+ trajectory.recordEvent(type, data);
4660
+ } catch {}
4068
4661
  },
4069
- flush: sink.flush
4662
+ flush: trajectory.flush
4070
4663
  };
4071
4664
  }
4072
4665
  /** Records compiled prompt/tool context at the start of a Codex runtime attempt. */
@@ -4095,12 +4688,6 @@ function recordCodexTrajectoryCompletion(recorder, params) {
4095
4688
  messagesSnapshot: params.result.messagesSnapshot
4096
4689
  });
4097
4690
  }
4098
- function parseTrajectoryEnabled(env) {
4099
- const value = env.OPENCLAW_TRAJECTORY?.trim().toLowerCase();
4100
- if (value === "1" || value === "true" || value === "yes" || value === "on") return true;
4101
- if (value === "0" || value === "false" || value === "no" || value === "off") return false;
4102
- return true;
4103
- }
4104
4691
  function toTrajectoryToolDefinitions(tools) {
4105
4692
  if (!tools || tools.length === 0) return;
4106
4693
  return flattenCodexDynamicToolFunctions(tools).flatMap((tool) => {
@@ -4109,31 +4696,10 @@ function toTrajectoryToolDefinitions(tools) {
4109
4696
  return [{
4110
4697
  name,
4111
4698
  description: tool.description,
4112
- parameters: sanitizeValue(tool.inputSchema)
4699
+ parameters: tool.inputSchema
4113
4700
  }];
4114
4701
  }).toSorted((left, right) => left.name.localeCompare(right.name));
4115
4702
  }
4116
- function sanitizeValue(value, depth = 0, key = "") {
4117
- if (value == null || typeof value === "boolean" || typeof value === "number") return value;
4118
- if (typeof value === "string") {
4119
- if (SENSITIVE_FIELD_RE.test(key)) return "<redacted>";
4120
- if (value.startsWith("data:") && value.length > 256) return `<redacted data-uri ${value.slice(0, value.indexOf(",")).length} chars>`;
4121
- if (PRIVATE_PAYLOAD_FIELD_RE.test(key) && value.length > 256) return "<redacted payload>";
4122
- const redacted = redactSensitiveString(value);
4123
- return redacted.length > 2e4 ? `${truncateUtf16Safe(redacted, 2e4)}…` : redacted;
4124
- }
4125
- if (depth >= 6) return "<truncated>";
4126
- if (Array.isArray(value)) return value.slice(0, 100).map((entry) => sanitizeValue(entry, depth + 1, key));
4127
- if (typeof value === "object") {
4128
- const next = {};
4129
- for (const [keyLocal, child] of Object.entries(value).slice(0, 100)) next[keyLocal] = sanitizeValue(child, depth + 1, keyLocal);
4130
- return next;
4131
- }
4132
- return JSON.stringify(value);
4133
- }
4134
- function redactSensitiveString(value) {
4135
- return value.replace(AUTHORIZATION_VALUE_RE, "$1 <redacted>").replace(JWT_VALUE_RE, "<redacted-jwt>").replace(COOKIE_PAIR_RE, "$1=<redacted>");
4136
- }
4137
4703
  /** Converts arbitrary prompt errors into trajectory-safe text. */
4138
4704
  function normalizeCodexTrajectoryError(value) {
4139
4705
  if (!value) return null;
@@ -4154,7 +4720,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4154
4720
  const { hookContextWindowFields, hookRunner } = context;
4155
4721
  const { connection, preparedAuthBinding } = runtime;
4156
4722
  const { effectiveRuntimeProviderId, effectiveRuntimeModelId } = runtime;
4157
- const { params, terminalState, runAbortController, activeContextEngine, bindingStore, bindingIdentity, appServer, usesSupervisionConnection, sessionAgentId, contextSessionKey, effectiveCwd, attemptStartedAt, startupAuthProfileId } = connection;
4723
+ const { params, terminalState, runAbortController, activeContextEngine, bindingStore, bindingIdentity, appServer, usesSupervisionConnection, sessionAgentId, contextSessionKey, effectiveCwd, agentDir, attemptStartedAt, startupAuthProfileId } = connection;
4158
4724
  const { toolBridge, toolState } = attemptTools;
4159
4725
  const { state, completion, pendingOpenClawDynamicToolCompletionIds, activeTurnItemIds, activeCompletionBlockerItemIds, activeFinalizationHookRunIds, turnWatches } = turnRuntime;
4160
4726
  const { emitLifecycleTerminal, buildLifecycleTerminalMeta } = lifecycle;
@@ -4162,7 +4728,20 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4162
4728
  const { codexModelCallDiagnostics } = requestRuntime;
4163
4729
  const { activeTurnId, activeProjector, streamState, freezeRunTerminalOutcome, notifyUserMessagePersisted } = activeTurn;
4164
4730
  await completion;
4165
- await drainNotificationQueue();
4731
+ await state.abortCleanup;
4732
+ const drain = drainNotificationQueue();
4733
+ const abortGraceElapsed = createDeferred();
4734
+ let abortGraceTimer;
4735
+ const abortListener = addAbortListener(runAbortController.signal, () => {
4736
+ abortGraceTimer = setTimeout(() => abortGraceElapsed.resolve(), TURN_FINALIZE_DRAIN_ABORT_GRACE_MS);
4737
+ abortGraceTimer.unref?.();
4738
+ });
4739
+ try {
4740
+ await Promise.race([drain, abortGraceElapsed.promise]);
4741
+ } finally {
4742
+ abortListener[Symbol.dispose]();
4743
+ clearTimeout(abortGraceTimer);
4744
+ }
4166
4745
  const hasQuiescentCompletedAssistant = activeProjector.hasCompletedTerminalAssistantText() && state.activeAppServerTurnRequests === 0 && activeTurnItemIds.size === 0 && activeCompletionBlockerItemIds.size === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0 && activeFinalizationHookRunIds.size === 0 && state.unsettledFinalizationHookCount === 0 && state.rejectedFinalizationHookAssistant === void 0;
4167
4746
  const hasRecoverableCompletedAssistant = !turnWatches.isCompletionIdleWatchPinnedByTerminalError() && turnWatches.isAssistantCompletionIdleWatchArmed() && hasQuiescentCompletedAssistant;
4168
4747
  const recoveredTurnWatchTimeout = state.turnCompletionIdleTimedOut && !terminalState.explicitCancellationObserved && hasRecoverableCompletedAssistant && activeProjector.recoverCompletedTerminalAssistantAfterTurnWatchTimeout();
@@ -4186,9 +4765,9 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4186
4765
  const projectedTerminal = attemptTerminal.project(result.terminal);
4187
4766
  const effectiveTimedOut = state.timedOut && !recoveredTurnWatchTimeout;
4188
4767
  const effectiveTurnCompletionIdleTimedOut = state.turnCompletionIdleTimedOut && !recoveredTurnWatchTimeout;
4189
- const isFinalAborted = () => projectedTerminal.aborted || terminalState.explicitCancellationObserved || runAbortController.signal.aborted && !state.clientClosedAbort && !recoveredTurnWatchTimeout;
4768
+ const isFinalAborted = () => terminalState.explicitCancellationObserved || !resourceState.executionDisconnectError && (projectedTerminal.aborted || runAbortController.signal.aborted && !state.clientClosedAbort && !recoveredTurnWatchTimeout);
4190
4769
  const clientClosedPromptErrorForFinal = state.clientClosedPromptError && hasRecoverableCompletedAssistant ? void 0 : state.clientClosedPromptError;
4191
- let finalPromptError = clientClosedPromptErrorForFinal ?? (effectiveTurnCompletionIdleTimedOut ? state.turnCompletionIdleTimeoutMessage : effectiveTimedOut ? "codex app-server attempt timed out" : projectedTerminal.promptError);
4770
+ let finalPromptError = resourceState.executionDisconnectError ?? clientClosedPromptErrorForFinal ?? (effectiveTurnCompletionIdleTimedOut ? state.turnCompletionIdleTimeoutMessage : effectiveTimedOut ? "codex app-server attempt timed out" : projectedTerminal.promptError);
4192
4771
  const finalPromptErrorMessage = typeof finalPromptError === "string" ? finalPromptError : finalPromptError instanceof Error ? finalPromptError.message : finalPromptError ? formatErrorMessage(finalPromptError) : void 0;
4193
4772
  if (isInvalidCodexImagePayloadError(finalPromptErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(bindingStore, bindingIdentity, {
4194
4773
  phase: "turn_completed",
@@ -4229,6 +4808,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4229
4808
  authProfileId: startupAuthProfileId,
4230
4809
  rateLimits: readRecentCodexRateLimits(resourceState.client)
4231
4810
  });
4811
+ finalPromptError = resourceState.executionDisconnectError ?? finalPromptError;
4232
4812
  const finalPromptErrorSource = effectiveTimedOut || clientClosedPromptErrorForFinal ? "prompt" : projectedTerminal.promptErrorSource;
4233
4813
  const codexAppServerFailureKind = clientClosedPromptErrorForFinal ? "client_closed_before_turn_completed" : effectiveTurnCompletionIdleTimedOut ? "turn_completion_idle_timeout" : void 0;
4234
4814
  const replayBlockedReason = codexAppServerFailureKind ? resolveCodexAppServerReplayBlockedReason(result) : void 0;
@@ -4332,20 +4912,11 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4332
4912
  modelIterations: result.modelIterations ?? 0,
4333
4913
  skillWorkshopAvailable: flattenCodexDynamicToolFunctions(attemptTools.toolBridge.availableSpecs).some((tool) => tool.name === "skill_workshop"),
4334
4914
  compacted: (result.compactionCount ?? 0) > 0,
4335
- messageChannel: params.messageChannel,
4336
- messageProvider: params.messageProvider,
4337
- chatType: params.chatType,
4338
- agentAccountId: params.agentAccountId,
4339
- groupId: params.groupId,
4340
- groupChannel: params.groupChannel,
4341
- groupSpace: params.groupSpace,
4342
- memberRoleIds: params.memberRoleIds,
4343
- spawnedBy: params.spawnedBy,
4344
4915
  senderId: params.senderId ?? void 0,
4345
- senderName: params.senderName,
4346
- senderUsername: params.senderUsername,
4347
- senderE164: params.senderE164,
4348
- senderIsOwner: params.senderIsOwner
4916
+ foregroundPromptContext: buildEmbeddedForegroundPromptContext({
4917
+ ...params,
4918
+ agentId: sessionAgentId
4919
+ }, agentDir)
4349
4920
  },
4350
4921
  hookRunner
4351
4922
  });
@@ -4354,7 +4925,11 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4354
4925
  await markCodexAppServerBindingCoveredThroughTurn({
4355
4926
  bindingStore,
4356
4927
  identity: bindingIdentity,
4357
- threadId: resourceState.thread.threadId
4928
+ threadId: resourceState.thread.threadId,
4929
+ continuityCalibration: context.promptState.noEngineContinuityProjectionApplied ? buildCodexContinuityCalibration({
4930
+ promptChars: prompt.turnState.codexTurnPromptText.length,
4931
+ inputTokens: (result.attemptUsage?.input ?? 0) + (result.attemptUsage?.cacheRead ?? 0) + (result.attemptUsage?.cacheWrite ?? 0)
4932
+ }) : void 0
4358
4933
  });
4359
4934
  } catch (error) {
4360
4935
  if (resourceState.thread.connectionScope === "supervision") throw error;
@@ -4407,6 +4982,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4407
4982
  });
4408
4983
  const finalizedResult = {
4409
4984
  ...result,
4985
+ ...toolState.yieldAcknowledgment ? { yieldAcknowledgment: toolState.yieldAcknowledgment } : {},
4410
4986
  terminal: attemptTerminal.normalize({
4411
4987
  timedOut: effectiveTimedOut,
4412
4988
  aborted: finalAborted,
@@ -4420,6 +4996,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4420
4996
  ...terminalAnchor ? { contextEngineTerminalAnchor: terminalAnchor } : {},
4421
4997
  ...settledTurnFinalizationContext ? { settledTurnFinalizationContext } : {},
4422
4998
  ...resourceState.runtimeArtifact ? { runtimeArtifact: resourceState.runtimeArtifact } : {},
4999
+ ...resourceState.runtimeContinuationStarted ? { runtimeContinuationStarted: true } : {},
4423
5000
  ...!finalAborted && !effectiveTimedOut && !finalPromptError && preparedAuthBinding ? { authBindingFingerprint: preparedAuthBinding.fingerprint } : {},
4424
5001
  systemPromptReport
4425
5002
  };
@@ -4764,7 +5341,7 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
4764
5341
  state.turnCrossedToolHandoff = notificationState.turnCrossedToolHandoff;
4765
5342
  if (notificationState.isCurrentTurnNotification && notification.method === "item/completed") {
4766
5343
  const item = readCodexNotificationItem(notification.params);
4767
- if (item?.type === "userMessage" && typeof item.clientId === "string") steeringQueue?.confirmConsumed(item.clientId);
5344
+ if (item?.type === "userMessage" && typeof item.clientId === "string") await steeringQueue?.confirmConsumed(item.clientId);
4768
5345
  }
4769
5346
  if (notificationState.isTurnAbortMarker) state.sawCodexInterruptMarker = true;
4770
5347
  const hookNotification = readCodexFinalizationHookNotification(notification, resourceState.thread.threadId, turnId);
@@ -4775,7 +5352,6 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
4775
5352
  }
4776
5353
  if (notificationState.isTurnTerminal) state.terminalTurnNotificationQueued = true;
4777
5354
  try {
4778
- await waitForCodexNotificationDispatchTurn();
4779
5355
  await projector.handleNotification(notification);
4780
5356
  const canRelease = isAssistantCompletionReleaseNotification(notification, state.turnCrossedToolHandoff) || notificationState.isCurrentTurnNotification && state.turnCrossedToolHandoff && notification.method === "rawResponseItem/completed" && projector.canReleaseLatestTerminalAssistantAfterToolHandoff();
4781
5357
  if (notificationState.isCurrentTurnNotification && canRelease) {
@@ -4871,11 +5447,12 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
4871
5447
  }
4872
5448
  //#endregion
4873
5449
  //#region extensions/codex/src/app-server/run-attempt-prompt.ts
5450
+ const CODEX_META_KEY = "__openclaw";
4874
5451
  function isRestrictivePromptToolsAllow(toolsAllow) {
4875
5452
  return toolsAllow !== void 0 && !toolsAllow.some((name) => name.trim() === "*");
4876
5453
  }
4877
5454
  async function prepareCodexAttemptPrompt(context) {
4878
- const { runtime, attemptTools, historyState, hookContext, workspaceBootstrapContext, buildActiveContextEngineRuntimeContext, baseDeveloperInstructions, openClawPromptContext, skillsCollaborationInstructions, promptState, codexContextProjectionMaxChars } = context;
5455
+ const { runtime, attemptTools, historyState, hookContext, workspaceBootstrapContext, buildActiveContextEngineRuntimeContext, baseDeveloperInstructions, openClawPromptContext, skillsCollaborationInstructions, promptState, codexContextProjectionMaxChars, codexContinuityProjectionMaxChars } = context;
4879
5456
  const { connection, buildActiveRunAttemptParams, effectiveContextTokenBudget, effectiveRuntimeModelId, effectiveRuntimeProviderId } = runtime;
4880
5457
  const { params, activeContextEngine, usesSupervisionConnection, mutable, isInactiveThreadBootstrapBinding, bindingStore, bindingIdentity, agentDir, appServer, contextSessionKey, effectiveWorkspace, sandbox } = connection;
4881
5458
  const { toolBridge } = attemptTools;
@@ -4884,11 +5461,12 @@ async function prepareCodexAttemptPrompt(context) {
4884
5461
  assembledMessages: historyState.messages,
4885
5462
  originalHistoryMessages: historyState.messages,
4886
5463
  prompt: params.prompt,
4887
- maxRenderedContextChars: codexContextProjectionMaxChars
5464
+ maxRenderedContextChars: codexContinuityProjectionMaxChars
4888
5465
  });
4889
5466
  promptState.promptText = projection.promptText;
4890
5467
  promptState.promptContextRange = projection.promptContextRange;
4891
5468
  promptState.prePromptMessageCount = projection.prePromptMessageCount;
5469
+ promptState.noEngineContinuityProjectionApplied = true;
4892
5470
  };
4893
5471
  const applyActiveContextEngineProjection = async (decisionStartupBinding) => {
4894
5472
  if (!activeContextEngine) return;
@@ -4962,15 +5540,21 @@ async function prepareCodexAttemptPrompt(context) {
4962
5540
  }
4963
5541
  const codexModelInputHistoryMessages = [];
4964
5542
  const buildPromptFromCurrentInputs = async () => {
4965
- const result = await resolveAgentHarnessBeforePromptBuildResult({
5543
+ return await resolveAgentHarnessBeforePromptBuildResult({
4966
5544
  prompt: prependCurrentInboundContext(promptState.promptText, params.currentInboundContext),
4967
- developerInstructions: promptState.developerInstructions,
5545
+ developerInstructions: { build: ({ toolsAllow }) => {
5546
+ 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.");
5547
+ return promptState.developerInstructions;
5548
+ } },
4968
5549
  messages: structuredClone(historyState.messages),
4969
5550
  ctx: hookContext,
4970
- bootstrapContextRunKind: params.bootstrapContextRunKind
5551
+ bootstrapContextRunKind: params.bootstrapContextRunKind,
5552
+ toolAuthority: {
5553
+ fingerprint: params.toolAuthorityFingerprint,
5554
+ activeToolNames: () => flattenCodexDynamicToolFunctions(toolBridge.availableSpecs).map((tool) => tool.name).filter(isNonEmptyString),
5555
+ assertActive: params.hostCapabilities.assertActive
5556
+ }
4971
5557
  });
4972
- if (isRestrictivePromptToolsAllow(result.toolsAllow)) throw new Error("Codex app-server cannot enforce before_prompt_build toolsAllow; use the embedded or Copilot runtime for turn-scoped tool policy.");
4973
- return result;
4974
5558
  };
4975
5559
  const resolveShiftedPromptInputRange = (prompt, promptInputRange, turnPromptText) => {
4976
5560
  if (!promptInputRange || promptInputRange.start < 0 || promptInputRange.end < promptInputRange.start || promptInputRange.end > prompt.length || !turnPromptText.endsWith(prompt)) return;
@@ -5040,12 +5624,11 @@ async function prepareCodexAttemptPrompt(context) {
5040
5624
  const cutoff = Date.parse(binding.historyCoveredThrough ?? "");
5041
5625
  return historyState.messages.filter((message) => {
5042
5626
  if (message.role !== "user" && message.role !== "assistant") return false;
5043
- const record = message;
5044
- const meta = record["__openclaw"];
5627
+ const meta = CODEX_META_KEY in message ? message[CODEX_META_KEY] : void 0;
5045
5628
  const mirrorIdentity = meta && typeof meta === "object" && !Array.isArray(meta) ? meta.mirrorIdentity : void 0;
5046
5629
  const mirrorOrigin = meta && typeof meta === "object" && !Array.isArray(meta) ? meta.mirrorOrigin : void 0;
5047
5630
  const timestamp = typeof message.timestamp === "number" ? message.timestamp : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
5048
- return !(typeof record.idempotencyKey === "string" && record.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);
5631
+ 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);
5049
5632
  });
5050
5633
  };
5051
5634
  const applyResumeStaleBindingContinuityProjection = (binding) => {
@@ -5055,11 +5638,12 @@ async function prepareCodexAttemptPrompt(context) {
5055
5638
  assembledMessages: newerVisibleMessages,
5056
5639
  originalHistoryMessages: historyState.messages,
5057
5640
  prompt: params.prompt,
5058
- maxRenderedContextChars: codexContextProjectionMaxChars
5641
+ maxRenderedContextChars: codexContinuityProjectionMaxChars
5059
5642
  });
5060
5643
  promptState.promptText = projection.promptText;
5061
5644
  promptState.promptContextRange = projection.promptContextRange;
5062
5645
  promptState.prePromptMessageCount = projection.prePromptMessageCount;
5646
+ promptState.noEngineContinuityProjectionApplied = true;
5063
5647
  return true;
5064
5648
  };
5065
5649
  const precomputeNoContextEngineStaleBindingProjection = () => {
@@ -5171,15 +5755,13 @@ function prepareCodexAttemptResources(prompt) {
5171
5755
  const { connection, hookChannelId } = runtime;
5172
5756
  const { appServer, params, effectiveCwd, sessionAgentId, sandboxSessionKey, runAbortController, sandbox, options, nativeHookRelayEvents } = connection;
5173
5757
  const { toolBridge } = attemptTools;
5174
- const hostTrajectoryRecorder = params.trajectoryRecorder;
5175
5758
  const trajectoryRecorder = createCodexTrajectoryRecorder({
5176
5759
  attempt: params,
5177
5760
  cwd: effectiveCwd,
5178
5761
  developerInstructions: buildRenderedCodexDeveloperInstructions(),
5179
5762
  prompt: turnState.codexTurnPromptText,
5180
- trajectoryRecorder: hostTrajectoryRecorder,
5181
- tools: toolBridge.availableSpecs,
5182
- warn: (message, fields) => embeddedAgentLog.warn(message, fields)
5763
+ trajectory: params.hostCapabilities.trajectory,
5764
+ tools: toolBridge.availableSpecs
5183
5765
  });
5184
5766
  const state = {
5185
5767
  client: void 0,
@@ -5192,12 +5774,14 @@ function prepareCodexAttemptResources(prompt) {
5192
5774
  trajectoryEndRecorded: false,
5193
5775
  nativeHookRelay: void 0,
5194
5776
  nativeSubagentMonitor: void 0,
5777
+ runtimeContinuationStarted: false,
5195
5778
  nativePreToolUseFailureFallbackActive: false,
5196
5779
  nativePreToolUseFailureFallbackTerminalReason: void 0,
5197
5780
  releaseSharedClientLease: void 0,
5198
5781
  startupClientUnsafe: false,
5199
5782
  sharedCodexClientRetiredForOneShotCleanup: false,
5200
- sandboxExecEnvironmentAcquired: false,
5783
+ sandboxExecEnvironment: void 0,
5784
+ executionDisconnectError: void 0,
5201
5785
  codexEnvironmentSelection: void 0,
5202
5786
  codexExecutionCwd: effectiveCwd,
5203
5787
  codexSandboxPolicy: void 0,
@@ -5236,7 +5820,7 @@ function prepareCodexAttemptResources(prompt) {
5236
5820
  if (params.cleanupBundleMcpOnRunEnd !== true || state.sharedCodexClientRetiredForOneShotCleanup) return;
5237
5821
  state.sharedCodexClientRetiredForOneShotCleanup = true;
5238
5822
  const retired = clearSharedCodexAppServerClientIfCurrentAndUnclaimed(state.client);
5239
- embeddedAgentLog.info("codex app-server one-shot cleanup checked shared client retirement", {
5823
+ embeddedAgentLog.debug("codex app-server one-shot cleanup checked shared client retirement", {
5240
5824
  runId: params.runId,
5241
5825
  sessionId: params.sessionId,
5242
5826
  sessionKey: params.sessionKey,
@@ -5250,16 +5834,27 @@ function prepareCodexAttemptResources(prompt) {
5250
5834
  forceKillDelayMs: 250
5251
5835
  });
5252
5836
  };
5837
+ const releaseSandboxExecEnvironment = async () => {
5838
+ if (state.sandboxExecEnvironment) {
5839
+ const environment = state.sandboxExecEnvironment;
5840
+ state.sandboxExecEnvironment = void 0;
5841
+ await releaseCodexSandboxExecServerEnvironment(sandbox, environment);
5842
+ }
5843
+ };
5253
5844
  const releaseSharedClientLeaseAndRetireOneShotClient = async () => {
5845
+ if (connection.attemptClientFactory === createIsolatedCodexAppServerClient) {
5846
+ await releaseSandboxExecEnvironment();
5847
+ const ownedClient = state.releaseSharedClientLease ? state.client : void 0;
5848
+ releaseSharedClientLeaseOnce();
5849
+ await ownedClient?.closeAndWait({
5850
+ exitTimeoutMs: 2e3,
5851
+ forceKillDelayMs: 250
5852
+ });
5853
+ return;
5854
+ }
5254
5855
  releaseSharedClientLeaseOnce();
5255
5856
  await retireSharedCodexClientForOneShotCleanup();
5256
5857
  };
5257
- const releaseSandboxExecEnvironment = async () => {
5258
- if (state.sandboxExecEnvironmentAcquired) {
5259
- state.sandboxExecEnvironmentAcquired = false;
5260
- await releaseCodexSandboxExecServerEnvironment(sandbox);
5261
- }
5262
- };
5263
5858
  const runCleanupStep = (step, operation) => runAgentCleanupStep({
5264
5859
  runId: params.runId,
5265
5860
  sessionId: params.sessionId,
@@ -5284,7 +5879,10 @@ function prepareCodexAttemptResources(prompt) {
5284
5879
  retainClient: () => retainSharedCodexAppServerClientIfCurrent(state.client),
5285
5880
  retainParentThread: (protectedThreadId) => protectCodexAppServerLiveThread(state.client, protectedThreadId),
5286
5881
  claimDirectChild: (childThreadId) => state.nativeHookRelay?.claimDirectChild(childThreadId),
5287
- rejectPendingDirectChild: (childThreadId, reason) => state.nativeHookRelay?.rejectPendingDirectChild(childThreadId, reason)
5882
+ rejectPendingDirectChild: (childThreadId, reason) => state.nativeHookRelay?.rejectPendingDirectChild(childThreadId, reason),
5883
+ ...params.sessionKey && params.agentHarnessTaskRuntimeScope ? { onDirectChildAccepted: () => {
5884
+ state.runtimeContinuationStarted = true;
5885
+ } } : {}
5288
5886
  });
5289
5887
  };
5290
5888
  const releaseCurrentRoute = () => {
@@ -5347,8 +5945,7 @@ function prepareCodexAttemptResources(prompt) {
5347
5945
  configPatch: state.nativeHookRelay ? buildCodexNativeHookRelayConfig({
5348
5946
  relay: state.nativeHookRelay,
5349
5947
  events: nativeHookRelayEvents,
5350
- hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec,
5351
- loopDetectionPreToolUseRelay: appServer.loopDetectionPreToolUseRelay
5948
+ hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec
5352
5949
  }) : options.nativeHookRelay?.enabled === false ? buildCodexNativeHookRelayDisabledConfig() : void 0,
5353
5950
  nativeHookRelayGeneration: state.nativeHookRelay?.generation
5354
5951
  };
@@ -5480,10 +6077,6 @@ function resolveScheduledCodexAppCreatorCaptureDecision(params) {
5480
6077
  ...unavailableReason ? { unavailableReason } : {}
5481
6078
  };
5482
6079
  }
5483
- function readConnectorId(tool) {
5484
- const meta = asOptionalRecord(asOptionalRecord(tool)?.["_meta"]);
5485
- return normalizeOptionalString(meta?.connector_id) ?? normalizeOptionalString(meta?.connectorId);
5486
- }
5487
6080
  function normalizeApprovalMode(value) {
5488
6081
  return value === "allow" || value === "deny" || value === "auto" || value === "ask" ? value : void 0;
5489
6082
  }
@@ -5550,7 +6143,7 @@ async function readCodexScheduledAppToolNamesByApp(params) {
5550
6143
  if (!isJsonObject(status) || !isJsonObject(status.tools)) throw new Error("Codex scheduled app inventory contained an invalid server status");
5551
6144
  if (status.name !== CODEX_APPS_MCP_SERVER) continue;
5552
6145
  for (const [toolName, tool] of Object.entries(status.tools)) {
5553
- const connectorId = readConnectorId(tool);
6146
+ const connectorId = readCodexMcpToolConnectorId(tool);
5554
6147
  if (connectorId) {
5555
6148
  const names = toolNamesByApp.get(connectorId) ?? /* @__PURE__ */ new Set();
5556
6149
  names.add(toolName);
@@ -5783,8 +6376,8 @@ async function prepareCodexAttemptRuntime(connection) {
5783
6376
  const effectiveContextWindowInfo = usesSupervisionConnection ? void 0 : params.contextWindowInfo;
5784
6377
  const effectiveContextTokenBudget = usesSupervisionConnection ? void 0 : params.contextTokenBudget;
5785
6378
  const effectiveRuntimeProviderId = usesSupervisionConnection ? mutable.startupBinding?.modelProvider ?? "codex" : params.provider;
5786
- const effectiveRuntimeModelId = usesSupervisionConnection ? mutable.startupBinding?.model ?? "codex-native" : params.modelId;
5787
- const { authProfileId: _outerAuthProfileId, contextWindowInfo: _outerContextWindowInfo, contextTokenBudget: _outerContextTokenBudget, model: _outerModel, modelId: _outerModelId, provider: _outerProvider, runtimePlan: _outerRuntimePlan, requestedModelId: _outerRequestedModelId, fallbackReason: _outerFallbackReason, degradedReason: _outerDegradedReason, thinkLevel: _outerThinkLevel, fastMode: _outerFastMode, ...paramsWithoutOuterNativeOwnership } = params;
6379
+ const effectiveRuntimeModelId = usesSupervisionConnection ? mutable.startupBinding?.model ?? "codex-native" : connection.options.runtimeModelId ?? params.modelId;
6380
+ const { authProfileId: _outerAuthProfileId, authoredContextTokenCap: _outerAuthoredContextTokenCap, contextWindowInfo: _outerContextWindowInfo, contextTokenBudget: _outerContextTokenBudget, model: _outerModel, modelId: _outerModelId, provider: _outerProvider, runtimePlan: _outerRuntimePlan, requestedModelId: _outerRequestedModelId, fallbackReason: _outerFallbackReason, degradedReason: _outerDegradedReason, thinkLevel: _outerThinkLevel, fastMode: _outerFastMode, ...paramsWithoutOuterNativeOwnership } = params;
5788
6381
  const supervisedRuntimeModel = {
5789
6382
  id: effectiveRuntimeModelId,
5790
6383
  name: effectiveRuntimeModelId,
@@ -6017,18 +6610,23 @@ function resolveCodexDynamicToolDirectNames(params, hostSystemAgentActive = fals
6017
6610
  const names = [];
6018
6611
  if (hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) names.push("openclaw");
6019
6612
  if (params.sourceReplyDeliveryMode === "message_tool_only") names.push("message");
6020
- if (params.pluginHarnessToolPolicyRestricted === true) names.push("update_plan");
6613
+ names.push("progress_card");
6021
6614
  return names;
6022
6615
  }
6023
6616
  //#endregion
6024
6617
  //#region extensions/codex/src/app-server/run-attempt-server-requests.ts
6618
+ const DYNAMIC_TOOL_TERMINAL_DIAGNOSTIC_TYPES = [
6619
+ "tool.execution.completed",
6620
+ "tool.execution.error",
6621
+ "tool.execution.blocked"
6622
+ ];
6025
6623
  function createCodexAttemptServerRequestController(resources, turnRuntime, lifecycle) {
6026
6624
  const { prompt, state: resourceState, projectorRef, trajectoryRecorder } = resources;
6027
6625
  const { context } = prompt;
6028
6626
  const { runtime, attemptTools } = context;
6029
6627
  const { connection } = runtime;
6030
- const { params, computerUseConfig, runAbortController, appServer, approvalPolicyPromotedForOpenClawToolPolicy, sessionAgentId } = connection;
6031
- const { toolBridge, toolOutcomeOrdinals, suppressedDynamicToolOutcomeOrdinals, allocateCodexToolOutcomeOrdinal } = attemptTools;
6628
+ const { params, computerUseConfig, runAbortController, appServer, sessionAgentId } = connection;
6629
+ const { compactionPlanState, toolBridge, toolOutcomeOrdinals, suppressedDynamicToolOutcomeOrdinals, allocateCodexToolOutcomeOrdinal } = attemptTools;
6032
6630
  const { state, turnIdRef, userInputBridgeRef, openClawDynamicToolExecutions, pendingOpenClawDynamicToolCompletionIds, postToolRawAssistantCompletionIdleTimeoutMs, turnWatches } = turnRuntime;
6033
6631
  const { emitExecutionPhaseOnce, scheduleTurnReleaseAfterTerminalDynamicTool, scheduleTerminalDynamicToolReleaseCheck } = lifecycle;
6034
6632
  const handleServerRequest = async (request, scope, requestSignal = new AbortController().signal) => {
@@ -6054,7 +6652,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6054
6652
  armCompletionWatchOnResponse = true;
6055
6653
  markCurrentTurnRequestProgress();
6056
6654
  }
6057
- return await handleCodexAppServerElicitationRequest({
6655
+ const approvalResult = await routeCodexAppServerElicitationRequest({
6058
6656
  requestParams: request.params,
6059
6657
  paramsForRun: params,
6060
6658
  threadId: resourceState.thread.threadId,
@@ -6063,6 +6661,11 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6063
6661
  ...computerUseConfig.enabled ? { computerUseMcpServerName: computerUseConfig.mcpServerName } : {},
6064
6662
  signal
6065
6663
  });
6664
+ if (approvalResult.kind === "handled") return approvalResult.response;
6665
+ return await userInputBridgeRef.current?.handleElicitationRequest({
6666
+ id: request.id,
6667
+ params: request.params
6668
+ });
6066
6669
  }
6067
6670
  if (request.method === "item/tool/requestUserInput") {
6068
6671
  if (scope.turnId === turnId) {
@@ -6088,9 +6691,8 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6088
6691
  turnId,
6089
6692
  nativeHookRelay: resourceState.nativeHookRelay,
6090
6693
  autoApprove: shouldAutoApproveCodexAppServerApprovals(appServer),
6091
- autoApproveOpenClawToolPolicy: approvalPolicyPromotedForOpenClawToolPolicy,
6092
6694
  signal,
6093
- onNativeToolFailureDisposition: (itemId, disposition) => projector?.recordNativeToolApprovalFailure(itemId, disposition)
6695
+ onNativeToolFailureDisposition: (itemId, disposition, approvalKind) => projector?.recordNativeToolApprovalFailure(itemId, disposition, approvalKind)
6094
6696
  });
6095
6697
  }
6096
6698
  return;
@@ -6126,13 +6728,6 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6126
6728
  tool: call.tool,
6127
6729
  toolCallId: call.callId
6128
6730
  });
6129
- emitDynamicToolStartedDiagnostic({
6130
- call,
6131
- agentId: sessionAgentId,
6132
- runId: params.runId,
6133
- sessionId: params.sessionId,
6134
- sessionKey: params.sessionKey
6135
- });
6136
6731
  const toolMeta = inferCodexDynamicToolMeta(call, resolveCodexToolProgressDetailMode(params.toolProgressDetail));
6137
6732
  const toolArgs = sanitizeCodexToolArguments(call.arguments);
6138
6733
  const commandBearing = isCodexCommandBearingToolCall(call.tool, toolArgs);
@@ -6142,6 +6737,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6142
6737
  data: {
6143
6738
  phase: "start",
6144
6739
  name: call.tool,
6740
+ itemId: call.callId,
6145
6741
  toolCallId: call.callId,
6146
6742
  ...toolMeta ? { meta: toolMeta } : {},
6147
6743
  ...toolArgs ? { args: toolArgs } : {},
@@ -6162,30 +6758,43 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6162
6758
  sessionId: params.sessionId,
6163
6759
  sessionKey: params.sessionKey
6164
6760
  })) terminalDiagnosticObserved = true;
6165
- });
6761
+ }, { include: DYNAMIC_TOOL_TERMINAL_DIAGNOSTIC_TYPES });
6166
6762
  try {
6167
- const { execution } = openClawDynamicToolExecutions.claim(call, () => handleDynamicToolCallWithTimeout({
6168
- call,
6169
- toolBridge,
6170
- signal,
6171
- timeoutMs: dynamicToolTimeoutMs,
6172
- toolMeta,
6173
- toolCallOrdinal,
6174
- onAgentToolResult: params.onAgentToolResult,
6175
- observeToolTerminal: params.observeToolTerminal,
6176
- onFallbackSelected: () => {
6177
- if (toolCallOrdinal !== void 0) suppressedDynamicToolOutcomeOrdinals.add(toolCallOrdinal);
6178
- },
6179
- onTimeout: () => {
6180
- trajectoryRecorder?.recordEvent("tool.timeout", {
6181
- threadId: call.threadId,
6182
- turnId: call.turnId,
6183
- toolCallId: call.callId,
6184
- name: call.tool,
6185
- timeoutMs: dynamicToolTimeoutMs
6186
- });
6187
- }
6188
- }));
6763
+ const { execution } = openClawDynamicToolExecutions.claim(call, async () => {
6764
+ await projector?.transcriptCheckpoint.flush();
6765
+ emitDynamicToolStartedDiagnostic({
6766
+ call,
6767
+ agentId: sessionAgentId,
6768
+ runId: params.runId,
6769
+ sessionId: params.sessionId,
6770
+ sessionKey: params.sessionKey
6771
+ });
6772
+ const response = await handleDynamicToolCallWithTimeout({
6773
+ call,
6774
+ toolBridge,
6775
+ signal,
6776
+ timeoutMs: dynamicToolTimeoutMs,
6777
+ toolMeta,
6778
+ toolCallOrdinal,
6779
+ onAgentToolResult: params.onAgentToolResult,
6780
+ observeToolTerminal: params.observeToolTerminal,
6781
+ onFallbackSelected: () => {
6782
+ if (toolCallOrdinal !== void 0) suppressedDynamicToolOutcomeOrdinals.add(toolCallOrdinal);
6783
+ },
6784
+ onTimeout: () => {
6785
+ trajectoryRecorder?.recordEvent("tool.timeout", {
6786
+ threadId: call.threadId,
6787
+ turnId: call.turnId,
6788
+ toolCallId: call.callId,
6789
+ name: call.tool,
6790
+ timeoutMs: dynamicToolTimeoutMs
6791
+ });
6792
+ }
6793
+ });
6794
+ recordCodexDynamicToolResult(projector, call, response, toCodexDynamicToolProtocolResponse(response));
6795
+ await projector?.transcriptCheckpoint.flush();
6796
+ return response;
6797
+ });
6189
6798
  const response = await execution;
6190
6799
  const protocolResponse = toCodexDynamicToolProtocolResponse(response);
6191
6800
  if (!protocolResponse.success && toolCallOrdinal !== void 0) {
@@ -6208,8 +6817,11 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6208
6817
  success: protocolResponse.success,
6209
6818
  contentItems: protocolResponse.contentItems
6210
6819
  });
6211
- recordCodexDynamicToolResult(projector, call, response, protocolResponse);
6212
- if (protocolResponse.success && call.tool === "update_plan") projector?.recordDynamicPlanUpdate(response.executedArguments ?? call.arguments);
6820
+ if (protocolResponse.success && call.tool === "progress_card") {
6821
+ const progressCardInput = response.executedArguments ?? call.arguments;
6822
+ await projector?.recordDynamicProgressCardUpdate(progressCardInput);
6823
+ compactionPlanState.recordProgressCardInput(progressCardInput);
6824
+ }
6213
6825
  if (shouldEmitDynamicToolProgress) {
6214
6826
  const progressResponse = toCodexDynamicToolProgressResponse(response, protocolResponse);
6215
6827
  emitCodexAppServerEvent(params, {
@@ -6217,6 +6829,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6217
6829
  data: {
6218
6830
  phase: "result",
6219
6831
  name: call.tool,
6832
+ itemId: call.callId,
6220
6833
  toolCallId: call.callId,
6221
6834
  ...toolMeta ? { meta: toolMeta } : {},
6222
6835
  ...commandBearing ? { commandBearing: true } : {},
@@ -6289,6 +6902,20 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6289
6902
  return { handleServerRequest };
6290
6903
  }
6291
6904
  //#endregion
6905
+ //#region extensions/codex/src/app-server/attempt-runtime-artifact.ts
6906
+ async function verifyStartupArtifact(params) {
6907
+ if (!params.request) return;
6908
+ const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-DeBvleJw.js");
6909
+ const runtimeArtifact = readCodexAppServerClientRuntimeArtifact(params.client);
6910
+ const expected = params.request.expected;
6911
+ const matchesExpected = !expected || runtimeArtifact?.id === expected.id && runtimeArtifact.fingerprint === expected.fingerprint;
6912
+ if (!runtimeArtifact || !matchesExpected || !await validateCodexAppServerRuntimeArtifact(runtimeArtifact, params.signal)) {
6913
+ retireSharedCodexAppServerClientIfCurrent(params.client);
6914
+ throw new Error(expected ? "Codex app-server runtime artifact does not match verified inference" : "Codex app-server runtime artifact is unavailable or stale");
6915
+ }
6916
+ return runtimeArtifact;
6917
+ }
6918
+ //#endregion
6292
6919
  //#region extensions/codex/src/app-server/computer-use-health.ts
6293
6920
  const COMPUTER_USE_HEALTH_MONITOR_STATE = Symbol.for("openclaw.codexComputerUseHealthMonitorState");
6294
6921
  function getComputerUseHealthMonitorState() {
@@ -6308,19 +6935,17 @@ function startCodexComputerUseHealthMonitor(params) {
6308
6935
  }
6309
6936
  const fingerprint = buildComputerUseHealthMonitorFingerprint(params.config);
6310
6937
  const intervalMs = params.config.healthCheckIntervalMinutes * 6e4;
6311
- if (existing?.fingerprint === fingerprint && existing.repairComputerUseMcpChildren === params.repairComputerUseMcpChildren) return {
6938
+ if (existing?.fingerprint === fingerprint) return {
6312
6939
  started: false,
6313
6940
  intervalMs,
6314
6941
  reason: "already_started"
6315
6942
  };
6316
6943
  if (existing) clearComputerUseHealthMonitor(params.client, existing);
6317
- const repairComputerUseMcpChildren = params.repairComputerUseMcpChildren ?? (() => killStaleComputerUseMcpChildren({ ancestorPid: params.client.getTransportPid() }));
6318
6944
  const monitor = {
6319
6945
  fingerprint,
6320
6946
  intervalMs,
6321
- repairComputerUseMcpChildren: params.repairComputerUseMcpChildren,
6322
6947
  timer: setInterval(() => {
6323
- runCodexComputerUseHealthProbe(params.client, params.config, monitor, { repairComputerUseMcpChildren });
6948
+ runCodexComputerUseHealthProbe(params.client, params.config, monitor);
6324
6949
  }, intervalMs),
6325
6950
  disposeCloseHandler: () => void 0,
6326
6951
  running: false
@@ -6345,13 +6970,12 @@ function buildComputerUseHealthMonitorFingerprint(config) {
6345
6970
  toolCallTimeoutMs: config.toolCallTimeoutMs
6346
6971
  });
6347
6972
  }
6348
- async function runCodexComputerUseHealthProbe(client, config, monitor, options) {
6973
+ async function runCodexComputerUseHealthProbe(client, config, monitor) {
6349
6974
  if (monitor.running) return;
6350
6975
  monitor.running = true;
6351
6976
  try {
6352
6977
  const { liveTest, repair } = await runCodexComputerUseLiveTest({
6353
6978
  config,
6354
- repairComputerUseMcpChildren: options.repairComputerUseMcpChildren,
6355
6979
  request: async (method, requestParams, requestOptions) => await client.request(method, requestParams, { timeoutMs: requestOptions?.timeoutMs ?? config.liveTestTimeoutMs })
6356
6980
  });
6357
6981
  if (!liveTest.ok) {
@@ -6364,10 +6988,7 @@ async function runCodexComputerUseHealthProbe(client, config, monitor, options)
6364
6988
  });
6365
6989
  return;
6366
6990
  }
6367
- if (repair?.killedPids.length) embeddedAgentLog.info("codex computer-use periodic health repaired stale children", {
6368
- mcpServerName: config.mcpServerName,
6369
- killedPids: repair.killedPids
6370
- });
6991
+ if (repair?.attempted && repair.warnings.length === 0) embeddedAgentLog.info("codex computer-use periodic health reloaded MCP servers", { mcpServerName: config.mcpServerName });
6371
6992
  } catch (error) {
6372
6993
  embeddedAgentLog.warn("codex computer-use periodic health probe crashed", {
6373
6994
  mcpServerName: config.mcpServerName,
@@ -6462,6 +7083,7 @@ function createCodexPluginThreadConfigStartupProvider(params) {
6462
7083
  build: async (buildOptions) => {
6463
7084
  const config = await buildCodexPluginThreadConfigWithinDeadline({
6464
7085
  ...buildParams,
7086
+ threadId: buildOptions?.threadId,
6465
7087
  appCache: appCache ?? defaultCodexAppInventoryCache,
6466
7088
  metadataCache,
6467
7089
  failClosedOnTimeout: Boolean(params.scheduledRuntimeAuthority),
@@ -6494,7 +7116,7 @@ function isCodexPluginThreadConfigTimeoutError(error) {
6494
7116
  * Startup orchestration for Codex app-server attempts, including shared-client
6495
7117
  * leasing, plugin thread config, sandbox environment, and thread lifecycle binding.
6496
7118
  */
6497
- const CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS = 3;
7119
+ const CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS = 3;
6498
7120
  const CODEX_APP_SERVER_CONTEXT_RESTART_SELECTION_CHANGED = "CODEX_APP_SERVER_CONTEXT_RESTART_SELECTION_CHANGED";
6499
7121
  /** True when a pre-write context restart must replay on the newly selected owner. */
6500
7122
  function isCodexContextRestartSelectionChangedError(error) {
@@ -6575,24 +7197,19 @@ async function startCodexAttemptThread(params) {
6575
7197
  startupClientLease = () => {
6576
7198
  if (startupClientLeaseReleased) return;
6577
7199
  startupClientLeaseReleased = true;
6578
- releaseLeasedSharedCodexAppServerClient(activeStartupClient);
7200
+ if (params.attemptClientFactory === createIsolatedCodexAppServerClient) activeStartupClient.close();
7201
+ else releaseLeasedSharedCodexAppServerClient(activeStartupClient);
6579
7202
  };
6580
7203
  releaseSharedClientLease = startupClientLease;
6581
7204
  attemptedClient = activeStartupClient;
6582
7205
  startupClientForAbandonedRequestCleanup = activeStartupClient;
6583
7206
  if (startupAbandoned) throw new CodexAppServerStartupError("timed_out");
6584
7207
  if (startupAbandonController.signal.aborted) throw new CodexAppServerStartupError("aborted");
6585
- let runtimeArtifact;
6586
- if (params.runtimeArtifactRequest) {
6587
- const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-BVSPdd6g.js");
6588
- runtimeArtifact = readCodexAppServerClientRuntimeArtifact(activeStartupClient);
6589
- const expected = params.runtimeArtifactRequest.expected;
6590
- const matchesExpected = !expected || Boolean(runtimeArtifact && runtimeArtifact.id === expected.id && runtimeArtifact.fingerprint === expected.fingerprint);
6591
- if (!runtimeArtifact || !matchesExpected || !await validateCodexAppServerRuntimeArtifact(runtimeArtifact, startupAbandonController.signal)) {
6592
- retireSharedCodexAppServerClientIfCurrent(activeStartupClient);
6593
- throw new Error(expected ? "Codex app-server runtime artifact does not match verified inference" : "Codex app-server runtime artifact is unavailable or stale");
6594
- }
6595
- }
7208
+ const runtimeArtifact = await verifyStartupArtifact({
7209
+ client: activeStartupClient,
7210
+ request: params.runtimeArtifactRequest,
7211
+ signal: startupAbandonController.signal
7212
+ });
6596
7213
  ensureCodexAppServerClientRuntime(activeStartupClient, {
6597
7214
  agentDir: params.agentDir,
6598
7215
  authProfileId: startupRuntimeAuthProfileId,
@@ -6611,7 +7228,7 @@ async function startCodexAttemptThread(params) {
6611
7228
  signal: startupAbandonController.signal
6612
7229
  });
6613
7230
  } catch (error) {
6614
- if (startupAbandonController.signal.aborted) throw error;
7231
+ if (startupAbandonController.signal.aborted || isCodexAppServerStartSelectionChangedError(error)) throw error;
6615
7232
  throw new AgentHarnessPreflightError(`Codex Computer Use readiness failed: ${formatErrorMessage(error)}`, {
6616
7233
  cause: error,
6617
7234
  scope: "harness"
@@ -6625,7 +7242,8 @@ async function startCodexAttemptThread(params) {
6625
7242
  accountId: params.startupAuthAccountCacheKey,
6626
7243
  envApiKeyFingerprint: params.startupEnvApiKeyCacheKey,
6627
7244
  appServerVersion: activeStartupClient.getServerVersion(),
6628
- runtimeIdentity: startupRuntimeIdentity
7245
+ runtimeIdentity: startupRuntimeIdentity,
7246
+ desktopGenerationFingerprint: readCodexAppServerClientDesktopGenerationFingerprint(activeStartupClient)
6629
7247
  });
6630
7248
  const appServerRuntimeFingerprint = buildCodexAppServerRuntimeFingerprint({
6631
7249
  appServer: params.appServer,
@@ -6652,7 +7270,7 @@ async function startCodexAttemptThread(params) {
6652
7270
  const releaseStartupSandboxEnvironment = async () => {
6653
7271
  if (startupSandboxEnvironmentAcquired) {
6654
7272
  startupSandboxEnvironmentAcquired = false;
6655
- await releaseCodexSandboxExecServerEnvironment(params.sandbox);
7273
+ await releaseCodexSandboxExecServerEnvironment(params.sandbox, startupSandboxEnvironment);
6656
7274
  }
6657
7275
  };
6658
7276
  releaseStartupResourcesOnTimeout = releaseStartupSandboxEnvironment;
@@ -6665,9 +7283,11 @@ async function startCodexAttemptThread(params) {
6665
7283
  startupSandboxEnvironment = sandboxEnvironmentRequired ? await ensureCodexSandboxExecServerEnvironment({
6666
7284
  client: activeStartupClient,
6667
7285
  sandbox: params.sandbox ?? null,
7286
+ runtime: params.runtime,
6668
7287
  appServerStartOptions: params.appServer.start,
6669
7288
  timeoutMs: params.appServer.requestTimeoutMs,
6670
- signal: startupAbandonController.signal
7289
+ signal: AbortSignal.any([params.signal, startupAbandonController.signal]),
7290
+ onExecutionDisconnect: params.onExecutionDisconnect
6671
7291
  }) : void 0;
6672
7292
  startupSandboxEnvironmentAcquired = Boolean(startupSandboxEnvironment);
6673
7293
  if (startupAbandonController.signal.aborted) {
@@ -6711,17 +7331,23 @@ async function startCodexAttemptThread(params) {
6711
7331
  reserveResumeThread,
6712
7332
  bindingStore: params.bindingStore,
6713
7333
  params: params.buildAttemptParams(),
7334
+ runtimeModelId: params.runtimeModelId,
6714
7335
  agentId: params.sessionAgentId,
7336
+ agentDir: params.agentDir,
6715
7337
  cwd: startupExecutionCwd,
6716
7338
  dynamicTools: params.dynamicTools,
6717
7339
  persistentWebSearchAllowed: params.persistentWebSearchAllowed,
6718
7340
  webSearchAllowed: params.webSearchAllowed,
6719
7341
  appServer: pluginAppServer,
6720
7342
  developerInstructions: params.developerInstructions,
7343
+ agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
6721
7344
  config: threadConfig,
7345
+ shellEnvironment: params.shellEnvironment,
7346
+ disableLoginShell: params.disableLoginShell,
6722
7347
  finalConfigPatch: params.finalConfigPatch,
6723
7348
  buildFinalConfigPatch: params.buildFinalConfigPatch,
6724
7349
  nativeHookRelayGeneration: params.nativeHookRelayGeneration,
7350
+ nativeHookRelayRequired: params.nativeHookRelayRequired,
6725
7351
  nativeCodeModeEnabled: params.nativeToolSurfaceEnabled,
6726
7352
  nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
6727
7353
  nativeCodeModeOnlyEnabled: params.appServer.codeModeOnly,
@@ -6816,30 +7442,31 @@ async function startCodexAttemptThread(params) {
6816
7442
  }
6817
7443
  }
6818
7444
  };
6819
- for (let attempt = 1; attempt <= CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS; attempt += 1) try {
7445
+ for (let attempt = 1; attempt <= CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS; attempt += 1) try {
6820
7446
  return await startupAttempt();
6821
7447
  } catch (error) {
6822
7448
  const selectionChanged = isCodexAppServerStartSelectionChangedError(error);
6823
7449
  if (startupAbandoned || params.signal.aborted || !selectionChanged && !isCodexAppServerConnectionClosedError(error)) throw error;
6824
- const failedClient = attemptedClient;
6825
- const refreshedSharedClient = selectionChanged ? retireSharedCodexAppServerClientIfCurrent(failedClient) : clearSharedCodexAppServerClientIfCurrent(failedClient);
6826
- if (startupClientForAbandonedRequestCleanup === failedClient) startupClientForAbandonedRequestCleanup = void 0;
6827
- if (attempt >= CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS) {
7450
+ const refreshedSharedClient = selectionChanged ? retireSharedCodexAppServerClientIfCurrent(attemptedClient) : clearSharedCodexAppServerClientIfCurrent(attemptedClient);
7451
+ if (startupClientForAbandonedRequestCleanup === attemptedClient) startupClientForAbandonedRequestCleanup = void 0;
7452
+ if (attempt >= CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS) {
6828
7453
  embeddedAgentLog.warn(selectionChanged ? "codex app-server executable selection kept changing during startup; retries exhausted" : "codex app-server connection closed during startup; retries exhausted", {
6829
7454
  attempt,
6830
- maxAttempts: CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS,
7455
+ maxAttempts: CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS,
6831
7456
  refreshedSharedClient,
6832
7457
  error: formatErrorMessage(error)
6833
7458
  });
6834
7459
  throw error;
6835
7460
  }
7461
+ const retryDelayMs = selectionChanged ? 0 : 1e3 * 2 ** (attempt - 1);
6836
7462
  embeddedAgentLog.warn(selectionChanged ? "codex app-server executable selection changed during startup; restarting app-server and retrying" : "codex app-server connection closed during startup; restarting app-server and retrying", {
6837
7463
  attempt,
6838
7464
  nextAttempt: attempt + 1,
6839
- maxAttempts: CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS,
7465
+ maxAttempts: CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS,
6840
7466
  refreshedSharedClient,
6841
7467
  error: formatErrorMessage(error)
6842
7468
  });
7469
+ await sleepWithAbort(retryDelayMs, startupAbandonController.signal);
6843
7470
  }
6844
7471
  throw new Error("codex app-server startup retry loop exited unexpectedly");
6845
7472
  }
@@ -6852,7 +7479,7 @@ async function startCodexAttemptThread(params) {
6852
7479
  releaseSharedClientLease
6853
7480
  };
6854
7481
  } catch (error) {
6855
- if (params.signal.aborted || shouldClearSharedClientAfterStartupAbandon(error)) {
7482
+ if (params.signal.aborted || isCodexAppServerStartupError(error)) {
6856
7483
  releaseSharedClientLease?.();
6857
7484
  releaseSharedClientLease = void 0;
6858
7485
  await closeCodexStartupClientBestEffort(startupClientForAbandonedRequestCleanup);
@@ -6871,13 +7498,11 @@ async function startCodexAttemptThread(params) {
6871
7498
  params.signal.removeEventListener("abort", abandonStartupAcquire);
6872
7499
  }
6873
7500
  }
6874
- function shouldClearSharedClientAfterStartupAbandon(error) {
6875
- return isCodexAppServerStartupError(error);
6876
- }
6877
7501
  function shouldClearSharedClientAfterStartupRace(error) {
6878
- return shouldClearSharedClientAfterStartupAbandon(error) || isCodexAppServerRequestTimeoutError(error);
7502
+ return isCodexAppServerStartupError(error) || isCodexAppServerRequestTimeoutError(error);
6879
7503
  }
6880
7504
  function shouldClearSharedClientAfterStartupFailure(params) {
7505
+ if (isCodexAppServerOverloadError(params.error)) return false;
6881
7506
  return isCodexAppServerBrokenPipeError(params.error) || !params.spawnedBy;
6882
7507
  }
6883
7508
  //#endregion
@@ -6886,11 +7511,12 @@ async function startCodexAttemptRuntime(resources) {
6886
7511
  const { prompt, state, trajectoryRecorder, activateNativePreToolUseFailureFallback, releaseSandboxExecEnvironment, releaseSharedClientLeaseAndRetireOneShotClient, releaseCurrentRoute, runCleanupStep, startupTimeoutMs, buildNativeHookRelayFinalConfigPatch } = resources;
6887
7512
  const { context, turnState, buildRenderedCodexDeveloperInstructions, rebuildCodexTurnPromptTextFromCurrentProjection, applyNoContextEngineContinuityProjection } = prompt;
6888
7513
  const { runtime, attemptTools, promptState } = context;
6889
- const { connection, runtimeParams, preparedAuthBinding, buildActiveRunAttemptParams, startupAuthAccountCacheKey, startupEnvApiKeyCacheKey, bundleMcpThreadConfig, nativeToolSurfaceEnabled, nativeProviderWebSearchSupport, sandboxExecServerEnabled } = runtime;
7514
+ const { connection, runtimeParams, preparedAuthBinding, buildActiveRunAttemptParams, startupAuthAccountCacheKey, startupEnvApiKeyCacheKey, bundleMcpThreadConfig, nativeToolSurfaceEnabled, nativeProviderWebSearchSupport, effectiveRuntimeModelId, sandboxExecServerEnabled } = runtime;
6890
7515
  const { toolBridge, toolState } = attemptTools;
6891
7516
  const developerInstructions = joinPresentSections(turnState.promptBuild.developerInstructions, attemptTools.scheduledConfiguredMcp?.diagnosticNotice);
6892
7517
  const { params, attemptClientFactory, bindingStore, appServer, pluginConfig, computerUseConfig, startupClientAuthProfileId, runtimeArtifactRequest, startupPreparedAuth, agentDir, sessionAgentId, effectiveWorkspace, effectiveCwd, sandbox, runAbortController, usesSupervisionConnection, resolveReviewerPolicyContext, resolveRuntimeOptionsForCurrentBinding, startupAuthProfileId, startupAuthRequirement, abortFromUpstream } = connection;
6893
7518
  let pluginAppServer = withCodexAppServerFastModeServiceTier(appServer, runtimeParams);
7519
+ const loopDetectionEnabled = (sessionAgentId && params.config ? resolveAgentConfig$1(params.config, sessionAgentId)?.tools?.loopDetection?.enabled : void 0) ?? params.config?.tools?.loopDetection?.enabled ?? false;
6894
7520
  try {
6895
7521
  emitCodexAppServerEvent(params, {
6896
7522
  stream: "codex_app_server.lifecycle",
@@ -6899,6 +7525,7 @@ async function startCodexAttemptRuntime(resources) {
6899
7525
  const startupResult = await startCodexAttemptThread({
6900
7526
  attemptClientFactory,
6901
7527
  bindingStore,
7528
+ runtime: connection.options.runtime,
6902
7529
  appServer: pluginAppServer,
6903
7530
  pluginConfig,
6904
7531
  computerUseConfig,
@@ -6911,7 +7538,10 @@ async function startCodexAttemptRuntime(resources) {
6911
7538
  startupEnvApiKeyCacheKey,
6912
7539
  agentDir,
6913
7540
  config: params.config,
7541
+ shellEnvironment: connection.shellEnvironment,
7542
+ disableLoginShell: connection.disableLoginShell,
6914
7543
  buildAttemptParams: buildActiveRunAttemptParams,
7544
+ ...effectiveRuntimeModelId !== runtimeParams.modelId ? { runtimeModelId: effectiveRuntimeModelId } : {},
6915
7545
  sessionAgentId,
6916
7546
  effectiveWorkspace,
6917
7547
  effectiveCwd,
@@ -6919,7 +7549,9 @@ async function startCodexAttemptRuntime(resources) {
6919
7549
  persistentWebSearchAllowed: toolState.persistentWebSearchAllowed,
6920
7550
  webSearchAllowed: toolState.webSearchAllowed,
6921
7551
  developerInstructions,
7552
+ agentWorkspaceDeveloperInstructions: context.agentWorkspaceDeveloperInstructions,
6922
7553
  buildFinalConfigPatch: buildNativeHookRelayFinalConfigPatch,
7554
+ nativeHookRelayRequired: connection.options.nativeHookRelay?.enabled !== false && params.pluginHarnessToolPolicyRestricted !== true && connection.nativeHookRelayEvents.includes("pre_tool_use") && (hasBeforeToolCallPolicy() || appServer.loopDetectionPreToolUseRelay && Boolean(connection.sandboxSessionKey) && loopDetectionEnabled),
6923
7555
  bundleMcpThreadConfig,
6924
7556
  configuredMcpOwnershipVersion: attemptTools.configuredMcpOwnershipVersion,
6925
7557
  nativeToolSurfaceEnabled,
@@ -6930,6 +7562,11 @@ async function startCodexAttemptRuntime(resources) {
6930
7562
  startupTimeoutMs,
6931
7563
  signal: runAbortController.signal,
6932
7564
  onStartupTimeout: () => runAbortController.abort("codex_startup_timeout"),
7565
+ onExecutionDisconnect: (error) => {
7566
+ state.executionDisconnectError = error;
7567
+ embeddedAgentLog.warn(error.message);
7568
+ runAbortController.abort("client_closed");
7569
+ },
6933
7570
  spawnedBy: params.spawnedBy
6934
7571
  });
6935
7572
  state.client = startupResult.client;
@@ -6937,7 +7574,7 @@ async function startCodexAttemptRuntime(resources) {
6937
7574
  state.runtimeArtifact = startupResult.runtimeArtifact;
6938
7575
  state.turnRouter = startupResult.turnRouter;
6939
7576
  state.turnRoute = startupResult.turnRoute;
6940
- state.sandboxExecEnvironmentAcquired = Boolean(startupResult.sandboxEnvironment);
7577
+ state.sandboxExecEnvironment = startupResult.sandboxEnvironment;
6941
7578
  state.releaseSharedClientLease = startupResult.releaseSharedClientLease;
6942
7579
  state.restartContextEngineCodexThread = startupResult.restartContextEngineCodexThread;
6943
7580
  pluginAppServer = startupResult.pluginAppServer;
@@ -7011,7 +7648,7 @@ async function startCodexAttemptRuntime(resources) {
7011
7648
  await runCleanupStep("codex-start-failure-sandbox-release", releaseSandboxExecEnvironment);
7012
7649
  await runCleanupStep("codex-start-failure-shared-client-release", releaseSharedClientLeaseAndRetireOneShotClient);
7013
7650
  await runCleanupStep("codex-start-failure-abort-listener", () => params.abortSignal?.removeEventListener("abort", abortFromUpstream));
7014
- throw error;
7651
+ throw state.executionDisconnectError ?? error;
7015
7652
  }
7016
7653
  }
7017
7654
  //#endregion
@@ -7021,7 +7658,7 @@ function isAuthorityResolutionOperationAbort(error, signal) {
7021
7658
  }
7022
7659
  async function prepareCodexAttemptTools(runtime) {
7023
7660
  const { connection, bundleMcpThreadConfig, bundleManifestRegistry, runtimeParams, effectiveRuntimeModelId, nativeToolSurfaceEnabled, nativeProviderWebSearchSupport, hookChannelId, codexMcpToolOverrides, authenticatedScheduledMode, ownsScheduledConfiguredMcpSurface, canResolveScheduledConfiguredMcpCreatorAuthority } = runtime;
7024
- const { params, preDynamicStartupStages, mutable, startupAuthProfileId, resolvedWorkspace, effectiveWorkspace, effectiveCwd, sandboxSessionKey, sandbox, runAbortController, sessionAgentId, pluginConfig, profilerEnabled, agentDir } = connection;
7661
+ const { params, preDynamicStartupStages, mutable, startupAuthProfileId, resolvedWorkspace, effectiveWorkspace, effectiveCwd, sandboxSessionKey, sandbox, sessionPermissionPolicy, runAbortController, sessionAgentId, pluginConfig, profilerEnabled, agentDir } = connection;
7025
7662
  const preDynamicSummary = preDynamicStartupStages.snapshot();
7026
7663
  if (shouldWarnCodexDynamicToolBuildStageSummary(preDynamicSummary)) embeddedAgentLog.warn(`codex app-server pre-dynamic startup timings runId=${params.runId} sessionId=${params.sessionId} totalMs=${preDynamicSummary.totalMs} stages=${formatCodexDynamicToolBuildStageSummary(preDynamicSummary)}`, {
7027
7664
  runId: params.runId,
@@ -7035,6 +7672,7 @@ async function prepareCodexAttemptTools(runtime) {
7035
7672
  });
7036
7673
  const toolState = {
7037
7674
  yieldDetected: false,
7675
+ yieldAcknowledgment: void 0,
7038
7676
  persistentWebSearchAllowed: void 0,
7039
7677
  webSearchAllowed: false
7040
7678
  };
@@ -7052,12 +7690,18 @@ async function prepareCodexAttemptTools(runtime) {
7052
7690
  if (toolCallId) toolOutcomeOrdinals.set(toolCallId, ordinal);
7053
7691
  return ordinal;
7054
7692
  } : void 0;
7055
- const dynamicToolParams = allocateCodexToolOutcomeOrdinal || onCodexToolOutcome ? {
7693
+ const compactionPlanState = new CodexCompactionPlanState();
7694
+ const dynamicToolParams = {
7056
7695
  ...runtimeParams,
7696
+ onAgentEvent: (event) => {
7697
+ compactionPlanState.record(event);
7698
+ return runtimeParams.onAgentEvent?.(event);
7699
+ },
7057
7700
  ...allocateCodexToolOutcomeOrdinal ? { allocateToolOutcomeOrdinal: allocateCodexToolOutcomeOrdinal } : {},
7058
7701
  ...onCodexToolOutcome ? { onToolOutcome: onCodexToolOutcome } : {}
7059
- } : runtimeParams;
7702
+ };
7060
7703
  const computerContextEpoch = { value: 0 };
7704
+ const runCleanups = [];
7061
7705
  const cronCreatorToolAllowlist = [];
7062
7706
  const cronCreatorToolAllowlistCaptureRef = {};
7063
7707
  const scheduledAppAuthoritySourceRef = {};
@@ -7080,6 +7724,7 @@ async function prepareCodexAttemptTools(runtime) {
7080
7724
  let toolBridge;
7081
7725
  let creatorAuthorityPromise;
7082
7726
  let resolveCreatorAuthorityImpl;
7727
+ const runtimeYieldCompletionClaim = {};
7083
7728
  const commonToolParams = {
7084
7729
  params: dynamicToolParams,
7085
7730
  resolvedWorkspace,
@@ -7087,6 +7732,7 @@ async function prepareCodexAttemptTools(runtime) {
7087
7732
  effectiveCwd,
7088
7733
  sandboxSessionKey,
7089
7734
  sandbox,
7735
+ sessionPermissionPolicy,
7090
7736
  nativeToolSurfaceEnabled,
7091
7737
  nativeProviderWebSearchSupport,
7092
7738
  runAbortController,
@@ -7094,9 +7740,11 @@ async function prepareCodexAttemptTools(runtime) {
7094
7740
  pluginConfig,
7095
7741
  profilerEnabled,
7096
7742
  ...params.cronCreatorAuthorityUnavailableReason === "queued-local-operator" && bundleMcpThreadConfig.staticServerNames.length > 0 ? { cronCreatorAuthorityUnavailableReason: "queued-local-operator-configured-mcp" } : {},
7097
- onYieldDetected: () => {
7743
+ onYieldDetected: (acknowledgment) => {
7098
7744
  toolState.yieldDetected = true;
7745
+ toolState.yieldAcknowledgment = acknowledgment;
7099
7746
  },
7747
+ claimYieldCompletion: () => runtimeYieldCompletionClaim.current?.() ?? false,
7100
7748
  onCodexAppServerEvent: (event) => {
7101
7749
  emitCodexAppServerEvent(params, event);
7102
7750
  },
@@ -7115,6 +7763,7 @@ async function prepareCodexAttemptTools(runtime) {
7115
7763
  };
7116
7764
  const tools = await buildDynamicTools({
7117
7765
  ...commonToolParams,
7766
+ registerRunCleanup: (cleanup) => runCleanups.push(cleanup),
7118
7767
  cronCreatorToolAllowlistRef: cronCreatorToolAllowlist,
7119
7768
  cronCreatorToolAllowlistCaptureRef,
7120
7769
  onPersistentWebSearchPolicyResolved: (allowed) => {
@@ -7333,13 +7982,16 @@ async function prepareCodexAttemptTools(runtime) {
7333
7982
  cronCreatorToolAllowlistCaptureRef,
7334
7983
  scheduledAppAuthoritySourceRef,
7335
7984
  dynamicToolParams,
7985
+ compactionPlanState,
7336
7986
  computerContextEpoch,
7987
+ runCleanups,
7337
7988
  toolBridge,
7338
7989
  toolState,
7339
7990
  toolOutcomeOrdinals,
7340
7991
  suppressedDynamicToolOutcomeOrdinals,
7341
7992
  onCodexToolOutcome,
7342
- allocateCodexToolOutcomeOrdinal
7993
+ allocateCodexToolOutcomeOrdinal,
7994
+ runtimeYieldCompletionClaim
7343
7995
  };
7344
7996
  } catch (error) {
7345
7997
  await scopedMcpTools?.dispose();
@@ -7457,7 +8109,7 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
7457
8109
  data: {
7458
8110
  phase: "turn_starting",
7459
8111
  threadId: resourceState.thread.threadId,
7460
- model: turnStartParams.model,
8112
+ model: params.modelId,
7461
8113
  effort: turnStartParams.effort,
7462
8114
  collaborationEffort: turnStartParams.collaborationMode?.settings.reasoning_effort,
7463
8115
  serviceTier: turnStartParams.serviceTier
@@ -7814,8 +8466,10 @@ function createCodexAttemptTurnWatchController(params) {
7814
8466
  function scheduleAttemptIdleWatch() {
7815
8467
  scheduleWatch("attempt", fireAttemptIdleTimeout, attemptLastProgressAt, attemptIdleTimeoutOverrideMs ?? turnAttemptIdleTimeoutMs, attemptIdleWatchArmed && !shouldPauseAttemptIdleWatch());
7816
8468
  }
8469
+ const terminalWorkIsActive = () => params.getActiveAppServerTurnRequests() > 0 || params.isTerminalTurnNotificationQueued() && params.getActiveFinalizationHookCount() > 0;
8470
+ const terminalIdleTimeoutMs = () => params.isTerminalTurnNotificationQueued() && !terminalWorkIsActive() ? Math.min(TURN_TERMINAL_SETTLEMENT_IDLE_TIMEOUT_MS, turnTerminalIdleTimeoutMs) : turnTerminalIdleTimeoutMs;
7817
8471
  function scheduleTerminalIdleWatch() {
7818
- scheduleWatch("terminal", fireTerminalIdleTimeout, completionLastActivityAt, turnTerminalIdleTimeoutMs, terminalIdleWatchArmed && params.getActiveAppServerTurnRequests() === 0);
8472
+ scheduleWatch("terminal", fireTerminalIdleTimeout, completionLastActivityAt, terminalIdleTimeoutMs(), terminalIdleWatchArmed && (params.isTerminalTurnNotificationQueued() || params.getActiveAppServerTurnRequests() === 0));
7819
8473
  }
7820
8474
  function scheduleProgressWatches() {
7821
8475
  scheduleAttemptIdleWatch();
@@ -7936,18 +8590,26 @@ function createCodexAttemptTurnWatchController(params) {
7936
8590
  });
7937
8591
  }
7938
8592
  function fireTerminalIdleTimeout() {
7939
- if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !terminalIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return;
8593
+ if (params.isCompleted() || params.signal.aborted || !terminalIdleWatchArmed) return;
8594
+ if (!params.isTerminalTurnNotificationQueued() && params.getActiveAppServerTurnRequests() > 0) {
8595
+ scheduleTerminalIdleWatch();
8596
+ return;
8597
+ }
8598
+ const timeoutMs = terminalIdleTimeoutMs();
7940
8599
  const idleMs = Math.max(0, Date.now() - completionLastActivityAt);
7941
- if (idleMs < turnTerminalIdleTimeoutMs) {
8600
+ if (idleMs < timeoutMs) {
7942
8601
  scheduleTerminalIdleWatch();
7943
8602
  return;
7944
8603
  }
7945
8604
  reportTimeout({
7946
8605
  kind: "terminal",
7947
8606
  idleMs,
7948
- timeoutMs: turnTerminalIdleTimeoutMs,
8607
+ timeoutMs,
7949
8608
  lastActivityReason: completionLastActivityReason,
7950
- details: completionLastActivityDetails
8609
+ details: {
8610
+ ...completionLastActivityDetails,
8611
+ terminalTurnNotificationQueued: params.isTerminalTurnNotificationQueued()
8612
+ }
7951
8613
  });
7952
8614
  }
7953
8615
  return {
@@ -8004,6 +8666,7 @@ function createCodexAttemptTurnWatchController(params) {
8004
8666
  completionLastActivityReason = `notification:${method}`;
8005
8667
  if (options?.details !== void 0) completionLastActivityDetails = options.details;
8006
8668
  if (options?.attemptProgress) recordAttemptProgress(completionLastActivityReason, options);
8669
+ if (params.isTerminalTurnNotificationQueued()) scheduleTerminalIdleWatch();
8007
8670
  },
8008
8671
  extendAttemptIdleWatch: (timeoutMs) => {
8009
8672
  attemptIdleTimeoutOverrideMs = resolveWatchTimeoutMs(timeoutMs);
@@ -8026,6 +8689,7 @@ function createCodexAttemptTurnState(resources) {
8026
8689
  latestStartupErrorNotification: void 0,
8027
8690
  rateLimitsRevisionBeforeLastTurnStart: void 0,
8028
8691
  completed: false,
8692
+ abortCleanup: Promise.resolve(),
8029
8693
  localCompletionRequested: false,
8030
8694
  terminalTurnNotificationQueued: false,
8031
8695
  sawCodexInterruptMarker: false,
@@ -8179,11 +8843,14 @@ function createCodexAttemptTurnState(resources) {
8179
8843
  //#endregion
8180
8844
  //#region extensions/codex/src/app-server/run-attempt.ts
8181
8845
  async function runCodexAppServerAttempt(params, options) {
8182
- const runtime = await prepareCodexAttemptRuntime(await prepareCodexAttemptConnection({
8846
+ const connection = await prepareCodexAttemptConnection({
8183
8847
  params,
8184
8848
  options
8185
- }));
8186
- const resources = prepareCodexAttemptResources(await prepareCodexAttemptPrompt(await prepareCodexAttemptContext(runtime, await prepareCodexAttemptTools(runtime))));
8849
+ });
8850
+ const runtime = await prepareCodexAttemptRuntime(connection);
8851
+ const attemptTools = await prepareCodexAttemptTools(runtime);
8852
+ const resources = prepareCodexAttemptResources(await prepareCodexAttemptPrompt(await prepareCodexAttemptContext(runtime, attemptTools)));
8853
+ attemptTools.runtimeYieldCompletionClaim.current = () => resources.state.nativeHookRelay?.hasClaimedDirectChild() ?? false;
8187
8854
  await startCodexAttemptRuntime(resources);
8188
8855
  const turnRuntime = createCodexAttemptTurnState(resources);
8189
8856
  const lifecycle = createCodexAttemptLifecycleController(resources, turnRuntime);
@@ -8193,11 +8860,14 @@ async function runCodexAppServerAttempt(params, options) {
8193
8860
  const turnStart = await startCodexAttemptTurn(resources, turnRuntime, notifications, turnRequest);
8194
8861
  if ("result" in turnStart) return turnStart.result;
8195
8862
  const activeTurn = await activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notifications, turnStart.turn);
8863
+ let finalizedResult;
8196
8864
  try {
8197
- return await finalizeCodexAttempt(resources, turnRuntime, lifecycle, notifications, turnRequest, activeTurn);
8865
+ finalizedResult = await finalizeCodexAttempt(resources, turnRuntime, lifecycle, notifications, turnRequest, activeTurn);
8198
8866
  } finally {
8199
8867
  await cleanupCodexAttempt(resources, turnRuntime, lifecycle, turnRequest, activeTurn);
8200
8868
  }
8869
+ if (resources.state.executionDisconnectError && !connection.terminalState.explicitCancellationObserved) throw resources.state.executionDisconnectError;
8870
+ return finalizedResult;
8201
8871
  }
8202
8872
  //#endregion
8203
8873
  export { runCodexAppServerAttempt };