@openclaw/codex 2026.6.11 → 2026.7.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/{app-server-policy-BPTiVNsW.js → app-server-policy-B7hqD3Xj.js} +2 -2
  2. package/dist/{attempt-notifications-BYnndyFl.js → attempt-notifications-tqyQ_Tdn.js} +26 -2
  3. package/dist/{client-CLrtWgrD.js → client-IcTBKEqA.js} +5 -2
  4. package/dist/{client-factory-Bm6HsGob.js → client-factory-DfFu9JH_.js} +2 -5
  5. package/dist/{command-handlers-DEPTJYk0.js → command-handlers-C9K-KRU-.js} +25 -19
  6. package/dist/{compact-aQJ0ZKhe.js → compact-CILptrPS.js} +5 -5
  7. package/dist/{computer-use-ClrJwoFp.js → computer-use-CpurU-G9.js} +3 -3
  8. package/dist/{config-CszD0vP3.js → config-C_whBx3M.js} +8 -3
  9. package/dist/{conversation-binding-t0XMLz-j.js → conversation-binding-DOJtUxOy.js} +28 -15
  10. package/dist/harness.js +6 -6
  11. package/dist/index.js +27 -26
  12. package/dist/{media-understanding-provider-ZUQHN7gT.js → media-understanding-provider-BOeoBhEw.js} +6 -6
  13. package/dist/media-understanding-provider.js +1 -1
  14. package/dist/{models-CAmfBWRl.js → models-DpzY1xpp.js} +2 -2
  15. package/dist/{plugin-app-cache-key-C9ILt7bl.js → plugin-app-cache-key-BrZUG9jj.js} +8 -4
  16. package/dist/{protocol-validators-B19q5BIX.js → protocol-validators-CJiWigAJ.js} +107 -15
  17. package/dist/{provider-capabilities-D7hnTO76.js → provider-capabilities-wqedK49Z.js} +184 -16
  18. package/dist/provider-catalog.js +4 -2
  19. package/dist/{provider-B-OHpbD3.js → provider-nmtMDcGH.js} +53 -18
  20. package/dist/provider.js +2 -2
  21. package/dist/{request-Dcd452Nk.js → request-BQuSQF29.js} +2 -2
  22. package/dist/{run-attempt-CXxtFC9d.js → run-attempt-DikbXia_.js} +416 -105
  23. package/dist/{sandbox-guard-3tnjhjFb.js → sandbox-guard-pTY2COoC.js} +2 -0
  24. package/dist/{session-binding-HOuPt-E0.js → session-binding-Cm0Gg7Z1.js} +2 -1
  25. package/dist/{shared-client-Dtx5i7Ez.js → shared-client-rR6TDJA3.js} +98 -47
  26. package/dist/{side-question-C-DhgJOd.js → side-question-Ca9wqmd0.js} +12 -12
  27. package/dist/{thread-lifecycle-DtT3-ehU.js → thread-lifecycle-D6R40plk.js} +148 -42
  28. package/dist/{web-search-provider.runtime-CkwxrXQj.js → web-search-provider.runtime-B6eZEFmd.js} +2 -2
  29. package/npm-shrinkwrap.json +30 -30
  30. package/openclaw.plugin.json +3 -3
  31. package/package.json +5 -5
@@ -1,17 +1,17 @@
1
1
  import { n as isJsonObject, t as flattenCodexDynamicToolFunctions } from "./protocol-dh-ETiNd.js";
