@openclaw/codex 2026.7.2-beta.2 → 2026.7.2-beta.4

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 (39) hide show
  1. package/dist/{app-server-policy-Scc-Wevo.js → app-server-policy-BO3jYBkv.js} +1 -1
  2. package/dist/{attempt-notifications-BFmNhqFl.js → attempt-notifications-DdmnBMUp.js} +1 -1
  3. package/dist/{media-understanding-provider-D31dOJwb.js → bounded-turn-CaQL96kg.js} +43 -183
  4. package/dist/capabilities-CLICvTxf.js +36 -0
  5. package/dist/cli-metadata.js +1 -1
  6. package/dist/{command-formatters-Dq9InZUK.js → command-formatters-DgADqnHa.js} +4 -34
  7. package/dist/{command-handlers-IgqmL_kv.js → command-handlers-l_lVPNlB.js} +72 -13
  8. package/dist/{compact-DCTmg3id.js → compact-CC5KhBzX.js} +5 -4
  9. package/dist/{computer-use-CPcU8TjG.js → computer-use-Ip1-QGa-.js} +4 -4
  10. package/dist/{conversation-control-CGkBlfc5.js → conversation-control-sNPsbTCP.js} +6 -5
  11. package/dist/doctor-contract-api.js +1 -1
  12. package/dist/{dynamic-tools-C_1tEs34.js → dynamic-tools-CQI3UwqY.js} +117 -2543
  13. package/dist/event-projector-assistant-message-DTgnPPVF.js +101 -0
  14. package/dist/event-projector-usage-D6tE7_qD.js +69 -0
  15. package/dist/harness.js +27 -7
  16. package/dist/incognito-session-ET_ufDH-.js +2 -0
  17. package/dist/index.js +60 -26
  18. package/dist/media-understanding-provider.js +168 -1
  19. package/dist/{models-uh26C8QU.js → models-BNeMlHgg.js} +2 -2
  20. package/dist/{notification-correlation-KmfV4EkP.js → notification-correlation-BGLixv5_.js} +3 -3
  21. package/dist/{plugin-app-cache-key-Ceb-lm2c.js → plugin-app-cache-key-CvzvmBRz.js} +2 -2
  22. package/dist/{rate-limits-DyXaYAxU.js → rate-limits-D0IJT-a6.js} +3 -1
  23. package/dist/{request-B_oQsCXy.js → request-BTccUTD3.js} +1 -1
  24. package/dist/{run-attempt-MLzoMe_m.js → run-attempt--858NXDl.js} +2798 -172
  25. package/dist/{session-binding-h1mmCGnl.js → session-binding-ZSMX1omP.js} +9 -2
  26. package/dist/{session-catalog-CEvoXWHA.js → session-catalog-DsKaezgR.js} +119 -96
  27. package/dist/{session-cli-XsEuWb_C.js → session-cli-CYJTfRLG.js} +2 -2
  28. package/dist/session-upstream-marker-D7XlI9us.js +82 -0
  29. package/dist/settled-turn-finalizer-DHltNgtF.js +303 -0
  30. package/dist/{shared-client-JiAnW6pc.js → shared-client-wmgNFNYp.js} +82 -13
  31. package/dist/{side-question-DqDvIwSU.js → side-question-BfFZCsJ-.js} +21 -15
  32. package/dist/{thread-lifecycle-CN_pPtPh.js → thread-lifecycle-cqrOlJkq.js} +157 -32
  33. package/dist/{transcript-mirror-D9rTxx2P.js → transcript-mirror-2p7kl6e4.js} +433 -145
  34. package/dist/upstream-session-fork-vPjwkT1i.js +320 -0
  35. package/dist/{usage-D5Rohxoc.js → usage-DM_I3mPT.js} +3 -3
  36. package/dist/{web-search-provider.runtime-CKVBn3fP.js → web-search-provider.runtime-Bx6HAc_k.js} +3 -3
  37. package/npm-shrinkwrap.json +38 -38
  38. package/openclaw.plugin.json +11 -1
  39. package/package.json +7 -7