2
- import { A as resolveCodexContextEngineProjectionReserveTokens, D as fitCodexProjectedContextForTurnStart, E as resolveCodexDynamicToolsLoadingForRuntime, H as readCodexNotificationTurnId, M as buildCodexPluginThreadConfigInputFingerprint, N as mergeCodexThreadConfigs, O as projectContextEngineAssemblyForCodex, P as shouldBuildCodexPluginThreadConfig, R as defaultCodexAppInventoryCache, V as readCodexNotificationThreadId, _ as resolveCodexWebSearchPlan, a as buildDeveloperInstructions, b as sanitizeCodexHistoryImagePayloads, c as codexDynamicToolsFingerprint, f as resolveCodexAppServerThreadModelSelection, h as startOrResumeThread, i as buildContextEngineBinding, j as buildCodexPluginThreadConfig, k as resolveCodexContextEngineProjectionMaxChars, l as isContextEngineBindingCompatible, n as areCodexDynamicToolFingerprintsCompatible, o as buildTurnCollaborationMode, s as buildTurnStartParams, v as isCodexAppServerProfilerEnabled, z as describeCodexNotificationCorrelation } from "./thread-lifecycle-DtT3-ehU.js";
3
- import { C as readCodexNotificationItem, D as updateActiveTurnItemIds, E as shouldDisarmAssistantCompletionIdleWatch, S as isTurnNotification, T as readRawResponseToolCallId, _ as isRawToolOutputCompletionNotification, a as isCodexNotificationOutsideActiveRun, b as isRetryableErrorNotification, c as isCurrentThreadOptionalTurnRequestParams, d as isNativeResponseStreamDeltaNotification, f as isNativeToolProgressNotification, g as isRawReasoningCompletionNotification, h as isRawFunctionToolOutputCompletionNotification, i as isAssistantCompletionReleaseNotification, l as isCurrentThreadTurnRequestParams, m as isRawAssistantProgressNotification, n as describeNotificationActivity, o as isCodexTurnAbortMarkerNotification, p as isPendingOpenClawDynamicToolCompletionNotification, r as isAssistantCommentaryCompletionNotification, s as isCurrentApprovalTurnRequestParams, t as codexExecutionToolName, u as isFileChangePatchUpdatedNotification, v as isReasoningItemCompletionNotification, w as readNotificationItemId, x as isTerminalTurnStatus, y as isReasoningProgressNotification } from "./attempt-notifications-BYnndyFl.js";
4
- import { _ as withMcpElicitationsApprovalPolicy, a as isCodexAppServerApprovalPolicyAllowedByRequirements, d as resolveCodexAppServerRuntimeOptions, f as resolveCodexComputerUseConfig, g as shouldAutoApproveCodexAppServerApprovals, h as resolveOpenClawExecPolicyForCodexAppServer, m as resolveCodexPluginsPolicy, p as resolveCodexModelBackedReviewerPolicyContext, s as isCodexSandboxExecServerEnabled, u as readCodexPluginConfig } from "./config-CszD0vP3.js";
5
- import { a as readCodexDynamicToolCallParams, c as readCodexTurn, i as assertCodexTurnStartResponse } from "./protocol-validators-B19q5BIX.js";
6
- import { i as formatCodexUsageLimitErrorMessage, o as resolveCodexUsageLimitResetAtMs, s as shouldRefreshCodexRateLimitsForUsageLimitMessage } from "./provider-B-OHpbD3.js";
7
- import { a as isCodexAppServerConnectionClosedError, i as isCodexAppServerApprovalRequest, n as CodexAppServerRpcError, r as compareCodexAppServerVersions, s as MIN_CODEX_SANDBOX_EXEC_SERVER_APP_SERVER_VERSION } from "./client-CLrtWgrD.js";
8
- import { i as isCodexAppServerNativeAuthProfile, l as writeCodexAppServerBinding, n as clearCodexAppServerBinding, o as readCodexAppServerBinding, r as clearCodexAppServerBindingForThread } from "./session-binding-HOuPt-E0.js";
9
- import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS, t as buildCodexAppServerRuntimeFingerprint } from "./plugin-app-cache-key-C9ILt7bl.js";
10
- import { a as formatCodexDisplayText, n as resolveCodexAppServerForOpenClawToolPolicy, t as resolveCodexAppServerForModelProvider } from "./app-server-policy-BPTiVNsW.js";
11
- import { d as resolveCodexAppServerAuthAccountCacheKey, f as resolveCodexAppServerAuthProfileId, h as resolveCodexAppServerHomeDir, m as resolveCodexAppServerFallbackApiKeyCacheKey, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, o as releaseLeasedSharedCodexAppServerClient, p as resolveCodexAppServerAuthProfileIdForAgent, s as retireSharedCodexAppServerClientIfCurrent, t as clearSharedCodexAppServerClientIfCurrent, u as refreshCodexAppServerAuthTokens } from "./shared-client-Dtx5i7Ez.js";
12
- import { t as defaultLeasedCodexAppServerClientFactory } from "./client-factory-Bm6HsGob.js";
13
- import { A as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, C as resolveCodexMessageToolProvider, D as shouldWarnCodexDynamicToolBuildStageSummary, E as shouldRequireCodexSandboxExecServerEnvironment, F as resolveCodexTurnTerminalIdleTimeoutMs, I as withCodexStartupTimeout, M as resolveCodexStartupTimeoutMs, N as resolveCodexTurnAssistantCompletionIdleTimeoutMs, P as resolveCodexTurnCompletionIdleTimeoutMs, S as resolveCodexExternalSandboxPolicyForOpenClawSandbox, T as shouldEnableCodexAppServerNativeToolSurface, _ as createCodexDynamicToolBuildStageTracker, a as buildCodexNativeHookRelayConfig, b as resolveCodexAppServerExecutionCwd, c as resolveCodexNativeHookRelayEvents, d as handleCodexAppServerElicitationRequest, f as createCodexDynamicToolBridge, g as buildDynamicTools, h as emitDynamicToolTerminalDiagnostic, i as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, j as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, k as handleCodexAppServerApprovalRequest, l as resolveCodexNativeHookRelayTtlMs, m as emitDynamicToolStartedDiagnostic, o as buildCodexNativeHookRelayDisabledConfig, p as emitDynamicToolErrorDiagnostic, s as createCodexNativeHookRelay, t as resolveCodexProviderWebSearchSupport, u as scheduleCodexNativeHookRelayUnregister, v as disableCodexPluginThreadConfig, w as resolveCodexSandboxEnvironmentSelection, x as resolveCodexAppServerHookChannelId, y as formatCodexDynamicToolBuildStageSummary } from "./provider-capabilities-D7hnTO76.js";
14
- import { t as ensureCodexComputerUse } from "./computer-use-ClrJwoFp.js";
2
+ import { A as resolveCodexContextEngineProjectionReserveTokens, B as describeCodexNotificationCorrelation, D as fitCodexProjectedContextForTurnStart, E as resolveCodexDynamicToolsLoadingForRuntime, F as shouldBuildCodexPluginThreadConfig, H as readCodexNotificationThreadId, M as buildCodexPluginThreadConfig, N as buildCodexPluginThreadConfigInputFingerprint, O as projectContextEngineAssemblyForCodex, P as mergeCodexThreadConfigs, U as readCodexNotificationTurnId, _ as resolveCodexWebSearchPlan, a as buildDeveloperInstructions, b as sanitizeCodexHistoryImagePayloads, c as codexDynamicToolsFingerprint, f as resolveCodexAppServerThreadModelSelection, h as startOrResumeThread, i as buildContextEngineBinding, k as resolveCodexContextEngineProjectionMaxChars, l as isContextEngineBindingCompatible, n as areCodexDynamicToolFingerprintsCompatible, o as buildTurnCollaborationMode, s as buildTurnStartParams, v as isCodexAppServerProfilerEnabled, z as defaultCodexAppInventoryCache } from "./thread-lifecycle-D6R40plk.js";
3
+ import { C as readCodexNotificationItem, D as updateActiveCompletionBlockerItemIds, E as shouldDisarmAssistantCompletionIdleWatch, O as updateActiveTurnItemIds, S as isTurnNotification, T as readRawResponseToolCallId, _ as isRawToolOutputCompletionNotification, a as isCodexNotificationOutsideActiveRun, b as isRetryableErrorNotification, c as isCurrentThreadOptionalTurnRequestParams, d as isNativeResponseStreamDeltaNotification, f as isNativeToolProgressNotification, g as isRawReasoningCompletionNotification, h as isRawFunctionToolOutputCompletionNotification, i as isAssistantCompletionReleaseNotification, l as isCurrentThreadTurnRequestParams, m as isRawAssistantProgressNotification, n as describeNotificationActivity, o as isCodexTurnAbortMarkerNotification, p as isPendingOpenClawDynamicToolCompletionNotification, r as isAssistantCommentaryCompletionNotification, s as isCurrentApprovalTurnRequestParams, t as codexExecutionToolName, u as isFileChangePatchUpdatedNotification, v as isReasoningItemCompletionNotification, w as readNotificationItemId, x as isTerminalTurnStatus, y as isReasoningProgressNotification } from "./attempt-notifications-tqyQ_Tdn.js";
4
+ import { _ as withMcpElicitationsApprovalPolicy, a as isCodexAppServerApprovalPolicyAllowedByRequirements, d as resolveCodexAppServerRuntimeOptions, f as resolveCodexComputerUseConfig, g as shouldAutoApproveCodexAppServerApprovals, h as resolveOpenClawExecPolicyForCodexAppServer, m as resolveCodexPluginsPolicy, p as resolveCodexModelBackedReviewerPolicyContext, s as isCodexSandboxExecServerEnabled, u as readCodexPluginConfig } from "./config-C_whBx3M.js";
5
+ import { a as readCodexDynamicToolCallParams, c as readCodexTurn, i as assertCodexTurnStartResponse } from "./protocol-validators-CJiWigAJ.js";
6
+ import { l as resolveCodexUsageLimitResetAtMs, s as formatCodexUsageLimitErrorMessage, u as shouldRefreshCodexRateLimitsForUsageLimitMessage } from "./provider-nmtMDcGH.js";
7
+ import { a as isCodexAppServerConnectionClosedError, c as MIN_CODEX_SANDBOX_EXEC_SERVER_APP_SERVER_VERSION, i as isCodexAppServerApprovalRequest, n as CodexAppServerRpcError, r as compareCodexAppServerVersions } from "./client-IcTBKEqA.js";
8
+ import { i as isCodexAppServerNativeAuthProfile, l as writeCodexAppServerBinding, n as clearCodexAppServerBinding, o as readCodexAppServerBinding, r as clearCodexAppServerBindingForThread } from "./session-binding-Cm0Gg7Z1.js";
9
+ import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS, t as buildCodexAppServerRuntimeFingerprint } from "./plugin-app-cache-key-BrZUG9jj.js";
10
+ import { a as formatCodexDisplayText, n as resolveCodexAppServerForOpenClawToolPolicy, t as resolveCodexAppServerForModelProvider } from "./app-server-policy-B7hqD3Xj.js";
11
+ import { d as resolveCodexAppServerAuthAccountCacheKey, f as resolveCodexAppServerAuthProfileId, h as resolveCodexAppServerHomeDir, m as resolveCodexAppServerFallbackApiKeyCacheKey, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, o as releaseLeasedSharedCodexAppServerClient, p as resolveCodexAppServerAuthProfileIdForAgent, s as retireSharedCodexAppServerClientIfCurrent, t as clearSharedCodexAppServerClientIfCurrent, u as refreshCodexAppServerAuthTokens } from "./shared-client-rR6TDJA3.js";
12
+ import { t as defaultLeasedCodexAppServerClientFactory } from "./client-factory-DfFu9JH_.js";
13
+ import { A as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, C as resolveCodexMessageToolProvider, D as shouldWarnCodexDynamicToolBuildStageSummary, E as shouldRequireCodexSandboxExecServerEnvironment, F as resolveCodexTurnTerminalIdleTimeoutMs, I as withCodexStartupTimeout, M as resolveCodexStartupTimeoutMs, N as resolveCodexTurnAssistantCompletionIdleTimeoutMs, P as resolveCodexTurnCompletionIdleTimeoutMs, S as resolveCodexExternalSandboxPolicyForOpenClawSandbox, T as shouldEnableCodexAppServerNativeToolSurface, _ as createCodexDynamicToolBuildStageTracker, a as buildCodexNativeHookRelayConfig, b as resolveCodexAppServerExecutionCwd, c as resolveCodexNativeHookRelayEvents, d as handleCodexAppServerElicitationRequest, f as createCodexDynamicToolBridge, g as buildDynamicTools, h as emitDynamicToolTerminalDiagnostic, i as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, j as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, k as handleCodexAppServerApprovalRequest, l as resolveCodexNativeHookRelayTtlMs, m as emitDynamicToolStartedDiagnostic, o as buildCodexNativeHookRelayDisabledConfig, p as emitDynamicToolErrorDiagnostic, s as createCodexNativeHookRelay, t as resolveCodexProviderWebSearchSupport, u as scheduleCodexNativeHookRelayUnregister, v as disableCodexPluginThreadConfig, w as resolveCodexSandboxEnvironmentSelection, x as resolveCodexAppServerHookChannelId, y as formatCodexDynamicToolBuildStageSummary } from "./provider-capabilities-wqedK49Z.js";
14
+ import { t as ensureCodexComputerUse } from "./computer-use-CpurU-G9.js";
15
15
  import { n as rememberCodexRateLimits, t as readRecentCodexRateLimits } from "./rate-limit-cache-C7qmZ0Jh.js";
16
16
  import fs from "node:fs/promises";
17
17
  import path, { posix } from "node:path";
@@ -947,6 +947,7 @@ function applyCodexTurnNotificationState(params) {
947
947
  });
948
948
  params.onReportExecutionNotification(notification);
949
949
  updateActiveTurnItemIds(notification, params.activeTurnItemIds);
950
+ updateActiveCompletionBlockerItemIds(notification, params.activeCompletionBlockerItemIds);
950
951
  if (notification.method === "item/completed" && params.activeTurnItemIds.size === 0) params.onScheduleTerminalDynamicToolReleaseCheck();
951
952
  }
952
953
  const unblockedAssistantCompletionRelease = isCurrentTurnNotification && turnWatches.isAssistantCompletionIdleWatchArmed() && notification.method === "item/completed" && params.activeTurnItemIds.size === 0;
@@ -2628,6 +2629,7 @@ async function startCodexAttemptThread(params) {
2628
2629
  timeoutMs: params.appServer.requestTimeoutMs,
2629
2630
  signal
2630
2631
  }),
2632
+ configCwd: startupExecutionCwd,
2631
2633
  appCache: defaultCodexAppInventoryCache,
2632
2634
  appCacheKey: pluginAppCacheKey
2633
2635
  })
@@ -2902,7 +2904,7 @@ function createCodexAttemptTurnWatchController(params) {
2902
2904
  };
2903
2905
  function scheduleCompletionIdleWatch() {
2904
2906
  clearCompletionIdleTimer();
2905
- if (params.isCompleted() || params.signal.aborted || !completionIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return;
2907
+ if (params.isCompleted() || params.signal.aborted || !completionIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0 || params.getActiveCompletionBlockerItemCount() > 0) return;
2906
2908
  const elapsedMs = Math.max(0, Date.now() - completionLastActivityAt);
2907
2909
  const delayMs = Math.max(1, (completionIdleTimeoutOverrideMs ?? turnCompletionIdleTimeoutMs) - elapsedMs);
2908
2910
  completionIdleTimer = setTimeout(fireCompletionIdleTimeout, delayMs);
@@ -2910,7 +2912,7 @@ function createCodexAttemptTurnWatchController(params) {
2910
2912
  }
2911
2913
  function scheduleAssistantCompletionIdleWatch() {
2912
2914
  clearAssistantCompletionIdleTimer();
2913
- if (params.isCompleted() || params.signal.aborted || !assistantCompletionIdleWatchArmed) return;
2915
+ if (params.isCompleted() || params.signal.aborted || !assistantCompletionIdleWatchArmed || params.getActiveFinalizationHookCount() > 0) return;
2914
2916
  const elapsedMs = Math.max(0, Date.now() - assistantCompletionLastActivityAt);
2915
2917
  const delayMs = Math.max(1, turnAssistantCompletionIdleTimeoutMs - elapsedMs);
2916
2918
  assistantCompletionIdleTimer = setTimeout(fireAssistantCompletionIdleRelease, delayMs);
@@ -2938,7 +2940,7 @@ function createCodexAttemptTurnWatchController(params) {
2938
2940
  scheduleTerminalIdleWatch();
2939
2941
  }
2940
2942
  function isCompletionIdleTimeoutDueBeforeAttempt(timeoutMs) {
2941
- if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !completionIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return false;
2943
+ if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !completionIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0 || params.getActiveCompletionBlockerItemCount() > 0) return false;
2942
2944
  const completionTimeoutMs = completionIdleTimeoutOverrideMs ?? turnCompletionIdleTimeoutMs;
2943
2945
  if (completionTimeoutMs > timeoutMs) return false;
2944
2946
  return Math.max(0, Date.now() - completionLastActivityAt) >= completionTimeoutMs;
@@ -2953,10 +2955,16 @@ function createCodexAttemptTurnWatchController(params) {
2953
2955
  }
2954
2956
  function fireAssistantCompletionIdleRelease() {
2955
2957
  if (params.isCompleted() || params.signal.aborted || !assistantCompletionIdleWatchArmed) return;
2956
- if (params.getActiveAppServerTurnRequests() > 0 || params.getActiveTurnItemCount() > 0) {
2958
+ if (params.getActiveAppServerTurnRequests() > 0 || params.getActiveTurnItemCount() > 0 || params.getActiveFinalizationHookCount() > 0) {
2957
2959
  scheduleAssistantCompletionIdleWatch();
2958
2960
  return;
2959
2961
  }
2962
+ if (!params.canReleaseAssistantCompletionIdle()) {
2963
+ assistantCompletionIdleWatchArmed = false;
2964
+ assistantCompletionLastActivityDetails = void 0;
2965
+ clearAssistantCompletionIdleTimer();
2966
+ return;
2967
+ }
2960
2968
  const idleMs = Math.max(0, Date.now() - assistantCompletionLastActivityAt);
2961
2969
  if (idleMs < turnAssistantCompletionIdleTimeoutMs) {
2962
2970
  scheduleAssistantCompletionIdleWatch();
@@ -3028,7 +3036,7 @@ function createCodexAttemptTurnWatchController(params) {
3028
3036
  params.onAbort("turn_progress_idle_timeout");
3029
3037
  }
3030
3038
  function fireCompletionIdleTimeout() {
3031
- if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !completionIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return;
3039
+ if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !completionIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0 || params.getActiveCompletionBlockerItemCount() > 0) return;
3032
3040
  const timeoutMs = completionIdleTimeoutOverrideMs ?? turnCompletionIdleTimeoutMs;
3033
3041
  const idleMs = Math.max(0, Date.now() - completionLastActivityAt);
3034
3042
  if (idleMs < timeoutMs) {
@@ -3805,6 +3813,9 @@ var CodexAppServerEventProjector = class {
3805
3813
  this.assistantTextByItem = /* @__PURE__ */ new Map();
3806
3814
  this.assistantItemOrder = [];
3807
3815
  this.assistantPhaseByItem = /* @__PURE__ */ new Map();
3816
+ this.latestTerminalAssistantCandidateSuperseded = false;
3817
+ this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
3818
+ this.terminalAssistantCandidateEarlierActiveItemIds = /* @__PURE__ */ new Set();
3808
3819
  this.lastCommentaryProgressTextByItem = /* @__PURE__ */ new Map();
3809
3820
  this.pendingRawCommentaryEchoes = 0;
3810
3821
  this.reasoningTextByGroup = /* @__PURE__ */ new Map();
@@ -3853,8 +3864,33 @@ var CodexAppServerEventProjector = class {
3853
3864
  return this.completedTurn?.status;
3854
3865
  }
3855
3866
  hasCompletedTerminalAssistantText() {
3867
+ const latestCompletedItemId = this.latestCompletedTerminalAssistantItemId;
3868
+ if (!latestCompletedItemId) return false;
3856
3869
  const finalItem = this.resolveFinalAssistantTextItem();
3857
- return finalItem !== void 0 && this.completedItemIds.has(finalItem.itemId);
3870
+ return this.latestCompletedItemId === latestCompletedItemId && finalItem?.itemId === latestCompletedItemId && this.completedItemIds.has(latestCompletedItemId);
3871
+ }
3872
+ getLatestTerminalAssistantCandidate() {
3873
+ const itemId = this.latestTerminalAssistantCandidateItemId;
3874
+ if (!itemId) return;
3875
+ const text = this.assistantTextByItem.get(itemId)?.trim();
3876
+ return {
3877
+ itemId,
3878
+ hasText: Boolean(text && !this.toolProgressTexts.has(text))
3879
+ };
3880
+ }
3881
+ hasLatestTerminalAssistantCandidateText() {
3882
+ return !this.latestTerminalAssistantCandidateSuperseded && this.getLatestTerminalAssistantCandidate()?.hasText === true;
3883
+ }
3884
+ canReleaseLatestTerminalAssistantAfterToolHandoff() {
3885
+ return this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff && this.hasLatestTerminalAssistantCandidateText();
3886
+ }
3887
+ /** Restores a completed final item after only the enclosing turn timeout fired. */
3888
+ recoverCompletedTerminalAssistantAfterTurnWatchTimeout() {
3889
+ if (!this.aborted || this.promptError !== "codex app-server attempt timed out" || !this.hasCompletedTerminalAssistantText()) return false;
3890
+ this.aborted = false;
3891
+ this.promptError = void 0;
3892
+ this.promptErrorSource = null;
3893
+ return true;
3858
3894
  }
3859
3895
  /** Resolves the shared model-order position for a native tool item. */
3860
3896
  recordNativeToolOutcome(item) {
@@ -3974,6 +4010,7 @@ var CodexAppServerEventProjector = class {
3974
4010
  currentAttemptAssistant,
3975
4011
  ...this.lastNativeToolError ? { lastToolError: this.lastNativeToolError } : {},
3976
4012
  didSendViaMessagingTool: toolTelemetry.didSendViaMessagingTool,
4013
+ didDeliverSourceReplyViaMessageTool: toolTelemetry.didDeliverSourceReplyViaMessageTool === true,
3977
4014
  messagingToolSentTexts: toolTelemetry.messagingToolSentTexts,
3978
4015
  messagingToolSentMediaUrls: toolTelemetry.messagingToolSentMediaUrls,
3979
4016
  messagingToolSentTargets: toolTelemetry.messagingToolSentTargets,
@@ -4044,9 +4081,12 @@ var CodexAppServerEventProjector = class {
4044
4081
  const itemId = readString$5(params, "itemId") ?? readString$5(params, "id") ?? "assistant";
4045
4082
  const delta = readString$5(params, "delta") ?? "";
4046
4083
  if (!delta) return;
4084
+ if (itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
4047
4085
  this.rememberAssistantPhase(readItem(params.item));
4048
4086
  const phase = readString$5(params, "phase");
4049
4087
  if (phase) this.assistantPhaseByItem.set(itemId, phase);
4088
+ const isCommentary = this.isCommentaryAssistantItem(itemId);
4089
+ if (!isCommentary && itemId !== this.latestTerminalAssistantCandidateItemId) this.markTerminalAssistantCandidateSupersededBy();
4050
4090
  if (!this.assistantStarted) {
4051
4091
  this.assistantStarted = true;
4052
4092
  await this.params.onAssistantMessageStart?.();
@@ -4054,7 +4094,7 @@ var CodexAppServerEventProjector = class {
4054
4094
  this.rememberAssistantItem(itemId);
4055
4095
  const text = `${this.assistantTextByItem.get(itemId) ?? ""}${delta}`;
4056
4096
  this.assistantTextByItem.set(itemId, text);
4057
- if (this.isCommentaryAssistantItem(itemId)) this.emitCommentaryProgress({
4097
+ if (isCommentary) this.emitCommentaryProgress({
4058
4098
  itemId,
4059
4099
  text
4060
4100
  });
@@ -4128,8 +4168,15 @@ var CodexAppServerEventProjector = class {
4128
4168
  async handleItemStarted(params) {
4129
4169
  const item = readItem(params.item);
4130
4170
  const itemId = item?.id ?? readString$5(params, "itemId") ?? readString$5(params, "id");
4171
+ if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
4131
4172
  this.rememberAssistantPhase(item);
4132
- if (itemId) this.activeItemIds.add(itemId);
4173
+ if (itemId) {
4174
+ this.activeItemIds.add(itemId);
4175
+ if (itemId !== this.latestTerminalAssistantCandidateItemId) {
4176
+ this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
4177
+ if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
4178
+ }
4179
+ }
4133
4180
  this.recordNativeToolOutcome(item);
4134
4181
  if (item?.type === "contextCompaction" && itemId) {
4135
4182
  this.activeCompactionItemIds.add(itemId);
@@ -4183,11 +4230,21 @@ var CodexAppServerEventProjector = class {
4183
4230
  this.recordNativeToolOutcome(item);
4184
4231
  this.clearTerminalPresentationForNativeItem(item);
4185
4232
  const itemId = item?.id ?? readString$5(params, "itemId") ?? readString$5(params, "id");
4233
+ if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
4186
4234
  if (itemId) {
4187
4235
  this.activeItemIds.delete(itemId);
4188
4236
  this.completedItemIds.add(itemId);
4237
+ this.latestCompletedItemId = itemId;
4189
4238
  }
4190
4239
  this.rememberAssistantPhase(item);
4240
+ if (item?.type === "agentMessage" && !this.isCommentaryAssistantItem(item.id)) {
4241
+ this.latestCompletedTerminalAssistantItemId = item.id;
4242
+ this.markLatestTerminalAssistantCandidate(item.id);
4243
+ this.pendingRawTerminalAssistantEchoItemId = item.id;
4244
+ } else if (itemId) {
4245
+ this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
4246
+ if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
4247
+ }
4191
4248
  if (item?.type === "agentMessage" && typeof item.text === "string") {
4192
4249
  this.rememberAssistantItem(item.id);
4193
4250
  this.assistantTextByItem.set(item.id, item.text);
@@ -4425,16 +4482,36 @@ var CodexAppServerEventProjector = class {
4425
4482
  async handleRawResponseItemCompleted(params) {
4426
4483
  const item = isJsonObject(params.item) ? params.item : void 0;
4427
4484
  if (!item) return;
4428
- await this.recordRawGeneratedImageMedia(item);
4429
- if (readString$5(item, "role") !== "assistant") return;
4485
+ const role = readString$5(item, "role");
4430
4486
  const phase = readString$5(item, "phase");
4487
+ const rawItemId = readString$5(item, "id");
4488
+ const candidateWasSupersededBeforeRaw = this.latestTerminalAssistantCandidateSuperseded;
4489
+ const pendingTerminalAssistantEchoItemId = this.pendingRawTerminalAssistantEchoItemId;
4490
+ const isPendingTerminalAssistantEcho = role === "assistant" && phase !== "commentary" && pendingTerminalAssistantEchoItemId !== void 0 && (rawItemId === void 0 || rawItemId === pendingTerminalAssistantEchoItemId);
4491
+ if (pendingTerminalAssistantEchoItemId !== void 0 && !isPendingTerminalAssistantEcho) this.pendingRawTerminalAssistantEchoItemId = void 0;
4492
+ if (!isPendingTerminalAssistantEcho) {
4493
+ this.latestCompletedItemId = void 0;
4494
+ this.markTerminalAssistantCandidateSupersededBy(rawItemId);
4495
+ }
4496
+ await this.recordRawGeneratedImageMedia(item);
4497
+ if (role !== "assistant") return;
4431
4498
  if (phase === "commentary" && this.pendingRawCommentaryEchoes > 0) {
4432
4499
  this.pendingRawCommentaryEchoes -= 1;
4433
4500
  return;
4434
4501
  }
4435
4502
  const text = extractRawAssistantText(item);
4503
+ if (isPendingTerminalAssistantEcho) {
4504
+ const typedItemId = pendingTerminalAssistantEchoItemId;
4505
+ this.pendingRawTerminalAssistantEchoItemId = void 0;
4506
+ if (this.assistantTextByItem.get(typedItemId)?.trim() || !text) return;
4507
+ this.rememberAssistantItem(typedItemId);
4508
+ this.assistantTextByItem.set(typedItemId, text);
4509
+ return;
4510
+ }
4436
4511
  if (!text) return;
4437
- const itemId = readString$5(item, "id") ?? `raw-assistant-${this.assistantItemOrder.length + 1}`;
4512
+ const itemId = rawItemId ?? `raw-assistant-${this.assistantItemOrder.length + 1}`;
4513
+ const isIdlessTerminalAssistantAfterCompletedWork = candidateWasSupersededBeforeRaw && rawItemId === void 0 && pendingTerminalAssistantEchoItemId === void 0 && this.activeItemIds.size === 0;
4514
+ if (phase !== "commentary" && candidateWasSupersededBeforeRaw && itemId !== this.streamedPartialAssistantItemId && !isIdlessTerminalAssistantAfterCompletedWork) return;
4438
4515
  if (phase) this.assistantPhaseByItem.set(itemId, phase);
4439
4516
  this.rememberAssistantItem(itemId);
4440
4517
  this.assistantTextByItem.set(itemId, text);
@@ -4442,6 +4519,23 @@ var CodexAppServerEventProjector = class {
4442
4519
  itemId,
4443
4520
  text
4444
4521
  });
4522
+ else this.markLatestTerminalAssistantCandidate(itemId, { canReleaseAfterToolHandoff: isIdlessTerminalAssistantAfterCompletedWork });
4523
+ }
4524
+ markLatestTerminalAssistantCandidate(itemId, options) {
4525
+ this.latestTerminalAssistantCandidateItemId = itemId;
4526
+ this.latestTerminalAssistantCandidateSuperseded = false;
4527
+ this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = options?.canReleaseAfterToolHandoff === true;
4528
+ this.terminalAssistantCandidateEarlierActiveItemIds = new Set(this.activeItemIds);
4529
+ }
4530
+ markTerminalAssistantCandidateSupersededBy(itemId, options) {
4531
+ if (!this.latestTerminalAssistantCandidateItemId) return;
4532
+ if (itemId && this.terminalAssistantCandidateEarlierActiveItemIds.has(itemId)) {
4533
+ if (!options?.preserveEarlierActiveItem) this.terminalAssistantCandidateEarlierActiveItemIds.delete(itemId);
4534
+ return;
4535
+ }
4536
+ this.latestTerminalAssistantCandidateSuperseded = true;
4537
+ this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
4538
+ this.terminalAssistantCandidateEarlierActiveItemIds.clear();
4445
4539
  }
4446
4540
  recordNativeGeneratedMedia(item) {
4447
4541
  if (item?.type !== "imageGeneration") return;
@@ -7112,6 +7206,7 @@ const JWT_VALUE_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]
7112
7206
  const COOKIE_PAIR_RE = /\b([A-Za-z][A-Za-z0-9_.-]{1,64})=([A-Za-z0-9+/._~%=-]{16,})(?=;|\s|$)/gu;
7113
7207
  const TRAJECTORY_RUNTIME_FILE_MAX_BYTES = 50 * 1024 * 1024;
7114
7208
  const TRAJECTORY_RUNTIME_EVENT_MAX_BYTES = 256 * 1024;
7209
+ const TRAJECTORY_RUNTIME_OVERSIZE_PRESERVED_DATA_KEYS = ["usage", "promptCache"];
7115
7210
  /** Resolves secure create/truncate flags for trajectory pointer files. */
7116
7211
  function resolveCodexTrajectoryPointerFlags(constants = fsSync.constants) {
7117
7212
  const noFollow = constants.O_NOFOLLOW;
@@ -7129,16 +7224,41 @@ function boundedTrajectoryLine(event) {
7129
7224
  const line = JSON.stringify(event);
7130
7225
  const bytes = Buffer.byteLength(line, "utf8");
7131
7226
  if (bytes <= TRAJECTORY_RUNTIME_EVENT_MAX_BYTES) return `${line}\n`;
7132
- const truncated = JSON.stringify({
7133
- ...event,
7134
- data: {
7135
- truncated: true,
7136
- originalBytes: bytes,
7137
- limitBytes: TRAJECTORY_RUNTIME_EVENT_MAX_BYTES,
7138
- reason: "trajectory-event-size-limit"
7227
+ const originalData = event.data && typeof event.data === "object" && !Array.isArray(event.data) ? event.data : {};
7228
+ const originalDataKeys = Object.keys(originalData);
7229
+ const preservedDataKeys = /* @__PURE__ */ new Set();
7230
+ const baseData = {
7231
+ truncated: true,
7232
+ originalBytes: bytes,
7233
+ limitBytes: TRAJECTORY_RUNTIME_EVENT_MAX_BYTES,
7234
+ reason: "trajectory-event-size-limit"
7235
+ };
7236
+ const buildTruncatedLine = (includeDroppedFields) => {
7237
+ const data = { ...baseData };
7238
+ for (const key of TRAJECTORY_RUNTIME_OVERSIZE_PRESERVED_DATA_KEYS) if (preservedDataKeys.has(key)) data[key] = originalData[key];
7239
+ if (includeDroppedFields) {
7240
+ const droppedFields = originalDataKeys.filter((key) => !preservedDataKeys.has(key));
7241
+ if (droppedFields.length > 0) data.droppedFields = droppedFields;
7242
+ }
7243
+ const truncated = JSON.stringify({
7244
+ ...event,
7245
+ data
7246
+ });
7247
+ if (Buffer.byteLength(truncated, "utf8") <= TRAJECTORY_RUNTIME_EVENT_MAX_BYTES) return `${truncated}\n`;
7248
+ };
7249
+ let best = buildTruncatedLine(true) ?? buildTruncatedLine(false);
7250
+ if (!best) return;
7251
+ for (const key of TRAJECTORY_RUNTIME_OVERSIZE_PRESERVED_DATA_KEYS) {
7252
+ if (!Object.hasOwn(originalData, key)) continue;
7253
+ preservedDataKeys.add(key);
7254
+ const next = buildTruncatedLine(true) ?? buildTruncatedLine(false);
7255
+ if (next) {
7256
+ best = next;
7257
+ continue;
7139
7258
  }
7140
- });
7141
- if (Buffer.byteLength(truncated, "utf8") <= TRAJECTORY_RUNTIME_EVENT_MAX_BYTES) return `${truncated}\n`;
7259
+ preservedDataKeys.delete(key);
7260
+ }
7261
+ return best;
7142
7262
  }
7143
7263
  function resolveTrajectoryPointerFilePath(sessionFile) {
7144
7264
  return sessionFile.endsWith(".jsonl") ? `${sessionFile.slice(0, -6)}.trajectory-path.json` : `${sessionFile}.trajectory-path.json`;
@@ -7585,6 +7705,10 @@ function readRequestId(record) {
7585
7705
  const CODEX_NATIVE_HOOK_RELAY_RENEW_INTERVAL_MS = 6e4;
7586
7706
  const CODEX_APP_SERVER_PROJECTED_CHARS_PER_TOKEN = 4;
7587
7707
  const CODEX_APP_SERVER_ACTIVE_NATIVE_TURN_WAIT_TIMEOUT_MS = 3e4;
7708
+ function shouldKeepCodexSharedAbortOpen(params) {
7709
+ if (params.explicitCancellationObserved || params.result.aborted || params.result.externalAbort) return false;
7710
+ return params.trigger === "memory" || !params.attemptSucceeded;
7711
+ }
7588
7712
  const ensuredCodexWorkspaceDirs = /* @__PURE__ */ new Set();
7589
7713
  function withCodexAppServerFastModeServiceTier(appServer, params) {
7590
7714
  const fastMode = typeof params.fastMode === "function" ? params.fastMode() : params.fastMode;
@@ -7802,8 +7926,28 @@ async function runCodexAppServerAttempt(params, options = {}) {
7802
7926
  });
7803
7927
  preDynamicStartupStages.mark("native-hook-relay");
7804
7928
  const runAbortController = new AbortController();
7929
+ let explicitCancellationObserved = false;
7930
+ let explicitCancellationReason;
7931
+ let terminalOutcomeFrozen = false;
7932
+ let sharedAbortAllowedAfterTerminalOutcome = false;
7933
+ let attemptAbortNotified = false;
7934
+ const notifyAttemptAbort = () => {
7935
+ if (attemptAbortNotified) return;
7936
+ attemptAbortNotified = true;
7937
+ params.onAttemptAbort?.();
7938
+ };
7939
+ const abortExplicitly = (reason) => {
7940
+ if (terminalOutcomeFrozen) {
7941
+ if (sharedAbortAllowedAfterTerminalOutcome) notifyAttemptAbort();
7942
+ return;
7943
+ }
7944
+ notifyAttemptAbort();
7945
+ explicitCancellationObserved = true;
7946
+ explicitCancellationReason ??= reason;
7947
+ runAbortController.abort(reason);
7948
+ };
7805
7949
  const abortFromUpstream = () => {
7806
- runAbortController.abort(params.abortSignal?.reason ?? "upstream_abort");
7950
+ abortExplicitly(params.abortSignal?.reason ?? "upstream_abort");
7807
7951
  };
7808
7952
  if (params.abortSignal?.aborted) abortFromUpstream();
7809
7953
  else params.abortSignal?.addEventListener("abort", abortFromUpstream, { once: true });
@@ -8021,9 +8165,11 @@ async function runCodexAppServerAttempt(params, options = {}) {
8021
8165
  currentChannelProvider: resolveCodexMessageToolProvider(params),
8022
8166
  currentChannelId: params.currentChannelId,
8023
8167
  currentMessagingTarget: params.currentMessagingTarget,
8168
+ currentMessageId: params.currentMessageId,
8024
8169
  currentThreadId: params.currentThreadTs,
8025
8170
  replyToMode: params.replyToMode,
8026
8171
  hasRepliedRef: params.hasRepliedRef,
8172
+ sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
8027
8173
  onToolOutcome: onCodexToolOutcome,
8028
8174
  allocateToolOutcomeOrdinal: allocateCodexToolOutcomeOrdinal
8029
8175
  }
@@ -8193,6 +8339,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
8193
8339
  developerInstructions,
8194
8340
  messages: codexModelInputHistoryMessages,
8195
8341
  ctx: hookContext,
8342
+ bootstrapContextRunKind: params.bootstrapContextRunKind,
8196
8343
  ..."beforeAgentStartResult" in params ? { beforeAgentStartResult: params.beforeAgentStartResult } : {}
8197
8344
  });
8198
8345
  const resolveShiftedPromptInputRange = (prompt, promptInputRange, turnPromptText) => {
@@ -8488,6 +8635,46 @@ async function runCodexAppServerAttempt(params, options = {}) {
8488
8635
  client = startupResult.client;
8489
8636
  thread = startupResult.thread;
8490
8637
  pluginAppServer = startupResult.pluginAppServer;
8638
+ if (thread.lifecycle.action === "started") {
8639
+ const activeThreadReviewerPolicyContext = resolveCodexModelBackedReviewerPolicyContext({
8640
+ provider: params.provider,
8641
+ model: params.modelId,
8642
+ bindingModelProvider: thread.modelProvider,
8643
+ bindingModel: thread.model,
8644
+ nativeAuthProfile: isCodexAppServerNativeAuthProfile({
8645
+ authProfileId: startupAuthProfileId,
8646
+ authProfileStore: params.authProfileStore,
8647
+ agentDir,
8648
+ config: params.config
8649
+ })
8650
+ });
8651
+ const activeThreadAppServer = resolveCodexAppServerForModelProvider({
8652
+ appServer: resolveCodexAppServerRuntimeOptions({
8653
+ pluginConfig,
8654
+ execPolicy,
8655
+ modelProvider: activeThreadReviewerPolicyContext.modelProvider,
8656
+ model: activeThreadReviewerPolicyContext.model,
8657
+ config: params.config,
8658
+ agentDir,
8659
+ openClawSandboxActive: sandbox?.enabled === true
8660
+ }),
8661
+ provider: activeThreadReviewerPolicyContext.modelProvider,
8662
+ model: activeThreadReviewerPolicyContext.model,
8663
+ config: params.config,
8664
+ env: process.env,
8665
+ agentDir
8666
+ });
8667
+ const previousApprovalsReviewer = pluginAppServer.approvalsReviewer;
8668
+ pluginAppServer = {
8669
+ ...pluginAppServer,
8670
+ approvalsReviewer: activeThreadAppServer.approvalsReviewer
8671
+ };
8672
+ if (pluginAppServer.approvalsReviewer !== previousApprovalsReviewer) embeddedAgentLog.info("codex app-server approval reviewer updated from active thread model provider", {
8673
+ from: previousApprovalsReviewer,
8674
+ to: pluginAppServer.approvalsReviewer,
8675
+ modelProvider: activeThreadReviewerPolicyContext.modelProvider
8676
+ });
8677
+ }
8491
8678
  sandboxExecEnvironmentAcquired = Boolean(startupResult.sandboxEnvironment);
8492
8679
  codexEnvironmentSelection = startupResult.environmentSelection;
8493
8680
  codexExecutionCwd = startupResult.executionCwd;
@@ -8546,12 +8733,17 @@ async function runCodexAppServerAttempt(params, options = {}) {
8546
8733
  const turnCompletionIdleTimeoutMs = resolveCodexTurnCompletionIdleTimeoutMs(options.turnCompletionIdleTimeoutMs ?? appServer.turnCompletionIdleTimeoutMs);
8547
8734
  const turnAssistantCompletionIdleTimeoutMs = resolveCodexTurnAssistantCompletionIdleTimeoutMs(options.turnAssistantCompletionIdleTimeoutMs);
8548
8735
  const postToolRawAssistantCompletionIdleTimeoutMs = resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs(options.postToolRawAssistantCompletionIdleTimeoutMs ?? appServer.postToolRawAssistantCompletionIdleTimeoutMs, turnAssistantCompletionIdleTimeoutMs);
8549
- const turnTerminalIdleTimeoutMs = resolveCodexTurnTerminalIdleTimeoutMs(options.turnTerminalIdleTimeoutMs);
8736
+ const turnTerminalIdleTimeoutMs = resolveCodexTurnTerminalIdleTimeoutMs(options.turnTerminalIdleTimeoutMs, params.runTimeoutOverrideMs);
8550
8737
  const turnAttemptIdleTimeoutMs = Math.max(100, Math.floor(params.timeoutMs));
8551
8738
  let nativeHookRelayLastRenewedAt = 0;
8552
8739
  let activeAppServerTurnRequests = 0;
8553
8740
  const pendingOpenClawDynamicToolCompletionIds = /* @__PURE__ */ new Set();
8554
8741
  const activeTurnItemIds = /* @__PURE__ */ new Set();
8742
+ const activeCompletionBlockerItemIds = /* @__PURE__ */ new Set();
8743
+ const activeFinalizationHookRunIds = /* @__PURE__ */ new Set();
8744
+ const finalizationHookBatchStatuses = /* @__PURE__ */ new Map();
8745
+ let unsettledFinalizationHookCount = 0;
8746
+ let rejectedFinalizationHookAssistant;
8555
8747
  let turnCrossedToolHandoff = false;
8556
8748
  let pendingTerminalDynamicToolRelease;
8557
8749
  let terminalDynamicToolReleaseCheckScheduled = false;
@@ -8582,6 +8774,9 @@ async function runCodexAppServerAttempt(params, options = {}) {
8582
8774
  isTerminalTurnNotificationQueued: () => terminalTurnNotificationQueued,
8583
8775
  getActiveAppServerTurnRequests: () => activeAppServerTurnRequests,
8584
8776
  getActiveTurnItemCount: () => activeTurnItemIds.size,
8777
+ getActiveCompletionBlockerItemCount: () => activeCompletionBlockerItemIds.size,
8778
+ getActiveFinalizationHookCount: () => unsettledFinalizationHookCount,
8779
+ canReleaseAssistantCompletionIdle: () => projectorRef.current?.hasLatestTerminalAssistantCandidateText() === true,
8585
8780
  turnCompletionIdleTimeoutMs,
8586
8781
  turnAssistantCompletionIdleTimeoutMs,
8587
8782
  turnAttemptIdleTimeoutMs,
@@ -8793,6 +8988,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
8793
8988
  currentPromptTexts: [codexTurnPromptText],
8794
8989
  turnWatches,
8795
8990
  activeTurnItemIds,
8991
+ activeCompletionBlockerItemIds,
8796
8992
  activeAppServerTurnRequests,
8797
8993
  pendingOpenClawDynamicToolCompletionIds,
8798
8994
  turnCrossedToolHandoff,
@@ -8801,10 +8997,23 @@ async function runCodexAppServerAttempt(params, options = {}) {
8801
8997
  onReportExecutionNotification: reportExecutionNotification
8802
8998
  });
8803
8999
  turnCrossedToolHandoff = notificationState.turnCrossedToolHandoff;
9000
+ const finalizationHookNotification = readCodexFinalizationHookNotification(notification, thread.threadId, turnId);
9001
+ if (finalizationHookNotification?.phase === "started") {
9002
+ if (activeFinalizationHookRunIds.size === 0) finalizationHookBatchStatuses.clear();
9003
+ activeFinalizationHookRunIds.add(finalizationHookNotification.runId);
9004
+ turnWatches.disarmAssistantCompletionIdleWatch();
9005
+ }
8804
9006
  if (notificationState.isTurnTerminal) terminalTurnNotificationQueued = true;
8805
9007
  try {
8806
9008
  await waitForCodexNotificationDispatchTurn();
8807
9009
  await projector.handleNotification(notification);
9010
+ const projectedAssistantCompletionCanRelease = isAssistantCompletionReleaseNotification(notification, turnCrossedToolHandoff) || notificationState.isCurrentTurnNotification && turnCrossedToolHandoff && notification.method === "rawResponseItem/completed" && projector.canReleaseLatestTerminalAssistantAfterToolHandoff();
9011
+ if (notificationState.isCurrentTurnNotification && projectedAssistantCompletionCanRelease) {
9012
+ const completedAssistantItemId = projector.getLatestTerminalAssistantCandidate()?.itemId;
9013
+ if (rejectedFinalizationHookAssistant !== void 0 && completedAssistantItemId !== void 0 && completedAssistantItemId !== rejectedFinalizationHookAssistant.itemId) rejectedFinalizationHookAssistant = void 0;
9014
+ else if (rejectedFinalizationHookAssistant !== void 0) turnWatches.disarmAssistantCompletionIdleWatch();
9015
+ else if (activeFinalizationHookRunIds.size === 0 && !terminalTurnNotificationQueued && activeAppServerTurnRequests === 0 && activeTurnItemIds.size === 0 && activeCompletionBlockerItemIds.size === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0 && projector.hasLatestTerminalAssistantCandidateText()) turnWatches.armAssistantCompletionIdleWatch(describeNotificationActivity(notification));
9016
+ }
8808
9017
  if (notificationState.isCurrentTurnNotification && activeTurnItemIds.size === 0 && isRawFunctionToolOutputCompletionNotification(notification)) await maybeAnnounceFastModeAutoOff();
8809
9018
  } catch (error) {
8810
9019
  embeddedAgentLog.debug("codex app-server projector notification threw", {
@@ -8812,6 +9021,24 @@ async function runCodexAppServerAttempt(params, options = {}) {
8812
9021
  error
8813
9022
  });
8814
9023
  } finally {
9024
+ if (finalizationHookNotification?.phase === "completed") {
9025
+ unsettledFinalizationHookCount = Math.max(0, unsettledFinalizationHookCount - 1);
9026
+ activeFinalizationHookRunIds.delete(finalizationHookNotification.runId);
9027
+ finalizationHookBatchStatuses.set(finalizationHookNotification.runId, finalizationHookNotification.status);
9028
+ if (activeFinalizationHookRunIds.size === 0) {
9029
+ const statuses = new Set(finalizationHookBatchStatuses.values());
9030
+ if (statuses.has("blocked") && !statuses.has("stopped")) {
9031
+ const itemId = projector.getLatestTerminalAssistantCandidate()?.itemId;
9032
+ rejectedFinalizationHookAssistant = itemId ? { itemId } : {};
9033
+ turnWatches.disarmAssistantCompletionIdleWatch();
9034
+ } else rejectedFinalizationHookAssistant = void 0;
9035
+ }
9036
+ if (activeFinalizationHookRunIds.size === 0 && rejectedFinalizationHookAssistant === void 0 && !terminalTurnNotificationQueued && activeAppServerTurnRequests === 0 && activeTurnItemIds.size === 0 && activeCompletionBlockerItemIds.size === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0 && projector.hasLatestTerminalAssistantCandidateText()) turnWatches.armAssistantCompletionIdleWatch({
9037
+ lastNotificationMethod: notification.method,
9038
+ hookRunId: finalizationHookNotification.runId,
9039
+ hookStatus: finalizationHookNotification.status
9040
+ });
9041
+ }
8815
9042
  if (notificationState.isTurnTerminal) {
8816
9043
  if (notificationState.isTurnAbortMarker) projector.markAborted();
8817
9044
  if (!timedOut && !runAbortController.signal.aborted) await steeringQueue?.flushPending();
@@ -8879,14 +9106,27 @@ async function runCodexAppServerAttempt(params, options = {}) {
8879
9106
  const nativeItem = readCodexNotificationItem(notification.params);
8880
9107
  if (nativeItem?.type === "webSearch") projector.recordNativeToolOutcome(nativeItem);
8881
9108
  }
8882
- if (notificationMatchesActiveTurn) turnWatches.noteNotificationReceived(notification.method, isNativeResponseStreamDelta ? {
8883
- attemptProgress: true,
8884
- ...turnCrossedToolHandoff ? { attemptTimeoutMs: postToolRawAssistantCompletionIdleTimeoutMs } : {},
8885
- details: { lastNotificationMethod: notification.method }
8886
- } : void 0);
9109
+ if (notificationMatchesActiveTurn) {
9110
+ if (readCodexFinalizationHookNotification(notification, thread.threadId, turnId)?.phase === "started") {
9111
+ unsettledFinalizationHookCount += 1;
9112
+ turnWatches.disarmAssistantCompletionIdleWatch();
9113
+ }
9114
+ turnWatches.noteNotificationReceived(notification.method, isNativeResponseStreamDelta ? {
9115
+ attemptProgress: true,
9116
+ ...turnCrossedToolHandoff ? { attemptTimeoutMs: postToolRawAssistantCompletionIdleTimeoutMs } : {},
9117
+ details: { lastNotificationMethod: notification.method }
9118
+ } : void 0);
9119
+ }
8887
9120
  notificationQueue = notificationQueue.then(() => handleNotification(notification), () => handleNotification(notification));
8888
9121
  return notificationQueue;
8889
9122
  };
9123
+ const drainNotificationQueue = async () => {
9124
+ while (true) {
9125
+ const queued = notificationQueue;
9126
+ await queued;
9127
+ if (queued === notificationQueue) return;
9128
+ }
9129
+ };
8890
9130
  const nativeSubagentCodexHome = appServer.start.transport === "stdio" ? appServer.start.env?.CODEX_HOME ?? resolveCodexAppServerHomeDir(agentDir) : void 0;
8891
9131
  registerCodexNativeSubagentMonitor({
8892
9132
  client,
@@ -9504,14 +9744,23 @@ async function runCodexAppServerAttempt(params, options = {}) {
9504
9744
  steeringQueueRef.current = activeSteeringQueue;
9505
9745
  const handle = {
9506
9746
  kind: "embedded",
9747
+ runId: params.runId,
9507
9748
  queueMessage: async (text, optionsLocal) => activeSteeringQueue.queue(text, optionsLocal),
9508
9749
  isStreaming: () => !completed && !runAbortController.signal.aborted,
9750
+ isStopped: () => completed || timedOut || runAbortController.signal.aborted,
9751
+ isAbortable: () => !terminalOutcomeFrozen || sharedAbortAllowedAfterTerminalOutcome,
9509
9752
  isCompacting: () => projectorRef.current?.isCompacting() ?? false,
9510
9753
  sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
9511
- cancel: () => runAbortController.abort("cancelled"),
9512
- abort: () => runAbortController.abort("aborted")
9754
+ cancel: () => abortExplicitly("cancelled"),
9755
+ abort: () => abortExplicitly("aborted")
9756
+ };
9757
+ params.replyOperation?.attachBackend(handle);
9758
+ setActiveEmbeddedRun(params.sessionId, handle, params.sessionKey, params.sessionFile);
9759
+ const freezeRunTerminalOutcome = () => {
9760
+ if (terminalOutcomeFrozen) return;
9761
+ terminalOutcomeFrozen = true;
9762
+ params.abortSignal?.removeEventListener("abort", abortFromUpstream);
9513
9763
  };
9514
- setActiveEmbeddedRun(params.sessionId, handle, params.sessionKey);
9515
9764
  const notifyUserMessagePersisted = createCodexAppServerUserMessagePersistenceNotifier(params);
9516
9765
  mirrorPromptAtTurnStartBestEffort({
9517
9766
  params,
@@ -9546,12 +9795,32 @@ async function runCodexAppServerAttempt(params, options = {}) {
9546
9795
  if (runAbortController.signal.aborted) abortListener();
9547
9796
  try {
9548
9797
  await completion;
9549
- await notificationQueue;
9798
+ await drainNotificationQueue();
9799
+ const hasQuiescentCompletedAssistant = activeProjector.hasCompletedTerminalAssistantText() && activeAppServerTurnRequests === 0 && activeTurnItemIds.size === 0 && activeCompletionBlockerItemIds.size === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0 && activeFinalizationHookRunIds.size === 0 && unsettledFinalizationHookCount === 0 && rejectedFinalizationHookAssistant === void 0;
9800
+ const hasRecoverableCompletedAssistant = !turnWatches.isCompletionIdleWatchPinnedByTerminalError() && turnWatches.isAssistantCompletionIdleWatchArmed() && hasQuiescentCompletedAssistant;
9801
+ const recoveredTurnWatchTimeout = turnCompletionIdleTimedOut && !explicitCancellationObserved && !terminalTurnNotificationQueued && hasRecoverableCompletedAssistant && activeProjector.recoverCompletedTerminalAssistantAfterTurnWatchTimeout();
9802
+ if (recoveredTurnWatchTimeout) {
9803
+ embeddedAgentLog.warn("codex app-server recovered completed assistant output after missing turn completion", {
9804
+ threadId: thread.threadId,
9805
+ turnId: activeTurnId,
9806
+ timeoutKind: turnWatchTimeoutKind,
9807
+ idleMs: turnWatchTimeoutIdleMs,
9808
+ timeoutMs: turnWatchTimeoutMs
9809
+ });
9810
+ trajectoryRecorder?.recordEvent("turn.watch_timeout_recovered", {
9811
+ threadId: thread.threadId,
9812
+ turnId: activeTurnId,
9813
+ timeoutKind: turnWatchTimeoutKind,
9814
+ idleMs: turnWatchTimeoutIdleMs,
9815
+ timeoutMs: turnWatchTimeoutMs
9816
+ });
9817
+ }
9550
9818
  const result = activeProjector.buildResult(toolBridge.telemetry, { yieldDetected });
9551
- const finalAborted = result.aborted || runAbortController.signal.aborted && !clientClosedAbort;
9552
- const canUseCompletedAssistantTextAfterClientClose = activeProjector.hasCompletedTerminalAssistantText() && activeAppServerTurnRequests === 0 && activeTurnItemIds.size === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0;
9553
- const clientClosedPromptErrorForFinal = clientClosedPromptError && canUseCompletedAssistantTextAfterClientClose ? void 0 : clientClosedPromptError;
9554
- let finalPromptError = clientClosedPromptErrorForFinal ?? (turnCompletionIdleTimedOut ? turnCompletionIdleTimeoutMessage : timedOut ? "codex app-server attempt timed out" : result.promptError);
9819
+ const effectiveTimedOut = timedOut && !recoveredTurnWatchTimeout;
9820
+ const effectiveTurnCompletionIdleTimedOut = turnCompletionIdleTimedOut && !recoveredTurnWatchTimeout;
9821
+ const isFinalAborted = () => result.aborted || explicitCancellationObserved || runAbortController.signal.aborted && !clientClosedAbort && !recoveredTurnWatchTimeout;
9822
+ const clientClosedPromptErrorForFinal = clientClosedPromptError && hasRecoverableCompletedAssistant ? void 0 : clientClosedPromptError;
9823
+ let finalPromptError = clientClosedPromptErrorForFinal ?? (effectiveTurnCompletionIdleTimedOut ? turnCompletionIdleTimeoutMessage : effectiveTimedOut ? "codex app-server attempt timed out" : result.promptError);
9555
9824
  const finalPromptErrorMessage = typeof finalPromptError === "string" ? finalPromptError : finalPromptError ? formatErrorMessage(finalPromptError) : void 0;
9556
9825
  if (isInvalidCodexImagePayloadError(finalPromptErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(activeSessionFile, {
9557
9826
  phase: "turn_completed",
@@ -9580,12 +9849,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
9580
9849
  signal: runAbortController.signal
9581
9850
  });
9582
9851
  if (refreshedUsageLimitPromptError) finalPromptError = refreshedUsageLimitPromptError;
9583
- const finalPromptErrorSource = timedOut || clientClosedPromptErrorForFinal ? "prompt" : result.promptErrorSource;
9584
- const codexAppServerFailureKind = clientClosedPromptErrorForFinal ? "client_closed_before_turn_completed" : turnCompletionIdleTimedOut ? "turn_completion_idle_timeout" : void 0;
9852
+ const finalPromptErrorSource = effectiveTimedOut || clientClosedPromptErrorForFinal ? "prompt" : result.promptErrorSource;
9853
+ const codexAppServerFailureKind = clientClosedPromptErrorForFinal ? "client_closed_before_turn_completed" : effectiveTurnCompletionIdleTimedOut ? "turn_completion_idle_timeout" : void 0;
9585
9854
  const codexAppServerReplayBlockedReason = codexAppServerFailureKind ? resolveCodexAppServerReplayBlockedReason(result) : void 0;
9586
9855
  const promptTimeoutOutcome = buildCodexAppServerPromptTimeoutOutcome({
9587
9856
  result,
9588
- turnCompletionIdleTimedOut,
9857
+ turnCompletionIdleTimedOut: effectiveTurnCompletionIdleTimedOut,
9589
9858
  turnWatchTimeoutKind
9590
9859
  });
9591
9860
  const codexAppServerFailureDiagnostics = codexAppServerFailureKind === "turn_completion_idle_timeout" && turnWatchTimeoutKind === "completion" ? buildCodexAppServerTimeoutDiagnostics({
@@ -9594,35 +9863,39 @@ async function runCodexAppServerAttempt(params, options = {}) {
9594
9863
  lastActivityReason: turnWatchTimeoutLastActivityReason,
9595
9864
  details: turnWatchTimeoutDetails
9596
9865
  }) : void 0;
9866
+ const codexAppServerFailure = codexAppServerFailureKind ? {
9867
+ kind: codexAppServerFailureKind,
9868
+ ...codexAppServerFailureKind === "turn_completion_idle_timeout" && turnWatchTimeoutKind ? { turnWatchTimeoutKind } : {},
9869
+ transport: appServer.start.transport,
9870
+ threadId: thread.threadId,
9871
+ turnId: activeTurnId,
9872
+ replaySafe: codexAppServerReplayBlockedReason === void 0,
9873
+ ...codexAppServerReplayBlockedReason ? { replayBlockedReason: codexAppServerReplayBlockedReason } : {},
9874
+ ...codexAppServerFailureDiagnostics ? { diagnostics: codexAppServerFailureDiagnostics } : {}
9875
+ } : void 0;
9876
+ const finalAborted = isFinalAborted();
9877
+ const completedTurnStatus = activeProjector.getCompletedTurnStatus();
9878
+ const completedWithoutTerminalNotification = completed && !terminalTurnNotificationQueued && !timedOut && clientClosedPromptErrorForFinal === void 0;
9879
+ const attemptSucceeded = !finalAborted && !effectiveTimedOut && (finalPromptError === null || finalPromptError === void 0) && result.agentHarnessResultClassification === void 0 && (completedTurnStatus === "completed" || recoveredTurnWatchTimeout || completedWithoutTerminalNotification);
9880
+ sharedAbortAllowedAfterTerminalOutcome = shouldKeepCodexSharedAbortOpen({
9881
+ trigger: params.trigger,
9882
+ result,
9883
+ attemptSucceeded,
9884
+ explicitCancellationObserved
9885
+ });
9886
+ freezeRunTerminalOutcome();
9597
9887
  const modelCallFailureKind = classifyCodexModelCallFailureKind({
9598
9888
  error: finalPromptError,
9599
- timedOut,
9600
- turnCompletionIdleTimedOut,
9601
- runAborted: runAbortController.signal.aborted,
9602
- abortReason: runAbortController.signal.reason,
9889
+ timedOut: effectiveTimedOut,
9890
+ turnCompletionIdleTimedOut: effectiveTurnCompletionIdleTimedOut,
9891
+ runAborted: finalAborted,
9892
+ abortReason: explicitCancellationReason ?? runAbortController.signal.reason,
9603
9893
  clientClosedAbort,
9604
9894
  formatError: formatErrorMessage
9605
9895
  }) ?? (finalAborted ? "aborted" : void 0);
9606
9896
  if (modelCallFailureKind) codexModelCallDiagnostics.emitError(finalPromptError ?? "codex app-server attempt interrupted", { failureKind: modelCallFailureKind });
9607
9897
  else if (finalPromptError) codexModelCallDiagnostics.emitError(finalPromptError);
9608
9898
  else codexModelCallDiagnostics.emitCompleted(result);
9609
- recordCodexTrajectoryCompletion(trajectoryRecorder, {
9610
- attempt: params,
9611
- result,
9612
- threadId: thread.threadId,
9613
- turnId: activeTurnId,
9614
- timedOut,
9615
- yieldDetected
9616
- });
9617
- trajectoryRecorder?.recordEvent("session.ended", {
9618
- status: finalPromptError ? "error" : finalAborted || timedOut ? "interrupted" : "success",
9619
- threadId: thread.threadId,
9620
- turnId: activeTurnId,
9621
- timedOut,
9622
- yieldDetected,
9623
- promptError: normalizeCodexTrajectoryError(finalPromptError)
9624
- });
9625
- markTrajectoryEndRecorded();
9626
9899
  await mirrorTranscriptBestEffort({
9627
9900
  params,
9628
9901
  agentId: sessionAgentId,
@@ -9633,21 +9906,9 @@ async function runCodexAppServerAttempt(params, options = {}) {
9633
9906
  threadId: thread.threadId,
9634
9907
  turnId: activeTurnId
9635
9908
  });
9636
- const terminalAssistantText = collectTerminalAssistantText(result);
9637
- if (terminalAssistantText && (!assistantStreamEventEmitted || assistantStreamNeedsTerminalSnapshot) && !finalAborted && !finalPromptError) emitCodexAppServerEvent(params, {
9638
- stream: "assistant",
9639
- data: { text: terminalAssistantText }
9640
- });
9641
- if (finalPromptError) emitLifecycleTerminal({
9642
- phase: "error",
9643
- error: formatErrorMessage(finalPromptError)
9644
- });
9645
- else emitLifecycleTerminal({
9646
- phase: "end",
9647
- ...finalAborted ? { aborted: true } : {}
9648
- });
9649
9909
  if (activeContextEngine) {
9650
9910
  const activeContextEnginePluginIdLocal = resolveContextEngineOwnerPluginId(activeContextEngine);
9911
+ const isHeartbeatLifecycleRun = params.bootstrapContextRunKind === "heartbeat" || params.bootstrapContextRunKind === "commitment-only";
9651
9912
  const finalMessages = await readMirroredSessionHistoryMessages(activeTranscriptTarget) ?? historyMessages.concat(result.messagesSnapshot);
9652
9913
  await finalizeHarnessContextEngineTurn({
9653
9914
  contextEngine: activeContextEngine,
@@ -9680,7 +9941,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
9680
9941
  runMaintenance: runHarnessContextEngineMaintenance,
9681
9942
  config: params.config,
9682
9943
  warn: (message) => embeddedAgentLog.warn(message),
9683
- isHeartbeat: params.bootstrapContextRunKind === "heartbeat"
9944
+ isHeartbeat: isHeartbeatLifecycleRun
9684
9945
  });
9685
9946
  }
9686
9947
  runAgentHarnessLlmOutputHook({
@@ -9709,30 +9970,64 @@ async function runCodexAppServerAttempt(params, options = {}) {
9709
9970
  ctx: hookContext,
9710
9971
  hookRunner
9711
9972
  });
9712
- shouldDelayNativeHookRelayUnregister = activeProjector.getCompletedTurnStatus() === "completed" && !timedOut && !runAbortController.signal.aborted && !finalAborted && !finalPromptError;
9713
- if (shouldDelayNativeHookRelayUnregister) await markCodexAppServerBindingCoveredThroughTurn({
9714
- sessionFile: params.sessionFile,
9973
+ shouldDelayNativeHookRelayUnregister = completedTurnStatus === "completed" && !effectiveTimedOut && !runAbortController.signal.aborted && !finalAborted && !finalPromptError;
9974
+ if (shouldDelayNativeHookRelayUnregister) try {
9975
+ await markCodexAppServerBindingCoveredThroughTurn({
9976
+ sessionFile: params.sessionFile,
9977
+ threadId: thread.threadId,
9978
+ authProfileStore: params.authProfileStore,
9979
+ agentDir: params.agentDir,
9980
+ config: params.config
9981
+ });
9982
+ } catch (error) {
9983
+ if (!await clearCodexAppServerBindingForThread(params.sessionFile, thread.threadId, {
9984
+ authProfileStore: params.authProfileStore,
9985
+ agentDir: params.agentDir,
9986
+ config: params.config
9987
+ })) throw error;
9988
+ embeddedAgentLog.warn("codex app-server binding coverage update failed after completed turn; cleared stale binding", {
9989
+ threadId: thread.threadId,
9990
+ turnId: activeTurnId,
9991
+ error
9992
+ });
9993
+ }
9994
+ recordCodexTrajectoryCompletion(trajectoryRecorder, {
9995
+ attempt: params,
9996
+ result,
9715
9997
  threadId: thread.threadId,
9716
- authProfileStore: params.authProfileStore,
9717
- agentDir: params.agentDir,
9718
- config: params.config
9998
+ turnId: activeTurnId,
9999
+ timedOut: effectiveTimedOut,
10000
+ yieldDetected
10001
+ });
10002
+ trajectoryRecorder?.recordEvent("session.ended", {
10003
+ status: finalPromptError ? "error" : finalAborted || effectiveTimedOut ? "interrupted" : "success",
10004
+ threadId: thread.threadId,
10005
+ turnId: activeTurnId,
10006
+ timedOut: effectiveTimedOut,
10007
+ yieldDetected,
10008
+ promptError: normalizeCodexTrajectoryError(finalPromptError)
10009
+ });
10010
+ markTrajectoryEndRecorded();
10011
+ const terminalAssistantText = collectTerminalAssistantText(result);
10012
+ if (terminalAssistantText && (!assistantStreamEventEmitted || assistantStreamNeedsTerminalSnapshot) && !finalAborted && !finalPromptError) emitCodexAppServerEvent(params, {
10013
+ stream: "assistant",
10014
+ data: { text: terminalAssistantText }
10015
+ });
10016
+ if (finalPromptError) emitLifecycleTerminal({
10017
+ phase: "error",
10018
+ error: formatErrorMessage(finalPromptError)
10019
+ });
10020
+ else emitLifecycleTerminal({
10021
+ phase: "end",
10022
+ ...finalAborted ? { aborted: true } : {}
9719
10023
  });
9720
10024
  return {
9721
10025
  ...result,
9722
- timedOut,
10026
+ timedOut: effectiveTimedOut,
9723
10027
  aborted: finalAborted,
9724
10028
  promptError: finalPromptError,
9725
10029
  promptErrorSource: finalPromptErrorSource,
9726
- ...codexAppServerFailureKind ? { codexAppServerFailure: {
9727
- kind: codexAppServerFailureKind,
9728
- ...codexAppServerFailureKind === "turn_completion_idle_timeout" && turnWatchTimeoutKind ? { turnWatchTimeoutKind } : {},
9729
- transport: appServer.start.transport,
9730
- threadId: thread.threadId,
9731
- turnId: activeTurnId,
9732
- replaySafe: codexAppServerReplayBlockedReason === void 0,
9733
- ...codexAppServerReplayBlockedReason ? { replayBlockedReason: codexAppServerReplayBlockedReason } : {},
9734
- ...codexAppServerFailureDiagnostics ? { diagnostics: codexAppServerFailureDiagnostics } : {}
9735
- } } : {},
10030
+ ...codexAppServerFailure ? { codexAppServerFailure } : {},
9736
10031
  ...promptTimeoutOutcome ? { promptTimeoutOutcome } : {},
9737
10032
  systemPromptReport
9738
10033
  };
@@ -9777,9 +10072,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
9777
10072
  else nativeHookRelay.unregister();
9778
10073
  await releaseSandboxExecEnvironment();
9779
10074
  runAbortController.signal.removeEventListener("abort", abortListener);
9780
- params.abortSignal?.removeEventListener("abort", abortFromUpstream);
9781
10075
  steeringQueueRef.current?.cancel();
9782
- clearActiveEmbeddedRun(params.sessionId, handle, params.sessionKey);
10076
+ freezeRunTerminalOutcome();
10077
+ params.replyOperation?.detachBackend(handle);
10078
+ clearActiveEmbeddedRun(params.sessionId, handle, params.sessionKey, params.sessionFile);
9783
10079
  }
9784
10080
  }
9785
10081
  function readDynamicToolCallParams(value) {
@@ -9842,6 +10138,21 @@ function isCodexThreadTurnCompletedNotification(notification, threadId, turnIds)
9842
10138
  const turnId = correlation.turnId ?? correlation.nestedTurnId;
9843
10139
  return !turnIds || turnId !== void 0 && turnIds.has(turnId);
9844
10140
  }
10141
+ function readCodexFinalizationHookNotification(notification, threadId, turnId) {
10142
+ if (notification.method !== "hook/started" && notification.method !== "hook/completed") return;
10143
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
10144
+ const run = params && isJsonObject(params.run) ? params.run : void 0;
10145
+ if (params?.threadId !== threadId || params.turnId !== turnId || run?.eventName !== "stop" && run?.eventName !== "subagentStop" || typeof run.id !== "string" || !run.id) return;
10146
+ if (notification.method === "hook/started") return {
10147
+ phase: "started",
10148
+ runId: run.id
10149
+ };
10150
+ return {
10151
+ phase: "completed",
10152
+ runId: run.id,
10153
+ status: typeof run.status === "string" ? run.status : void 0
10154
+ };
10155
+ }
9845
10156
  function joinPresentSections(...sections) {
9846
10157
  return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
9847
10158
  }