@@ -1,32 +1,43 @@
1
- import { A as isCodexAppServerStartupError, B as isCodexAppServerApprovalRequest, C as resolveCodexAppServerFallbackApiKeyCacheKey, D as resolveCodexAppServerHomeDir, F as resolveCodexTurnCompletionIdleTimeoutMs, H as isCodexAppServerConnectionClosedError, I as resolveCodexTurnTerminalIdleTimeoutMs, K as isCodexAppServerRequestTimeoutError, L as withCodexStartupTimeout, M as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, N as resolveCodexStartupTimeoutMs, O as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, P as resolveCodexTurnAssistantCompletionIdleTimeoutMs, R as CodexAppServerRpcError, S as resolveCodexAppServerAuthProfileIdForAgent, T as resolveCodexAppServerPreparedAuthHandoff, V as isCodexAppServerBrokenPipeError, X as flattenCodexDynamicToolFunctions, Z as isJsonObject, _ as readCodexRateLimitsRevision, a as getLeasedSharedCodexAppServerClient, b as resolveCodexAppServerAuthAccountCacheKey, c as releaseLeasedSharedCodexAppServerClient, d as retireSharedCodexAppServerClientIfCurrent, g as ensureCodexAppServerClientRuntime, k as CodexAppServerStartupError, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, o as isCodexAppServerStartSelectionChangedError, t as clearSharedCodexAppServerClientIfCurrent, u as retainSharedCodexAppServerClientIfCurrent, v as readRecentCodexRateLimits, w as resolveCodexAppServerPreparedApiKeyCacheKey, x as resolveCodexAppServerAuthProfileId } from "./shared-client-JiAnW6pc.js";
2
- import { S as updateActiveTurnItemIds, _ as readCodexNotificationItem, a as isCodexTurnAbortMarkerNotification, b as shouldDisarmAssistantCompletionIdleWatch, c as isPendingOpenClawDynamicToolCompletionNotification, d as isRawReasoningCompletionNotification, f as isRawToolOutputCompletionNotification, g as isTerminalTurnStatus, h as isRetryableErrorNotification, i as isAssistantCompletionReleaseNotification, l as isRawAssistantProgressNotification, m as isReasoningProgressNotification, n as describeNotificationActivity, o as isFileChangePatchUpdatedNotification, p as isReasoningItemCompletionNotification, r as isAssistantCommentaryCompletionNotification, s as isNativeToolProgressNotification, t as codexExecutionToolName, u as isRawFunctionToolOutputCompletionNotification, v as readNotificationItemId, x as updateActiveCompletionBlockerItemIds, y as readRawResponseToolCallId } from "./attempt-notifications-BFmNhqFl.js";
3
- import { C as resolveCodexComputerUseConfig, D as resolveOpenClawExecPolicyForCodexAppServer, O as shouldAutoApproveCodexAppServerApprovals, T as resolveCodexPluginsPolicy, c as sessionBindingIdentity, g as isCodexAppServerApprovalPolicyAllowedByRequirements, k as withMcpElicitationsApprovalPolicy, s as reclaimCurrentCodexSessionGeneration, v as isCodexSandboxExecServerEnabled, w as resolveCodexModelBackedReviewerPolicyContext, y as readCodexPluginConfig } from "./session-binding-h1mmCGnl.js";
4
- import { _ as readCodexTurnCompletedNotification, f as assertCodexTurnStartResponse, i as mirrorPromptAtTurnStartBestEffort, n as createCodexAppServerUserMessagePersistenceNotifier, o as buildCodexUserPromptMessage, p as readCodexDynamicToolCallParams, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-D9rTxx2P.js";
5
- import { i as defaultCodexAppInventoryCache, n as buildCodexAppServerRuntimeFingerprint, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-Ceb-lm2c.js";
6
- import { A as CodexAppServerUnsafeSubscriptionError, B as fitCodexProjectedContextForTurnStart, D as resolveCodexDynamicToolsLoadingForRuntime, F as areCodexDynamicToolFingerprintsCompatible, G as buildCodexPluginThreadConfig, H as resolveCodexContextEngineProjectionMaxChars, I as codexDynamicToolsFingerprint, J as mergeCodexThreadConfigs, K as buildCodexPluginThreadConfigInputFingerprint, L as codexLegacyDynamicToolsFingerprint, M as interruptCodexTurnBestEffort, N as retireCodexAppServerClientAfterTimedOutTurn, O as CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS, P as unsubscribeCodexThreadBestEffort, Q as resolveRecoverableCodexPluginConfigKeys, R as buildContextEngineBinding, U as resolveCodexContextEngineProjectionReserveTokens, V as projectContextEngineAssemblyForCodex, Y as shouldBuildCodexPluginThreadConfig, b as isCodexAppServerProfilerEnabled, d as resolveCodexWebSearchPlan, f as buildDeveloperInstructions, g as resolveCodexAppServerThreadModelSelection, j as closeCodexStartupClientBestEffort, k as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, n as buildTurnStartParams, q as buildCodexPluginThreadConfigTimeoutFallback, r as buildCodexUserInput, s as startOrResumeThread, t as buildTurnCollaborationMode, w as isSystemAgentOnlyCodexDynamicToolAllowlist, x as filterCodexDynamicTools, z as isContextEngineBindingCompatible } from "./thread-lifecycle-CN_pPtPh.js";
7
- import { r as formatCodexDisplayText } from "./command-formatters-Dq9InZUK.js";
8
- import { n as resolveCodexAppServerForOpenClawToolPolicy, t as resolveCodexAppServerForModelProvider } from "./app-server-policy-Scc-Wevo.js";
9
- import { a as resolveCodexBindingAppServerConnection, n as readCodexNotificationThreadId, r as readCodexNotificationTurnId, t as isCodexNotificationForTurn } from "./notification-correlation-KmfV4EkP.js";
1
+ import { A as isCodexAppServerStartupError, B as isCodexAppServerApprovalRequest, C as resolveCodexAppServerFallbackApiKeyCacheKey, D as resolveCodexAppServerHomeDir, F as resolveCodexTurnCompletionIdleTimeoutMs, H as isCodexAppServerConnectionClosedError, I as resolveCodexTurnTerminalIdleTimeoutMs, K as isCodexAppServerRequestTimeoutError, L as withCodexStartupTimeout, M as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, N as resolveCodexStartupTimeoutMs, O as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, P as resolveCodexTurnAssistantCompletionIdleTimeoutMs, Q as isJsonObject, R as CodexAppServerRpcError, S as resolveCodexAppServerAuthProfileIdForAgent, T as resolveCodexAppServerPreparedAuthHandoff, V as isCodexAppServerBrokenPipeError, Z as flattenCodexDynamicToolFunctions, _ as readCodexRateLimitsRevision, a as getLeasedSharedCodexAppServerClient, b as resolveCodexAppServerAuthAccountCacheKey, c as releaseLeasedSharedCodexAppServerClient, d as retireSharedCodexAppServerClientIfCurrent, g as ensureCodexAppServerClientRuntime, k as CodexAppServerStartupError, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, o as isCodexAppServerStartSelectionChangedError, t as clearSharedCodexAppServerClientIfCurrent, u as retainSharedCodexAppServerClientIfCurrent, v as readRecentCodexRateLimits, w as resolveCodexAppServerPreparedApiKeyCacheKey, x as resolveCodexAppServerAuthProfileId, y as rememberCodexRateLimitsRead } from "./shared-client-wmgNFNYp.js";
2
+ import { S as updateActiveTurnItemIds, _ as readCodexNotificationItem, a as isCodexTurnAbortMarkerNotification, b as shouldDisarmAssistantCompletionIdleWatch, c as isPendingOpenClawDynamicToolCompletionNotification, d as isRawReasoningCompletionNotification, f as isRawToolOutputCompletionNotification, g as isTerminalTurnStatus, h as isRetryableErrorNotification, i as isAssistantCompletionReleaseNotification, l as isRawAssistantProgressNotification, m as isReasoningProgressNotification, n as describeNotificationActivity, o as isFileChangePatchUpdatedNotification, p as isReasoningItemCompletionNotification, r as isAssistantCommentaryCompletionNotification, s as isNativeToolProgressNotification, t as codexExecutionToolName, u as isRawFunctionToolOutputCompletionNotification, v as readNotificationItemId, x as updateActiveCompletionBlockerItemIds, y as readRawResponseToolCallId } from "./attempt-notifications-DdmnBMUp.js";
3
+ import { A as withMcpElicitationsApprovalPolicy, E as resolveCodexPluginsPolicy, O as resolveOpenClawExecPolicyForCodexAppServer, T as resolveCodexModelBackedReviewerPolicyContext, _ as isCodexAppServerApprovalPolicyAllowedByRequirements, b as readCodexPluginConfig, c as reclaimCurrentCodexSessionGeneration, i as createCodexSessionGenerationSupersededError, k as shouldAutoApproveCodexAppServerApprovals, l as sessionBindingIdentity, w as resolveCodexComputerUseConfig, y as isCodexSandboxExecServerEnabled } from "./session-binding-ZSMX1omP.js";
4
+ import { $ as shouldBuildCodexPluginThreadConfig, B as codexDynamicToolsFingerprint, F as closeCodexStartupClientBestEffort, G as projectContextEngineAssemblyForCodex, H as buildContextEngineBinding, I as interruptCodexTurnBestEffort, K as resolveCodexContextEngineProjectionMaxChars, L as retireCodexAppServerClientAfterTimedOutTurn, M as CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS, N as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, O as isSystemAgentOnlyCodexDynamicToolAllowlist, P as CodexAppServerUnsafeSubscriptionError, Q as mergeCodexThreadConfigs, R as unsubscribeCodexThreadBestEffort, T as filterCodexDynamicTools, U as isContextEngineBindingCompatible, V as codexLegacyDynamicToolsFingerprint, W as fitCodexProjectedContextForTurnStart, X as buildCodexPluginThreadConfigInputFingerprint, Y as buildCodexPluginThreadConfig, Z as buildCodexPluginThreadConfigTimeoutFallback, a as sanitizeCodexHistoryImagePayloads, at as readCodexErrorNotificationMessage, b as resolveCodexAppServerThreadModelSelection, ct as readItemString, dt as readNonNegativeInteger, ft as readNullableString$1, g as buildDeveloperInstructions, h as resolveCodexWebSearchPlan, ht as splitPlanText, it as normalizeNonEmptyString, j as resolveCodexDynamicToolsLoadingForRuntime, lt as readNonEmptyString$1, mt as readString$5, n as buildTurnStartParams, nt as resolveRecoverableCodexPluginConfigKeys, ot as readHookOutputEntries, pt as readNumber, q as resolveCodexContextEngineProjectionReserveTokens, r as buildCodexUserInput, rt as extractRawAssistantText, s as startOrResumeThread, st as readItem, t as buildTurnCollaborationMode, ut as readNonEmptyStringArray, w as isCodexAppServerProfilerEnabled, z as areCodexDynamicToolFingerprintsCompatible } from "./thread-lifecycle-cqrOlJkq.js";
5
+ import { n as projectCodexThreadUsageUpdate, t as normalizeCodexResponseTokenUsage } from "./event-projector-usage-D6tE7_qD.js";
6
+ import { C as readCodexTurnCompletedNotification, S as readCodexTurn, d as attachCodexMirrorIdentity, f as readMirrorIdentity, i as mirrorPromptAtTurnStartBestEffort, n as createCodexAppServerUserMessagePersistenceNotifier, o as buildCodexUserPromptMessage, s as promptSnapshot, t as codexTranscriptMirrorRuntime, u as serializeCodexMirrorSourceEvidence, v as assertCodexTurnStartResponse, y as readCodexDynamicToolCallParams } from "./transcript-mirror-2p7kl6e4.js";
7
+ import { i as defaultCodexAppInventoryCache, n as buildCodexAppServerRuntimeFingerprint, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-CvzvmBRz.js";
8
+ import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
9
+ import { t as CODEX_CONTROL_METHODS } from "./capabilities-CLICvTxf.js";
10
+ import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-D0IJT-a6.js";
11
+ import { r as formatCodexDisplayText } from "./command-formatters-DgADqnHa.js";
12
+ import { n as resolveCodexAppServerForOpenClawToolPolicy, t as resolveCodexAppServerForModelProvider } from "./app-server-policy-BO3jYBkv.js";
13
+ import { a as resolveCodexBindingAppServerConnection, n as readCodexNotificationThreadId, r as readCodexNotificationTurnId, t as isCodexNotificationForTurn } from "./notification-correlation-BGLixv5_.js";
10
14
  import { n as prepareCodexAppServerAuthBinding } from "./auth-binding-C1czuHPA.js";
11
- import { $ as resolveCodexToolProgressDetailMode, B as hasPendingDynamicToolTerminalDiagnostic, C as CodexAppServerEventProjector, D as markCodexAuthProfileBlockedFromRateLimits, E as isCodexUsageLimitPromptError, F as emitCodexNativePreToolUseFailureDiagnostic, G as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult, H as isMatchingDynamicToolTerminalDiagnostic, I as resolveCodexNativeHookRelayEvents, J as toCodexDynamicToolProtocolResponse, K as shouldReleaseTurnAfterTerminalDynamicTool, L as resolveCodexNativeHookRelayTtlMs, M as buildCodexNativeHookRelayConfig, N as buildCodexNativeHookRelayDisabledConfig, O as refreshCodexUsageLimitPromptError, P as createCodexNativeHookRelay, Q as inferCodexDynamicToolMeta, R as scheduleCodexNativeHookRelayUnregister, T as formatCodexTurnStartUsageLimitError, U as resolveDynamicToolCallTimeoutMs, V as isDynamicToolTerminalDiagnosticEvent, W as resolveTerminalDynamicToolBatchAction, X as readCodexMirroredSessionHistoryMessages, Y as resolveCodexToolAbortTerminalReason, Z as shouldEmitTranscriptToolProgress, _ as resolveCodexMessageToolProvider, a as emitDynamicToolStartedDiagnostic, b as shouldRequireCodexSandboxExecServerEnvironment, d as createCodexDynamicToolBuildStageTracker, et as sanitizeCodexToolArguments, f as disableCodexPluginThreadConfig, g as resolveCodexExternalSandboxPolicyForOpenClawSandbox, h as resolveCodexAppServerHookChannelId, i as emitDynamicToolErrorDiagnostic, j as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, l as settleCodexSourceReplyFinality, m as resolveCodexAppServerExecutionCwd, n as handleCodexAppServerApprovalRequest, nt as resolveCodexLocalRuntimeAttribution, o as emitDynamicToolTerminalDiagnostic, p as formatCodexDynamicToolBuildStageSummary, q as toCodexDynamicToolProgressResponse, r as handleCodexAppServerElicitationRequest, s as resolveCodexProviderWebSearchSupport, t as createCodexDynamicToolBridge, tt as sanitizeCodexToolResponse, u as buildDynamicTools, v as resolveCodexSandboxEnvironmentSelection, w as createCodexUsageLimitPromptError, x as shouldWarnCodexDynamicToolBuildStageSummary, y as shouldEnableCodexAppServerNativeToolSurface, z as handleDynamicToolCallWithTimeout } from "./dynamic-tools-C_1tEs34.js";
12
- import { a as runCodexComputerUseLiveTest, r as killStaleComputerUseMcpChildren, t as ensureCodexComputerUse } from "./computer-use-CPcU8TjG.js";
15
+ import { i as resolveCodexLocalRuntimeAttribution, n as createAssistantMessage, r as createAssistantMirrorMessage, t as createAssistantCommentaryMessage } from "./event-projector-assistant-message-DTgnPPVF.js";
16
+ import { $ as shouldSynthesizeToolProgressForItem, A as resolveCodexNativeHookRelayEvents, B as shouldReleaseTurnAfterTerminalDynamicTool, C as CodexNativeToolLifecycleProjector, D as buildCodexNativeHookRelayDisabledConfig, E as buildCodexNativeHookRelayConfig, F as isDynamicToolTerminalDiagnosticEvent, G as isNonSuccessItemStatus, H as toCodexDynamicToolProtocolResponse, I as isMatchingDynamicToolTerminalDiagnostic, J as itemName, K as isSideEffectingNativeToolItem, L as resolveDynamicToolCallTimeoutMs, M as scheduleCodexNativeHookRelayUnregister, N as handleDynamicToolCallWithTimeout, O as createCodexNativeHookRelay, P as hasPendingDynamicToolTerminalDiagnostic, Q as shouldRecordNativeToolTranscript, R as resolveTerminalDynamicToolBatchAction, T as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, U as resolveCodexToolAbortTerminalReason, V as toCodexDynamicToolProgressResponse, W as isMutatingNativeToolItem, X as itemTitle, Y as itemStatus, Z as shouldClearTerminalPresentationForNativeItem, _ as resolveCodexMessageToolProvider, a as emitDynamicToolStartedDiagnostic, b as shouldRequireCodexSandboxExecServerEnvironment, d as createCodexDynamicToolBuildStageTracker, et as unknownItemStatus, f as disableCodexPluginThreadConfig, g as resolveCodexExternalSandboxPolicyForOpenClawSandbox, h as resolveCodexAppServerHookChannelId, i as emitDynamicToolErrorDiagnostic, j as resolveCodexNativeHookRelayTtlMs, k as emitCodexNativePreToolUseFailureDiagnostic, l as settleCodexSourceReplyFinality, m as resolveCodexAppServerExecutionCwd, n as handleCodexAppServerApprovalRequest, o as emitDynamicToolTerminalDiagnostic, p as formatCodexDynamicToolBuildStageSummary, q as itemKind, r as handleCodexAppServerElicitationRequest, s as resolveCodexProviderWebSearchSupport, t as createCodexDynamicToolBridge, u as buildDynamicTools, v as resolveCodexSandboxEnvironmentSelection, x as shouldWarnCodexDynamicToolBuildStageSummary, y as shouldEnableCodexAppServerNativeToolSurface, z as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult } from "./dynamic-tools-CQI3UwqY.js";
17
+ import { a as runCodexComputerUseLiveTest, r as killStaleComputerUseMcpChildren, t as ensureCodexComputerUse } from "./computer-use-Ip1-QGa-.js";
13
18
  import fs from "node:fs/promises";
14
19
  import path, { posix } from "node:path";
15
- import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
20
+ import { asDateTimestampMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
16
21
  import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
17
22
  import { createHash, randomUUID } from "node:crypto";
18
- import { resolveAgentDir, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
23
+ import { markAuthProfileBlockedUntil, resolveAgentDir, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
19
24
  import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
20
25
  import { asFiniteNumber, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
21
- import { CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildAgentHarnessUserInputAnswers, buildBootstrapContextForFiles, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, clearActiveEmbeddedRun, deliverAgentHarnessUserInputPrompt, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, finalizeHarnessContextEngineTurn, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, getBeforeToolCallPolicyDiagnosticState, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, resolveAgentDir as resolveAgentDir$1, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveBootstrapFilesForRun, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveSessionAgentIds as resolveSessionAgentIds$1, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
26
+ import { CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, TOOL_PROGRESS_OUTPUT_MAX_CHARS, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildAgentHarnessUserInputAnswers, buildBootstrapContextForFiles, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, callGatewayTool, cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, deliverAgentHarnessUserInputPrompt, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, finalizeHarnessContextEngineTurn, formatErrorMessage, formatFastModeAutoProgressText, formatToolAggregate, formatToolProgressOutput, getAgentHarnessHookRunner, getBeforeToolCallPolicyDiagnosticState, inferToolMetaFromArgs, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, projectAgentHarnessTranscriptMessageForDisplay, resolveAgentDir as resolveAgentDir$1, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveBootstrapFilesForRun, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveSessionAgentIds as resolveSessionAgentIds$1, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessAfterCompactionHook, runAgentHarnessAfterToolCallHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessGatewayQuestion, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
22
27
  import { spawn } from "node:child_process";
23
28
  import { once } from "node:events";
24
29
  import { isIP } from "node:net";
25
30
  import { WebSocketServer } from "ws";
26
- import { parseSqliteSessionFileMarker } from "openclaw/plugin-sdk/session-store-runtime";
31
+ import { listSessionEntries, parseSqliteSessionFileMarker } from "openclaw/plugin-sdk/session-store-runtime";
27
32
  import { fileURLToPath } from "node:url";
33
+ import { redactSensitiveFieldValue, redactSensitiveText, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
28
34
  import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
29
- import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";
35
+ import { readSessionTranscriptEvents } from "openclaw/plugin-sdk/session-transcript-runtime";
36
+ import { prepareMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";
37
+ import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
38
+ import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "openclaw/plugin-sdk/agent-sessions";
39
+ import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
40
+ import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
30
41
  import { MESSAGE_TOOL_DELIVERY_HINTS } from "openclaw/plugin-sdk/message-tool-delivery-hints";
31
42
  import { createAgentHarnessTaskRuntime, deliverAgentHarnessTaskCompletion, isDurableAgentHarnessCompletionDelivery } from "openclaw/plugin-sdk/agent-harness-task-runtime";
32
43
  import { SsrFBlockedError, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
@@ -190,41 +201,2467 @@ function normalizeCodexSteerDebounceMs(value) {
190
201
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : CODEX_STEER_ALL_DEBOUNCE_MS;
191
202
  }
192
203
  //#endregion
193
- //#region extensions/codex/src/app-server/user-input-bridge.ts
204
+ //#region extensions/codex/src/app-server/event-projector-assistant.ts
205
+ var CodexAssistantProjection = class {
206
+ constructor(params, emitAgentEvent, matchesToolProgressEcho, nextTranscriptTimestamp) {
207
+ this.params = params;
208
+ this.emitAgentEvent = emitAgentEvent;
209
+ this.matchesToolProgressEcho = matchesToolProgressEcho;
210
+ this.nextTranscriptTimestamp = nextTranscriptTimestamp;
211
+ this.assistantTextByItem = /* @__PURE__ */ new Map();
212
+ this.assistantItemOrder = [];
213
+ this.assistantTimestampByItem = /* @__PURE__ */ new Map();
214
+ this.assistantPhaseByItem = /* @__PURE__ */ new Map();
215
+ this.latestTerminalAssistantCandidateSuperseded = false;
216
+ this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
217
+ this.terminalAssistantCandidateEarlierActiveItemIds = /* @__PURE__ */ new Set();
218
+ this.lastCommentaryProgressTextByItem = /* @__PURE__ */ new Map();
219
+ this.lastAnswerCandidateEventByItem = /* @__PURE__ */ new Map();
220
+ this.pendingRawCommentaryEchoes = 0;
221
+ this.rawPromotedAssistantItemIds = /* @__PURE__ */ new Set();
222
+ this.assistantStarted = false;
223
+ this.streamedPartialAssistantItemReplaceable = false;
224
+ }
225
+ hasCompletedTerminalAssistantText(completedItemIds) {
226
+ const latestCompletedItemId = this.latestCompletedTerminalAssistantItemId;
227
+ if (!latestCompletedItemId) return false;
228
+ const finalItem = this.resolveFinalAssistantTextItem();
229
+ return this.latestCompletedItemId === latestCompletedItemId && finalItem?.itemId === latestCompletedItemId && completedItemIds.has(latestCompletedItemId);
230
+ }
231
+ getLatestTerminalAssistantCandidate() {
232
+ const itemId = this.latestTerminalAssistantCandidateItemId;
233
+ if (!itemId) return;
234
+ const text = this.assistantTextByItem.get(itemId)?.trim();
235
+ return {
236
+ itemId,
237
+ hasText: Boolean(text && !this.isToolProgressEchoText(itemId, text))
238
+ };
239
+ }
240
+ hasLatestTerminalAssistantCandidateText() {
241
+ return !this.latestTerminalAssistantCandidateSuperseded && this.getLatestTerminalAssistantCandidate()?.hasText === true;
242
+ }
243
+ canReleaseLatestTerminalAssistantAfterToolHandoff() {
244
+ return this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff && this.hasLatestTerminalAssistantCandidateText();
245
+ }
246
+ async handleAssistantDelta(params) {
247
+ const itemId = readString$5(params, "itemId") ?? "assistant";
248
+ const delta = readString$5(params, "delta") ?? "";
249
+ if (!delta) return;
250
+ if (itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
251
+ const isCommentary = this.isCommentaryAssistantItem(itemId);
252
+ if (!isCommentary && itemId !== this.latestTerminalAssistantCandidateItemId) this.markTerminalAssistantCandidateSupersededBy();
253
+ if (!this.assistantStarted) {
254
+ this.assistantStarted = true;
255
+ await this.params.onAssistantMessageStart?.();
256
+ }
257
+ this.rememberAssistantItem(itemId);
258
+ const text = `${this.assistantTextByItem.get(itemId) ?? ""}${delta}`;
259
+ this.assistantTextByItem.set(itemId, text);
260
+ if (isCommentary) {
261
+ this.emitCommentaryProgress({
262
+ itemId,
263
+ text
264
+ });
265
+ return;
266
+ }
267
+ if (this.isFinalAnswerAssistantItem(itemId)) this.emitAnswerCandidate(itemId, "candidate");
268
+ const knownFinalAnswer = this.shouldStreamAssistantPartial(itemId);
269
+ const replace = this.streamedPartialAssistantItemId !== void 0 && this.streamedPartialAssistantItemId !== itemId;
270
+ if (replace && (!knownFinalAnswer || this.streamedPartialAssistantItemReplaceable)) this.streamedPartialAssistantItemReplaceable = true;
271
+ else if (this.streamedPartialAssistantItemId === void 0) this.streamedPartialAssistantItemReplaceable = !knownFinalAnswer;
272
+ this.streamedPartialAssistantItemId = itemId;
273
+ const replaceable = this.streamedPartialAssistantItemReplaceable;
274
+ const replacement = replace && replaceable;
275
+ const streamPayload = {
276
+ text,
277
+ delta: replacement ? "" : delta,
278
+ ...replacement ? { replace: true } : {}
279
+ };
280
+ this.emitAgentEvent({
281
+ stream: "assistant",
282
+ data: {
283
+ ...streamPayload,
284
+ ...replaceable ? { replaceable: true } : {}
285
+ }
286
+ });
287
+ if (knownFinalAnswer && !replaceable) await this.params.onPartialReply?.(streamPayload);
288
+ }
289
+ recordItemStarted(item, itemId) {
290
+ if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
291
+ this.rememberAssistantPhase(item);
292
+ if (item?.type === "agentMessage" && itemId) this.rememberAssistantItem(itemId);
293
+ if (itemId && itemId !== this.latestTerminalAssistantCandidateItemId) {
294
+ this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
295
+ if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
296
+ }
297
+ }
298
+ recordItemCompleted(item, itemId, activeItemIds) {
299
+ if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
300
+ if (itemId) this.latestCompletedItemId = itemId;
301
+ this.rememberAssistantPhase(item);
302
+ if (item?.type === "agentMessage" && !this.isCommentaryAssistantItem(item.id)) {
303
+ this.latestCompletedTerminalAssistantItemId = item.id;
304
+ this.markLatestTerminalAssistantCandidate(item.id, activeItemIds);
305
+ this.pendingRawTerminalAssistantEchoItemId = item.id;
306
+ } else if (itemId) {
307
+ this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
308
+ if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
309
+ }
310
+ if (item?.type === "agentMessage" && typeof item.text === "string") {
311
+ this.rememberAssistantItem(item.id);
312
+ this.assistantTextByItem.set(item.id, item.text);
313
+ if (item.text && this.isCommentaryAssistantItem(item.id)) {
314
+ this.emitCommentaryProgress({
315
+ itemId: item.id,
316
+ text: item.text
317
+ });
318
+ this.pendingRawCommentaryEchoes += 1;
319
+ } else if (item.text && this.isFinalAnswerAssistantItem(item.id)) this.emitAnswerCandidate(item.id, "candidate");
320
+ }
321
+ }
322
+ recordSnapshotItem(item) {
323
+ this.rememberAssistantPhase(item);
324
+ if (item.type === "agentMessage" && typeof item.text === "string") {
325
+ this.rememberAssistantItem(item.id);
326
+ this.assistantTextByItem.set(item.id, item.text);
327
+ }
328
+ }
329
+ handleRawResponseItemCompleted(item, activeItemIds) {
330
+ const role = readString$5(item, "role");
331
+ const phase = readString$5(item, "phase");
332
+ const rawItemId = readString$5(item, "id");
333
+ const candidateWasSupersededBeforeRaw = this.latestTerminalAssistantCandidateSuperseded;
334
+ const pendingTerminalAssistantEchoItemId = this.pendingRawTerminalAssistantEchoItemId;
335
+ const isPendingTerminalAssistantEcho = role === "assistant" && phase !== "commentary" && pendingTerminalAssistantEchoItemId !== void 0 && (rawItemId === void 0 || rawItemId === pendingTerminalAssistantEchoItemId);
336
+ if (pendingTerminalAssistantEchoItemId !== void 0 && !isPendingTerminalAssistantEcho) this.pendingRawTerminalAssistantEchoItemId = void 0;
337
+ if (!isPendingTerminalAssistantEcho) {
338
+ this.latestCompletedItemId = void 0;
339
+ this.markTerminalAssistantCandidateSupersededBy(rawItemId);
340
+ }
341
+ if (role !== "assistant") return;
342
+ if (phase === "commentary" && this.pendingRawCommentaryEchoes > 0) {
343
+ this.pendingRawCommentaryEchoes -= 1;
344
+ return;
345
+ }
346
+ const text = extractRawAssistantText(item);
347
+ if (isPendingTerminalAssistantEcho) {
348
+ const typedItemId = pendingTerminalAssistantEchoItemId;
349
+ this.pendingRawTerminalAssistantEchoItemId = void 0;
350
+ if (this.assistantTextByItem.get(typedItemId)?.trim() || !text) return;
351
+ this.rememberAssistantItem(typedItemId);
352
+ this.assistantTextByItem.set(typedItemId, text);
353
+ return;
354
+ }
355
+ if (!text) return;
356
+ const itemId = rawItemId ?? `raw-assistant-${this.assistantItemOrder.length + 1}`;
357
+ const isIdlessTerminalAssistantAfterCompletedWork = candidateWasSupersededBeforeRaw && rawItemId === void 0 && pendingTerminalAssistantEchoItemId === void 0 && activeItemIds.size === 0;
358
+ if (phase !== "commentary" && candidateWasSupersededBeforeRaw && itemId !== this.streamedPartialAssistantItemId && !isIdlessTerminalAssistantAfterCompletedWork) return;
359
+ if (phase) this.assistantPhaseByItem.set(itemId, phase);
360
+ this.rememberAssistantItem(itemId);
361
+ this.assistantTextByItem.set(itemId, text);
362
+ this.rawPromotedAssistantItemIds.add(itemId);
363
+ if (phase === "commentary") this.emitCommentaryProgress({
364
+ itemId,
365
+ text
366
+ });
367
+ else this.markLatestTerminalAssistantCandidate(itemId, activeItemIds, { canReleaseAfterToolHandoff: isIdlessTerminalAssistantAfterCompletedWork });
368
+ }
369
+ collectAssistantTexts() {
370
+ const finalText = this.resolveFinalAssistantTextItem()?.text;
371
+ return finalText ? [finalText] : [];
372
+ }
373
+ collectCommentaryMessages() {
374
+ return this.assistantItemOrder.flatMap((itemId) => {
375
+ if (!this.isCommentaryAssistantItem(itemId)) return [];
376
+ const text = this.assistantTextByItem.get(itemId)?.trim();
377
+ const timestamp = this.assistantTimestampByItem.get(itemId);
378
+ if (!text || timestamp === void 0) return [];
379
+ return [{
380
+ itemId,
381
+ message: createAssistantCommentaryMessage(this.params, text, itemId, timestamp)
382
+ }];
383
+ });
384
+ }
385
+ finalizeAnswerCandidate(turn) {
386
+ if (turn.status !== "completed") {
387
+ this.supersedeVisibleAnswerCandidate();
388
+ return;
389
+ }
390
+ const turnItems = turn.items ?? [];
391
+ const authoritativeIndex = turnItems.findLastIndex((item) => item.type === "agentMessage" && readItemString(item, "phase") === "final_answer" && typeof item.text === "string" && item.text.trim().length > 0);
392
+ const authoritative = authoritativeIndex >= 0 ? turnItems[authoritativeIndex] : void 0;
393
+ if (turnItems.slice(authoritativeIndex + 1).some(shouldClearTerminalPresentationForNativeItem) || authoritative?.id === this.latestTerminalAssistantCandidateItemId && this.latestTerminalAssistantCandidateSuperseded) {
394
+ this.supersedeVisibleAnswerCandidate();
395
+ return;
396
+ }
397
+ const itemId = authoritative?.id ?? this.visibleAnswerCandidateItemId;
398
+ if (!itemId) return;
399
+ if (itemId !== this.visibleAnswerCandidateItemId) {
400
+ this.supersedeVisibleAnswerCandidate();
401
+ this.visibleAnswerCandidateItemId = itemId;
402
+ }
403
+ this.emitAnswerCandidate(itemId, "selected");
404
+ }
405
+ hasAssistantItemTextForSynthesis() {
406
+ for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
407
+ const itemId = this.assistantItemOrder[i];
408
+ if (!itemId || this.assistantPhaseByItem.get(itemId) === "commentary") continue;
409
+ const text = this.assistantTextByItem.get(itemId);
410
+ if (text && text.length > 0) return true;
411
+ }
412
+ return false;
413
+ }
414
+ createCurrentAttemptAssistantMessage(options) {
415
+ for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
416
+ const itemId = this.assistantItemOrder[i];
417
+ if (!itemId || this.isCommentaryAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
418
+ const text = this.assistantTextByItem.get(itemId) ?? "";
419
+ const normalizedText = text.trim();
420
+ if (normalizedText && this.isToolProgressEchoText(itemId, normalizedText)) continue;
421
+ return this.createAssistantMessage(text, options);
422
+ }
423
+ }
424
+ createAssistantMessage(text, options) {
425
+ return createAssistantMessage(this.params, text, options);
426
+ }
427
+ createAssistantMirrorMessage(title, text) {
428
+ return createAssistantMirrorMessage(this.params, title, text);
429
+ }
430
+ rememberAssistantPhase(item) {
431
+ if (item?.type !== "agentMessage") return;
432
+ const phase = readItemString(item, "phase");
433
+ if (phase) this.assistantPhaseByItem.set(item.id, phase);
434
+ }
435
+ isCommentaryAssistantItem(itemId) {
436
+ return this.assistantPhaseByItem.get(itemId) === "commentary";
437
+ }
438
+ isFinalAnswerAssistantItem(itemId) {
439
+ return this.assistantPhaseByItem.get(itemId) === "final_answer";
440
+ }
441
+ shouldStreamAssistantPartial(itemId) {
442
+ return this.assistantPhaseByItem.get(itemId) === "final_answer";
443
+ }
444
+ emitCommentaryProgress(params) {
445
+ const progressText = params.text.replace(/\s+/g, " ").trim();
446
+ if (!progressText || this.lastCommentaryProgressTextByItem.get(params.itemId) === progressText) return;
447
+ this.lastCommentaryProgressTextByItem.set(params.itemId, progressText);
448
+ this.emitAgentEvent({
449
+ stream: "item",
450
+ data: {
451
+ itemId: params.itemId,
452
+ kind: "preamble",
453
+ title: "Preamble",
454
+ phase: "update",
455
+ progressText,
456
+ source: "codex-app-server"
457
+ }
458
+ });
459
+ }
460
+ emitAnswerCandidate(itemId, status) {
461
+ const text = this.assistantTextByItem.get(itemId)?.trim();
462
+ if (!text) return;
463
+ if (status === "candidate" && this.visibleAnswerCandidateItemId !== itemId) {
464
+ this.supersedeVisibleAnswerCandidate();
465
+ this.visibleAnswerCandidateItemId = itemId;
466
+ }
467
+ const signature = `${status}\0${text}`;
468
+ if (this.lastAnswerCandidateEventByItem.get(itemId) === signature) return;
469
+ this.lastAnswerCandidateEventByItem.set(itemId, signature);
470
+ this.emitAgentEvent({
471
+ stream: "item",
472
+ data: {
473
+ itemId,
474
+ kind: "answer_candidate",
475
+ title: "Answer candidate",
476
+ phase: "update",
477
+ status,
478
+ progressText: text,
479
+ source: "codex-app-server",
480
+ hideFromChannelProgress: true
481
+ }
482
+ });
483
+ }
484
+ supersedeVisibleAnswerCandidate() {
485
+ const itemId = this.visibleAnswerCandidateItemId;
486
+ if (!itemId) return;
487
+ this.emitAnswerCandidate(itemId, "superseded");
488
+ this.visibleAnswerCandidateItemId = void 0;
489
+ }
490
+ markLatestTerminalAssistantCandidate(itemId, activeItemIds, options) {
491
+ this.latestTerminalAssistantCandidateItemId = itemId;
492
+ this.latestTerminalAssistantCandidateSuperseded = false;
493
+ this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = options?.canReleaseAfterToolHandoff === true;
494
+ this.terminalAssistantCandidateEarlierActiveItemIds = new Set(activeItemIds);
495
+ }
496
+ markTerminalAssistantCandidateSupersededBy(itemId, options) {
497
+ if (!this.latestTerminalAssistantCandidateItemId) return;
498
+ if (itemId && this.terminalAssistantCandidateEarlierActiveItemIds.has(itemId)) {
499
+ if (!options?.preserveEarlierActiveItem) this.terminalAssistantCandidateEarlierActiveItemIds.delete(itemId);
500
+ return;
501
+ }
502
+ this.latestTerminalAssistantCandidateSuperseded = true;
503
+ this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
504
+ this.terminalAssistantCandidateEarlierActiveItemIds.clear();
505
+ this.supersedeVisibleAnswerCandidate();
506
+ }
507
+ resolveFinalAssistantTextItem() {
508
+ for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
509
+ const itemId = this.assistantItemOrder[i];
510
+ if (!itemId) continue;
511
+ const text = this.assistantTextByItem.get(itemId)?.trim();
512
+ if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
513
+ if (text && !this.isToolProgressEchoText(itemId, text)) return {
514
+ itemId,
515
+ text
516
+ };
517
+ }
518
+ }
519
+ rememberAssistantItem(itemId) {
520
+ if (!itemId || this.assistantItemOrder.includes(itemId)) return;
521
+ this.assistantItemOrder.push(itemId);
522
+ this.assistantTimestampByItem.set(itemId, this.nextTranscriptTimestamp());
523
+ }
524
+ isToolProgressEchoText(itemId, text) {
525
+ return this.rawPromotedAssistantItemIds.has(itemId) && this.matchesToolProgressEcho(text);
526
+ }
527
+ };
528
+ //#endregion
529
+ //#region extensions/codex/src/app-server/event-projector-diagnostics.ts
530
+ function redactCodexEventKind(method) {
531
+ return redactSensitiveText(sanitizeTerminalText(method));
532
+ }
533
+ var CodexProjectionDiagnostics = class {
534
+ constructor(threadId, turnId) {
535
+ this.threadId = threadId;
536
+ this.turnId = turnId;
537
+ this.warningKeys = /* @__PURE__ */ new Set();
538
+ }
539
+ warnUnknownItemStatus(item) {
540
+ if (!item) return;
541
+ const status = unknownItemStatus(item);
542
+ if (!status) return;
543
+ const safeStatus = redactCodexEventKind(status);
544
+ const safeItemType = redactCodexEventKind(item.type);
545
+ this.warnOnce(JSON.stringify([
546
+ "status",
547
+ item.type,
548
+ status
549
+ ]), "codex app-server item reported unknown status; continuing projection", {
550
+ itemId: item.id,
551
+ itemType: safeItemType,
552
+ status: safeStatus
553
+ });
554
+ }
555
+ warnUnknownEvent(notification, params) {
556
+ const notificationThreadId = readCodexNotificationThreadId(params);
557
+ const notificationTurnId = readCodexNotificationTurnId(params);
558
+ const eventKind = redactCodexEventKind(notification.method);
559
+ this.warnOnce(JSON.stringify(["method", notification.method]), `codex app-server projector received unknown event kind; continuing: ${eventKind}`, {
560
+ eventKind,
561
+ activeThreadId: this.threadId,
562
+ activeTurnId: this.turnId,
563
+ threadId: notificationThreadId,
564
+ turnId: notificationTurnId,
565
+ matchesActiveThread: notificationThreadId === this.threadId,
566
+ matchesActiveTurn: notificationTurnId === this.turnId
567
+ });
568
+ }
569
+ warnOnce(key, message, context) {
570
+ if (this.warningKeys.has(key)) return;
571
+ this.warningKeys.add(key);
572
+ embeddedAgentLog.warn(message, context);
573
+ }
574
+ };
575
+ //#endregion
576
+ //#region extensions/codex/src/app-server/event-projector-tool-output.ts
577
+ const TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS = 1e4;
578
+ const TOOL_OUTPUT_TRUNCATION_NOTICE_PREFIX = "...(OpenClaw truncated Codex native tool output";
579
+ var ToolOutputAccumulator = class {
580
+ constructor() {
581
+ this.prefixByItem = /* @__PURE__ */ new Map();
582
+ this.originalLengthByItem = /* @__PURE__ */ new Map();
583
+ this.normalizedLengthByItem = /* @__PURE__ */ new Map();
584
+ this.trimStateByItem = /* @__PURE__ */ new Map();
585
+ this.truncatedItemIds = /* @__PURE__ */ new Set();
586
+ this.textByItem = /* @__PURE__ */ new Map();
587
+ }
588
+ append(itemId, delta) {
589
+ const originalLength = (this.originalLengthByItem.get(itemId) ?? this.textByItem.get(itemId)?.length ?? 0) + delta.length;
590
+ this.originalLengthByItem.set(itemId, originalLength);
591
+ const normalizedLength = updateToolOutputTrimState(this.trimStateByItem, itemId, delta);
592
+ this.normalizedLengthByItem.set(itemId, normalizedLength);
593
+ if (this.truncatedItemIds.has(itemId)) {
594
+ const next = appendBoundedToolTranscriptText(this.prefixByItem.get(itemId) ?? this.textByItem.get(itemId) ?? "", "", originalLength);
595
+ this.prefixByItem.set(itemId, next.rawPrefix);
596
+ this.textByItem.set(itemId, next.text);
597
+ return {
598
+ text: next.text,
599
+ originalLength,
600
+ normalizedLength,
601
+ rawPrefix: next.rawPrefix
602
+ };
603
+ }
604
+ const next = appendBoundedToolTranscriptText(this.prefixByItem.get(itemId) ?? this.textByItem.get(itemId) ?? "", delta, originalLength);
605
+ this.prefixByItem.set(itemId, next.rawPrefix);
606
+ this.textByItem.set(itemId, next.text);
607
+ if (originalLength > 1e4) this.truncatedItemIds.add(itemId);
608
+ return {
609
+ text: next.text,
610
+ originalLength,
611
+ normalizedLength,
612
+ rawPrefix: next.rawPrefix
613
+ };
614
+ }
615
+ };
616
+ function updateToolOutputTrimState(trimStateByItem, itemId, delta) {
617
+ const state = trimStateByItem.get(itemId) ?? {
618
+ totalLength: 0,
619
+ leadingWhitespaceLength: 0,
620
+ trailingWhitespaceLength: 0,
621
+ sawNonWhitespace: false
622
+ };
623
+ state.totalLength += delta.length;
624
+ const firstNonWhitespace = delta.search(/\S/u);
625
+ if (firstNonWhitespace === -1) {
626
+ if (!state.sawNonWhitespace) state.leadingWhitespaceLength += delta.length;
627
+ state.trailingWhitespaceLength += delta.length;
628
+ trimStateByItem.set(itemId, state);
629
+ return state.sawNonWhitespace ? state.totalLength - state.leadingWhitespaceLength - state.trailingWhitespaceLength : 0;
630
+ }
631
+ if (!state.sawNonWhitespace) {
632
+ state.leadingWhitespaceLength += firstNonWhitespace;
633
+ state.sawNonWhitespace = true;
634
+ }
635
+ state.trailingWhitespaceLength = delta.match(/\s*$/u)?.[0].length ?? 0;
636
+ trimStateByItem.set(itemId, state);
637
+ return state.totalLength - state.leadingWhitespaceLength - state.trailingWhitespaceLength;
638
+ }
639
+ function toolOutputRawEchoSignature(text) {
640
+ const trimmed = text.trim();
641
+ if (!trimmed) return;
642
+ return {
643
+ rawLength: trimmed.length,
644
+ rawPrefix: trimmed.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)
645
+ };
646
+ }
647
+ function normalizeToolTranscriptArguments(value) {
648
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
649
+ return value;
650
+ }
651
+ function collectDynamicToolContentText(contentItems) {
652
+ if (!Array.isArray(contentItems)) return "";
653
+ return contentItems.flatMap((entry) => {
654
+ if (!isJsonObject(entry)) return [];
655
+ const text = readString$5(entry, "text");
656
+ return text ? [text] : [];
657
+ }).join("\n");
658
+ }
659
+ function appendBoundedToolTranscriptText(currentPrefix, delta, originalLength) {
660
+ if (originalLength <= 1e4) {
661
+ const rawPrefix = currentPrefix + delta;
662
+ return {
663
+ text: rawPrefix,
664
+ rawPrefix
665
+ };
666
+ }
667
+ const notice = toolTranscriptTruncationNotice(originalLength);
668
+ if (notice.length >= 1e4) return {
669
+ text: notice.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS),
670
+ rawPrefix: ""
671
+ };
672
+ const textBudget = TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS - notice.length;
673
+ const remaining = Math.max(0, textBudget - currentPrefix.length);
674
+ const rawPrefix = truncateUtf16Safe(remaining > 0 ? `${currentPrefix}${truncateUtf16Safe(delta, remaining)}` : currentPrefix, textBudget);
675
+ return {
676
+ text: `${rawPrefix}${notice}`,
677
+ rawPrefix
678
+ };
679
+ }
680
+ function toolTranscriptTruncationNotice(originalLength) {
681
+ return `\n${`${TOOL_OUTPUT_TRUNCATION_NOTICE_PREFIX}: original ${originalLength} chars, showing ${TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS}; rerun with narrower args.)`}`;
682
+ }
683
+ function truncateToolTranscriptText(text, originalLength = text.length) {
684
+ if (originalLength <= 1e4 && text.length <= 1e4) return text;
685
+ const notice = toolTranscriptTruncationNotice(originalLength);
686
+ if (notice.length >= 1e4) return notice.slice(1, 10001);
687
+ return `${truncateUtf16Safe(text, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS - notice.length)}${notice}`;
688
+ }
689
+ function formatToolSummary(toolName, meta) {
690
+ const trimmedMeta = meta?.trim();
691
+ return formatToolAggregate(toolName, trimmedMeta ? [trimmedMeta] : void 0, { markdown: true });
692
+ }
693
+ function formatToolOutput(toolName, meta, output) {
694
+ const formattedOutput = formatToolProgressOutput(output);
695
+ if (!formattedOutput) return formatToolSummary(toolName, meta);
696
+ const fence = markdownFenceForText(formattedOutput);
697
+ return `${formatToolSummary(toolName, meta)}\n${fence}txt\n${formattedOutput}\n${fence}`;
698
+ }
699
+ function markdownFenceForText(text) {
700
+ return "`".repeat(Math.max(3, longestBacktickRun(text) + 1));
701
+ }
702
+ function longestBacktickRun(value) {
703
+ let longest = 0;
704
+ let current = 0;
705
+ for (const char of value) {
706
+ if (char === "`") {
707
+ current += 1;
708
+ longest = Math.max(longest, current);
709
+ continue;
710
+ }
711
+ current = 0;
712
+ }
713
+ return longest;
714
+ }
715
+ //#endregion
716
+ //#region extensions/codex/src/app-server/tool-progress-normalization.ts
717
+ /**
718
+ * Normalizes and sanitizes Codex dynamic-tool progress payloads before they are
719
+ * emitted into OpenClaw events or logs.
720
+ */
721
+ /** Maps OpenClaw tool-progress config to the mode used by Codex progress metadata. */
722
+ function resolveCodexToolProgressDetailMode(value) {
723
+ return value === "raw" ? "raw" : "explain";
724
+ }
725
+ /** Recursively redacts sensitive strings and handles circular values in event payloads. */
726
+ function sanitizeCodexAgentEventValue(value, seen = /* @__PURE__ */ new WeakSet()) {
727
+ if (typeof value === "string") return redactToolPayloadText(value);
728
+ if (Array.isArray(value)) {
729
+ if (seen.has(value)) return "[Circular]";
730
+ seen.add(value);
731
+ return value.map((entry) => sanitizeCodexAgentEventValue(entry, seen));
732
+ }
733
+ if (value && typeof value === "object") {
734
+ if (seen.has(value)) return "[Circular]";
735
+ seen.add(value);
736
+ const out = {};
737
+ for (const [key, child] of Object.entries(value)) out[key] = typeof child === "string" ? redactSensitiveFieldValue(key, child) : sanitizeCodexAgentEventValue(child, seen);
738
+ return out;
739
+ }
740
+ return value;
741
+ }
742
+ /** Sanitizes a record-shaped Codex agent event payload. */
743
+ function sanitizeCodexAgentEventRecord(value) {
744
+ return sanitizeCodexAgentEventValue(value);
745
+ }
746
+ /** Sanitizes dynamic-tool arguments before diagnostic/event emission. */
747
+ function sanitizeCodexToolArguments(value) {
748
+ if (!isJsonObject(value)) return;
749
+ return sanitizeCodexAgentEventRecord(value);
750
+ }
751
+ /** Sanitizes a Codex dynamic-tool response before diagnostic/event emission. */
752
+ function sanitizeCodexToolResponse(response) {
753
+ return sanitizeCodexAgentEventRecord(response);
754
+ }
755
+ /** Infers compact human-readable tool metadata from Codex dynamic-tool arguments. */
756
+ function inferCodexDynamicToolMeta(call, detailMode) {
757
+ return inferToolMetaFromArgs(call.tool, call.arguments, { detailMode });
758
+ }
759
+ //#endregion
760
+ //#region extensions/codex/src/app-server/event-projector-tool-items.ts
761
+ function nativeToolActionFingerprint(item) {
762
+ if (item.type === "commandExecution" && typeof item.command === "string") return JSON.stringify({
763
+ type: item.type,
764
+ command: item.command,
765
+ cwd: typeof item.cwd === "string" ? item.cwd : ""
766
+ });
767
+ if (item.type === "fileChange") return JSON.stringify({
768
+ type: item.type,
769
+ changes: itemFileChanges(item)
770
+ });
771
+ }
772
+ function isNativePostToolUseRelayItem(item) {
773
+ switch (item.type) {
774
+ case "commandExecution":
775
+ case "fileChange":
776
+ case "mcpToolCall": return true;
777
+ default: return false;
778
+ }
779
+ }
780
+ function shouldSuppressChannelProgressForItem(item) {
781
+ if (shouldSynthesizeToolProgressForItem(item)) return true;
782
+ return item.type === "dynamicToolCall";
783
+ }
784
+ function itemToolArgs(item) {
785
+ if (item.type === "commandExecution") return sanitizeCodexAgentEventRecord({
786
+ command: item.command,
787
+ ...typeof item.cwd === "string" ? { cwd: item.cwd } : {}
788
+ });
789
+ if (item.type === "fileChange") return sanitizeCodexAgentEventRecord({ changes: itemFileChangesForTranscript(item) });
790
+ if (item.type === "webSearch") return webSearchToolArgs(item);
791
+ if (item.type === "dynamicToolCall" || item.type === "mcpToolCall") return sanitizeCodexToolArguments(item.arguments);
792
+ }
793
+ function webSearchToolArgs(item) {
794
+ const action = isJsonObject(item.action) ? item.action : void 0;
795
+ const actionType = action ? readNonEmptyString$1(action, "type") : void 0;
796
+ const queries = action && actionType === "search" ? readNonEmptyStringArray(action, "queries") : [];
797
+ const query = normalizeNonEmptyString(item.query) ?? (action && actionType === "search" ? readNonEmptyString$1(action, "query") : void 0) ?? queries[0];
798
+ const url = action ? readNonEmptyString$1(action, "url") : void 0;
799
+ const pattern = action ? readNonEmptyString$1(action, "pattern") : void 0;
800
+ const args = {};
801
+ if (query) args.query = query;
802
+ if (queries.length > 0) args.queries = queries;
803
+ if (actionType && actionType !== "search") args.action = actionType;
804
+ if (url) args.url = url;
805
+ if (pattern) args.pattern = pattern;
806
+ if (!query && !url && !pattern) args.queryUnavailable = true;
807
+ return sanitizeCodexAgentEventRecord(args);
808
+ }
809
+ function itemToolResult(item) {
810
+ if (item.type === "commandExecution") return { result: sanitizeCodexAgentEventRecord({
811
+ status: item.status,
812
+ exitCode: item.exitCode,
813
+ durationMs: item.durationMs
814
+ }) };
815
+ if (item.type === "fileChange") return { result: sanitizeCodexAgentEventRecord({
816
+ status: item.status,
817
+ changes: itemFileChanges(item)
818
+ }) };
819
+ if (item.type === "mcpToolCall") return { result: sanitizeCodexAgentEventRecord({
820
+ status: item.status,
821
+ durationMs: item.durationMs,
822
+ ...item.error ? { error: item.error } : {},
823
+ ...item.result ? { result: item.result } : {}
824
+ }) };
825
+ if (item.type === "webSearch") return { result: webSearchToolResult(item) };
826
+ return {};
827
+ }
828
+ function webSearchToolResult(item) {
829
+ return sanitizeCodexAgentEventRecord({
830
+ status: itemStatus(item),
831
+ ...typeof item.durationMs === "number" ? { durationMs: item.durationMs } : {},
832
+ ...webSearchToolArgs(item)
833
+ });
834
+ }
835
+ function itemFileChangeRecords(item) {
836
+ const changes = item.changes;
837
+ return Array.isArray(changes) ? changes.filter(isJsonObject) : [];
838
+ }
839
+ function itemFileChanges(item) {
840
+ return itemFileChangeRecords(item).flatMap((change) => {
841
+ const path = normalizeNonEmptyString(change.path);
842
+ if (!path || change.kind === void 0) return [];
843
+ return [{
844
+ path,
845
+ kind: change.kind
846
+ }];
847
+ });
848
+ }
849
+ function fileChangeKindType(kind) {
850
+ if (typeof kind === "string") return kind;
851
+ return isJsonObject(kind) ? normalizeNonEmptyString(kind.type) : void 0;
852
+ }
853
+ function countFileContentLines(content) {
854
+ if (!content) return 0;
855
+ const lines = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
856
+ if (lines.length > 1 && lines.at(-1) === "") lines.pop();
857
+ return lines.length;
858
+ }
859
+ function fileChangeDiffStat(diff, kind) {
860
+ const kindType = fileChangeKindType(kind);
861
+ if (kindType === "add") return {
862
+ added: countFileContentLines(diff),
863
+ removed: 0
864
+ };
865
+ if (kindType === "delete") return {
866
+ added: 0,
867
+ removed: countFileContentLines(diff)
868
+ };
869
+ let added = 0;
870
+ let removed = 0;
871
+ let inHunk = false;
872
+ for (const line of diff.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n")) {
873
+ if (line.startsWith("@@")) {
874
+ inHunk = true;
875
+ continue;
876
+ }
877
+ if (!inHunk) continue;
878
+ if (line.startsWith("+")) added += 1;
879
+ else if (line.startsWith("-")) removed += 1;
880
+ }
881
+ return {
882
+ added,
883
+ removed
884
+ };
885
+ }
886
+ function truncateFileChangeDiffAtLineBoundary(diff, maxChars) {
887
+ if (diff.length <= maxChars) return { diff };
888
+ if (maxChars <= 0) return { diffTruncated: true };
889
+ const boundary = diff.lastIndexOf("\n", maxChars - 1);
890
+ return boundary >= 0 ? {
891
+ diff: diff.slice(0, boundary + 1),
892
+ diffTruncated: true
893
+ } : { diffTruncated: true };
894
+ }
895
+ function itemFileChangesForTranscript(item) {
896
+ let remainingDiffChars = 1e4;
897
+ return itemFileChangeRecords(item).flatMap((change) => {
898
+ const path = normalizeNonEmptyString(change.path);
899
+ if (!path || change.kind === void 0) return [];
900
+ const result = {
901
+ path,
902
+ kind: change.kind
903
+ };
904
+ if (typeof change.diff !== "string") return [result];
905
+ result.stat = fileChangeDiffStat(change.diff, change.kind);
906
+ const bounded = truncateFileChangeDiffAtLineBoundary(change.diff, remainingDiffChars);
907
+ if (bounded.diff !== void 0) {
908
+ result.diff = bounded.diff;
909
+ remainingDiffChars -= bounded.diff.length;
910
+ }
911
+ if (bounded.diffTruncated) result.diffTruncated = true;
912
+ return [result];
913
+ });
914
+ }
915
+ function itemToolError(item, status, outputTextByItem) {
916
+ if (status === "blocked") return "codex native tool blocked";
917
+ if (status !== "failed") return;
918
+ return itemOutputText(item, outputTextByItem) ?? "codex native tool failed";
919
+ }
920
+ function itemMeta(item, detailMode = "explain") {
921
+ if (item.type === "commandExecution" && typeof item.command === "string") return inferToolMetaFromArgs("exec", {
922
+ command: item.command,
923
+ cwd: typeof item.cwd === "string" ? item.cwd : void 0
924
+ }, { detailMode });
925
+ if (item.type === "webSearch") return inferToolMetaFromArgs("web_search", webSearchToolArgs(item), { detailMode });
926
+ const toolName = itemName(item);
927
+ if ((item.type === "dynamicToolCall" || item.type === "mcpToolCall") && toolName) return inferToolMetaFromArgs(toolName, item.arguments, { detailMode });
928
+ }
929
+ function itemOutputText(item, outputTextByItem) {
930
+ if (item.type === "commandExecution") {
931
+ const output = item.aggregatedOutput?.trim() || outputTextByItem?.get(item.id)?.trim();
932
+ return output ? truncateToolTranscriptText(output) : void 0;
933
+ }
934
+ if (item.type === "dynamicToolCall") {
935
+ const output = collectDynamicToolContentText(item.contentItems).trim();
936
+ return output ? truncateToolTranscriptText(output) : void 0;
937
+ }
938
+ if (item.type === "mcpToolCall") {
939
+ const output = item.error ? stringifyJsonValue(item.error) : item.result ? stringifyJsonValue(item.result) : void 0;
940
+ return output ? truncateToolTranscriptText(output) : void 0;
941
+ }
942
+ }
943
+ function itemTranscriptResultText(item, outputTextByItem) {
944
+ const output = itemOutputText(item, outputTextByItem);
945
+ if (output) return output;
946
+ const result = itemToolResult(item).result;
947
+ const resultText = result ? stringifyJsonValue(result) : void 0;
948
+ return resultText ? truncateToolTranscriptText(resultText) : itemStatus(item);
949
+ }
950
+ function stringifyJsonValue(value) {
951
+ try {
952
+ return JSON.stringify(value, null, 2);
953
+ } catch {
954
+ return;
955
+ }
956
+ }
957
+ //#endregion
958
+ //#region extensions/codex/src/app-server/event-projector-tool-progress.ts
959
+ const TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES = /* @__PURE__ */ new Set([
960
+ "message",
961
+ "messages",
962
+ "reply",
963
+ "send",
964
+ "reaction",
965
+ "react",
966
+ "typing"
967
+ ]);
968
+ function shouldEmitTranscriptToolProgress(toolName, _args) {
969
+ const normalized = typeof toolName === "string" ? toolName.trim().toLowerCase() : "";
970
+ return Boolean(normalized && !TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES.has(normalized));
971
+ }
972
+ var CodexToolProgressProjection = class {
973
+ constructor(params) {
974
+ this.params = params;
975
+ this.echoesByItem = /* @__PURE__ */ new Map();
976
+ this.resultSummaryItemIds = /* @__PURE__ */ new Set();
977
+ this.resultOutputItemIds = /* @__PURE__ */ new Set();
978
+ this.resultOutputStreamedItemIds = /* @__PURE__ */ new Set();
979
+ this.transcriptProgressSuppressedIds = /* @__PURE__ */ new Set();
980
+ this.transcriptArgumentsById = /* @__PURE__ */ new Map();
981
+ this.resultOutputDeltaState = /* @__PURE__ */ new Map();
982
+ this.output = new ToolOutputAccumulator();
983
+ this.metas = /* @__PURE__ */ new Map();
984
+ this.sideEffectingNativeIds = /* @__PURE__ */ new Set();
985
+ this.sideEffectingDynamicIds = /* @__PURE__ */ new Set();
986
+ this.transcriptProgressCallIds = /* @__PURE__ */ new Set();
987
+ }
988
+ get outputTextByItem() {
989
+ return this.output.textByItem;
990
+ }
991
+ get toolMetas() {
992
+ return [...this.metas.values()];
993
+ }
994
+ getToolMeta(itemId) {
995
+ return this.metas.get(itemId);
996
+ }
997
+ get lastToolError() {
998
+ return this.lastNativeToolError;
999
+ }
1000
+ get hasPotentialSideEffects() {
1001
+ return this.sideEffectingNativeIds.size > 0 || this.sideEffectingDynamicIds.size > 0;
1002
+ }
1003
+ setLastToolError(error) {
1004
+ if (!error) {
1005
+ this.lastNativeToolError = void 0;
1006
+ return;
1007
+ }
1008
+ const terminalResolution = this.params.observeToolTerminal?.({
1009
+ toolName: error.toolName,
1010
+ ...error.meta ? { meta: error.meta } : {},
1011
+ outcome: "failure",
1012
+ failure: {
1013
+ ...error.errorCode ? { errorCode: error.errorCode } : {},
1014
+ ...error.error ? { error: error.error } : {},
1015
+ ...error.validationErrorSummary ? { validationErrorSummary: error.validationErrorSummary } : {},
1016
+ ...error.timedOut ? { timedOut: true } : {},
1017
+ ...error.middlewareError ? { middlewareError: true } : {}
1018
+ },
1019
+ nativeMutation: {
1020
+ mutatingAction: error.mutatingAction === true,
1021
+ replaySafe: error.mutatingAction !== true,
1022
+ ...error.actionFingerprint ? { actionFingerprint: error.actionFingerprint } : {},
1023
+ ...error.fileTarget ? { fileTarget: error.fileTarget } : {}
1024
+ }
1025
+ });
1026
+ this.lastNativeToolError = terminalResolution?.lastToolError ?? (this.lastNativeToolError?.mutatingAction && error.mutatingAction !== true ? this.lastNativeToolError : error);
1027
+ }
1028
+ recordDynamicToolResult(params) {
1029
+ const resultText = collectDynamicToolContentText(params.contentItems);
1030
+ const existing = this.metas.get(params.callId);
1031
+ this.metas.set(params.callId, {
1032
+ toolName: existing?.toolName ?? params.tool,
1033
+ ...existing?.meta ? { meta: existing.meta } : {},
1034
+ ...params.asyncStarted === true ? { asyncStarted: true } : {},
1035
+ ...!params.success ? { isError: true } : {}
1036
+ });
1037
+ if (params.terminalResolution) this.lastNativeToolError = params.terminalResolution.lastToolError;
1038
+ else if (!params.success) this.lastNativeToolError = {
1039
+ toolName: params.tool,
1040
+ error: resultText || (params.terminalType === "blocked" ? "codex dynamic tool blocked" : "codex dynamic tool failed")
1041
+ };
1042
+ else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
1043
+ if (params.sideEffectEvidence === true) this.sideEffectingDynamicIds.add(params.callId);
1044
+ }
1045
+ handleOutputDelta(params, toolName) {
1046
+ const itemId = readString$5(params, "itemId");
1047
+ const delta = readString$5(params, "delta");
1048
+ if (!itemId || !delta) return;
1049
+ const storedOutput = this.output.append(itemId, delta);
1050
+ this.rememberEcho(itemId, {
1051
+ displayText: storedOutput.text,
1052
+ rawLength: storedOutput.normalizedLength,
1053
+ rawPrefix: storedOutput.rawPrefix,
1054
+ streamedDisplay: true
1055
+ });
1056
+ if (!this.shouldEmitToolOutput()) return;
1057
+ if (this.transcriptProgressSuppressedIds.has(itemId) || !shouldEmitTranscriptToolProgress(toolName, this.transcriptArgumentsById.get(itemId))) return;
1058
+ const state = this.resultOutputDeltaState.get(itemId) ?? {
1059
+ chars: 0,
1060
+ messages: 0,
1061
+ truncated: false
1062
+ };
1063
+ if (state.truncated) return;
1064
+ const remainingChars = Math.max(0, TOOL_PROGRESS_OUTPUT_MAX_CHARS - state.chars);
1065
+ const remainingMessages = Math.max(0, 20 - state.messages);
1066
+ if (remainingChars === 0 || remainingMessages === 0) {
1067
+ state.truncated = true;
1068
+ this.resultOutputDeltaState.set(itemId, state);
1069
+ this.emitToolResultMessage({
1070
+ itemId,
1071
+ text: formatToolOutput(toolName, void 0, "(output truncated)")
1072
+ });
1073
+ return;
1074
+ }
1075
+ const chunk = delta.length > remainingChars ? truncateUtf16Safe(delta, remainingChars) : delta;
1076
+ state.chars += chunk.length;
1077
+ state.messages += 1;
1078
+ const reachedLimit = delta.length > remainingChars || state.chars >= TOOL_PROGRESS_OUTPUT_MAX_CHARS || state.messages >= 20;
1079
+ if (reachedLimit) state.truncated = true;
1080
+ this.resultOutputDeltaState.set(itemId, state);
1081
+ this.resultOutputStreamedItemIds.add(itemId);
1082
+ this.emitToolResultMessage({
1083
+ itemId,
1084
+ text: formatToolOutput(toolName, void 0, reachedLimit ? `${chunk}\n...(truncated)...` : chunk)
1085
+ });
1086
+ }
1087
+ recordNativeToolError(params) {
1088
+ const executionStarted = params.status !== "blocked";
1089
+ const mutatingAction = executionStarted && isMutatingNativeToolItem(params.item);
1090
+ const actionFingerprint = mutatingAction ? nativeToolActionFingerprint(params.item) : void 0;
1091
+ const isFailure = isNonSuccessItemStatus(params.status);
1092
+ const error = isFailure ? itemToolError(params.item, params.status, this.output.textByItem) : void 0;
1093
+ const terminalResolution = this.params.observeToolTerminal?.({
1094
+ toolCallId: params.item.id,
1095
+ toolName: params.name,
1096
+ arguments: itemToolArgs(params.item),
1097
+ ...params.meta ? { meta: params.meta } : {},
1098
+ executionStarted,
1099
+ outcome: isFailure ? "failure" : "success",
1100
+ ...isFailure ? { failure: error ? { error } : {} } : {},
1101
+ nativeMutation: {
1102
+ mutatingAction,
1103
+ replaySafe: !mutatingAction,
1104
+ ...actionFingerprint ? { actionFingerprint } : {}
1105
+ }
1106
+ });
1107
+ if (terminalResolution) {
1108
+ this.lastNativeToolError = terminalResolution.lastToolError;
1109
+ return;
1110
+ }
1111
+ if (isFailure) this.lastNativeToolError = {
1112
+ toolName: params.name,
1113
+ ...params.meta ? { meta: params.meta } : {},
1114
+ ...error ? { error } : {},
1115
+ ...mutatingAction ? { mutatingAction: true } : {},
1116
+ ...actionFingerprint ? { actionFingerprint } : {}
1117
+ };
1118
+ else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
1119
+ }
1120
+ emitToolResultSummary(item) {
1121
+ if (!item || !this.params.onToolResult || !this.shouldEmitToolResult()) return;
1122
+ if (this.resultSummaryItemIds.has(item.id)) return;
1123
+ const toolName = itemName(item);
1124
+ if (!toolName || !shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
1125
+ this.resultSummaryItemIds.add(item.id);
1126
+ this.emitToolResultMessage({
1127
+ itemId: item.id,
1128
+ text: formatToolSummary(toolName, itemMeta(item, this.toolProgressDetailMode()))
1129
+ });
1130
+ }
1131
+ emitToolResultOutput(item) {
1132
+ if (!item || !this.params.onToolResult || !this.shouldEmitToolOutput()) return;
1133
+ if (this.resultOutputItemIds.has(item.id) || this.resultOutputStreamedItemIds.has(item.id)) return;
1134
+ const toolName = itemName(item);
1135
+ const output = itemOutputText(item, this.output.textByItem);
1136
+ if (!toolName || !output || !shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
1137
+ this.emitToolResultMessage({
1138
+ itemId: item.id,
1139
+ text: formatToolOutput(toolName, itemMeta(item, this.toolProgressDetailMode()), output),
1140
+ finalOutput: true,
1141
+ isError: isNonSuccessItemStatus(itemStatus(item))
1142
+ });
1143
+ }
1144
+ recordToolMeta(item) {
1145
+ if (!item) return;
1146
+ if (isSideEffectingNativeToolItem(item)) this.sideEffectingNativeIds.add(item.id);
1147
+ else this.sideEffectingNativeIds.delete(item.id);
1148
+ const toolName = itemName(item);
1149
+ if (!toolName) return;
1150
+ const meta = itemMeta(item, this.toolProgressDetailMode());
1151
+ const status = itemStatus(item);
1152
+ const existing = this.metas.get(item.id);
1153
+ this.metas.set(item.id, {
1154
+ toolName,
1155
+ ...meta ? { meta } : {},
1156
+ ...existing?.asyncStarted ? { asyncStarted: true } : {},
1157
+ ...status !== "running" && isNonSuccessItemStatus(status) ? { isError: true } : {}
1158
+ });
1159
+ }
1160
+ recordTranscriptCall(params) {
1161
+ this.transcriptArgumentsById.set(params.id, params.arguments);
1162
+ if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) this.transcriptProgressSuppressedIds.add(params.id);
1163
+ else this.transcriptProgressSuppressedIds.delete(params.id);
1164
+ this.emitTranscriptToolCallProgress(params);
1165
+ }
1166
+ recordTranscriptResult(params) {
1167
+ this.emitTranscriptToolResultProgress(params);
1168
+ }
1169
+ matchesEcho(text) {
1170
+ for (const state of this.echoesByItem.values()) {
1171
+ if (state.streamedDisplayText === text || state.displayTexts.includes(text)) return true;
1172
+ if (state.streamedRawSignature && text.length === state.streamedRawSignature.length && text.startsWith(state.streamedRawSignature.prefix)) return true;
1173
+ for (const signature of state.rawSignatures) if (text.length === signature.length && text.startsWith(signature.prefix)) return true;
1174
+ }
1175
+ return false;
1176
+ }
1177
+ rememberCommandAggregateOutputEcho(item) {
1178
+ if (item?.type !== "commandExecution" || typeof item.aggregatedOutput !== "string") return;
1179
+ const signature = toolOutputRawEchoSignature(item.aggregatedOutput);
1180
+ if (signature) this.rememberEcho(item.id, signature);
1181
+ }
1182
+ toolProgressDetailMode() {
1183
+ return resolveCodexToolProgressDetailMode(this.params.toolProgressDetail);
1184
+ }
1185
+ emitToolResultMessage(params) {
1186
+ const rawText = params.text.trim();
1187
+ const text = truncateToolTranscriptText(rawText);
1188
+ if (!text) return;
1189
+ this.rememberEcho(params.itemId, {
1190
+ displayText: text,
1191
+ rawText
1192
+ });
1193
+ if (params.finalOutput) this.resultOutputItemIds.add(params.itemId);
1194
+ try {
1195
+ Promise.resolve(this.params.onToolResult?.({
1196
+ text,
1197
+ ...params.isError === true ? { isError: true } : {}
1198
+ })).catch(() => {});
1199
+ } catch {}
1200
+ }
1201
+ shouldEmitToolResult() {
1202
+ return typeof this.params.shouldEmitToolResult === "function" ? this.params.shouldEmitToolResult() : this.params.verboseLevel === "on" || this.params.verboseLevel === "full";
1203
+ }
1204
+ shouldEmitToolOutput() {
1205
+ return typeof this.params.shouldEmitToolOutput === "function" ? this.params.shouldEmitToolOutput() : this.params.verboseLevel === "full";
1206
+ }
1207
+ emitTranscriptToolCallProgress(params) {
1208
+ if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) return;
1209
+ this.transcriptProgressCallIds.add(params.id);
1210
+ const args = normalizeToolTranscriptArguments(params.arguments);
1211
+ const meta = inferToolMetaFromArgs(params.name, args, { detailMode: this.toolProgressDetailMode() });
1212
+ if (!this.params.onToolResult || !this.shouldEmitToolResult() || this.resultSummaryItemIds.has(params.id) || this.resultOutputStreamedItemIds.has(params.id)) return;
1213
+ this.resultSummaryItemIds.add(params.id);
1214
+ this.emitToolResultMessage({
1215
+ itemId: params.id,
1216
+ text: formatToolSummary(params.name, meta)
1217
+ });
1218
+ }
1219
+ emitTranscriptToolResultProgress(params) {
1220
+ if (this.transcriptProgressSuppressedIds.has(params.id) || !shouldEmitTranscriptToolProgress(params.name, this.transcriptArgumentsById.get(params.id))) return;
1221
+ if (!this.transcriptProgressCallIds.has(params.id)) this.emitTranscriptToolCallProgress({
1222
+ id: params.id,
1223
+ name: params.name,
1224
+ arguments: {}
1225
+ });
1226
+ if (!this.params.onToolResult || !this.shouldEmitToolOutput() || this.resultOutputItemIds.has(params.id) || this.resultOutputStreamedItemIds.has(params.id)) return;
1227
+ const text = params.text?.trim();
1228
+ if (text) this.emitToolResultMessage({
1229
+ itemId: params.id,
1230
+ text: formatToolOutput(params.name, void 0, text),
1231
+ finalOutput: true,
1232
+ isError: params.isError
1233
+ });
1234
+ }
1235
+ rememberEcho(itemId, signature) {
1236
+ if (!itemId) return;
1237
+ const existing = this.echoesByItem.get(itemId) ?? {
1238
+ displayTexts: [],
1239
+ rawSignatures: []
1240
+ };
1241
+ const displayText = signature.displayText?.trim();
1242
+ if (displayText) {
1243
+ if (signature.streamedDisplay) existing.streamedDisplayText = displayText;
1244
+ else if (!existing.displayTexts.includes(displayText)) {
1245
+ if (existing.displayTexts.length >= 24) existing.displayTexts.shift();
1246
+ existing.displayTexts.push(displayText);
1247
+ }
1248
+ }
1249
+ const rawText = signature.rawText?.trim();
1250
+ const rawLength = signature.rawLength ?? rawText?.length;
1251
+ const rawPrefix = signature.rawPrefix?.trim() ?? rawText;
1252
+ if (rawLength !== void 0 && rawPrefix && rawPrefix.length >= 1024) {
1253
+ const next = {
1254
+ length: rawLength,
1255
+ prefix: rawPrefix.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)
1256
+ };
1257
+ if (signature.streamedDisplay) existing.streamedRawSignature = next;
1258
+ else {
1259
+ const matchIndex = existing.rawSignatures.findIndex((entry) => entry.prefix === next.prefix);
1260
+ if (matchIndex >= 0) existing.rawSignatures[matchIndex] = next;
1261
+ else {
1262
+ if (existing.rawSignatures.length >= 24) existing.rawSignatures.shift();
1263
+ existing.rawSignatures.push(next);
1264
+ }
1265
+ }
1266
+ }
1267
+ this.echoesByItem.set(itemId, existing);
1268
+ }
1269
+ };
1270
+ //#endregion
1271
+ //#region extensions/codex/src/app-server/session-history.ts
1272
+ /**
1273
+ * Reads OpenClaw session history for Codex transcript mirroring and sanitizes
1274
+ * image payloads before replaying messages into the app-server projector.
1275
+ */
1276
+ function isMissingFileError(error) {
1277
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
1278
+ }
1279
+ /** Returns sanitized session-context messages for a Codex mirrored session file. */
1280
+ async function readCodexMirroredSessionHistoryMessages(target) {
1281
+ try {
1282
+ const entries = await readCodexMirroredSessionEntries(target);
1283
+ if (entries.length === 0) return [];
1284
+ const firstEntry = entries[0];
1285
+ if (firstEntry?.type !== "session") return [];
1286
+ if (typeof firstEntry.id !== "string") return;
1287
+ migrateSessionEntries(entries);
1288
+ return sanitizeCodexHistoryImagePayloads(buildSessionContext(entries.filter((entry) => {
1289
+ return entry !== null && typeof entry === "object" && !Array.isArray(entry) && entry.type !== "session";
1290
+ })).messages, "codex mirrored history");
1291
+ } catch (error) {
1292
+ if (isMissingFileError(error)) return [];
1293
+ return;
1294
+ }
1295
+ }
1296
+ async function readCodexMirroredSessionEntries(target) {
1297
+ const sqliteMarker = parseSqliteSessionFileMarker(target.sessionFile);
1298
+ if (sqliteMarker) {
1299
+ if (sqliteMarker.sessionId !== target.sessionId || target.agentId !== void 0 && sqliteMarker.agentId !== target.agentId) return [];
1300
+ const sessionKey = resolveSqliteMarkerSessionKey(target, sqliteMarker);
1301
+ if (!sessionKey) return [];
1302
+ return await readSessionTranscriptEvents({
1303
+ agentId: sqliteMarker.agentId,
1304
+ sessionId: sqliteMarker.sessionId,
1305
+ sessionKey,
1306
+ storePath: sqliteMarker.storePath
1307
+ });
1308
+ }
1309
+ return parseSessionEntries(await fs.readFile(target.sessionFile, "utf-8"));
1310
+ }
1311
+ function resolveSqliteMarkerSessionKey(target, marker) {
1312
+ const explicitSessionKey = target.sessionKey?.trim();
1313
+ if (explicitSessionKey) return explicitSessionKey;
1314
+ const entries = listSessionEntries({
1315
+ agentId: marker.agentId,
1316
+ readOnly: true,
1317
+ storePath: marker.storePath
1318
+ });
1319
+ return (entries.find(({ entry }) => {
1320
+ return entry.sessionId === marker.sessionId && entry.sessionFile === target.sessionFile;
1321
+ }) ?? entries.find(({ entry }) => {
1322
+ return entry.sessionId === marker.sessionId;
1323
+ }))?.sessionKey;
1324
+ }
1325
+ //#endregion
1326
+ //#region extensions/codex/src/app-server/event-projector-tool-transcript.ts
1327
+ const ZERO_USAGE = {
1328
+ input: 0,
1329
+ output: 0,
1330
+ cacheRead: 0,
1331
+ cacheWrite: 0,
1332
+ totalTokens: 0,
1333
+ cost: {
1334
+ input: 0,
1335
+ output: 0,
1336
+ cacheRead: 0,
1337
+ cacheWrite: 0,
1338
+ total: 0
1339
+ }
1340
+ };
1341
+ const MISSING_TOOL_RESULT_ERROR = "OpenClaw recorded a native Codex tool.call without a matching tool.result before the turn completed.";
1342
+ var CodexToolTranscriptProjection = class {
1343
+ constructor(params, threadId, turnId, progress, nextTranscriptTimestamp, options = {}) {
1344
+ this.params = params;
1345
+ this.threadId = threadId;
1346
+ this.turnId = turnId;
1347
+ this.progress = progress;
1348
+ this.nextTranscriptTimestamp = nextTranscriptTimestamp;
1349
+ this.options = options;
1350
+ this.messages = [];
1351
+ this.callIds = /* @__PURE__ */ new Set();
1352
+ this.resultIds = /* @__PURE__ */ new Set();
1353
+ this.namesById = /* @__PURE__ */ new Map();
1354
+ this.trajectoryCallIds = /* @__PURE__ */ new Set();
1355
+ this.trajectoryResultIds = /* @__PURE__ */ new Set();
1356
+ this.trajectoryNamesById = /* @__PURE__ */ new Map();
1357
+ this.trajectoryItemsById = /* @__PURE__ */ new Map();
1358
+ this.afterToolCallObservedItemIds = /* @__PURE__ */ new Set();
1359
+ }
1360
+ get transcriptMessages() {
1361
+ return this.messages;
1362
+ }
1363
+ recordDynamicToolCall(params) {
1364
+ this.recordToolCall({
1365
+ id: params.callId,
1366
+ name: params.tool,
1367
+ arguments: sanitizeCodexToolArguments(params.arguments)
1368
+ });
1369
+ }
1370
+ recordDynamicToolResult(params) {
1371
+ this.recordToolResult({
1372
+ id: params.callId,
1373
+ name: params.tool,
1374
+ text: collectDynamicToolContentText(params.contentItems),
1375
+ isError: !params.success
1376
+ });
1377
+ }
1378
+ recordNativeToolCall(item) {
1379
+ if (!item || !shouldRecordNativeToolTranscript(item)) return;
1380
+ const name = itemName(item);
1381
+ if (name) this.recordToolCall({
1382
+ id: item.id,
1383
+ name,
1384
+ arguments: itemToolArgs(item)
1385
+ });
1386
+ }
1387
+ recordNativeToolResult(item) {
1388
+ if (!item || !shouldRecordNativeToolTranscript(item)) return;
1389
+ const name = itemName(item);
1390
+ if (name) this.recordToolResult({
1391
+ id: item.id,
1392
+ name,
1393
+ text: itemTranscriptResultText(item, this.progress.outputTextByItem),
1394
+ isError: isNonSuccessItemStatus(itemStatus(item))
1395
+ });
1396
+ }
1397
+ recordTrajectoryEvent(params) {
1398
+ if (params.phase === "start") {
1399
+ this.trajectoryCallIds.add(params.item.id);
1400
+ this.trajectoryNamesById.set(params.item.id, params.name);
1401
+ this.trajectoryItemsById.set(params.item.id, params.item);
1402
+ this.options.trajectoryRecorder?.recordEvent("tool.call", {
1403
+ threadId: this.threadId,
1404
+ turnId: this.turnId,
1405
+ itemId: params.item.id,
1406
+ toolCallId: params.item.id,
1407
+ name: params.name,
1408
+ arguments: params.args
1409
+ });
1410
+ return;
1411
+ }
1412
+ this.trajectoryResultIds.add(params.item.id);
1413
+ const toolResult = itemToolResult(params.item).result;
1414
+ const output = itemOutputText(params.item, this.progress.outputTextByItem);
1415
+ this.options.trajectoryRecorder?.recordEvent("tool.result", {
1416
+ threadId: this.threadId,
1417
+ turnId: this.turnId,
1418
+ itemId: params.item.id,
1419
+ toolCallId: params.item.id,
1420
+ name: params.name,
1421
+ status: params.status,
1422
+ isError: isNonSuccessItemStatus(params.status),
1423
+ ...toolResult ? { result: toolResult } : {},
1424
+ ...output ? { output } : {}
1425
+ });
1426
+ }
1427
+ emitAfterToolCallObservation(item) {
1428
+ if (!this.shouldEmitAfterToolCallObservation(item)) return;
1429
+ const name = itemName(item);
1430
+ const status = itemStatus(item);
1431
+ if (!name || status === "running") return;
1432
+ this.afterToolCallObservedItemIds.add(item.id);
1433
+ const result = itemToolResult(item).result;
1434
+ const error = itemToolError(item, status, this.progress.outputTextByItem);
1435
+ const startedAt = resolveStartedAtFromDurationMs(item.durationMs);
1436
+ const hookParams = {
1437
+ toolName: name,
1438
+ toolCallId: item.id,
1439
+ runId: this.params.runId,
1440
+ agentId: this.params.agentId,
1441
+ sessionId: this.params.sessionId,
1442
+ sessionKey: this.params.sessionKey,
1443
+ startArgs: itemToolArgs(item) ?? {},
1444
+ ...result !== void 0 ? { result } : {},
1445
+ ...error ? { error } : {},
1446
+ ...startedAt !== void 0 ? { startedAt } : {}
1447
+ };
1448
+ setImmediate(() => {
1449
+ runAgentHarnessAfterToolCallHook(hookParams);
1450
+ });
1451
+ }
1452
+ synthesizeMissingToolResults(params) {
1453
+ if (!params.synthesize) return;
1454
+ const missingTranscriptIds = [...this.callIds].filter((id) => !this.resultIds.has(id));
1455
+ const missingTrajectoryIds = [...this.trajectoryCallIds].filter((id) => !this.trajectoryResultIds.has(id));
1456
+ if (missingTranscriptIds.length === 0 && missingTrajectoryIds.length === 0) return;
1457
+ for (const id of missingTranscriptIds) {
1458
+ const name = this.namesById.get(id) ?? this.trajectoryNamesById.get(id);
1459
+ if (name) this.recordToolResult({
1460
+ id,
1461
+ name,
1462
+ text: formatMissingToolResultError({
1463
+ id,
1464
+ name
1465
+ }),
1466
+ isError: true
1467
+ });
1468
+ }
1469
+ for (const id of missingTrajectoryIds) {
1470
+ const name = this.trajectoryNamesById.get(id) ?? this.namesById.get(id);
1471
+ if (!name) continue;
1472
+ this.trajectoryResultIds.add(id);
1473
+ const text = formatMissingToolResultError({
1474
+ id,
1475
+ name
1476
+ });
1477
+ this.options.trajectoryRecorder?.recordEvent("tool.result", {
1478
+ threadId: this.threadId,
1479
+ turnId: this.turnId,
1480
+ itemId: id,
1481
+ toolCallId: id,
1482
+ name,
1483
+ status: "failed",
1484
+ isError: true,
1485
+ result: {
1486
+ status: "failed",
1487
+ reason: "missing_tool_result"
1488
+ },
1489
+ output: text
1490
+ });
1491
+ }
1492
+ if (!params.recordPromptError) {
1493
+ this.recordMissingToolError(missingTranscriptIds, missingTrajectoryIds);
1494
+ return;
1495
+ }
1496
+ const missingCount = (/* @__PURE__ */ new Set([...missingTranscriptIds, ...missingTrajectoryIds])).size;
1497
+ return missingCount === 1 ? MISSING_TOOL_RESULT_ERROR : `${MISSING_TOOL_RESULT_ERROR} missingToolResultCount=${missingCount}`;
1498
+ }
1499
+ async readMirroredSessionMessages() {
1500
+ return await readCodexMirroredSessionHistoryMessages({
1501
+ agentId: this.params.agentId,
1502
+ sessionFile: this.params.sessionFile,
1503
+ sessionId: this.params.sessionId,
1504
+ sessionKey: this.params.sessionKey
1505
+ }) ?? [];
1506
+ }
1507
+ recordToolCall(params) {
1508
+ if (!params.id || !params.name || this.callIds.has(params.id)) return;
1509
+ this.callIds.add(params.id);
1510
+ this.namesById.set(params.id, params.name);
1511
+ this.progress.recordTranscriptCall(params);
1512
+ this.messages.push(attachCodexMirrorIdentity(this.createToolCallMessage(params), `${this.turnId}:tool:${params.id}:call`));
1513
+ }
1514
+ recordToolResult(params) {
1515
+ if (!params.id || !params.name || this.resultIds.has(params.id)) return;
1516
+ this.resultIds.add(params.id);
1517
+ this.progress.recordTranscriptResult(params);
1518
+ this.messages.push(attachCodexMirrorIdentity(this.createToolResultMessage(params), `${this.turnId}:tool:${params.id}:result`));
1519
+ }
1520
+ recordMissingToolError(missingTranscriptIds, missingTrajectoryIds) {
1521
+ const firstMissingId = missingTranscriptIds.find((id) => Boolean(this.namesById.get(id))) ?? missingTrajectoryIds.find((id) => Boolean(this.trajectoryNamesById.get(id) ?? this.namesById.get(id)));
1522
+ if (!firstMissingId) return;
1523
+ const name = this.namesById.get(firstMissingId) ?? this.trajectoryNamesById.get(firstMissingId);
1524
+ if (!name) return;
1525
+ const item = this.trajectoryItemsById.get(firstMissingId);
1526
+ const meta = item ? itemMeta(item, this.progress.toolProgressDetailMode()) : this.progress.getToolMeta(firstMissingId)?.meta;
1527
+ const actionFingerprint = item ? nativeToolActionFingerprint(item) : void 0;
1528
+ this.progress.setLastToolError({
1529
+ toolName: name,
1530
+ ...meta ? { meta } : {},
1531
+ error: formatMissingToolResultError({
1532
+ id: firstMissingId,
1533
+ name
1534
+ }),
1535
+ ...item && isMutatingNativeToolItem(item) ? { mutatingAction: true } : {},
1536
+ ...actionFingerprint ? { actionFingerprint } : {}
1537
+ });
1538
+ }
1539
+ shouldEmitAfterToolCallObservation(item) {
1540
+ if (!shouldSynthesizeToolProgressForItem(item) || this.afterToolCallObservedItemIds.has(item.id)) return false;
1541
+ return !(this.options.nativePostToolUseRelayEnabled && isNativePostToolUseRelayItem(item));
1542
+ }
1543
+ createToolCallMessage(params) {
1544
+ const args = normalizeToolTranscriptArguments(params.arguments);
1545
+ const attribution = resolveCodexLocalRuntimeAttribution(this.params);
1546
+ return {
1547
+ role: "assistant",
1548
+ content: [{
1549
+ type: "toolCall",
1550
+ id: params.id,
1551
+ name: params.name,
1552
+ arguments: args,
1553
+ input: args
1554
+ }],
1555
+ api: attribution.api ?? "openai-chatgpt-responses",
1556
+ provider: attribution.provider,
1557
+ model: this.params.modelId,
1558
+ usage: ZERO_USAGE,
1559
+ stopReason: "toolUse",
1560
+ timestamp: this.nextTranscriptTimestamp()
1561
+ };
1562
+ }
1563
+ createToolResultMessage(params) {
1564
+ const text = truncateToolTranscriptText(params.text?.trim() || toolResultStatusText(params));
1565
+ return {
1566
+ role: "toolResult",
1567
+ toolCallId: params.id,
1568
+ toolName: params.name,
1569
+ isError: params.isError,
1570
+ content: [{
1571
+ type: "toolResult",
1572
+ id: params.id,
1573
+ name: params.name,
1574
+ toolName: params.name,
1575
+ toolCallId: params.id,
1576
+ toolUseId: params.id,
1577
+ tool_use_id: params.id,
1578
+ content: text,
1579
+ text
1580
+ }],
1581
+ timestamp: this.nextTranscriptTimestamp()
1582
+ };
1583
+ }
1584
+ };
1585
+ function formatMissingToolResultError(params) {
1586
+ return `${MISSING_TOOL_RESULT_ERROR} toolCallId=${params.id}; toolName=${params.name}`;
1587
+ }
1588
+ function toolResultStatusText(params) {
1589
+ return params.isError ? `${params.name} failed` : `${params.name} completed`;
1590
+ }
1591
+ function resolveStartedAtFromDurationMs(durationMs) {
1592
+ if (typeof durationMs !== "number" || !Number.isFinite(durationMs)) return;
1593
+ return asDateTimestampMs(Date.now() - Math.max(0, durationMs));
1594
+ }
1595
+ //#endregion
1596
+ //#region extensions/codex/src/app-server/event-projector-events.ts
1597
+ var CodexEventProjection = class {
1598
+ constructor(threadId, turnId, emitAgentEvent, toolProgress, toolTranscript, onNativeToolResultRecorded) {
1599
+ this.threadId = threadId;
1600
+ this.turnId = turnId;
1601
+ this.emitAgentEvent = emitAgentEvent;
1602
+ this.toolProgress = toolProgress;
1603
+ this.toolTranscript = toolTranscript;
1604
+ this.onNativeToolResultRecorded = onNativeToolResultRecorded;
1605
+ this.reviewCount = 0;
1606
+ }
1607
+ get guardianReviewCount() {
1608
+ return this.reviewCount;
1609
+ }
1610
+ handleGuardianReview(method, params) {
1611
+ this.reviewCount += 1;
1612
+ const review = isJsonObject(params.review) ? params.review : void 0;
1613
+ const action = isJsonObject(params.action) ? params.action : void 0;
1614
+ this.emitAgentEvent({
1615
+ stream: "codex_app_server.guardian",
1616
+ data: {
1617
+ method,
1618
+ phase: method.endsWith("/started") ? "started" : "completed",
1619
+ reviewId: readString$5(params, "reviewId"),
1620
+ targetItemId: readNullableString$1(params, "targetItemId"),
1621
+ decisionSource: readString$5(params, "decisionSource"),
1622
+ status: review ? readString$5(review, "status") : void 0,
1623
+ riskLevel: review ? readString$5(review, "riskLevel") : void 0,
1624
+ userAuthorization: review ? readString$5(review, "userAuthorization") : void 0,
1625
+ rationale: review ? readNullableString$1(review, "rationale") : void 0,
1626
+ actionType: action ? readString$5(action, "type") : void 0
1627
+ }
1628
+ });
1629
+ }
1630
+ handleGuardianWarning(params) {
1631
+ this.emitAgentEvent({
1632
+ stream: "codex_app_server.guardian",
1633
+ data: {
1634
+ phase: "warning",
1635
+ message: readString$5(params, "message")
1636
+ }
1637
+ });
1638
+ }
1639
+ handleHook(method, params) {
1640
+ const run = isJsonObject(params.run) ? params.run : void 0;
1641
+ if (!run) return;
1642
+ const durationMs = readNumber(run, "durationMs");
1643
+ const entries = readHookOutputEntries(run.entries);
1644
+ const hookTurnId = readNullableString$1(params, "turnId");
1645
+ this.emitAgentEvent({
1646
+ stream: "codex_app_server.hook",
1647
+ data: {
1648
+ phase: method === "hook/started" ? "started" : "completed",
1649
+ threadId: this.threadId,
1650
+ turnId: hookTurnId === void 0 ? this.turnId : hookTurnId,
1651
+ hookRunId: readString$5(run, "id"),
1652
+ eventName: readString$5(run, "eventName"),
1653
+ handlerType: readString$5(run, "handlerType"),
1654
+ executionMode: readString$5(run, "executionMode"),
1655
+ scope: readString$5(run, "scope"),
1656
+ source: readString$5(run, "source"),
1657
+ sourcePath: readString$5(run, "sourcePath"),
1658
+ status: readString$5(run, "status"),
1659
+ statusMessage: readNullableString$1(run, "statusMessage"),
1660
+ ...durationMs !== void 0 ? { durationMs } : {},
1661
+ ...entries.length > 0 ? { entries } : {}
1662
+ }
1663
+ });
1664
+ }
1665
+ emitStandardItemEvent(params) {
1666
+ const { item } = params;
1667
+ if (!item) return;
1668
+ const kind = itemKind(item);
1669
+ if (!kind) return;
1670
+ const meta = itemMeta(item, this.toolProgress.toolProgressDetailMode());
1671
+ const suppressChannelProgress = shouldSuppressChannelProgressForItem(item);
1672
+ this.emitAgentEvent({
1673
+ stream: "item",
1674
+ data: {
1675
+ itemId: item.id,
1676
+ phase: params.phase,
1677
+ kind,
1678
+ title: itemTitle(item),
1679
+ status: params.phase === "start" ? "running" : itemStatus(item),
1680
+ ...itemName(item) ? { name: itemName(item) } : {},
1681
+ ...meta ? { meta } : {},
1682
+ ...suppressChannelProgress ? { suppressChannelProgress: true } : {}
1683
+ }
1684
+ });
1685
+ }
1686
+ async emitNormalizedToolItemEvent(params) {
1687
+ const { item } = params;
1688
+ if (!item || !shouldSynthesizeToolProgressForItem(item)) return;
1689
+ const name = itemName(item);
1690
+ if (!name) return;
1691
+ const status = params.phase === "result" ? itemStatus(item) : "running";
1692
+ const args = itemToolArgs(item);
1693
+ const meta = itemMeta(item, this.toolProgress.toolProgressDetailMode());
1694
+ this.toolTranscript.recordTrajectoryEvent({
1695
+ phase: params.phase,
1696
+ item,
1697
+ name,
1698
+ args,
1699
+ status
1700
+ });
1701
+ if (params.phase === "result") this.toolProgress.recordNativeToolError({
1702
+ item,
1703
+ name,
1704
+ meta,
1705
+ status
1706
+ });
1707
+ if (!shouldEmitTranscriptToolProgress(name, args)) {
1708
+ if (params.phase === "result") {
1709
+ this.toolTranscript.emitAfterToolCallObservation(item);
1710
+ await this.onNativeToolResultRecorded?.();
1711
+ }
1712
+ return;
1713
+ }
1714
+ this.emitAgentEvent({
1715
+ stream: "tool",
1716
+ data: {
1717
+ phase: params.phase,
1718
+ name,
1719
+ itemId: item.id,
1720
+ toolCallId: item.id,
1721
+ ...meta ? { meta } : {},
1722
+ ...params.phase === "start" && args ? { args } : {},
1723
+ ...params.phase === "result" ? {
1724
+ status,
1725
+ isError: isNonSuccessItemStatus(status),
1726
+ ...itemToolResult(item)
1727
+ } : {}
1728
+ }
1729
+ });
1730
+ if (params.phase === "result") {
1731
+ this.toolTranscript.emitAfterToolCallObservation(item);
1732
+ await this.onNativeToolResultRecorded?.();
1733
+ }
1734
+ }
1735
+ };
1736
+ //#endregion
1737
+ //#region extensions/codex/src/app-server/event-projector-media.ts
1738
+ const GENERATED_IMAGE_MEDIA_SUBDIR = "tool-image-generation";
1739
+ const BYTES_PER_MB = 1024 * 1024;
1740
+ const DEFAULT_GENERATED_IMAGE_MAX_BYTES = 6 * BYTES_PER_MB;
1741
+ var CodexGeneratedMediaProjection = class {
1742
+ constructor(config) {
1743
+ this.config = config;
1744
+ this.itemIds = /* @__PURE__ */ new Set();
1745
+ this.urlsByItemId = /* @__PURE__ */ new Map();
1746
+ }
1747
+ hasGeneratedMedia() {
1748
+ return this.itemIds.size > 0;
1749
+ }
1750
+ recordNative(item) {
1751
+ if (item?.type !== "imageGeneration") return;
1752
+ const savedPath = readItemString(item, "savedPath")?.trim();
1753
+ if (savedPath) this.recordUrl({
1754
+ itemId: item.id,
1755
+ mediaUrl: savedPath
1756
+ });
1757
+ }
1758
+ async recordRaw(item) {
1759
+ if (readString$5(item, "type") !== "image_generation_call") return;
1760
+ const result = readString$5(item, "result");
1761
+ if (!result) return;
1762
+ const itemId = readString$5(item, "id") ?? `raw-image-${this.itemIds.size}`;
1763
+ this.itemIds.add(itemId);
1764
+ const maxBytes = resolveGeneratedImageMaxBytes(this.config);
1765
+ const estimatedDecodedBytes = estimateBase64DecodedBytes(result);
1766
+ if (estimatedDecodedBytes !== void 0 && estimatedDecodedBytes > maxBytes) {
1767
+ embeddedAgentLog.warn("codex app-server raw image generation result exceeds media limit", {
1768
+ itemId,
1769
+ estimatedDecodedBytes,
1770
+ maxBytes
1771
+ });
1772
+ return;
1773
+ }
1774
+ const asset = generatedImageAssetFromBase64({
1775
+ base64: result,
1776
+ index: this.itemIds.size,
1777
+ revisedPrompt: readString$5(item, "revised_prompt") ?? readString$5(item, "revisedPrompt"),
1778
+ fileNamePrefix: "codex-image-generation",
1779
+ sniffMimeType: true
1780
+ });
1781
+ if (!asset) return;
1782
+ try {
1783
+ const saved = await saveMediaBuffer(asset.buffer, asset.mimeType, GENERATED_IMAGE_MEDIA_SUBDIR, maxBytes, asset.fileName);
1784
+ this.recordUrl({
1785
+ itemId,
1786
+ mediaUrl: saved.path,
1787
+ replaceExisting: true
1788
+ });
1789
+ } catch (error) {
1790
+ embeddedAgentLog.warn("codex app-server raw image generation result save failed", {
1791
+ itemId,
1792
+ error
1793
+ });
1794
+ }
1795
+ }
1796
+ buildToolMediaUrls(params) {
1797
+ const mediaUrls = new Set(params.toolMediaUrls?.map((url) => url.trim()).filter(Boolean) ?? []);
1798
+ if ((params.messagingToolSentMediaUrls?.length ?? 0) === 0) for (const mediaUrl of this.urlsByItemId.values()) mediaUrls.add(mediaUrl);
1799
+ return mediaUrls.size > 0 ? [...mediaUrls] : params.toolMediaUrls;
1800
+ }
1801
+ buildHostOwnedMediaUrls(params) {
1802
+ if ((params.messagingToolSentMediaUrls?.length ?? 0) > 0) return;
1803
+ const mediaUrls = [...this.urlsByItemId.values()];
1804
+ return mediaUrls.length > 0 ? mediaUrls : void 0;
1805
+ }
1806
+ recordUrl(params) {
1807
+ if (this.urlsByItemId.has(params.itemId) && params.replaceExisting !== true) {
1808
+ this.itemIds.add(params.itemId);
1809
+ return;
1810
+ }
1811
+ this.urlsByItemId.set(params.itemId, params.mediaUrl);
1812
+ this.itemIds.add(params.itemId);
1813
+ }
1814
+ };
1815
+ function estimateBase64DecodedBytes(base64) {
1816
+ let nonWhitespaceLength = 0;
1817
+ let previousCode = -1;
1818
+ let lastCode = -1;
1819
+ for (let i = 0; i < base64.length; i += 1) {
1820
+ const code = base64.charCodeAt(i);
1821
+ if (isBase64WhitespaceCode(code)) continue;
1822
+ nonWhitespaceLength += 1;
1823
+ previousCode = lastCode;
1824
+ lastCode = code;
1825
+ }
1826
+ if (nonWhitespaceLength === 0) return;
1827
+ const equalsCode = "=".charCodeAt(0);
1828
+ const padding = lastCode === equalsCode ? previousCode === equalsCode ? 2 : 1 : 0;
1829
+ return Math.max(0, Math.floor(nonWhitespaceLength * 3 / 4) - padding);
1830
+ }
1831
+ function isBase64WhitespaceCode(code) {
1832
+ return code === 32 || code === 9 || code === 10 || code === 13;
1833
+ }
1834
+ function resolveGeneratedImageMaxBytes(config) {
1835
+ const configured = config?.agents?.defaults?.mediaMaxMb;
1836
+ if (typeof configured === "number" && Number.isFinite(configured) && configured > 0) return Math.floor(configured * BYTES_PER_MB);
1837
+ return DEFAULT_GENERATED_IMAGE_MAX_BYTES;
1838
+ }
1839
+ //#endregion
1840
+ //#region extensions/codex/src/app-server/event-projector-reasoning.ts
1841
+ var CodexReasoningProjection = class {
1842
+ constructor(params, emitAgentEvent) {
1843
+ this.params = params;
1844
+ this.emitAgentEvent = emitAgentEvent;
1845
+ this.reasoningTextByGroup = /* @__PURE__ */ new Map();
1846
+ this.reasoningItemOrder = /* @__PURE__ */ new Map();
1847
+ this.planTextByItem = /* @__PURE__ */ new Map();
1848
+ this.reasoningStarted = false;
1849
+ this.reasoningEnded = false;
1850
+ }
1851
+ async handleReasoningDelta(method, params) {
1852
+ const itemId = readString$5(params, "itemId") ?? "reasoning";
1853
+ const delta = readString$5(params, "delta") ?? "";
1854
+ if (!delta) return;
1855
+ this.reasoningStarted = true;
1856
+ if (!this.reasoningItemOrder.has(itemId)) this.reasoningItemOrder.set(itemId, this.reasoningItemOrder.size);
1857
+ const groupIndex = method === "item/reasoning/textDelta" ? readNonNegativeInteger(params, "contentIndex") ?? 0 : readNonNegativeInteger(params, "summaryIndex") ?? 0;
1858
+ const groupKey = `${method}\0${itemId}\0${groupIndex}`;
1859
+ const current = this.reasoningTextByGroup.get(groupKey);
1860
+ this.reasoningTextByGroup.set(groupKey, {
1861
+ itemId,
1862
+ method,
1863
+ index: groupIndex,
1864
+ text: `${current?.text ?? ""}${delta}`
1865
+ });
1866
+ await this.params.onReasoningStream?.({
1867
+ text: this.reasoningText(),
1868
+ isReasoningSnapshot: true
1869
+ });
1870
+ }
1871
+ handlePlanDelta(params) {
1872
+ const itemId = readString$5(params, "itemId") ?? "plan";
1873
+ const delta = readString$5(params, "delta") ?? "";
1874
+ if (!delta) return;
1875
+ const text = `${this.planTextByItem.get(itemId) ?? ""}${delta}`;
1876
+ this.planTextByItem.set(itemId, text);
1877
+ this.emitPlanUpdate({
1878
+ explanation: void 0,
1879
+ steps: splitPlanText(text).map((step) => ({
1880
+ step,
1881
+ status: "pending"
1882
+ }))
1883
+ });
1884
+ }
1885
+ handleTurnPlanUpdated(params) {
1886
+ const explanation = readNullableString$1(params, "explanation");
1887
+ const plan = Array.isArray(params.plan) ? params.plan.flatMap((entry) => {
1888
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
1889
+ const record = entry;
1890
+ const step = readString$5(record, "step");
1891
+ if (!step) return [];
1892
+ return [{
1893
+ step,
1894
+ status: normalizePlanStepStatus(readString$5(record, "status"))
1895
+ }];
1896
+ }) : void 0;
1897
+ const planText = [explanation, ...(plan ?? []).map(({ step, status }) => `- [${status}] ${step}`)].filter((part) => Boolean(part)).join("\n");
1898
+ if (planText) this.turnPlanText = planText;
1899
+ this.emitPlanUpdate({
1900
+ explanation,
1901
+ steps: plan
1902
+ });
1903
+ }
1904
+ recordItem(item) {
1905
+ if (item?.type === "plan" && typeof item.text === "string" && item.text) {
1906
+ this.planTextByItem.set(item.id, item.text);
1907
+ this.emitPlanUpdate({
1908
+ explanation: void 0,
1909
+ steps: splitPlanText(item.text).map((step) => ({
1910
+ step,
1911
+ status: "pending"
1912
+ }))
1913
+ });
1914
+ }
1915
+ }
1916
+ async maybeEndReasoning() {
1917
+ if (!this.reasoningStarted || this.reasoningEnded) return;
1918
+ this.reasoningEnded = true;
1919
+ await this.params.onReasoningEnd?.();
1920
+ }
1921
+ reasoningText() {
1922
+ return collectReasoningTextValues(this.reasoningTextByGroup, this.reasoningItemOrder).join("\n\n");
1923
+ }
1924
+ planText() {
1925
+ return this.turnPlanText ?? [...this.planTextByItem.values()].filter((text) => text.trim().length > 0).join("\n\n");
1926
+ }
1927
+ emitPlanUpdate(params) {
1928
+ if (!params.explanation && (!params.steps || params.steps.length === 0)) return;
1929
+ this.emitAgentEvent({
1930
+ stream: "plan",
1931
+ data: {
1932
+ phase: "update",
1933
+ title: "Plan updated",
1934
+ source: "codex-app-server",
1935
+ ...params.explanation ? { explanation: params.explanation } : {},
1936
+ ...params.steps && params.steps.length > 0 ? { steps: params.steps } : {}
1937
+ }
1938
+ });
1939
+ }
1940
+ };
1941
+ function normalizePlanStepStatus(status) {
1942
+ if (status === "inProgress" || status === "in_progress") return "in_progress";
1943
+ return status === "completed" ? "completed" : "pending";
1944
+ }
1945
+ function collectReasoningTextValues(groups, itemOrder) {
1946
+ return [...groups.values()].toSorted((left, right) => {
1947
+ const itemDelta = (itemOrder.get(left.itemId) ?? Number.MAX_SAFE_INTEGER) - (itemOrder.get(right.itemId) ?? Number.MAX_SAFE_INTEGER);
1948
+ if (itemDelta !== 0) return itemDelta;
1949
+ const methodDelta = reasoningMethodOrder(left.method) - reasoningMethodOrder(right.method);
1950
+ return methodDelta !== 0 ? methodDelta : left.index - right.index;
1951
+ }).map((group) => group.text).filter((text) => text.trim().length > 0);
1952
+ }
1953
+ function reasoningMethodOrder(method) {
1954
+ return method === "item/reasoning/summaryTextDelta" ? 0 : 1;
1955
+ }
1956
+ //#endregion
1957
+ //#region extensions/codex/src/app-server/event-projector-snapshot.ts
1958
+ function buildCodexMessagesSnapshot(params) {
1959
+ const messages = promptSnapshot(params.runParams, params.turnId, params.upstreamUserText);
1960
+ if (params.reasoningText) messages.push(attachCodexMirrorIdentity(params.createAssistantMirrorMessage("Codex reasoning", params.reasoningText), `${params.turnId}:reasoning`));
1961
+ if (params.planText) messages.push(attachCodexMirrorIdentity(params.createAssistantMirrorMessage("Codex plan", params.planText), `${params.turnId}:plan`));
1962
+ 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));
1963
+ messages.push(...visibleWorkMessages);
1964
+ if (params.lastAssistant) messages.push(attachCodexMirrorIdentity(params.lastAssistant, `${params.turnId}:assistant`));
1965
+ return messages.map((message) => projectAgentHarnessTranscriptMessageForDisplay({
1966
+ hidden: params.runParams.trigger === "memory",
1967
+ message
1968
+ }));
1969
+ }
1970
+ //#endregion
1971
+ //#region extensions/codex/src/app-server/usage-limit-error.ts
194
1972
  /**
195
- * Bridges Codex item/tool user-input requests to OpenClaw messaging prompts and
196
- * turns replies into app-server answer payloads.
1973
+ * Enriches Codex usage-limit failures with current rate-limit information and
1974
+ * marks blocked auth profiles when Codex exposes a reset time.
197
1975
  */
1976
+ const CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS = 5e3;
1977
+ function createCodexUsageLimitPromptError(message) {
1978
+ return Object.assign(new Error(message), { status: 429 });
1979
+ }
1980
+ function isCodexUsageLimitPromptError(error) {
1981
+ return error instanceof Error && "status" in error && error.status === 429;
1982
+ }
1983
+ /** Marks a Codex auth profile blocked until the reset time advertised by rate limits. */
1984
+ async function markCodexAuthProfileBlockedFromRateLimits(params) {
1985
+ const authProfileId = params.authProfileId?.trim();
1986
+ if (!authProfileId || !params.params.authProfileStore) return;
1987
+ const blockedUntil = resolveCodexUsageLimitResetAtMs(params.rateLimits);
1988
+ if (!blockedUntil) return;
1989
+ try {
1990
+ await markAuthProfileBlockedUntil({
1991
+ store: params.params.authProfileStore,
1992
+ profileId: authProfileId,
1993
+ blockedUntil,
1994
+ source: "codex_rate_limits",
1995
+ agentDir: params.params.agentDir,
1996
+ runId: params.params.runId,
1997
+ modelId: params.params.modelId
1998
+ });
1999
+ } catch (error) {
2000
+ embeddedAgentLog.debug("failed to mark Codex auth profile blocked from app-server limits", {
2001
+ authProfileId,
2002
+ error: formatErrorMessage(error)
2003
+ });
2004
+ }
2005
+ }
2006
+ /** Formats a turn-start usage-limit error, refreshing rate limits when needed. */
2007
+ async function formatCodexTurnStartUsageLimitError(params) {
2008
+ return refreshCodexUsageLimitError({
2009
+ client: params.client,
2010
+ source: readCodexTurnStartUsageLimitErrorSource(params.client, params.error, params.errorNotification, params.rateLimitsRevisionBeforeTurnStart),
2011
+ timeoutMs: params.timeoutMs,
2012
+ signal: params.signal
2013
+ });
2014
+ }
2015
+ /** Refreshes a generic prompt usage-limit message into a reset-aware message. */
2016
+ async function refreshCodexUsageLimitPromptError(params) {
2017
+ if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(params.message)) return;
2018
+ return refreshCodexUsageLimitError({
2019
+ client: params.client,
2020
+ source: {
2021
+ message: params.message,
2022
+ codexErrorInfo: "usageLimitExceeded",
2023
+ rateLimits: readRecentCodexRateLimits(params.client)
2024
+ },
2025
+ timeoutMs: params.timeoutMs,
2026
+ signal: params.signal
2027
+ });
2028
+ }
2029
+ async function refreshCodexUsageLimitError(params) {
2030
+ const initialMessage = formatCodexUsageLimitErrorMessage(params.source);
2031
+ if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(initialMessage)) return initialMessage ? {
2032
+ message: initialMessage,
2033
+ ...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
2034
+ } : void 0;
2035
+ const rateLimits = await readCodexRateLimitsFromAppServerForUsageLimitError({
2036
+ client: params.client,
2037
+ timeoutMs: params.timeoutMs,
2038
+ signal: params.signal
2039
+ });
2040
+ if (!rateLimits) return initialMessage ? {
2041
+ message: initialMessage,
2042
+ ...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
2043
+ } : void 0;
2044
+ const message = formatCodexUsageLimitErrorMessage({
2045
+ message: params.source.message,
2046
+ codexErrorInfo: params.source.codexErrorInfo,
2047
+ rateLimits,
2048
+ rateLimitsAuthoritative: true
2049
+ }) ?? initialMessage;
2050
+ return message ? {
2051
+ message,
2052
+ rateLimitsForProfile: rateLimits
2053
+ } : void 0;
2054
+ }
2055
+ async function readCodexRateLimitsFromAppServerForUsageLimitError(params) {
2056
+ if (params.signal?.aborted) return;
2057
+ try {
2058
+ const rateLimits = await params.client.request(CODEX_CONTROL_METHODS.rateLimits, void 0, {
2059
+ timeoutMs: resolveCodexUsageLimitRateLimitRefreshTimeoutMs(params.timeoutMs),
2060
+ signal: params.signal
2061
+ });
2062
+ rememberCodexRateLimitsRead(params.client, rateLimits);
2063
+ return rateLimits;
2064
+ } catch (error) {
2065
+ embeddedAgentLog.debug("codex app-server rate-limit refresh failed after usage-limit error", { error: formatErrorMessage(error) });
2066
+ return;
2067
+ }
2068
+ }
2069
+ function resolveCodexUsageLimitRateLimitRefreshTimeoutMs(timeoutMs) {
2070
+ if (timeoutMs === void 0 || !Number.isFinite(timeoutMs) || timeoutMs <= 0) return CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS;
2071
+ return Math.max(100, Math.min(timeoutMs, CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS));
2072
+ }
2073
+ function readCodexTurnStartUsageLimitErrorSource(client, error, errorNotification, rateLimitsRevisionBeforeTurnStart) {
2074
+ const notificationError = readCodexErrorNotification(errorNotification);
2075
+ const errorPayload = readCodexErrorPayload(error);
2076
+ const rateLimits = errorPayload.rateLimits ?? readRecentCodexRateLimits(client);
2077
+ const cacheUpdatedDuringTurnStart = rateLimitsRevisionBeforeTurnStart !== void 0 && readCodexRateLimitsRevision(client) > rateLimitsRevisionBeforeTurnStart;
2078
+ return {
2079
+ message: notificationError?.message ?? errorPayload.message ?? formatErrorMessage(error),
2080
+ codexErrorInfo: notificationError?.codexErrorInfo ?? errorPayload.codexErrorInfo,
2081
+ rateLimits,
2082
+ rateLimitsTrustedForProfile: errorPayload.rateLimits !== void 0 || cacheUpdatedDuringTurnStart
2083
+ };
2084
+ }
2085
+ function readCodexErrorNotification(notification) {
2086
+ if (notification?.method !== "error" || !isJsonObject(notification.params)) return;
2087
+ const error = notification.params.error;
2088
+ return isJsonObject(error) ? {
2089
+ message: readString$4(error, "message"),
2090
+ codexErrorInfo: error.codexErrorInfo
2091
+ } : void 0;
2092
+ }
2093
+ function readCodexErrorPayload(error) {
2094
+ const message = error instanceof Error ? error.message : void 0;
2095
+ if (!error || typeof error !== "object" || !("data" in error)) return { message };
2096
+ const data = error.data;
2097
+ if (!isJsonObject(data)) return { message };
2098
+ const nestedError = isJsonObject(data.error) ? data.error : data;
2099
+ const rateLimits = nestedError.rateLimits ?? data.rateLimits;
2100
+ return {
2101
+ message: readString$4(nestedError, "message") ?? message,
2102
+ codexErrorInfo: nestedError.codexErrorInfo,
2103
+ rateLimits
2104
+ };
2105
+ }
2106
+ function readString$4(record, key) {
2107
+ const value = record[key];
2108
+ return typeof value === "string" ? value : void 0;
2109
+ }
2110
+ //#endregion
2111
+ //#region extensions/codex/src/app-server/event-projector.ts
2112
+ var CodexAppServerEventProjector = class {
2113
+ constructor(params, threadId, turnId, options = {}) {
2114
+ this.params = params;
2115
+ this.threadId = threadId;
2116
+ this.turnId = turnId;
2117
+ this.options = options;
2118
+ this.activeItemIds = /* @__PURE__ */ new Set();
2119
+ this.completedItemIds = /* @__PURE__ */ new Set();
2120
+ this.activeCompactionItemIds = /* @__PURE__ */ new Set();
2121
+ this.terminalPresentationClearedItemIds = /* @__PURE__ */ new Set();
2122
+ this.nativeToolOutcomeOrdinals = /* @__PURE__ */ new Map();
2123
+ this.promptErrorSource = null;
2124
+ this.synthesizedMissingToolResultError = null;
2125
+ this.aborted = false;
2126
+ this.completedCompactionCount = 0;
2127
+ this.lastTranscriptTimestamp = 0;
2128
+ this.diagnostics = new CodexProjectionDiagnostics(threadId, turnId);
2129
+ this.nativeToolLifecycleProjector = new CodexNativeToolLifecycleProjector(params, threadId, turnId, { runAbortSignal: options.runAbortSignal });
2130
+ this.generatedMediaProjection = new CodexGeneratedMediaProjection(params.config);
2131
+ this.toolProgressProjection = new CodexToolProgressProjection(params);
2132
+ this.toolTranscriptProjection = new CodexToolTranscriptProjection(params, threadId, turnId, this.toolProgressProjection, () => this.nextTranscriptTimestamp(), {
2133
+ nativePostToolUseRelayEnabled: options.nativePostToolUseRelayEnabled,
2134
+ trajectoryRecorder: options.trajectoryRecorder
2135
+ });
2136
+ this.eventProjection = new CodexEventProjection(threadId, turnId, (event) => this.emitAgentEvent(event), this.toolProgressProjection, this.toolTranscriptProjection, options.onNativeToolResultRecorded);
2137
+ this.assistantProjection = new CodexAssistantProjection(params, (event) => this.emitAgentEvent(event), (text) => this.toolProgressProjection.matchesEcho(text), () => this.nextTranscriptTimestamp());
2138
+ this.reasoningProjection = new CodexReasoningProjection(params, (event) => this.emitAgentEvent(event));
2139
+ }
2140
+ nextTranscriptTimestamp() {
2141
+ this.lastTranscriptTimestamp = Math.max(Date.now(), this.lastTranscriptTimestamp + 1);
2142
+ return this.lastTranscriptTimestamp;
2143
+ }
2144
+ getCompletedTurnStatus() {
2145
+ return this.completedTurn?.status;
2146
+ }
2147
+ hasCompletedTerminalAssistantText() {
2148
+ return this.assistantProjection.hasCompletedTerminalAssistantText(this.completedItemIds);
2149
+ }
2150
+ getLatestTerminalAssistantCandidate() {
2151
+ return this.assistantProjection.getLatestTerminalAssistantCandidate();
2152
+ }
2153
+ hasLatestTerminalAssistantCandidateText() {
2154
+ return this.assistantProjection.hasLatestTerminalAssistantCandidateText();
2155
+ }
2156
+ canReleaseLatestTerminalAssistantAfterToolHandoff() {
2157
+ return this.assistantProjection.canReleaseLatestTerminalAssistantAfterToolHandoff();
2158
+ }
2159
+ /** Restores a completed final item after only the enclosing turn timeout fired. */
2160
+ recoverCompletedTerminalAssistantAfterTurnWatchTimeout() {
2161
+ if (!this.aborted || this.promptError !== "codex app-server attempt timed out" || !this.hasCompletedTerminalAssistantText()) return false;
2162
+ this.aborted = false;
2163
+ this.promptError = void 0;
2164
+ this.promptErrorSource = null;
2165
+ return true;
2166
+ }
2167
+ /** Resolves the shared model-order position for a native tool item. */
2168
+ recordNativeToolOutcome(item) {
2169
+ if (!item || this.nativeToolOutcomeOrdinals.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
2170
+ const ordinal = this.params.allocateToolOutcomeOrdinal?.(item.id);
2171
+ if (ordinal !== void 0) this.nativeToolOutcomeOrdinals.set(item.id, ordinal);
2172
+ }
2173
+ recordNativeToolApprovalFailure(toolCallId, disposition) {
2174
+ this.nativeToolLifecycleProjector.recordApprovalFailureDisposition(toolCallId, disposition);
2175
+ }
2176
+ recordNativeToolPreToolUseFailure(failure) {
2177
+ this.nativeToolLifecycleProjector.recordPreToolUseFailure(failure);
2178
+ }
2179
+ async handleNotification(notification) {
2180
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
2181
+ if (!params) return;
2182
+ if (isHookNotificationMethod(notification.method)) {
2183
+ if (!this.isHookNotificationForCurrentThread(params)) return;
2184
+ } else if (notification.method === "guardianWarning") {
2185
+ if (readCodexNotificationThreadId(params) !== this.threadId) return;
2186
+ } else if (!this.isNotificationForTurn(params)) return;
2187
+ this.nativeToolLifecycleProjector.handleNotification(notification);
2188
+ switch (notification.method) {
2189
+ case "item/agentMessage/delta":
2190
+ await this.assistantProjection.handleAssistantDelta(params);
2191
+ break;
2192
+ case "item/reasoning/summaryTextDelta":
2193
+ case "item/reasoning/textDelta":
2194
+ await this.reasoningProjection.handleReasoningDelta(notification.method, params);
2195
+ break;
2196
+ case "item/plan/delta":
2197
+ this.reasoningProjection.handlePlanDelta(params);
2198
+ break;
2199
+ case "turn/plan/updated":
2200
+ this.reasoningProjection.handleTurnPlanUpdated(params);
2201
+ break;
2202
+ case "item/started":
2203
+ await this.handleItemStarted(params);
2204
+ break;
2205
+ case "item/completed":
2206
+ await this.handleItemCompleted(params);
2207
+ break;
2208
+ case "item/commandExecution/outputDelta":
2209
+ this.toolProgressProjection.handleOutputDelta(params, "bash");
2210
+ break;
2211
+ case "item/autoApprovalReview/started":
2212
+ case "item/autoApprovalReview/completed":
2213
+ this.eventProjection.handleGuardianReview(notification.method, params);
2214
+ break;
2215
+ case "guardianWarning":
2216
+ this.eventProjection.handleGuardianWarning(params);
2217
+ break;
2218
+ case "hook/started":
2219
+ case "hook/completed":
2220
+ this.eventProjection.handleHook(notification.method, params);
2221
+ break;
2222
+ case "thread/tokenUsage/updated":
2223
+ projectCodexThreadUsageUpdate(params, this.tokenUsage, (usage) => this.tokenUsage = usage, (data) => this.emitAgentEvent({
2224
+ stream: "codex_app_server.usage",
2225
+ data
2226
+ }));
2227
+ break;
2228
+ case "turn/completed":
2229
+ await this.handleTurnCompleted(params);
2230
+ break;
2231
+ case "rawResponse/completed":
2232
+ this.handleRawResponseCompleted(params);
2233
+ break;
2234
+ case "rawResponseItem/completed":
2235
+ await this.handleRawResponseItemCompleted(params);
2236
+ break;
2237
+ case "error":
2238
+ this.responseUsage = void 0;
2239
+ if (params.willRetry === true) break;
2240
+ this.promptError = this.formatCodexErrorMessage(params) ?? "codex app-server error";
2241
+ this.promptErrorSource = "prompt";
2242
+ break;
2243
+ case "thread/compacted":
2244
+ case "turn/started":
2245
+ case "turn/diff/updated":
2246
+ case "item/reasoning/summaryPartAdded":
2247
+ case "item/commandExecution/terminalInteraction":
2248
+ case "item/fileChange/outputDelta":
2249
+ case "item/fileChange/patchUpdated":
2250
+ case "item/mcpToolCall/progress":
2251
+ case "model/rerouted":
2252
+ case "model/verification":
2253
+ case "turn/moderationMetadata":
2254
+ case "model/safetyBuffering/updated": break;
2255
+ default:
2256
+ this.diagnostics.warnUnknownEvent(notification, params);
2257
+ break;
2258
+ }
2259
+ }
2260
+ buildResult(toolTelemetry, options) {
2261
+ this.nativeToolLifecycleProjector.finalizeActive();
2262
+ const assistantTexts = this.assistantProjection.collectAssistantTexts();
2263
+ const commentaryMessages = this.assistantProjection.collectCommentaryMessages();
2264
+ const reasoningText = this.reasoningProjection.reasoningText();
2265
+ const planText = this.reasoningProjection.planText();
2266
+ const projectedUsage = this.aborted ? this.tokenUsage : this.responseUsage ?? this.tokenUsage;
2267
+ const hasAssistantItemText = this.assistantProjection.hasAssistantItemTextForSynthesis();
2268
+ const legacyFailClosed = !this.completedTurn || this.completedTurn.status !== "completed" || hasAssistantItemText;
2269
+ const hasDeliverableAssistantOnCompletedTurn = this.completedTurn?.status === "completed" && assistantTexts.some((text) => text.trim().length > 0);
2270
+ const synthesizedMissingToolResultError = this.toolTranscriptProjection.synthesizeMissingToolResults({
2271
+ synthesize: legacyFailClosed,
2272
+ recordPromptError: legacyFailClosed && !hasDeliverableAssistantOnCompletedTurn && !this.aborted
2273
+ });
2274
+ if (synthesizedMissingToolResultError) {
2275
+ this.synthesizedMissingToolResultError = synthesizedMissingToolResultError;
2276
+ this.promptErrorSource = this.promptErrorSource ?? "prompt";
2277
+ }
2278
+ const assistantMessageOptions = {
2279
+ tokenUsage: projectedUsage,
2280
+ aborted: this.aborted,
2281
+ promptError: this.promptError
2282
+ };
2283
+ const lastAssistant = assistantTexts.length ? this.assistantProjection.createAssistantMessage(assistantTexts.join("\n\n"), assistantMessageOptions) : void 0;
2284
+ const currentAttemptAssistant = this.assistantProjection.createCurrentAttemptAssistantMessage(assistantMessageOptions);
2285
+ const messagesSnapshot = buildCodexMessagesSnapshot({
2286
+ runParams: this.params,
2287
+ turnId: this.turnId,
2288
+ upstreamUserText: this.options.upstreamUserText,
2289
+ reasoningText,
2290
+ planText,
2291
+ commentaryMessages,
2292
+ toolMessages: this.toolTranscriptProjection.transcriptMessages,
2293
+ lastAssistant,
2294
+ createAssistantMirrorMessage: (title, text) => this.assistantProjection.createAssistantMirrorMessage(title, text)
2295
+ });
2296
+ const turnFailed = this.completedTurn?.status === "failed";
2297
+ const promptError = this.promptError ?? this.synthesizedMissingToolResultError ?? (turnFailed ? this.completedTurn?.error?.message ?? "codex app-server turn failed" : null);
2298
+ const agentHarnessResultClassification = classifyAgentHarnessTerminalOutcome({
2299
+ assistantTexts,
2300
+ reasoningText,
2301
+ planText,
2302
+ promptError,
2303
+ turnCompleted: Boolean(this.completedTurn)
2304
+ });
2305
+ const toolMetas = this.toolProgressProjection.toolMetas;
2306
+ const hadPotentialSideEffects = toolTelemetry.didSendViaMessagingTool || (toolTelemetry.successfulCronAdds ?? 0) > 0 || this.generatedMediaProjection.hasGeneratedMedia() || this.toolProgressProjection.hasPotentialSideEffects;
2307
+ return {
2308
+ aborted: this.aborted,
2309
+ externalAbort: false,
2310
+ timedOut: false,
2311
+ idleTimedOut: false,
2312
+ timedOutDuringCompaction: false,
2313
+ timedOutDuringToolExecution: false,
2314
+ promptError,
2315
+ promptErrorSource: promptError ? this.promptErrorSource || "prompt" : null,
2316
+ sessionIdUsed: this.params.sessionId,
2317
+ ...agentHarnessResultClassification ? { agentHarnessResultClassification } : {},
2318
+ bootstrapPromptWarningSignaturesSeen: this.params.bootstrapPromptWarningSignaturesSeen,
2319
+ bootstrapPromptWarningSignature: this.params.bootstrapPromptWarningSignature,
2320
+ messagesSnapshot,
2321
+ assistantTexts,
2322
+ toolMetas,
2323
+ lastAssistant,
2324
+ currentAttemptAssistant,
2325
+ ...this.toolProgressProjection.lastToolError ? { lastToolError: this.toolProgressProjection.lastToolError } : {},
2326
+ didSendViaMessagingTool: toolTelemetry.didSendViaMessagingTool,
2327
+ didDeliverSourceReplyViaMessageTool: toolTelemetry.didDeliverSourceReplyViaMessageTool === true,
2328
+ messagingToolSentTexts: toolTelemetry.messagingToolSentTexts,
2329
+ messagingToolSentMediaUrls: toolTelemetry.messagingToolSentMediaUrls,
2330
+ messagingToolSentTargets: toolTelemetry.messagingToolSentTargets,
2331
+ messagingToolSourceReplyPayloads: toolTelemetry.messagingToolSourceReplyPayloads ?? [],
2332
+ heartbeatToolResponse: toolTelemetry.heartbeatToolResponse,
2333
+ toolMediaUrls: this.generatedMediaProjection.buildToolMediaUrls(toolTelemetry),
2334
+ hostOwnedToolMediaUrls: this.generatedMediaProjection.buildHostOwnedMediaUrls(toolTelemetry),
2335
+ toolAudioAsVoice: toolTelemetry.toolAudioAsVoice,
2336
+ successfulCronAdds: toolTelemetry.successfulCronAdds,
2337
+ cloudCodeAssistFormatError: false,
2338
+ attemptUsage: projectedUsage,
2339
+ ...this.completedCompactionCount > 0 ? { compactionCount: this.completedCompactionCount } : {},
2340
+ replayMetadata: {
2341
+ hadPotentialSideEffects,
2342
+ replaySafe: !hadPotentialSideEffects
2343
+ },
2344
+ itemLifecycle: {
2345
+ startedCount: this.activeItemIds.size + this.completedItemIds.size,
2346
+ completedCount: this.completedItemIds.size,
2347
+ activeCount: this.activeItemIds.size
2348
+ },
2349
+ yieldDetected: options?.yieldDetected || false,
2350
+ didSendDeterministicApprovalPrompt: this.eventProjection.guardianReviewCount > 0 ? false : void 0
2351
+ };
2352
+ }
2353
+ recordDynamicToolCall(params) {
2354
+ this.toolTranscriptProjection.recordDynamicToolCall(params);
2355
+ }
2356
+ recordDynamicToolResult(params) {
2357
+ this.toolProgressProjection.recordDynamicToolResult(params);
2358
+ this.toolTranscriptProjection.recordDynamicToolResult(params);
2359
+ }
2360
+ markTimedOut() {
2361
+ this.aborted = true;
2362
+ this.promptError = "codex app-server attempt timed out";
2363
+ this.promptErrorSource = "prompt";
2364
+ }
2365
+ markAborted() {
2366
+ this.aborted = true;
2367
+ this.responseUsage = void 0;
2368
+ }
2369
+ isCompacting() {
2370
+ return this.activeCompactionItemIds.size > 0;
2371
+ }
2372
+ async handleItemStarted(params) {
2373
+ const item = readItem(params.item);
2374
+ const itemId = item?.id ?? readString$5(params, "itemId");
2375
+ this.assistantProjection.recordItemStarted(item, itemId);
2376
+ if (itemId) this.activeItemIds.add(itemId);
2377
+ this.recordNativeToolOutcome(item);
2378
+ if (item?.type === "contextCompaction" && itemId) {
2379
+ this.activeCompactionItemIds.add(itemId);
2380
+ await runAgentHarnessBeforeCompactionHook({
2381
+ sessionFile: this.params.sessionFile,
2382
+ messages: await this.toolTranscriptProjection.readMirroredSessionMessages(),
2383
+ ctx: {
2384
+ runId: this.params.runId,
2385
+ agentId: this.params.agentId,
2386
+ sessionKey: this.params.sessionKey,
2387
+ sessionId: this.params.sessionId,
2388
+ workspaceDir: this.params.workspaceDir,
2389
+ messageProvider: this.params.messageProvider ?? void 0,
2390
+ trigger: this.params.trigger,
2391
+ channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
2392
+ }
2393
+ });
2394
+ this.emitAgentEvent({
2395
+ stream: "compaction",
2396
+ data: {
2397
+ phase: "start",
2398
+ backend: "codex-app-server",
2399
+ threadId: this.threadId,
2400
+ turnId: this.turnId,
2401
+ itemId
2402
+ }
2403
+ });
2404
+ }
2405
+ this.toolProgressProjection.recordToolMeta(item);
2406
+ this.eventProjection.emitStandardItemEvent({
2407
+ phase: "start",
2408
+ item
2409
+ });
2410
+ await this.eventProjection.emitNormalizedToolItemEvent({
2411
+ phase: "start",
2412
+ item
2413
+ });
2414
+ this.toolTranscriptProjection.recordNativeToolCall(item);
2415
+ this.toolProgressProjection.emitToolResultSummary(item);
2416
+ this.emitAgentEvent({
2417
+ stream: "codex_app_server.item",
2418
+ data: {
2419
+ phase: "started",
2420
+ itemId,
2421
+ type: item?.type
2422
+ }
2423
+ });
2424
+ }
2425
+ async handleItemCompleted(params) {
2426
+ const item = readItem(params.item);
2427
+ this.diagnostics.warnUnknownItemStatus(item);
2428
+ this.recordNativeToolOutcome(item);
2429
+ this.clearTerminalPresentationForNativeItem(item);
2430
+ const itemId = item?.id ?? readString$5(params, "itemId");
2431
+ if (itemId) {
2432
+ this.activeItemIds.delete(itemId);
2433
+ this.completedItemIds.add(itemId);
2434
+ }
2435
+ this.assistantProjection.recordItemCompleted(item, itemId, this.activeItemIds);
2436
+ this.reasoningProjection.recordItem(item);
2437
+ this.generatedMediaProjection.recordNative(item);
2438
+ if (item?.type === "contextCompaction" && itemId) {
2439
+ this.activeCompactionItemIds.delete(itemId);
2440
+ this.completedCompactionCount += 1;
2441
+ this.options.onContextCompacted?.();
2442
+ await runAgentHarnessAfterCompactionHook({
2443
+ sessionFile: this.params.sessionFile,
2444
+ messages: await this.toolTranscriptProjection.readMirroredSessionMessages(),
2445
+ compactedCount: -1,
2446
+ ctx: {
2447
+ runId: this.params.runId,
2448
+ agentId: this.params.agentId,
2449
+ sessionKey: this.params.sessionKey,
2450
+ sessionId: this.params.sessionId,
2451
+ workspaceDir: this.params.workspaceDir,
2452
+ messageProvider: this.params.messageProvider ?? void 0,
2453
+ trigger: this.params.trigger,
2454
+ channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
2455
+ }
2456
+ });
2457
+ this.emitAgentEvent({
2458
+ stream: "compaction",
2459
+ data: {
2460
+ phase: "end",
2461
+ backend: "codex-app-server",
2462
+ completed: true,
2463
+ threadId: this.threadId,
2464
+ turnId: this.turnId,
2465
+ itemId
2466
+ }
2467
+ });
2468
+ }
2469
+ this.toolProgressProjection.recordToolMeta(item);
2470
+ this.toolProgressProjection.rememberCommandAggregateOutputEcho(item);
2471
+ this.eventProjection.emitStandardItemEvent({
2472
+ phase: "end",
2473
+ item
2474
+ });
2475
+ await this.eventProjection.emitNormalizedToolItemEvent({
2476
+ phase: "result",
2477
+ item
2478
+ });
2479
+ this.toolTranscriptProjection.recordNativeToolCall(item);
2480
+ this.toolTranscriptProjection.recordNativeToolResult(item);
2481
+ this.toolProgressProjection.emitToolResultSummary(item);
2482
+ this.toolProgressProjection.emitToolResultOutput(item);
2483
+ this.emitAgentEvent({
2484
+ stream: "codex_app_server.item",
2485
+ data: {
2486
+ phase: "completed",
2487
+ itemId,
2488
+ type: item?.type
2489
+ }
2490
+ });
2491
+ }
2492
+ handleRawResponseCompleted(params) {
2493
+ const usage = isJsonObject(params.usage) ? params.usage : void 0;
2494
+ this.responseUsage = usage ? normalizeCodexResponseTokenUsage(usage) : void 0;
2495
+ }
2496
+ async handleTurnCompleted(params) {
2497
+ const turn = readCodexTurn(params.turn);
2498
+ if (!turn || turn.id !== this.turnId) return;
2499
+ this.completedTurn = turn;
2500
+ if (turn.status !== "completed") this.responseUsage = void 0;
2501
+ if (turn.status === "failed") {
2502
+ const usageLimitMessage = formatCodexUsageLimitErrorMessage({
2503
+ message: turn.error?.message,
2504
+ codexErrorInfo: turn.error?.codexErrorInfo,
2505
+ rateLimits: this.options.readRecentRateLimits?.()
2506
+ });
2507
+ this.promptError = usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : turn.error?.message ?? "codex app-server turn failed";
2508
+ this.promptErrorSource = "prompt";
2509
+ }
2510
+ const turnItems = turn.items ?? [];
2511
+ for (let index = turnItems.length - 1; index >= 0; index -= 1) {
2512
+ const item = turnItems[index];
2513
+ if (!item || !this.isCurrentTurnSnapshotItem(item)) continue;
2514
+ if (item?.type === "dynamicToolCall") break;
2515
+ if (shouldClearTerminalPresentationForNativeItem(item)) {
2516
+ this.clearTerminalPresentationForNativeItem(item);
2517
+ break;
2518
+ }
2519
+ }
2520
+ for (const item of turnItems) {
2521
+ this.diagnostics.warnUnknownItemStatus(item);
2522
+ this.assistantProjection.recordSnapshotItem(item);
2523
+ this.reasoningProjection.recordItem(item);
2524
+ this.generatedMediaProjection.recordNative(item);
2525
+ this.toolProgressProjection.recordToolMeta(item);
2526
+ this.toolProgressProjection.rememberCommandAggregateOutputEcho(item);
2527
+ await this.emitSnapshotOnlyNativeToolProgress(item);
2528
+ this.toolTranscriptProjection.recordNativeToolCall(item);
2529
+ this.toolTranscriptProjection.recordNativeToolResult(item);
2530
+ this.toolTranscriptProjection.emitAfterToolCallObservation(item);
2531
+ this.toolProgressProjection.emitToolResultSummary(item);
2532
+ this.toolProgressProjection.emitToolResultOutput(item);
2533
+ }
2534
+ this.assistantProjection.finalizeAnswerCandidate(turn);
2535
+ this.activeCompactionItemIds.clear();
2536
+ await this.reasoningProjection.maybeEndReasoning();
2537
+ }
2538
+ async emitSnapshotOnlyNativeToolProgress(item) {
2539
+ if (!shouldSynthesizeToolProgressForItem(item) || !this.isCurrentTurnSnapshotItem(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
2540
+ if (!this.activeItemIds.has(item.id)) {
2541
+ this.eventProjection.emitStandardItemEvent({
2542
+ phase: "start",
2543
+ item
2544
+ });
2545
+ await this.eventProjection.emitNormalizedToolItemEvent({
2546
+ phase: "start",
2547
+ item
2548
+ });
2549
+ }
2550
+ this.activeItemIds.delete(item.id);
2551
+ this.eventProjection.emitStandardItemEvent({
2552
+ phase: "end",
2553
+ item
2554
+ });
2555
+ await this.eventProjection.emitNormalizedToolItemEvent({
2556
+ phase: "result",
2557
+ item
2558
+ });
2559
+ this.completedItemIds.add(item.id);
2560
+ }
2561
+ isCurrentTurnSnapshotItem(item) {
2562
+ const itemTurnId = readItemString(item, "turnId");
2563
+ return itemTurnId === void 0 || itemTurnId === this.turnId;
2564
+ }
2565
+ async handleRawResponseItemCompleted(params) {
2566
+ const item = isJsonObject(params.item) ? params.item : void 0;
2567
+ if (!item) return;
2568
+ this.assistantProjection.handleRawResponseItemCompleted(item, this.activeItemIds);
2569
+ await this.generatedMediaProjection.recordRaw(item);
2570
+ }
2571
+ clearTerminalPresentationForNativeItem(item) {
2572
+ if (!item || this.terminalPresentationClearedItemIds.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
2573
+ const toolCallOrdinal = this.nativeToolOutcomeOrdinals.get(item.id);
2574
+ this.terminalPresentationClearedItemIds.add(item.id);
2575
+ this.params.onToolOutcome?.({
2576
+ toolName: itemName(item) ?? item.type,
2577
+ argsHash: "",
2578
+ resultHash: "",
2579
+ ...toolCallOrdinal !== void 0 ? { toolCallOrdinal } : {},
2580
+ terminalPresentation: void 0,
2581
+ presentationOnly: true
2582
+ });
2583
+ }
2584
+ formatCodexErrorMessage(params) {
2585
+ const error = isJsonObject(params.error) ? params.error : void 0;
2586
+ const usageLimitMessage = formatCodexUsageLimitErrorMessage({
2587
+ message: error ? readString$5(error, "message") : void 0,
2588
+ codexErrorInfo: error?.codexErrorInfo,
2589
+ rateLimits: this.options.readRecentRateLimits?.()
2590
+ });
2591
+ return usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : readCodexErrorNotificationMessage(params);
2592
+ }
2593
+ emitAgentEvent(event) {
2594
+ try {
2595
+ emitAgentEvent({
2596
+ runId: this.params.runId,
2597
+ stream: event.stream,
2598
+ data: event.data,
2599
+ ...this.params.sessionKey ? { sessionKey: this.params.sessionKey } : {}
2600
+ });
2601
+ } catch (error) {
2602
+ embeddedAgentLog.debug("codex app-server global agent event emit failed", { error });
2603
+ }
2604
+ try {
2605
+ const maybePromise = this.params.onAgentEvent?.(event);
2606
+ Promise.resolve(maybePromise).catch((error) => {
2607
+ embeddedAgentLog.debug("codex app-server agent event handler rejected", { error });
2608
+ });
2609
+ } catch (error) {
2610
+ embeddedAgentLog.debug("codex app-server agent event handler threw", { error });
2611
+ }
2612
+ }
2613
+ isNotificationForTurn(params) {
2614
+ const threadId = readCodexNotificationThreadId(params);
2615
+ const turnId = readCodexNotificationTurnId(params);
2616
+ return threadId === this.threadId && turnId === this.turnId;
2617
+ }
2618
+ isHookNotificationForCurrentThread(params) {
2619
+ const threadId = readString$5(params, "threadId");
2620
+ const turnId = params.turnId;
2621
+ return threadId === this.threadId && (turnId === this.turnId || turnId === null);
2622
+ }
2623
+ };
2624
+ function isHookNotificationMethod(method) {
2625
+ return method === "hook/started" || method === "hook/completed";
2626
+ }
2627
+ //#endregion
2628
+ //#region extensions/codex/src/app-server/user-input-bridge.ts
2629
+ /** Bridges Codex request_user_input calls to gateway questions and secret text prompts. */
2630
+ const DEFAULT_USER_INPUT_TIMEOUT_MS = 15 * 6e4;
198
2631
  /** Creates a per-turn bridge for pending Codex user-input requests. */
199
2632
  function createCodexUserInputBridge(params) {
200
- let pending;
201
- const resolvePending = (value) => {
202
- const current = pending;
2633
+ let sensitiveInput;
2634
+ let pendingGateway;
2635
+ const gatewayCall = params.gatewayCall ?? callGatewayTool;
2636
+ const resolveSecret = (value) => {
2637
+ const current = sensitiveInput;
203
2638
  if (!current) return;
204
- pending = void 0;
2639
+ sensitiveInput = void 0;
205
2640
  current.cleanup();
206
2641
  current.resolve(value);
207
2642
  };
208
- const resolvePendingIfCurrent = (current, value) => {
209
- if (pending !== current) return false;
210
- resolvePending(value);
2643
+ const resolveSecretIfCurrent = (current, value) => {
2644
+ if (sensitiveInput !== current) return false;
2645
+ resolveSecret(value);
211
2646
  return true;
212
2647
  };
2648
+ const cancelGateway = () => {
2649
+ pendingGateway?.abort.abort(/* @__PURE__ */ new Error("Codex user input request cancelled"));
2650
+ };
213
2651
  return {
214
2652
  async handleRequest(request) {
215
2653
  const requestParams = readUserInputParams(request.params);
216
2654
  if (!requestParams) return;
217
2655
  if (requestParams.threadId !== params.threadId || requestParams.turnId !== params.turnId) return;
218
2656
  if (requestParams.questions.length === 0) return emptyUserInputResponse();
219
- resolvePending(emptyUserInputResponse());
220
- return new Promise((resolve) => {
221
- const abortListener = () => resolvePending(emptyUserInputResponse());
2657
+ resolveSecret(emptyUserInputResponse());
2658
+ cancelGateway();
2659
+ if (requestParams.questions.some((question) => question.isSecret)) return new Promise((resolve) => {
2660
+ const abortListener = () => resolveSecret(emptyUserInputResponse());
222
2661
  const cleanup = () => params.signal?.removeEventListener("abort", abortListener);
223
- pending = {
2662
+ sensitiveInput = {
224
2663
  requestId: request.id,
225
2664
  threadId: requestParams.threadId,
226
- turnId: requestParams.turnId,
227
- itemId: requestParams.itemId,
228
2665
  questions: requestParams.questions,
229
2666
  claimed: false,
230
2667
  resolve,
@@ -232,37 +2669,68 @@ function createCodexUserInputBridge(params) {
232
2669
  };
233
2670
  params.signal?.addEventListener("abort", abortListener, { once: true });
234
2671
  if (params.signal?.aborted) {
235
- resolvePending(emptyUserInputResponse());
2672
+ resolveSecret(emptyUserInputResponse());
236
2673
  return;
237
2674
  }
238
- deliverUserInputPrompt(params.paramsForRun, requestParams.questions).catch((error) => {
239
- embeddedAgentLog.warn("failed to deliver codex user input prompt", { error });
2675
+ deliverAgentHarnessUserInputPrompt(params.paramsForRun, requestParams.questions, {
2676
+ formatText: formatCodexDisplayText,
2677
+ intro: "Codex needs input:"
2678
+ }).catch((error) => {
2679
+ embeddedAgentLog.warn("failed to deliver secret codex user input prompt", { error });
240
2680
  });
241
2681
  });
2682
+ const abort = new AbortController();
2683
+ const abortFromRun = () => abort.abort(params.signal?.reason);
2684
+ params.signal?.addEventListener("abort", abortFromRun, { once: true });
2685
+ if (params.signal?.aborted) abortFromRun();
2686
+ pendingGateway = {
2687
+ requestId: request.id,
2688
+ threadId: requestParams.threadId,
2689
+ abort
2690
+ };
2691
+ try {
2692
+ const result = await runAgentHarnessGatewayQuestion({
2693
+ questions: requestParams.questions,
2694
+ sessionKey: params.paramsForRun.sessionKey ?? params.paramsForRun.sessionId,
2695
+ agentId: params.paramsForRun.agentId,
2696
+ timeoutMs: requestParams.autoResolutionMs ?? params.paramsForRun.timeoutMs ?? DEFAULT_USER_INPUT_TIMEOUT_MS,
2697
+ gatewayCall,
2698
+ delivery: params.paramsForRun,
2699
+ promptOptions: {
2700
+ formatText: formatCodexDisplayText,
2701
+ intro: "Codex needs input:"
2702
+ },
2703
+ signal: abort.signal
2704
+ });
2705
+ return result.status === "answered" ? gatewayAnswersToCodexResponse(result.answers.answers) : emptyUserInputResponse();
2706
+ } catch (error) {
2707
+ embeddedAgentLog.warn("failed to bridge codex user input through gateway", { error });
2708
+ return emptyUserInputResponse();
2709
+ } finally {
2710
+ params.signal?.removeEventListener("abort", abortFromRun);
2711
+ if (pendingGateway?.abort === abort) pendingGateway = void 0;
2712
+ }
242
2713
  },
243
2714
  claimPendingRequest() {
244
- const current = pending;
2715
+ const current = sensitiveInput;
245
2716
  if (!current || current.claimed) return;
246
2717
  current.claimed = true;
247
2718
  return {
248
- answer: (text) => resolvePendingIfCurrent(current, buildUserInputResponse(current.questions, text)),
249
- cancel: () => resolvePendingIfCurrent(current, emptyUserInputResponse())
2719
+ answer: (text) => resolveSecretIfCurrent(current, buildUserInputResponse(current.questions, text)),
2720
+ cancel: () => resolveSecretIfCurrent(current, emptyUserInputResponse())
250
2721
  };
251
2722
  },
252
- handleQueuedMessage(text) {
253
- const current = pending;
254
- if (!current || current.claimed) return false;
255
- resolvePending(buildUserInputResponse(current.questions, text));
256
- return true;
257
- },
258
2723
  handleNotification(notification) {
259
- if (notification.method !== "serverRequest/resolved" || !pending) return;
2724
+ if (notification.method !== "serverRequest/resolved") return;
260
2725
  const notificationParams = isJsonObject(notification.params) ? notification.params : void 0;
261
2726
  const requestId = notificationParams ? readRequestId(notificationParams) : void 0;
262
- if (notificationParams && readString$3(notificationParams, "threadId") === pending.threadId && requestId !== void 0 && String(requestId) === String(pending.requestId)) resolvePending(emptyUserInputResponse());
2727
+ if (!notificationParams || requestId === void 0) return;
2728
+ if (sensitiveInput && readString$3(notificationParams, "threadId") === sensitiveInput.threadId && String(requestId) === String(sensitiveInput.requestId)) resolveSecret(emptyUserInputResponse());
2729
+ if (pendingGateway && readString$3(notificationParams, "threadId") === pendingGateway.threadId && String(requestId) === String(pendingGateway.requestId)) pendingGateway.abort.abort(/* @__PURE__ */ new Error("Codex server request resolved"));
263
2730
  },
264
2731
  cancelPending() {
265
- resolvePending(emptyUserInputResponse());
2732
+ resolveSecret(emptyUserInputResponse());
2733
+ cancelGateway();
266
2734
  }
267
2735
  };
268
2736
  }
@@ -277,7 +2745,12 @@ function readUserInputParams(value) {
277
2745
  threadId,
278
2746
  turnId,
279
2747
  itemId,
280
- questions: questionsRaw.map(readQuestion).filter((question) => Boolean(question))
2748
+ questions: questionsRaw.map((rawQuestion) => {
2749
+ const question = readQuestion(rawQuestion);
2750
+ if (question && isJsonObject(rawQuestion) && rawQuestion.multiSelect === true) question.multiSelect = true;
2751
+ return question;
2752
+ }).filter((question) => Boolean(question)),
2753
+ autoResolutionMs: typeof value.autoResolutionMs === "number" && value.autoResolutionMs > 0 ? value.autoResolutionMs : void 0
281
2754
  };
282
2755
  }
283
2756
  function readQuestion(value) {
@@ -309,15 +2782,12 @@ function readOption(value) {
309
2782
  description
310
2783
  } : void 0;
311
2784
  }
312
- async function deliverUserInputPrompt(params, questions) {
313
- await deliverAgentHarnessUserInputPrompt(params, questions, {
314
- formatText: formatCodexDisplayText,
315
- intro: "Codex needs input:"
316
- });
317
- }
318
2785
  function buildUserInputResponse(questions, inputText) {
319
2786
  return buildAgentHarnessUserInputAnswers(questions, inputText);
320
2787
  }
2788
+ function gatewayAnswersToCodexResponse(answers) {
2789
+ return { answers: Object.fromEntries(Object.entries(answers).map(([questionId, values]) => [questionId, { answers: values }])) };
2790
+ }
321
2791
  function emptyUserInputResponse() {
322
2792
  return emptyAgentHarnessUserInputAnswers();
323
2793
  }
@@ -421,11 +2891,28 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
421
2891
  const handle = {
422
2892
  kind: "embedded",
423
2893
  runId: params.runId,
424
- queueMessage: async (text, optionsLocal) => activeSteeringQueue.queue(text, optionsLocal),
2894
+ queueMessage: async (text, optionsLocal) => {
2895
+ const isInboundUserMessage = optionsLocal?.isInboundUserMessage === true;
2896
+ if (isInboundUserMessage && !optionsLocal?.images?.length) {
2897
+ if (await claimPendingAgentQuestionAnswer({
2898
+ sessionKey: params.sessionKey ?? params.sessionId,
2899
+ text
2900
+ })) return;
2901
+ } else if (isInboundUserMessage) try {
2902
+ await cancelPendingAgentQuestionForSession({
2903
+ sessionKey: params.sessionKey ?? params.sessionId,
2904
+ resolvedBy: "image-reply"
2905
+ });
2906
+ } catch (error) {
2907
+ embeddedAgentLog.warn("failed to cancel codex gateway question before image steering", { error });
2908
+ }
2909
+ await activeSteeringQueue.queue(text, optionsLocal);
2910
+ },
425
2911
  isStreaming: () => !state.completed && !runAbortController.signal.aborted,
426
2912
  isStopped: () => state.completed || state.timedOut || runAbortController.signal.aborted,
427
2913
  isAbortable: () => !terminalState.terminalOutcomeFrozen || terminalState.sharedAbortAllowedAfterTerminalOutcome,
428
2914
  isCompacting: () => projectorRef.current?.isCompacting() ?? false,
2915
+ supportsTranscriptCommitWait: true,
429
2916
  supportsQueueMessageImages: true,
430
2917
  sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
431
2918
  cancel: () => abortExplicitly("cancelled"),
@@ -488,7 +2975,7 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
488
2975
  async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRuntime, activeTurn) {
489
2976
  const { prompt, state: resourceState, trajectoryRecorder, releaseCurrentRoute, releaseSharedClientLeaseAndRetireOneShotClient, releaseSandboxExecEnvironment } = resources;
490
2977
  const { connection } = prompt.context.runtime;
491
- const { params, options, runAbortController } = connection;
2978
+ const { params, options, runAbortController, terminalState, bindingStore, bindingIdentity } = connection;
492
2979
  const { state, steeringQueueRef, userInputBridgeRef, turnWatches } = turnRuntime;
493
2980
  const { maybeEmitFastModeAutoResetBestEffort, emitLifecycleTerminal, buildLifecycleTerminalMeta } = lifecycle;
494
2981
  const { codexModelCallDiagnostics } = requestRuntime;
@@ -518,10 +3005,17 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
518
3005
  cleanup: async () => trajectoryRecorder?.flush()
519
3006
  });
520
3007
  if (!state.timedOut && !runAbortController.signal.aborted) await steeringQueueRef.current?.flushPending();
521
- if (!state.timedOut) await unsubscribeCodexThreadBestEffort(resourceState.client, {
522
- threadId: resourceState.thread.threadId,
523
- timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
524
- });
3008
+ const retainLiveIncognitoThread = terminalState.turnSucceeded && isIncognitoSessionKey(params.sessionKey);
3009
+ const bindingReleased = isIncognitoSessionKey(params.sessionKey) && !retainLiveIncognitoThread ? await bindingStore.mutate(bindingIdentity, {
3010
+ kind: "clear",
3011
+ threadId: resourceState.thread.threadId
3012
+ }) : true;
3013
+ if (!state.timedOut && !retainLiveIncognitoThread) {
3014
+ if (bindingReleased) await unsubscribeCodexThreadBestEffort(resourceState.client, {
3015
+ threadId: resourceState.thread.threadId,
3016
+ timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
3017
+ });
3018
+ }
525
3019
  userInputBridgeRef.current?.cancelPending();
526
3020
  turnWatches.clearAllTimers();
527
3021
  releaseCurrentRoute();
@@ -782,19 +3276,11 @@ function readCodexAppServerRolloutTokenSnapshotLine(line) {
782
3276
  return;
783
3277
  }
784
3278
  }
785
- function toNonNegativeInt(value) {
786
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return;
787
- return Math.floor(value);
788
- }
789
3279
  function readCompactionConfig(config) {
790
3280
  return isJsonObject(config?.agents?.defaults?.compaction) ? config.agents.defaults.compaction : void 0;
791
3281
  }
792
- function resolveCodexAppServerNativeThreadReserveTokens(config) {
793
- const compaction = readCompactionConfig(config);
794
- const reserveTokens = toNonNegativeInt(compaction?.reserveTokens);
795
- const reserveTokensFloor = toNonNegativeInt(compaction?.reserveTokensFloor);
796
- if (reserveTokens !== void 0) return Math.max(reserveTokens, reserveTokensFloor ?? CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS);
797
- return reserveTokensFloor ?? CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS;
3282
+ function resolveCodexAppServerNativeThreadReserveTokens(_config) {
3283
+ return CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS;
798
3284
  }
799
3285
  function resolveCodexAppServerNativeThreadTokenFuse(params) {
800
3286
  const projectedTurnTokens = typeof params.projectedTurnTokens === "number" && Number.isFinite(params.projectedTurnTokens) && params.projectedTurnTokens > 0 ? Math.floor(params.projectedTurnTokens) : 0;
@@ -890,6 +3376,14 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
890
3376
  }
891
3377
  //#endregion
892
3378
  //#region extensions/codex/src/app-server/run-attempt-connection.ts
3379
+ function applyStoredBindingPermissions(params) {
3380
+ if (params.execPolicyTouched || params.binding?.connectionScope === "supervision") return params.appServer;
3381
+ return {
3382
+ ...params.appServer,
3383
+ approvalPolicy: params.binding?.approvalPolicy ?? params.appServer.approvalPolicy,
3384
+ sandbox: params.binding?.sandbox ?? params.appServer.sandbox
3385
+ };
3386
+ }
893
3387
  async function prepareCodexAttemptConnection({ params, options }) {
894
3388
  const attemptStartedAt = Date.now();
895
3389
  const profilerEnabled = isCodexAppServerProfilerEnabled(params.config);
@@ -947,23 +3441,27 @@ async function prepareCodexAttemptConnection({ params, options }) {
947
3441
  bindingStore,
948
3442
  identity: bindingIdentity,
949
3443
  config: params.config
950
- })) throw new Error(`Codex session generation is no longer current: ${bindingIdentity.sessionId}`);
3444
+ })) throw createCodexSessionGenerationSupersededError(bindingIdentity.sessionId);
951
3445
  startupBinding = await bindingStore.read(bindingIdentity);
952
3446
  }
953
3447
  preDynamicStartupStages.mark("read-binding");
954
3448
  const usesSupervisionConnection = startupBinding?.connectionScope === "supervision";
955
3449
  if (usesSupervisionConnection) activeContextEngine = void 0;
956
3450
  if (usesSupervisionConnection && pluginConfig.supervision?.enabled !== true) throw new Error("Codex supervision is disabled; refusing to open a native user-home supervised session");
957
- const resolveRuntimeOptionsForBinding = (selection) => resolveCodexBindingAppServerConnection({
3451
+ const resolveRuntimeOptionsForBinding = (selection) => applyStoredBindingPermissions({
3452
+ appServer: resolveCodexBindingAppServerConnection({
3453
+ binding: startupBinding,
3454
+ pluginConfig,
3455
+ execPolicy,
3456
+ modelProvider: selection.modelProvider,
3457
+ model: selection.model,
3458
+ config: params.config,
3459
+ agentDir,
3460
+ openClawSandboxActive: sandbox?.enabled === true
3461
+ }).appServer,
958
3462
  binding: startupBinding,
959
- pluginConfig,
960
- execPolicy,
961
- modelProvider: selection.modelProvider,
962
- model: selection.model,
963
- config: params.config,
964
- agentDir,
965
- openClawSandboxActive: sandbox?.enabled === true
966
- }).appServer;
3463
+ execPolicyTouched: execPolicy.touched
3464
+ });
967
3465
  const initialStartupBindingHadInactiveThreadBootstrap = isInactiveThreadBootstrapBinding(startupBinding);
968
3466
  const preparedAuthRoute = usesSupervisionConnection ? void 0 : params.runtimePlan?.auth.modelRoute;
969
3467
  const startupAuthProfileCandidate = usesSupervisionConnection ? void 0 : preparedAuthRoute ? params.runtimePlan?.auth.forwardedAuthProfileId : params.runtimePlan?.auth.forwardedAuthProfileId ?? params.authProfileId ?? startupBinding?.authProfileId;
@@ -1039,6 +3537,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
1039
3537
  preDynamicStartupStages.mark("app-server-policy");
1040
3538
  preDynamicStartupStages.mark("native-hook-relay");
1041
3539
  const terminalState = {
3540
+ turnSucceeded: false,
1042
3541
  explicitCancellationObserved: false,
1043
3542
  explicitCancellationReason: void 0,
1044
3543
  terminalOutcomeFrozen: false,
@@ -1100,16 +3599,20 @@ async function prepareCodexAttemptConnection({ params, options }) {
1100
3599
  startupBinding,
1101
3600
  pluginAppServer: appServer
1102
3601
  };
1103
- const resolveRuntimeOptionsForCurrentBinding = (selection) => resolveCodexBindingAppServerConnection({
3602
+ const resolveRuntimeOptionsForCurrentBinding = (selection) => applyStoredBindingPermissions({
3603
+ appServer: resolveCodexBindingAppServerConnection({
3604
+ binding: mutable.startupBinding,
3605
+ pluginConfig,
3606
+ execPolicy,
3607
+ modelProvider: selection.modelProvider,
3608
+ model: selection.model,
3609
+ config: params.config,
3610
+ agentDir,
3611
+ openClawSandboxActive: sandbox?.enabled === true
3612
+ }).appServer,
1104
3613
  binding: mutable.startupBinding,
1105
- pluginConfig,
1106
- execPolicy,
1107
- modelProvider: selection.modelProvider,
1108
- model: selection.model,
1109
- config: params.config,
1110
- agentDir,
1111
- openClawSandboxActive: sandbox?.enabled === true
1112
- }).appServer;
3614
+ execPolicyTouched: execPolicy.touched
3615
+ });
1113
3616
  return {
1114
3617
  params,
1115
3618
  options,
@@ -1169,7 +3672,6 @@ const CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = /* @__PURE__ */
1169
3672
  "user.md"
1170
3673
  ]);
1171
3674
  const CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = /* @__PURE__ */ new Set([...CODEX_INHERITED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES, ...CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES]);
1172
- const CODEX_HEARTBEAT_CONTEXT_BASENAME = "heartbeat.md";
1173
3675
  const CODEX_MEMORY_CONTEXT_BASENAME = "memory.md";
1174
3676
  const CODEX_MEMORY_TOOL_NAMES = /* @__PURE__ */ new Set(["memory_search", "memory_get"]);
1175
3677
  const CODEX_BOOTSTRAP_CONTEXT_ORDER = /* @__PURE__ */ new Map([
@@ -1178,8 +3680,7 @@ const CODEX_BOOTSTRAP_CONTEXT_ORDER = /* @__PURE__ */ new Map([
1178
3680
  ["user.md", 30],
1179
3681
  ["tools.md", 40],
1180
3682
  ["bootstrap.md", 50],
1181
- ["memory.md", 60],
1182
- ["heartbeat.md", 70]
3683
+ ["memory.md", 60]
1183
3684
  ]);
1184
3685
  /** Reads mirrored Codex session history for harness hooks. */
1185
3686
  async function readMirroredSessionHistoryMessages(params) {
@@ -1280,14 +3781,12 @@ async function buildCodexWorkspaceBootstrapContext(params) {
1280
3781
  });
1281
3782
  const developerInstructionFiles = shouldInjectCodexOpenClawPromptContext(params.params) ? selectCodexWorkspaceInheritedDeveloperInstructionFiles(contextFiles) : [];
1282
3783
  const turnScopedDeveloperInstructionFiles = shouldInjectCodexOpenClawPromptContext(params.params) ? selectCodexWorkspaceTurnScopedDeveloperInstructionFiles(contextFiles) : [];
1283
- const heartbeatReferenceFiles = selectCodexWorkspaceHeartbeatReferenceFiles(contextFiles);
1284
3784
  return {
1285
3785
  bootstrapFiles,
1286
3786
  contextFiles,
1287
3787
  promptContextFiles,
1288
3788
  developerInstructionFiles,
1289
3789
  turnScopedDeveloperInstructionFiles,
1290
- heartbeatReferenceFiles,
1291
3790
  memoryReferenceFiles,
1292
3791
  memoryToolRoutedBootstrapFiles,
1293
3792
  memoryToolNames: [...params.memoryToolNames],
@@ -1295,15 +3794,15 @@ async function buildCodexWorkspaceBootstrapContext(params) {
1295
3794
  promptContext: renderCodexWorkspaceBootstrapPromptContext(promptContextFiles),
1296
3795
  developerInstructions: renderCodexWorkspaceThreadDeveloperInstructions(developerInstructionFiles),
1297
3796
  turnScopedDeveloperInstructions: renderCodexWorkspaceCollaborationDeveloperInstructions(turnScopedDeveloperInstructionFiles),
1298
- memoryCollaborationInstructions: shouldInjectCodexOpenClawPromptContext(params.params) ? renderCodexWorkspaceMemoryCollaborationInstructions({
3797
+ memoryCollaborationInstructions: shouldInjectCodexOpenClawPromptContext(params.params) ? await renderCodexWorkspaceMemoryCollaborationInstructions({
1299
3798
  files: memoryReferenceFiles,
1300
3799
  toolNames: params.memoryToolNames,
1301
3800
  memoryToolRouted: memoryToolsAvailable,
1302
3801
  citationsMode: params.params.config?.memory?.citations,
1303
3802
  agentId: params.params.agentId ?? params.sessionAgentId,
1304
- agentSessionKey: params.sessionKey
1305
- }) : void 0,
1306
- heartbeatCollaborationInstructions: renderCodexWorkspaceHeartbeatReference(heartbeatReferenceFiles)
3803
+ agentSessionKey: params.sessionKey,
3804
+ sandboxed: params.sandboxed
3805
+ }) : void 0
1307
3806
  };
1308
3807
  } catch (error) {
1309
3808
  embeddedAgentLog.warn("failed to load codex workspace bootstrap instructions", { error });
@@ -1425,14 +3924,9 @@ function buildCodexBootstrapInjectionStats(params) {
1425
3924
  const injected = memoryToolRoutedFile ? void 0 : readCodexIndexedContextFileContent(injectedIndex, pathValue, fileName) ?? readCodexIndexedContextFileContent(developerInstructionIndex, pathValue, fileName);
1426
3925
  let injectedChars = memoryToolRoutedFile ? 0 : injected?.length ?? 0;
1427
3926
  let truncated = memoryToolRoutedFile ? false : !file.missing && injectedChars < rawChars;
1428
- if (injected === void 0) {
1429
- if (CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName)) {
1430
- injectedChars = rawChars;
1431
- truncated = false;
1432
- } else if (baseName === CODEX_HEARTBEAT_CONTEXT_BASENAME) {
1433
- injectedChars = 0;
1434
- truncated = false;
1435
- }
3927
+ if (injected === void 0 && CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName)) {
3928
+ injectedChars = rawChars;
3929
+ truncated = false;
1436
3930
  }
1437
3931
  return {
1438
3932
  name: displayName,
@@ -1556,7 +4050,7 @@ function renderCodexWorkspaceBootstrapPromptContext(contextFiles) {
1556
4050
  const files = contextFiles;
1557
4051
  if (files.length === 0) return;
1558
4052
  const lines = [
1559
- "OpenClaw loaded these user-editable workspace files for the current turn. Codex loads AGENTS.md natively. TOOLS.md is provided as inherited Codex developer instructions. SOUL.md, IDENTITY.md, and USER.md are provided as turn-scoped collaboration instructions so native Codex subagents do not inherit them. HEARTBEAT.md is handled by heartbeat collaboration-mode guidance. Those files are not repeated here.",
4053
+ "OpenClaw loaded these user-editable workspace files for the current turn. Codex loads AGENTS.md natively. TOOLS.md is provided as inherited Codex developer instructions. 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.",
1560
4054
  "",
1561
4055
  "# Project Context",
1562
4056
  "",
@@ -1570,7 +4064,7 @@ function selectCodexWorkspacePromptContextFiles(contextFiles, options = {}) {
1570
4064
  const excludeMemory = options.excludeMemory ?? true;
1571
4065
  return contextFiles.filter((file) => {
1572
4066
  const baseName = getCodexContextFileBasename(file.path);
1573
- return baseName && !CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName) && !CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES.has(baseName) && baseName !== CODEX_HEARTBEAT_CONTEXT_BASENAME && (!excludeMemory || !isCodexWorkspaceRootMemoryContextFile({
4067
+ return baseName && !CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName) && !CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES.has(baseName) && (!excludeMemory || !isCodexWorkspaceRootMemoryContextFile({
1574
4068
  file,
1575
4069
  workspaceDir: options.memoryWorkspaceDir
1576
4070
  })) && !isMissingCodexBootstrapContextFile(file);
@@ -1617,22 +4111,6 @@ function renderCodexWorkspaceDeveloperInstructions(params) {
1617
4111
  if (wrapperTag) lines.push(`</${wrapperTag}>`);
1618
4112
  return lines.join("\n").trim();
1619
4113
  }
1620
- function selectCodexWorkspaceHeartbeatReferenceFiles(contextFiles) {
1621
- return contextFiles.filter((file) => {
1622
- return getCodexContextFileBasename(file.path) === CODEX_HEARTBEAT_CONTEXT_BASENAME && !isMissingCodexBootstrapContextFile(file) && file.content.trim().length > 0;
1623
- }).toSorted(compareCodexContextFiles);
1624
- }
1625
- function renderCodexWorkspaceHeartbeatReference(files) {
1626
- if (files.length === 0) return;
1627
- const lines = [
1628
- "## OpenClaw Heartbeat Workspace",
1629
- "",
1630
- "HEARTBEAT.md exists in the active agent workspace. Read it before proceeding with this heartbeat, then decide what action is appropriate.",
1631
- ""
1632
- ];
1633
- for (const file of files) lines.push(`- ${file.path}`);
1634
- return lines.join("\n").trim();
1635
- }
1636
4114
  function selectCodexWorkspaceMemoryReferenceFiles(params) {
1637
4115
  return params.bootstrapFiles.filter((file) => {
1638
4116
  return isCodexWorkspaceRootMemoryBootstrapFile({
@@ -1656,24 +4134,26 @@ function renderCodexWorkspaceMemoryReference(params) {
1656
4134
  for (const file of params.files) lines.push(`- ${file.path}`);
1657
4135
  return lines.join("\n").trim();
1658
4136
  }
1659
- function renderCodexWorkspaceMemoryCollaborationInstructions(params) {
1660
- const sections = [params.memoryToolRouted ? renderCodexMemoryRecallInstructions({
4137
+ async function renderCodexWorkspaceMemoryCollaborationInstructions(params) {
4138
+ const sections = [params.memoryToolRouted ? await renderCodexMemoryRecallInstructions({
1661
4139
  toolNames: params.toolNames,
1662
4140
  citationsMode: params.citationsMode,
1663
4141
  agentId: params.agentId,
1664
- agentSessionKey: params.agentSessionKey
4142
+ agentSessionKey: params.agentSessionKey,
4143
+ sandboxed: params.sandboxed
1665
4144
  }) : void 0, renderCodexWorkspaceMemoryReference({
1666
4145
  files: params.files,
1667
4146
  toolNames: params.toolNames
1668
4147
  })].filter(isNonEmptyString$1);
1669
4148
  return sections.length > 0 ? sections.join("\n\n") : void 0;
1670
4149
  }
1671
- function renderCodexMemoryRecallInstructions(params) {
1672
- const memoryPrompt = buildMemorySystemPromptAddition({
4150
+ async function renderCodexMemoryRecallInstructions(params) {
4151
+ const memoryPrompt = await prepareMemorySystemPromptAddition({
1673
4152
  availableTools: new Set(params.toolNames),
1674
4153
  citationsMode: params.citationsMode,
1675
4154
  agentId: params.agentId,
1676
- agentSessionKey: params.agentSessionKey
4155
+ agentSessionKey: params.agentSessionKey,
4156
+ sandboxed: params.sandboxed
1677
4157
  });
1678
4158
  if (!memoryPrompt) return;
1679
4159
  return [memoryPrompt, renderCodexMemoryToolSearchBridge(params.toolNames)].filter(isNonEmptyString$1).join("\n").trim();
@@ -1870,7 +4350,7 @@ function buildCodexAppServerTimeoutDiagnostics(params) {
1870
4350
  //#region extensions/codex/src/app-server/run-attempt-context.ts
1871
4351
  async function prepareCodexAttemptContext(runtime, attemptTools) {
1872
4352
  const { connection, runtimeParams, activeSessionId, activeSessionFile, buildActiveRunAttemptParams, effectiveContextWindowInfo, effectiveContextTokenBudget, effectiveRuntimeProviderId, effectiveRuntimeModelId, hookChannelId } = runtime;
1873
- const { params, sessionAgentId, contextSessionKey, activeContextEngine, initialStartupBindingHadInactiveThreadBootstrap, sandboxSessionKey, effectiveWorkspace, effectiveCwd, agentDir, usesSupervisionConnection, resolvedWorkspace, initialInactiveThreadBootstrapBindingForcedFreshStart } = connection;
4353
+ const { params, sessionAgentId, contextSessionKey, activeContextEngine, initialStartupBindingHadInactiveThreadBootstrap, sandboxSessionKey, effectiveWorkspace, effectiveCwd, agentDir, usesSupervisionConnection, resolvedWorkspace, initialInactiveThreadBootstrapBindingForcedFreshStart, sandbox } = connection;
1874
4354
  const { toolBridge } = attemptTools;
1875
4355
  const activeTranscriptTarget = {
1876
4356
  agentId: sessionAgentId,
@@ -1934,7 +4414,8 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
1934
4414
  effectiveWorkspace,
1935
4415
  sessionKey: contextSessionKey,
1936
4416
  sessionAgentId,
1937
- memoryToolNames: getCodexWorkspaceMemoryToolNames(toolBridge.availableSpecs)
4417
+ memoryToolNames: getCodexWorkspaceMemoryToolNames(toolBridge.availableSpecs),
4418
+ sandboxed: sandbox?.enabled === true
1938
4419
  });
1939
4420
  const baseDeveloperInstructions = joinPresentSections(buildDeveloperInstructions(runtimeParams, { dynamicTools: toolBridge.availableSpecs }), workspaceBootstrapContext.developerInstructions);
1940
4421
  return {
@@ -1968,7 +4449,7 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
1968
4449
  },
1969
4450
  codexContextProjectionMaxChars: resolveCodexContextEngineProjectionMaxChars({
1970
4451
  contextTokenBudget: effectiveContextTokenBudget,
1971
- reserveTokens: resolveCodexContextEngineProjectionReserveTokens({ config: params.config })
4452
+ reserveTokens: resolveCodexContextEngineProjectionReserveTokens()
1972
4453
  })
1973
4454
  };
1974
4455
  }
@@ -2183,6 +4664,67 @@ function isInvalidCodexImagePayloadError(message) {
2183
4664
  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);
2184
4665
  }
2185
4666
  //#endregion
4667
+ //#region extensions/codex/src/app-server/settled-turn-context.ts
4668
+ function collectUniqueMessageIdentities(messages) {
4669
+ const identities = /* @__PURE__ */ new Map();
4670
+ for (const [index, message] of messages.entries()) {
4671
+ const identity = readMirrorIdentity(message);
4672
+ if (!identity) continue;
4673
+ if (identities.has(identity)) return;
4674
+ identities.set(identity, index);
4675
+ }
4676
+ return identities;
4677
+ }
4678
+ /** Freezes one complete active transcript branch through the settled tool-result boundary. */
4679
+ function buildCodexSettledTurnFinalizationContext(params) {
4680
+ const boundaryMessage = params.settledMessages.findLast((message) => message.role === "toolResult");
4681
+ const boundaryIdentity = boundaryMessage ? readMirrorIdentity(boundaryMessage) : void 0;
4682
+ if (!boundaryMessage || !boundaryIdentity || !boundaryIdentity.startsWith(`${params.turnId}:tool:`)) return;
4683
+ const settledBoundaryIndex = params.settledMessages.indexOf(boundaryMessage);
4684
+ const requiredIdentities = params.settledMessages.slice(0, settledBoundaryIndex + 1).map(readMirrorIdentity);
4685
+ if (requiredIdentities.length === 0 || requiredIdentities.some((identity) => !identity) || new Set(requiredIdentities).size !== requiredIdentities.length || !requiredIdentities.includes(`${params.turnId}:prompt`)) return;
4686
+ const historyIdentities = collectUniqueMessageIdentities(params.historyMessages);
4687
+ const mirroredIdentities = collectUniqueMessageIdentities(params.mirroredMessages);
4688
+ if (!historyIdentities || !mirroredIdentities) return;
4689
+ const mirroredBoundaryIndex = mirroredIdentities.get(boundaryIdentity);
4690
+ if (mirroredBoundaryIndex === void 0) return;
4691
+ const mirroredThroughBoundary = params.mirroredMessages.slice(0, mirroredBoundaryIndex + 1);
4692
+ if (mirroredThroughBoundary.length !== requiredIdentities.length || mirroredThroughBoundary.some((message, index) => readMirrorIdentity(message) !== requiredIdentities[index])) return;
4693
+ const historyBoundaryIndex = historyIdentities.get(boundaryIdentity);
4694
+ if (historyBoundaryIndex === void 0) return;
4695
+ let previousHistoryIndex = -1;
4696
+ for (const mirroredMessage of mirroredThroughBoundary) {
4697
+ const identity = readMirrorIdentity(mirroredMessage);
4698
+ const historyIndex = identity ? historyIdentities.get(identity) : void 0;
4699
+ const historyMessage = historyIndex === void 0 ? void 0 : params.historyMessages[historyIndex];
4700
+ if (historyIndex === void 0 || historyIndex <= previousHistoryIndex || historyIndex > historyBoundaryIndex || !historyMessage || serializeCodexMirrorSourceEvidence(historyMessage) !== serializeCodexMirrorSourceEvidence(mirroredMessage)) return;
4701
+ previousHistoryIndex = historyIndex;
4702
+ }
4703
+ return {
4704
+ source: "openclaw-transcript",
4705
+ messages: Object.freeze(structuredClone(params.historyMessages.slice(0, historyBoundaryIndex + 1)))
4706
+ };
4707
+ }
4708
+ /** Reads and freezes the current active transcript branch after mirroring has settled. */
4709
+ async function captureCodexSettledTurnFinalizationContext(params) {
4710
+ try {
4711
+ const historyMessages = await readCodexMirroredSessionHistoryMessages(params);
4712
+ if (!historyMessages) return;
4713
+ return buildCodexSettledTurnFinalizationContext({
4714
+ historyMessages,
4715
+ mirroredMessages: params.mirroredMessages,
4716
+ settledMessages: params.settledMessages,
4717
+ turnId: params.turnId
4718
+ });
4719
+ } catch (error) {
4720
+ embeddedAgentLog.warn("codex settled-turn finalization context capture failed", {
4721
+ error: formatErrorMessage(error),
4722
+ turnId: params.turnId
4723
+ });
4724
+ return;
4725
+ }
4726
+ }
4727
+ //#endregion
2186
4728
  //#region extensions/codex/src/app-server/trajectory.ts
2187
4729
  const SENSITIVE_FIELD_RE = /(?:authorization|cookie|credential|key|password|passwd|secret|token)/iu;
2188
4730
  const PRIVATE_PAYLOAD_FIELD_RE = /(?:image|screenshot|attachment|fileData|dataUri)/iu;
@@ -2476,6 +5018,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
2476
5018
  const turnSucceeded = !finalAborted && !effectiveTimedOut && (finalPromptError === null || finalPromptError === void 0) && (completedTurnStatus === "completed" || recoveredTurnWatchTimeout || completedWithoutTerminalNotification);
2477
5019
  if (settleCodexSourceReplyFinality(toolBridge.telemetry, turnSucceeded)) result.agentHarnessResultClassification = void 0;
2478
5020
  const attemptSucceeded = turnSucceeded && result.agentHarnessResultClassification === void 0;
5021
+ terminalState.turnSucceeded = turnSucceeded;
2479
5022
  terminalState.sharedAbortAllowedAfterTerminalOutcome = shouldKeepCodexSharedAbortOpen({
2480
5023
  trigger: params.trigger,
2481
5024
  result,
@@ -2495,7 +5038,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
2495
5038
  if (modelCallFailureKind) codexModelCallDiagnostics.emitError(finalPromptError ?? "codex app-server attempt interrupted", { failureKind: modelCallFailureKind });
2496
5039
  else if (finalPromptError) codexModelCallDiagnostics.emitError(finalPromptError);
2497
5040
  else codexModelCallDiagnostics.emitCompleted(result);
2498
- const assistantTranscriptOwned = await codexTranscriptMirrorRuntime.mirrorBestEffort({
5041
+ const mirrorOutcome = await codexTranscriptMirrorRuntime.mirrorBestEffort({
2499
5042
  params,
2500
5043
  agentId: sessionAgentId,
2501
5044
  notifyUserMessagePersisted,
@@ -2505,6 +5048,18 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
2505
5048
  threadId: resourceState.thread.threadId,
2506
5049
  turnId: activeTurnId
2507
5050
  });
5051
+ const { assistantTranscriptOwned, assistantTranscriptIdempotencyKey } = mirrorOutcome;
5052
+ const shouldCaptureSettledTurnFinalizationContext = turnSucceeded && result.assistantTexts.every((text) => !text.trim()) && result.messagesSnapshot.some((message) => message.role === "toolResult");
5053
+ const settledTurnFinalizationContext = shouldCaptureSettledTurnFinalizationContext ? await captureCodexSettledTurnFinalizationContext({
5054
+ ...activeTranscriptTarget,
5055
+ mirroredMessages: mirrorOutcome.mirroredMessages,
5056
+ settledMessages: result.messagesSnapshot,
5057
+ turnId: activeTurnId
5058
+ }) : void 0;
5059
+ if (shouldCaptureSettledTurnFinalizationContext && !settledTurnFinalizationContext) embeddedAgentLog.warn("codex settled-turn finalization context is unavailable", {
5060
+ threadId: resourceState.thread.threadId,
5061
+ turnId: activeTurnId
5062
+ });
2508
5063
  if (activeContextEngine) {
2509
5064
  const contextEnginePluginId = resolveContextEngineOwnerPluginId(activeContextEngine);
2510
5065
  const isHeartbeat = params.bootstrapContextRunKind === "heartbeat" || params.bootstrapContextRunKind === "commitment-only";
@@ -2635,6 +5190,8 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
2635
5190
  ...codexAppServerFailure ? { codexAppServerFailure } : {},
2636
5191
  ...promptTimeoutOutcome ? { promptTimeoutOutcome } : {},
2637
5192
  ...assistantTranscriptOwned ? { assistantTranscriptOwned: true } : {},
5193
+ ...assistantTranscriptIdempotencyKey ? { assistantTranscriptIdempotencyKey } : {},
5194
+ ...settledTurnFinalizationContext ? { settledTurnFinalizationContext } : {},
2638
5195
  ...resourceState.runtimeArtifact ? { runtimeArtifact: resourceState.runtimeArtifact } : {},
2639
5196
  ...!finalAborted && !effectiveTimedOut && !finalPromptError && preparedAuthBinding ? { authBindingFingerprint: preparedAuthBinding.fingerprint } : {},
2640
5197
  systemPromptReport
@@ -2851,7 +5408,7 @@ function createCodexAttemptLifecycleController(resources, turnRuntime) {
2851
5408
  startedAt: attemptStartedAt,
2852
5409
  endedAt: Date.now(),
2853
5410
  ...data,
2854
- ...params.deferTerminalLifecycle ?? params.deferTerminalLifecycleEnd ? { phase: "finishing" } : {}
5411
+ ...params.deferTerminalLifecycle ? { phase: "finishing" } : {}
2855
5412
  }
2856
5413
  });
2857
5414
  state.lifecycleTerminalEmitted = true;
@@ -2959,7 +5516,7 @@ var ClientTurnRouter = class {
2959
5516
  this.nativeTurnCompletionWatchers = /* @__PURE__ */ new Map();
2960
5517
  this.disposed = false;
2961
5518
  client.addNotificationHandler((notification) => this.routeNotification(notification));
2962
- client.addRequestHandler((request) => this.routeRequest(request));
5519
+ client.addRequestHandler((request, signal) => this.routeRequest(request, signal));
2963
5520
  client.addCloseHandler(() => this.dispose());
2964
5521
  }
2965
5522
  reserveThread(options) {
@@ -2974,7 +5531,8 @@ var ClientTurnRouter = class {
2974
5531
  gate: "open",
2975
5532
  pending: [],
2976
5533
  notificationTail: Promise.resolve(),
2977
- nativeTurnCompleted: false
5534
+ nativeTurnCompleted: false,
5535
+ ignoredTurnNotificationKeys: /* @__PURE__ */ new Set()
2978
5536
  };
2979
5537
  this.routes.set(threadId, route);
2980
5538
  if (options.onNotification || options.onRequest) this.activateNow(route, options);
@@ -3057,6 +5615,7 @@ var ClientTurnRouter = class {
3057
5615
  this.assertRoute(route);
3058
5616
  if (route.gate !== "open") throw new Error(`codex app-server thread route cannot arm from ${route.gate}`);
3059
5617
  route.gate = "armed";
5618
+ route.ignoredTurnNotificationKeys.clear();
3060
5619
  route.nativeTurnCompleted = false;
3061
5620
  route.binding = deferred();
3062
5621
  }
@@ -3106,7 +5665,10 @@ var ClientTurnRouter = class {
3106
5665
  }
3107
5666
  const handler = route.handlers.onNotification;
3108
5667
  if (!handler) return;
3109
- if (route.gate === "bound" && scope.turnId && scope.turnId !== route.turnId) return;
5668
+ if (route.gate === "bound" && scope.turnId && scope.turnId !== route.turnId) {
5669
+ this.warnDroppedStaleTurnNotification(route, notification, routeScope);
5670
+ return;
5671
+ }
3110
5672
  if (route.gate === "armed") {
3111
5673
  this.bufferNotification(route, notification, routeScope, receivedAtMs);
3112
5674
  return;
@@ -3115,45 +5677,66 @@ var ClientTurnRouter = class {
3115
5677
  this.enqueueNotification(route, handler, notification, routeScope);
3116
5678
  return route.notificationTail;
3117
5679
  }
3118
- async routeRequest(request) {
3119
- if (this.disposed) return;
5680
+ async routeRequest(request, signal = new AbortController().signal) {
5681
+ if (this.disposed || signal.aborted) return;
3120
5682
  const scope = readScope(request.params);
3121
5683
  if (!scope.threadId) return;
3122
5684
  const route = this.routes.get(scope.threadId);
3123
5685
  if (!route || route.released) return;
3124
- if (!route.handlers) await route.activated.promise;
3125
- if (route.released || !route.handlers) return;
5686
+ if (!route.handlers && !await waitForPromiseOrAbort(route.activated.promise, signal)) return;
5687
+ if (signal.aborted || route.released || !route.handlers) return;
3126
5688
  const handler = route.handlers.onRequest;
3127
5689
  if (!handler) return;
3128
5690
  while (route.gate === "armed") {
3129
- await route.binding?.promise;
3130
- if (route.released) return;
5691
+ const binding = route.binding?.promise;
5692
+ if (!binding || !await waitForPromiseOrAbort(binding, signal)) return;
5693
+ if (signal.aborted || route.released) return;
3131
5694
  }
3132
5695
  if (route.gate === "bound") {
3133
5696
  if (scope.turnId && scope.turnId !== route.turnId) return;
3134
5697
  if (route.released) return;
3135
5698
  }
3136
- await this.waitForNotifications(route);
3137
- if (route.released) return;
5699
+ if (!await waitForPromiseOrAbort(this.waitForNotifications(route), signal)) return;
5700
+ if (signal.aborted || route.released) return;
3138
5701
  try {
3139
5702
  const result = await handler(request, {
3140
5703
  threadId: scope.threadId,
3141
5704
  ...scope.turnId ? { turnId: scope.turnId } : {}
3142
- });
3143
- return route.released ? void 0 : result;
5705
+ }, signal);
5706
+ return signal.aborted || route.released ? void 0 : result;
3144
5707
  } catch (error) {
3145
- if (route.released) return;
5708
+ if (signal.aborted || route.released) return;
3146
5709
  throw error;
3147
5710
  }
3148
5711
  }
3149
5712
  flushNotifications(route) {
3150
5713
  const handler = route.handlers?.onNotification;
3151
5714
  if (!handler) return;
3152
- for (const pending of route.pending.splice(0)) if (!pending.scope.turnId || route.gate !== "bound" || pending.scope.turnId === route.turnId) {
5715
+ for (const pending of route.pending.splice(0)) {
5716
+ if (route.gate === "bound" && pending.scope.turnId && pending.scope.turnId !== route.turnId) {
5717
+ this.warnDroppedStaleTurnNotification(route, pending.notification, pending.scope);
5718
+ continue;
5719
+ }
3153
5720
  route.handlers?.onNotificationReceived?.(pending.notification, pending.scope, pending.receivedAtMs);
3154
5721
  this.enqueueNotification(route, handler, pending.notification, pending.scope);
3155
5722
  }
3156
5723
  }
5724
+ warnDroppedStaleTurnNotification(route, notification, scope) {
5725
+ if (notification.method === "turn/completed" || !scope.turnId || !route.turnId) return;
5726
+ const eventKind = redactCodexEventKind(notification.method);
5727
+ const key = JSON.stringify([notification.method, scope.turnId]);
5728
+ if (route.ignoredTurnNotificationKeys.has(key)) return;
5729
+ route.ignoredTurnNotificationKeys.add(key);
5730
+ embeddedAgentLog.warn("codex app-server notification ignored for inactive turn", {
5731
+ eventKind,
5732
+ activeThreadId: route.threadId,
5733
+ activeTurnId: route.turnId,
5734
+ threadId: scope.threadId,
5735
+ turnId: scope.turnId,
5736
+ matchesActiveThread: true,
5737
+ matchesActiveTurn: false
5738
+ });
5739
+ }
3157
5740
  bufferNotification(route, notification, scope, receivedAtMs) {
3158
5741
  if (route.pending.length < DEFAULT_PREBIND_NOTIFICATION_LIMIT) {
3159
5742
  route.pending.push({
@@ -3242,6 +5825,20 @@ function isCodexTerminalTurnNotification(notification) {
3242
5825
  if (notification.method === "turn/completed") return true;
3243
5826
  return notification.method === "error" && isJsonObject(notification.params) && notification.params.willRetry === false;
3244
5827
  }
5828
+ async function waitForPromiseOrAbort(promise, signal) {
5829
+ if (signal.aborted) return false;
5830
+ let removeAbort;
5831
+ try {
5832
+ return await Promise.race([promise.then(() => true), new Promise((resolve) => {
5833
+ const onAbort = () => resolve(false);
5834
+ signal.addEventListener("abort", onAbort, { once: true });
5835
+ removeAbort = () => signal.removeEventListener("abort", onAbort);
5836
+ if (signal.aborted) onAbort();
5837
+ })]);
5838
+ } finally {
5839
+ removeAbort?.();
5840
+ }
5841
+ }
3245
5842
  function deferred() {
3246
5843
  let resolve;
3247
5844
  return {
@@ -3415,7 +6012,7 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
3415
6012
  async function prepareCodexAttemptPrompt(context) {
3416
6013
  const { runtime, attemptTools, historyState, hookContext, workspaceBootstrapContext, baseDeveloperInstructions, openClawPromptContext, skillsCollaborationInstructions, promptState, codexContextProjectionMaxChars } = context;
3417
6014
  const { connection, buildActiveRunAttemptParams, effectiveContextTokenBudget, effectiveRuntimeModelId, effectiveRuntimeProviderId } = runtime;
3418
- const { params, activeContextEngine, usesSupervisionConnection, mutable, isInactiveThreadBootstrapBinding, bindingStore, bindingIdentity, agentDir, appServer, contextSessionKey, effectiveWorkspace } = connection;
6015
+ const { params, activeContextEngine, usesSupervisionConnection, mutable, isInactiveThreadBootstrapBinding, bindingStore, bindingIdentity, agentDir, appServer, contextSessionKey, effectiveWorkspace, sandbox } = connection;
3419
6016
  const { toolBridge } = attemptTools;
3420
6017
  const applyFreshThreadContinuityProjection = () => {
3421
6018
  const projection = projectContextEngineAssemblyForCodex({
@@ -3438,6 +6035,7 @@ async function prepareCodexAttemptPrompt(context) {
3438
6035
  tokenBudget: effectiveContextTokenBudget,
3439
6036
  availableTools: new Set(flattenCodexDynamicToolFunctions(toolBridge.availableSpecs).map((tool) => tool.name).filter(isNonEmptyString)),
3440
6037
  citationsMode: params.config?.memory?.citations,
6038
+ sandboxed: sandbox?.enabled === true,
3441
6039
  modelId: effectiveRuntimeModelId,
3442
6040
  contextEngineHostSupport: CODEX_APP_SERVER_CONTEXT_ENGINE_HOST,
3443
6041
  providerId: effectiveRuntimeProviderId,
@@ -3499,10 +6097,9 @@ async function prepareCodexAttemptPrompt(context) {
3499
6097
  const buildPromptFromCurrentInputs = () => resolveAgentHarnessBeforePromptBuildResult({
3500
6098
  prompt: prependCurrentInboundContext(promptState.promptText, params.currentInboundContext),
3501
6099
  developerInstructions: promptState.developerInstructions,
3502
- messages: codexModelInputHistoryMessages,
6100
+ messages: structuredClone(historyState.messages),
3503
6101
  ctx: hookContext,
3504
- bootstrapContextRunKind: params.bootstrapContextRunKind,
3505
- ..."beforeAgentStartResult" in params ? { beforeAgentStartResult: params.beforeAgentStartResult } : {}
6102
+ bootstrapContextRunKind: params.bootstrapContextRunKind
3506
6103
  });
3507
6104
  const resolveShiftedPromptInputRange = (prompt, promptInputRange, turnPromptText) => {
3508
6105
  if (!promptInputRange || promptInputRange.start < 0 || promptInputRange.end < promptInputRange.start || promptInputRange.end > prompt.length || !turnPromptText.endsWith(prompt)) return;
@@ -3553,8 +6150,7 @@ async function prepareCodexAttemptPrompt(context) {
3553
6150
  const buildRenderedCodexDeveloperInstructions = () => joinPresentSections(turnState.promptBuild.developerInstructions, buildTurnCollaborationMode(params, {
3554
6151
  turnScopedDeveloperInstructions: workspaceBootstrapContext.turnScopedDeveloperInstructions,
3555
6152
  skillsCollaborationInstructions,
3556
- memoryCollaborationInstructions: workspaceBootstrapContext.memoryCollaborationInstructions,
3557
- heartbeatCollaborationInstructions: workspaceBootstrapContext.heartbeatCollaborationInstructions
6153
+ memoryCollaborationInstructions: workspaceBootstrapContext.memoryCollaborationInstructions
3558
6154
  }).settings.developer_instructions ?? void 0);
3559
6155
  const rebuildCodexPromptBuildFromCurrentProjection = async () => {
3560
6156
  turnState.promptBuild = await buildPromptFromCurrentInputs();
@@ -5517,7 +8113,7 @@ async function readFile(execServer, params) {
5517
8113
  const fsBridge = requireFsBridge(execServer);
5518
8114
  const stat = await fsBridge.stat({ filePath });
5519
8115
  if (!stat) throw new JsonRpcProtocolError(JSON_RPC_NOT_FOUND, "file not found");
5520
- if (stat.type === "file" && stat.size > CODEX_SANDBOX_EXEC_SERVER_MAX_READ_FILE_BYTES) throw new Error(`file is too large to read through Codex sandbox exec-server: ${stat.size} bytes`);
8116
+ assertSandboxFileReadWithinLimit(stat);
5521
8117
  return { dataBase64: (await fsBridge.readFile({ filePath })).toString("base64") };
5522
8118
  }
5523
8119
  /** Writes base64 data to an existing sandbox directory after write-policy checks. */
@@ -5658,6 +8254,15 @@ async function copySandboxPath(execServer, params) {
5658
8254
  }
5659
8255
  return;
5660
8256
  }
8257
+ if (sourceStat.type === "file" && fsBridge.copyFile) {
8258
+ await fsBridge.copyFile({
8259
+ sourcePath: params.sourcePath,
8260
+ destinationPath: params.destinationPath,
8261
+ mkdir: true
8262
+ });
8263
+ return;
8264
+ }
8265
+ assertSandboxFileReadWithinLimit(sourceStat);
5661
8266
  const data = await fsBridge.readFile({ filePath: params.sourcePath });
5662
8267
  await fsBridge.writeFile({
5663
8268
  filePath: params.destinationPath,
@@ -5665,6 +8270,9 @@ async function copySandboxPath(execServer, params) {
5665
8270
  mkdir: true
5666
8271
  });
5667
8272
  }
8273
+ function assertSandboxFileReadWithinLimit(stat) {
8274
+ if (stat.type === "file" && stat.size > CODEX_SANDBOX_EXEC_SERVER_MAX_READ_FILE_BYTES) throw new Error(`file is too large to read through Codex sandbox exec-server: ${stat.size} bytes`);
8275
+ }
5668
8276
  function metadataResponse(stat) {
5669
8277
  return {
5670
8278
  isDirectory: stat?.type === "directory",
@@ -6662,16 +9270,17 @@ function prepareCodexAttemptResources(prompt) {
6662
9270
  const retireSharedCodexClientForOneShotCleanup = async () => {
6663
9271
  if (params.cleanupBundleMcpOnRunEnd !== true || state.sharedCodexClientRetiredForOneShotCleanup) return;
6664
9272
  state.sharedCodexClientRetiredForOneShotCleanup = true;
6665
- const retired = retireSharedCodexAppServerClientIfCurrent(state.client);
6666
- embeddedAgentLog.info("codex app-server one-shot cleanup retired shared client", {
9273
+ const retired = clearSharedCodexAppServerClientIfCurrentAndUnclaimed(state.client);
9274
+ embeddedAgentLog.info("codex app-server one-shot cleanup checked shared client retirement", {
6667
9275
  runId: params.runId,
6668
9276
  sessionId: params.sessionId,
6669
9277
  sessionKey: params.sessionKey,
6670
- activeLeases: retired?.activeLeases ?? null,
6671
- closed: retired?.closed ?? false,
6672
- matchedSharedClient: Boolean(retired)
9278
+ activeLeases: retired.activeLeases,
9279
+ pendingAcquires: retired.pendingAcquires,
9280
+ closed: retired.closed,
9281
+ matchedSharedClient: retired.found
6673
9282
  });
6674
- if (retired?.closed) await state.client.closeAndWait({
9283
+ if (retired.closed) await state.client.closeAndWait({
6675
9284
  exitTimeoutMs: 2e3,
6676
9285
  forceKillDelayMs: 250
6677
9286
  });
@@ -6713,6 +9322,15 @@ function prepareCodexAttemptResources(prompt) {
6713
9322
  timeoutMs: params.timeoutMs,
6714
9323
  timeoutFloorMs: options.startupTimeoutFloorMs
6715
9324
  });
9325
+ const requesterChannel = params.messageChannel ?? params.messageProvider;
9326
+ const requester = {
9327
+ ...requesterChannel ? { channel: requesterChannel } : {},
9328
+ ...params.agentAccountId ? { accountId: params.agentAccountId } : {},
9329
+ ...params.senderId ? { senderId: params.senderId } : {},
9330
+ ...params.senderIsOwner !== void 0 ? { senderIsOwner: params.senderIsOwner } : {},
9331
+ ...params.memberRoleIds?.length ? { roleIds: [...params.memberRoleIds] } : {}
9332
+ };
9333
+ const hasRequester = Object.keys(requester).length > 0;
6716
9334
  const buildNativeHookRelayFinalConfigPatch = (decision) => {
6717
9335
  state.nativeHookRelay?.unregister();
6718
9336
  state.nativeHookRelay = createCodexNativeHookRelay({
@@ -6726,6 +9344,7 @@ function prepareCodexAttemptResources(prompt) {
6726
9344
  config: params.config,
6727
9345
  runId: params.runId,
6728
9346
  channelId: hookChannelId,
9347
+ ...hasRequester ? { requester } : {},
6729
9348
  attemptTimeoutMs: params.timeoutMs,
6730
9349
  startupTimeoutMs,
6731
9350
  turnStartTimeoutMs: params.timeoutMs,
@@ -7071,7 +9690,8 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
7071
9690
  const { toolBridge, toolOutcomeOrdinals, suppressedDynamicToolOutcomeOrdinals, allocateCodexToolOutcomeOrdinal } = attemptTools;
7072
9691
  const { state, turnIdRef, userInputBridgeRef, openClawDynamicToolExecutions, pendingOpenClawDynamicToolCompletionIds, postToolRawAssistantCompletionIdleTimeoutMs, turnWatches } = turnRuntime;
7073
9692
  const { emitExecutionPhaseOnce, scheduleTurnReleaseAfterTerminalDynamicTool, scheduleTerminalDynamicToolReleaseCheck } = lifecycle;
7074
- const handleServerRequest = async (request, scope) => {
9693
+ const handleServerRequest = async (request, scope, requestSignal = new AbortController().signal) => {
9694
+ const signal = AbortSignal.any([runAbortController.signal, requestSignal]);
7075
9695
  const turnId = turnIdRef.current;
7076
9696
  const projector = projectorRef.current;
7077
9697
  let armCompletionWatchOnResponse = false;
@@ -7097,7 +9717,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
7097
9717
  turnId,
7098
9718
  pluginAppPolicyContext: resourceState.thread.pluginAppPolicyContext,
7099
9719
  ...computerUseConfig.enabled ? { computerUseMcpServerName: computerUseConfig.mcpServerName } : {},
7100
- signal: runAbortController.signal
9720
+ signal
7101
9721
  });
7102
9722
  }
7103
9723
  if (request.method === "item/tool/requestUserInput") {
@@ -7124,7 +9744,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
7124
9744
  turnId,
7125
9745
  nativeHookRelay: resourceState.nativeHookRelay,
7126
9746
  autoApprove: shouldAutoApproveCodexAppServerApprovals(appServer),
7127
- signal: runAbortController.signal,
9747
+ signal,
7128
9748
  onNativeToolFailureDisposition: (itemId, disposition) => projector?.recordNativeToolApprovalFailure(itemId, disposition)
7129
9749
  });
7130
9750
  }
@@ -7200,7 +9820,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
7200
9820
  const { execution } = openClawDynamicToolExecutions.claim(call, () => handleDynamicToolCallWithTimeout({
7201
9821
  call,
7202
9822
  toolBridge,
7203
- signal: runAbortController.signal,
9823
+ signal,
7204
9824
  timeoutMs: dynamicToolTimeoutMs,
7205
9825
  toolMeta,
7206
9826
  toolCallOrdinal,
@@ -7497,7 +10117,7 @@ function buildMetadataCacheEntryKey(appCacheKey, queryKind) {
7497
10117
  //#endregion
7498
10118
  //#region extensions/codex/src/app-server/plugin-thread-config-deadline.ts
7499
10119
  /** Enforces one bounded startup budget across Codex plugin config discovery. */
7500
- const CODEX_PLUGIN_THREAD_CONFIG_MAX_TIMEOUT_MS = 5e3;
10120
+ const CODEX_PLUGIN_THREAD_CONFIG_MAX_TIMEOUT_MS = 6e4;
7501
10121
  const CODEX_PLUGIN_THREAD_CONFIG_TIMEOUT_DIVISOR = 4;
7502
10122
  const CODEX_PLUGIN_THREAD_CONFIG_MIN_TIMEOUT_MS = 100;
7503
10123
  var CodexPluginThreadConfigDeadlineError = class extends Error {
@@ -8075,7 +10695,9 @@ async function startCodexAttemptRuntime(resources) {
8075
10695
  stream: "codex_app_server.lifecycle",
8076
10696
  data: {
8077
10697
  phase: "thread_ready",
8078
- threadId: state.thread.threadId
10698
+ threadId: state.thread.threadId,
10699
+ action: state.thread.lifecycle.action,
10700
+ clientId: state.client.getInstanceId()
8079
10701
  }
8080
10702
  });
8081
10703
  } catch (error) {
@@ -8299,6 +10921,7 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
8299
10921
  model: usesSupervisionConnection ? resourceState.thread.model ?? effectiveRuntimeModelId : params.modelId,
8300
10922
  api: usesSupervisionConnection ? runtimeParams.model.api : params.model.api,
8301
10923
  transport: appServer.start.transport,
10924
+ observationUnit: "turn",
8302
10925
  ...hookContextWindowFields,
8303
10926
  trace: codexModelCallTrace
8304
10927
  },
@@ -8336,7 +10959,6 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
8336
10959
  turnScopedDeveloperInstructions: workspaceBootstrapContext.turnScopedDeveloperInstructions,
8337
10960
  skillsCollaborationInstructions: context.skillsCollaborationInstructions,
8338
10961
  memoryCollaborationInstructions: workspaceBootstrapContext.memoryCollaborationInstructions,
8339
- heartbeatCollaborationInstructions: workspaceBootstrapContext.heartbeatCollaborationInstructions,
8340
10962
  preserveNativeTurnSettings: usesSupervisionConnection
8341
10963
  });
8342
10964
  codexModelCallDiagnostics.setRequestPayloadBytes(utf8JsonByteLength(turnStartParams));
@@ -8561,7 +11183,11 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
8561
11183
  ctx: hookContext,
8562
11184
  hookRunner
8563
11185
  });
8564
- if (!state.timedOut) await unsubscribeCodexThreadBestEffort(resourceState.client, {
11186
+ const bindingReleased = isIncognitoSessionKey(params.sessionKey) ? await bindingStore.mutate(bindingIdentity, {
11187
+ kind: "clear",
11188
+ threadId: resourceState.thread.threadId
11189
+ }) : true;
11190
+ if (!state.timedOut && bindingReleased) await unsubscribeCodexThreadBestEffort(resourceState.client, {
8565
11191
  threadId: resourceState.thread.threadId,
8566
11192
  timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
8567
11193
  });
@@ -9002,7 +11628,7 @@ function createCodexAttemptTurnState(resources) {
9002
11628
  state.resolveCompletion = resolve;
9003
11629
  });
9004
11630
  const turnCompletionIdleTimeoutMs = resolveCodexTurnCompletionIdleTimeoutMs(options.turnCompletionIdleTimeoutMs ?? appServer.turnCompletionIdleTimeoutMs);
9005
- const turnAssistantCompletionIdleTimeoutMs = resolveCodexTurnAssistantCompletionIdleTimeoutMs(options.turnAssistantCompletionIdleTimeoutMs);
11631
+ const turnAssistantCompletionIdleTimeoutMs = resolveCodexTurnAssistantCompletionIdleTimeoutMs(options.turnAssistantCompletionIdleTimeoutMs ?? appServer.turnAssistantCompletionIdleTimeoutMs);
9006
11632
  const postToolRawAssistantCompletionIdleTimeoutMs = resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs(options.postToolRawAssistantCompletionIdleTimeoutMs ?? appServer.postToolRawAssistantCompletionIdleTimeoutMs, turnAssistantCompletionIdleTimeoutMs);
9007
11633
  const turnTerminalIdleTimeoutMs = resolveCodexTurnTerminalIdleTimeoutMs(options.turnTerminalIdleTimeoutMs, params.runTimeoutOverrideMs);
9008
11634
  const turnAttemptIdleTimeoutMs = Math.max(100, Math.floor(params.timeoutMs));