@openclaw/codex 2026.6.8 → 2026.6.9-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/{notification-correlation-BGoG0N90.js → app-server-policy-KpyUye2D.js} +15 -74
  2. package/dist/attempt-notifications-Meja3TX6.js +249 -0
  3. package/dist/{client-factory-C71Xdb6z.js → client-factory-CPuoOOx6.js} +3 -2
  4. package/dist/{client-DovmVtbr.js → client-m7XiCQpO.js} +24 -2
  5. package/dist/{command-handlers-DHecBkuc.js → command-handlers-CqS0Kv3Z.js} +17 -13
  6. package/dist/{compact-GEKusemL.js → compact-CMVlldii.js} +5 -5
  7. package/dist/{computer-use-D1qxvTE_.js → computer-use-qLm7Gf35.js} +3 -3
  8. package/dist/{config-DJXpBkGf.js → config-BksX0T33.js} +160 -20
  9. package/dist/{conversation-binding-CbnMilVW.js → conversation-binding-DBof5w9J.js} +166 -58
  10. package/dist/doctor-contract-api.js +37 -3
  11. package/dist/harness.js +6 -6
  12. package/dist/index.js +108 -27
  13. package/dist/media-understanding-provider-CiwPkR58.js +463 -0
  14. package/dist/media-understanding-provider.js +1 -355
  15. package/dist/{models-DRKnOpzF.js → models-BsyV1Psm.js} +2 -2
  16. package/dist/{plugin-app-cache-key-kIqpGTQj.js → plugin-app-cache-key-CN9zBiCG.js} +17 -6
  17. package/dist/{protocol-oeJQu4rs.js → protocol-dh-ETiNd.js} +4 -1
  18. package/dist/{protocol-validators-B48C6S9O.js → protocol-validators-B19q5BIX.js} +210 -16
  19. package/dist/{native-hook-relay-C3i3FYtc.js → provider-capabilities-BOvTfMhF.js} +803 -78
  20. package/dist/{provider-C5P6352f.js → provider-qJygHQx4.js} +4 -4
  21. package/dist/provider.js +1 -1
  22. package/dist/{request-BxZ40QKY.js → request-DTIEyUrA.js} +2 -2
  23. package/dist/{run-attempt-D_q-VrpR.js → run-attempt-DRhB6bTc.js} +421 -774
  24. package/dist/{sandbox-guard-C-Yv9uwY.js → sandbox-guard-3tnjhjFb.js} +37 -10
  25. package/dist/{session-binding-ElbcSq2o.js → session-binding-BRUi8y6E.js} +27 -8
  26. package/dist/{shared-client-jFMumCOe.js → shared-client-Bg5d7VV9.js} +4 -4
  27. package/dist/{side-question-DqKLfWMB.js → side-question-BdYL4kj1.js} +106 -42
  28. package/dist/{thread-lifecycle-cban34w3.js → thread-lifecycle-CbJqqnEw.js} +912 -616
  29. package/dist/web-search-contract-api.js +10 -0
  30. package/dist/web-search-provider.runtime-DrpQ8Cyd.js +74 -0
  31. package/dist/web-search-provider.shared-BrZmlqyR.js +32 -0
  32. package/npm-shrinkwrap.json +2 -2
  33. package/openclaw.plugin.json +129 -4
  34. package/package.json +4 -4
@@ -1,34 +1,33 @@
1
- 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-DJXpBkGf.js";
2
- import { a as readCodexDynamicToolCallParams, c as readCodexTurn, i as assertCodexTurnStartResponse } from "./protocol-validators-B48C6S9O.js";
3
- import { t as isJsonObject } from "./protocol-oeJQu4rs.js";
4
- import { i as formatCodexUsageLimitErrorMessage, o as resolveCodexUsageLimitResetAtMs, s as shouldRefreshCodexRateLimitsForUsageLimitMessage } from "./provider-C5P6352f.js";
5
- 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-DovmVtbr.js";
6
- import { C as defaultCodexAppInventoryCache, D as filterCodexDynamicTools, M as projectContextEngineAssemblyForCodex, N as resolveCodexContextEngineProjectionMaxChars, O as isForcedPrivateQaCodexRuntime, P as resolveCodexContextEngineProjectionReserveTokens, T as sanitizeCodexHistoryImagePayloads, _ as buildCodexPluginThreadConfigInputFingerprint, a as buildDeveloperInstructions, c as codexDynamicToolsFingerprint, g as buildCodexPluginThreadConfig, h as isCodexAppServerProfilerEnabled, i as buildContextEngineBinding, j as resolveCodexDynamicToolsLoadingForModel, k as normalizeCodexDynamicToolName$1, l as isContextEngineBindingCompatible, m as startOrResumeThread, n as areCodexDynamicToolFingerprintsCompatible, o as buildTurnCollaborationMode, s as buildTurnStartParams, v as mergeCodexThreadConfigs, y as shouldBuildCodexPluginThreadConfig } from "./thread-lifecycle-cban34w3.js";
7
- import { i as isCodexAppServerNativeAuthProfile, l as writeCodexAppServerBinding, n as clearCodexAppServerBinding, o as readCodexAppServerBinding, r as clearCodexAppServerBindingForThread } from "./session-binding-ElbcSq2o.js";
8
- import { n as CODEX_CONTROL_METHODS, t as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-kIqpGTQj.js";
9
- import { a as resolveCodexAppServerForModelProvider, i as readCodexNotificationTurnId, l as formatCodexDisplayText, n as isCodexNotificationForTurn, o as resolveCodexAppServerForOpenClawToolPolicy, r as readCodexNotificationThreadId, t as describeCodexNotificationCorrelation } from "./notification-correlation-BGoG0N90.js";
10
- 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-jFMumCOe.js";
11
- import { i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-C-Yv9uwY.js";
12
- import { t as defaultLeasedCodexAppServerClientFactory } from "./client-factory-C71Xdb6z.js";
13
- import { S as withCodexStartupTimeout, _ as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, a as createCodexNativeHookRelay, b as resolveCodexTurnCompletionIdleTimeoutMs, c as scheduleCodexNativeHookRelayUnregister, d as emitDynamicToolErrorDiagnostic, f as emitDynamicToolStartedDiagnostic, g as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, h as handleCodexAppServerApprovalRequest, i as buildCodexNativeHookRelayDisabledConfig, l as handleCodexAppServerElicitationRequest, m as filterToolsForVisionInputs, n as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, o as resolveCodexNativeHookRelayEvents, p as emitDynamicToolTerminalDiagnostic, r as buildCodexNativeHookRelayConfig, s as resolveCodexNativeHookRelayTtlMs, u as createCodexDynamicToolBridge, v as resolveCodexStartupTimeoutMs, x as resolveCodexTurnTerminalIdleTimeoutMs, y as resolveCodexTurnAssistantCompletionIdleTimeoutMs } from "./native-hook-relay-C3i3FYtc.js";
14
- import { t as ensureCodexComputerUse } from "./computer-use-D1qxvTE_.js";
1
+ import { n as isJsonObject, t as flattenCodexDynamicToolFunctions } from "./protocol-dh-ETiNd.js";
2
+ import { A as buildCodexPluginThreadConfig, B as readCodexNotificationThreadId, D as projectContextEngineAssemblyForCodex, E as resolveCodexDynamicToolsLoadingForRuntime, L as defaultCodexAppInventoryCache, M as mergeCodexThreadConfigs, N as shouldBuildCodexPluginThreadConfig, O as resolveCodexContextEngineProjectionMaxChars, R as describeCodexNotificationCorrelation, V as readCodexNotificationTurnId, _ as resolveCodexWebSearchPlan, a as buildDeveloperInstructions, b as sanitizeCodexHistoryImagePayloads, c as codexDynamicToolsFingerprint, f as resolveCodexAppServerThreadModelSelection, h as startOrResumeThread, i as buildContextEngineBinding, j as buildCodexPluginThreadConfigInputFingerprint, k as resolveCodexContextEngineProjectionReserveTokens, l as isContextEngineBindingCompatible, n as areCodexDynamicToolFingerprintsCompatible, o as buildTurnCollaborationMode, s as buildTurnStartParams, v as isCodexAppServerProfilerEnabled } from "./thread-lifecycle-CbJqqnEw.js";
3
+ import { C as readNotificationItemId, E as updateActiveTurnItemIds, S as readCodexNotificationItem, T as shouldDisarmAssistantCompletionIdleWatch, _ as isReasoningItemCompletionNotification, a as isCodexNotificationOutsideActiveRun, b as isTerminalTurnStatus, c as isCurrentThreadOptionalTurnRequestParams, d as isNativeResponseStreamDeltaNotification, f as isNativeToolProgressNotification, g as isRawToolOutputCompletionNotification, h as isRawReasoningCompletionNotification, 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 isReasoningProgressNotification, w as readRawResponseToolCallId, x as isTurnNotification, y as isRetryableErrorNotification } from "./attempt-notifications-Meja3TX6.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-BksX0T33.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-qJygHQx4.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-m7XiCQpO.js";
8
+ import { i as isCodexAppServerNativeAuthProfile, l as writeCodexAppServerBinding, n as clearCodexAppServerBinding, o as readCodexAppServerBinding, r as clearCodexAppServerBindingForThread } from "./session-binding-BRUi8y6E.js";
9
+ import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS, t as buildCodexAppServerRuntimeFingerprint } from "./plugin-app-cache-key-CN9zBiCG.js";
10
+ import { a as formatCodexDisplayText, n as resolveCodexAppServerForOpenClawToolPolicy, t as resolveCodexAppServerForModelProvider } from "./app-server-policy-KpyUye2D.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-Bg5d7VV9.js";
12
+ import { t as defaultLeasedCodexAppServerClientFactory } from "./client-factory-CPuoOOx6.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-BOvTfMhF.js";
14
+ import { t as ensureCodexComputerUse } from "./computer-use-qLm7Gf35.js";
15
15
  import { n as rememberCodexRateLimits, t as readRecentCodexRateLimits } from "./rate-limit-cache-C7qmZ0Jh.js";
16
+ import fs from "node:fs/promises";
17
+ import path, { posix } from "node:path";
16
18
  import { asDateTimestampMs, parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
19
+ import { asFiniteNumber, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
17
20
  import { createHash, randomUUID } from "node:crypto";
18
21
  import fsSync from "node:fs";
19
- import path, { posix } from "node:path";
20
22
  import { markAuthProfileBlockedUntil, resolveAgentDir, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
21
23
  import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
22
- import { asBoolean, asFiniteNumber, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
23
- import { CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, TOOL_PROGRESS_OUTPUT_MAX_CHARS, acquireSessionWriteLock, appendSessionTranscriptMessage, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentHarnessAgentEndHook, bootstrapHarnessContextEngine, buildAgentHookContextChannelFields, buildBootstrapContextForFiles, buildEmbeddedAttemptToolRunContext, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, embeddedAgentLog, emitAgentEvent, emitSessionTranscriptUpdate, filterProviderNormalizableTools, finalizeHarnessContextEngineTurn, formatErrorMessage, formatToolAggregate, formatToolProgressOutput, getAgentHarnessHookRunner, getBeforeToolCallPolicyDiagnosticState, inferToolMetaFromArgs, isActiveHarnessContextEngine, isSubagentSessionKey, loadCodexBundleMcpThreadConfig, normalizeAgentRuntimeTools, normalizeUsage, resolveAgentHarnessBeforePromptBuildResult, resolveAttemptSpawnWorkspaceDir, resolveBootstrapFilesForRun, resolveContextEngineOwnerPluginId, resolveModelAuthMode, resolveSandboxContext, resolveSessionAgentIds as resolveSessionAgentIds$1, resolveSessionWriteLockOptions, resolveUserPath, runAgentCleanupStep, runAgentHarnessAfterCompactionHook, runAgentHarnessAfterToolCallHook, runAgentHarnessAgentEndHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessBeforeMessageWriteHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
24
+ import { CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, TOOL_PROGRESS_OUTPUT_MAX_CHARS, acquireSessionWriteLock, appendSessionTranscriptMessage, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentHarnessAgentEndHook, bootstrapHarnessContextEngine, buildBootstrapContextForFiles, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, embeddedAgentLog, emitAgentEvent, emitSessionTranscriptUpdate, finalizeHarnessContextEngineTurn, formatErrorMessage, formatToolAggregate, formatToolProgressOutput, getAgentHarnessHookRunner, getBeforeToolCallPolicyDiagnosticState, inferToolMetaFromArgs, isActiveHarnessContextEngine, loadCodexBundleMcpThreadConfig, normalizeUsage, resolveAgentHarnessBeforePromptBuildResult, resolveBootstrapFilesForRun, resolveContextEngineOwnerPluginId, resolveSandboxContext, resolveSessionAgentIds as resolveSessionAgentIds$1, resolveSessionWriteLockOptions, resolveUserPath, runAgentCleanupStep, runAgentHarnessAfterCompactionHook, runAgentHarnessAfterToolCallHook, runAgentHarnessAgentEndHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessBeforeMessageWriteHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
24
25
  import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";
25
26
  import { spawn } from "node:child_process";
26
27
  import { once } from "node:events";
27
28
  import { WebSocketServer } from "ws";
28
29
  import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
29
- import fs from "node:fs/promises";
30
30
  import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, hasPendingInternalDiagnosticEvent, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
31
- import { isToolAllowed } from "openclaw/plugin-sdk/sandbox";
32
31
  import { appendRegularFile, pathExists } from "openclaw/plugin-sdk/security-runtime";
33
32
  import { MESSAGE_TOOL_DELIVERY_HINTS } from "openclaw/plugin-sdk/message-tool-delivery-hints";
34
33
  import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "openclaw/plugin-sdk/agent-sessions";
@@ -39,12 +38,42 @@ import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
39
38
  import { createAgentHarnessTaskRuntime, deliverAgentHarnessTaskCompletion, isDurableAgentHarnessCompletionDelivery } from "openclaw/plugin-sdk/agent-harness-task-runtime";
40
39
  //#region extensions/codex/src/app-server/attempt-client-cleanup.ts
41
40
  /**
42
- * Best-effort cleanup helpers for timed-out or aborted Codex app-server turns.
41
+ * Best-effort cleanup helpers for Codex app-server startup attempts and turns.
43
42
  */
44
43
  /** Timeout for best-effort app-server turn interruption during cleanup. */
45
44
  const CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS = 5e3;
46
45
  /** Timeout for best-effort thread unsubscribe during cleanup. */
47
46
  const CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS = 5e3;
47
+ async function closeClientAndWaitIfAvailable(client) {
48
+ const closeable = client;
49
+ if (typeof closeable.closeAndWait === "function") {
50
+ await closeable.closeAndWait();
51
+ return;
52
+ }
53
+ closeable.close?.();
54
+ }
55
+ async function closeCodexStartupClientBestEffort(client) {
56
+ if (!client) return;
57
+ const unclaimedSharedClient = clearSharedCodexAppServerClientIfCurrentAndUnclaimed(client);
58
+ if (unclaimedSharedClient.closed) {
59
+ await closeClientAndWaitIfAvailable(client);
60
+ return;
61
+ }
62
+ if (unclaimedSharedClient.found) {
63
+ if (retireSharedCodexAppServerClientIfCurrent(client)?.closed) await closeClientAndWaitIfAvailable(client);
64
+ return;
65
+ }
66
+ const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
67
+ if (retiredSharedClient) {
68
+ if (retiredSharedClient.closed) await closeClientAndWaitIfAvailable(client);
69
+ return;
70
+ }
71
+ if (clearSharedCodexAppServerClientIfCurrent(client)) {
72
+ await closeClientAndWaitIfAvailable(client);
73
+ return;
74
+ }
75
+ await closeClientAndWaitIfAvailable(client);
76
+ }
48
77
  /** Sends a turn interrupt without blocking abort cleanup on app-server errors. */
49
78
  function interruptCodexTurnBestEffort(client, params) {
50
79
  const requestOptions = params.timeoutMs && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0 ? { timeoutMs: params.timeoutMs } : void 0;
@@ -293,7 +322,7 @@ async function buildCodexWorkspaceBootstrapContext(params) {
293
322
  * report for a Codex run.
294
323
  */
295
324
  function buildCodexSystemPromptReport(params) {
296
- const toolEntries = params.tools.map(buildCodexToolReportEntry);
325
+ const toolEntries = flattenCodexDynamicToolFunctions(params.tools).map(buildCodexToolReportEntry);
297
326
  const schemaChars = toolEntries.reduce((sum, tool) => sum + tool.schemaChars, 0);
298
327
  const skillsPrompt = params.skillsPrompt.trim();
299
328
  const bootstrapMaxChars = readPositiveNumber(params.attempt.config?.agents?.defaults?.bootstrapMaxChars);
@@ -389,7 +418,7 @@ function stableJsonHash(value) {
389
418
  function buildCodexBootstrapInjectionStats(params) {
390
419
  const injectedIndex = indexCodexContextFileContent(params.injectedFiles);
391
420
  const developerInstructionIndex = indexCodexContextFileContent(params.developerInstructionFiles ?? []);
392
- const memoryToolRoutedPaths = new Set((params.memoryToolRoutedBootstrapFiles ?? []).map((file) => readNonEmptyString$1(file.path)).filter(isNonEmptyString$2).map(normalizeCodexContextFilePath));
421
+ const memoryToolRoutedPaths = new Set((params.memoryToolRoutedBootstrapFiles ?? []).map((file) => readNonEmptyString$1(file.path)).filter(isNonEmptyString$1).map(normalizeCodexContextFilePath));
393
422
  return params.bootstrapFiles.map((file) => {
394
423
  const fileName = readNonEmptyString$1(file.name);
395
424
  const pathValue = readNonEmptyString$1(file.path) ?? fileName ?? "";
@@ -459,7 +488,7 @@ function buildCodexOpenClawPromptContext(params) {
459
488
  "## OpenClaw Workspace Context",
460
489
  "",
461
490
  params.workspacePromptContext.trim()
462
- ].join("\n") : void 0].filter(isNonEmptyString$2);
491
+ ].join("\n") : void 0].filter(isNonEmptyString$1);
463
492
  if (sections.length === 0) return;
464
493
  return [
465
494
  "OpenClaw runtime context for this turn:",
@@ -619,7 +648,7 @@ function renderCodexWorkspaceMemoryCollaborationInstructions(params) {
619
648
  }) : void 0, renderCodexWorkspaceMemoryReference({
620
649
  files: params.files,
621
650
  toolNames: params.toolNames
622
- })].filter(isNonEmptyString$2);
651
+ })].filter(isNonEmptyString$1);
623
652
  return sections.length > 0 ? sections.join("\n\n") : void 0;
624
653
  }
625
654
  function renderCodexMemoryRecallInstructions(params) {
@@ -628,7 +657,7 @@ function renderCodexMemoryRecallInstructions(params) {
628
657
  citationsMode: params.citationsMode
629
658
  });
630
659
  if (!memoryPrompt) return;
631
- return [memoryPrompt, renderCodexMemoryToolSearchBridge(params.toolNames)].filter(isNonEmptyString$2).join("\n").trim();
660
+ return [memoryPrompt, renderCodexMemoryToolSearchBridge(params.toolNames)].filter(isNonEmptyString$1).join("\n").trim();
632
661
  }
633
662
  function renderCodexMemoryToolSearchBridge(toolNames) {
634
663
  const memoryToolNames = toolNames.map((name) => normalizeCodexDynamicToolName(name)).filter((name) => CODEX_MEMORY_TOOL_NAMES.has(name)).toSorted();
@@ -637,7 +666,7 @@ function renderCodexMemoryToolSearchBridge(toolNames) {
637
666
  }
638
667
  /** Lists available memory tool names understood by Codex workspace memory routing. */
639
668
  function getCodexWorkspaceMemoryToolNames(tools) {
640
- const availableToolNames = new Set(tools.map((tool) => normalizeCodexDynamicToolName(tool.name)));
669
+ const availableToolNames = new Set(flattenCodexDynamicToolFunctions(tools).map((tool) => normalizeCodexDynamicToolName(tool.name)));
641
670
  return Array.from(CODEX_MEMORY_TOOL_NAMES).filter((name) => availableToolNames.has(name));
642
671
  }
643
672
  function canRouteCodexWorkspaceMemoryThroughTools(params) {
@@ -714,7 +743,7 @@ function getCodexContextFileBasename(filePath) {
714
743
  function normalizeCodexDynamicToolName(name) {
715
744
  return name.trim().toLowerCase();
716
745
  }
717
- function isNonEmptyString$2(value) {
746
+ function isNonEmptyString$1(value) {
718
747
  return typeof value === "string" && value.length > 0;
719
748
  }
720
749
  //#endregion
@@ -843,236 +872,6 @@ function classifyCodexModelCallFailureKind(params) {
843
872
  return errorMessage.includes("aborted") ? "aborted" : void 0;
844
873
  }
845
874
  //#endregion
846
- //#region extensions/codex/src/app-server/attempt-notifications.ts
847
- /**
848
- * Predicates and readers for Codex app-server notification envelopes.
849
- */
850
- const CODEX_TURN_ABORT_MARKER_START = "<turn_aborted>";
851
- const CODEX_TURN_ABORT_MARKER_END = "</turn_aborted>";
852
- const CODEX_INTERRUPTED_USER_GUIDANCE = "The user interrupted the previous turn on purpose. Any running unified exec processes may still be running in the background. If any tools/commands were aborted, they may have partially executed.";
853
- const CODEX_INTERRUPTED_DEVELOPER_GUIDANCE = "The previous turn was interrupted on purpose. Any running unified exec processes may still be running in the background. If any tools/commands were aborted, they may have partially executed.";
854
- /** Builds compact activity metadata for watchdog and diagnostic updates. */
855
- function describeNotificationActivity(notification) {
856
- if (!isJsonObject(notification.params)) return { lastNotificationMethod: notification.method };
857
- if (notification.method !== "rawResponseItem/completed") return { lastNotificationMethod: notification.method };
858
- const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
859
- if (!item) return { lastNotificationMethod: notification.method };
860
- return {
861
- lastNotificationMethod: notification.method,
862
- lastNotificationItemId: readString$6(item, "id"),
863
- lastNotificationItemType: readString$6(item, "type"),
864
- lastNotificationItemRole: readString$6(item, "role"),
865
- lastAssistantTextPreview: readRawAssistantTextPreview(item)
866
- };
867
- }
868
- /** Tracks active app-server item ids from item start/completion notifications. */
869
- function updateActiveTurnItemIds(notification, activeItemIds) {
870
- if (notification.method !== "item/started" && notification.method !== "item/completed") return;
871
- const itemId = readNotificationItemId(notification);
872
- if (!itemId) return;
873
- if (notification.method === "item/started") {
874
- activeItemIds.add(itemId);
875
- return;
876
- }
877
- activeItemIds.delete(itemId);
878
- }
879
- function isCompletedAssistantNotification(notification) {
880
- if (!isJsonObject(notification.params)) return false;
881
- if (notification.method !== "item/completed") return false;
882
- const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
883
- return Boolean(item && readString$6(item, "type") === "agentMessage" && readString$6(item, "phase") !== "commentary");
884
- }
885
- /** Returns true for completed app-server reasoning items. */
886
- function isReasoningItemCompletionNotification(notification) {
887
- if (!isJsonObject(notification.params) || notification.method !== "item/completed") return false;
888
- const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
889
- return item ? readString$6(item, "type") === "reasoning" : false;
890
- }
891
- /** Returns true for completed assistant commentary items. */
892
- function isAssistantCommentaryCompletionNotification(notification) {
893
- if (!isJsonObject(notification.params) || notification.method !== "item/completed") return false;
894
- const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
895
- return Boolean(item && readString$6(item, "type") === "agentMessage" && readString$6(item, "phase") === "commentary");
896
- }
897
- /** Returns true for completed raw response reasoning items. */
898
- function isRawReasoningCompletionNotification(notification) {
899
- if (!isJsonObject(notification.params) || notification.method !== "rawResponseItem/completed") return false;
900
- const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
901
- return item ? readString$6(item, "type") === "reasoning" : false;
902
- }
903
- /** Returns true for streamed app-server reasoning progress. */
904
- function isReasoningProgressNotification(notification) {
905
- return notification.method === "item/reasoning/textDelta" || notification.method === "item/reasoning/summaryTextDelta" || notification.method === "item/reasoning/summaryPartAdded";
906
- }
907
- /** Returns true when assistant completion can release the short idle watch. */
908
- function isAssistantCompletionReleaseNotification(notification, turnCrossedToolHandoff) {
909
- if (isCompletedAssistantNotification(notification)) return true;
910
- return !turnCrossedToolHandoff && isRawAssistantCompletionNotification(notification);
911
- }
912
- /** Returns true when a notification proves assistant output is still active. */
913
- function shouldDisarmAssistantCompletionIdleWatch(notification) {
914
- if (!isJsonObject(notification.params)) return false;
915
- if (notification.method === "item/started") return true;
916
- if (notification.method === "item/agentMessage/delta") return true;
917
- return false;
918
- }
919
- /** Reads an item id from supported notification envelope shapes. */
920
- function readNotificationItemId(notification) {
921
- if (!isJsonObject(notification.params)) return;
922
- const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
923
- return (item ? readString$6(item, "id") : void 0) ?? readString$6(notification.params, "itemId") ?? readString$6(notification.params, "id");
924
- }
925
- /** Detects completion for an OpenClaw dynamic tool result still awaited by Codex. */
926
- function isPendingOpenClawDynamicToolCompletionNotification(notification, pendingOpenClawDynamicToolCompletionIds) {
927
- if (notification.method !== "item/completed" || !isJsonObject(notification.params)) return false;
928
- const itemId = readNotificationItemId(notification);
929
- if (!itemId || !pendingOpenClawDynamicToolCompletionIds.has(itemId)) return false;
930
- const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
931
- const itemType = item ? readString$6(item, "type") : void 0;
932
- return itemType === void 0 || itemType === "dynamicToolCall";
933
- }
934
- /** Returns true for raw response tool-output completion notifications. */
935
- function isRawToolOutputCompletionNotification(notification) {
936
- if (notification.method !== "rawResponseItem/completed" || !isJsonObject(notification.params)) return false;
937
- const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
938
- return item ? readString$6(item, "type") === "custom_tool_call_output" : false;
939
- }
940
- /** Returns true for progress on Codex-native tool item types. */
941
- function isNativeToolProgressNotification(notification) {
942
- if (notification.method !== "item/started" && notification.method !== "item/completed" && notification.method !== "item/updated") return false;
943
- if (!isJsonObject(notification.params)) return false;
944
- const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
945
- switch (item ? readString$6(item, "type") : void 0) {
946
- case "commandExecution":
947
- case "fileChange":
948
- case "mcpToolCall":
949
- case "webSearch": return true;
950
- default: return false;
951
- }
952
- }
953
- /** Returns true for raw native response stream delta events. */
954
- function isNativeResponseStreamDeltaNotification(notification) {
955
- return notification.method.startsWith("response.") && notification.method.endsWith(".delta");
956
- }
957
- /** Returns true for file-change patch update notifications. */
958
- function isFileChangePatchUpdatedNotification(notification) {
959
- return notification.method === "item/fileChange/patchUpdated" && isJsonObject(notification.params);
960
- }
961
- /** Returns true for raw assistant message progress with readable text. */
962
- function isRawAssistantProgressNotification(notification) {
963
- if (notification.method !== "rawResponseItem/completed" || !isJsonObject(notification.params)) return false;
964
- const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
965
- return Boolean(item && readString$6(item, "type") === "message" && readString$6(item, "role") === "assistant" && readRawAssistantTextPreview(item));
966
- }
967
- /** Returns true for raw assistant completion outside commentary phase. */
968
- function isRawAssistantCompletionNotification(notification) {
969
- if (!isRawAssistantProgressNotification(notification) || !isJsonObject(notification.params)) return false;
970
- const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
971
- return Boolean(item && readString$6(item, "phase") !== "commentary");
972
- }
973
- function readRawAssistantTextPreview(item) {
974
- if (readString$6(item, "role") !== "assistant" || !Array.isArray(item.content)) return;
975
- const text = item.content.flatMap((content) => {
976
- if (!isJsonObject(content)) return [];
977
- const contentText = readString$6(content, "text");
978
- return contentText ? [contentText] : [];
979
- }).join("\n").trim();
980
- if (!text) return;
981
- return text.length > 240 ? `${text.slice(0, 237)}...` : text;
982
- }
983
- /** Returns true when notification params correlate to a specific thread/turn. */
984
- function isTurnNotification(value, threadId, turnId) {
985
- return isCodexNotificationForTurn(value, threadId, turnId);
986
- }
987
- /** Returns true when a correlated notification belongs to another active run. */
988
- function isCodexNotificationOutsideActiveRun(correlation) {
989
- if (!Boolean(correlation.threadId || correlation.nestedTurnThreadId)) return false;
990
- if (!correlation.matchesActiveThread) return true;
991
- return Boolean(correlation.turnId || correlation.nestedTurnId) && correlation.matchesActiveTurn === false;
992
- }
993
- /** Checks request params that must contain the current thread and turn ids. */
994
- function isCurrentThreadTurnRequestParams(value, threadId, turnId) {
995
- if (!isJsonObject(value)) return false;
996
- return readString$6(value, "threadId") === threadId && readString$6(value, "turnId") === turnId;
997
- }
998
- /** Checks approval request params, accepting `conversationId` as thread id. */
999
- function isCurrentApprovalTurnRequestParams(value, threadId, turnId) {
1000
- if (!isJsonObject(value)) return false;
1001
- return (readString$6(value, "threadId") ?? readString$6(value, "conversationId")) === threadId && readString$6(value, "turnId") === turnId;
1002
- }
1003
- /** Checks request params where `turnId` may be omitted or null for the thread. */
1004
- function isCurrentThreadOptionalTurnRequestParams(value, threadId, turnId) {
1005
- if (!isJsonObject(value) || readString$6(value, "threadId") !== threadId) return false;
1006
- const requestTurnId = value.turnId;
1007
- return requestTurnId === null || requestTurnId === void 0 || requestTurnId === turnId;
1008
- }
1009
- /** Returns true for app-server error notifications that will retry. */
1010
- function isRetryableErrorNotification(value) {
1011
- if (!isJsonObject(value)) return false;
1012
- return readBoolean$1(value, "willRetry") === true || readBoolean$1(value, "will_retry") === true;
1013
- }
1014
- /** Returns true for terminal app-server thread status strings. */
1015
- function isTerminalTurnStatus(status) {
1016
- return status === "completed" || status === "interrupted" || status === "failed";
1017
- }
1018
- /**
1019
- * Detects Codex's synthetic interrupted-turn marker while ignoring the current
1020
- * user prompt echoed through raw response events.
1021
- */
1022
- function isCodexTurnAbortMarkerNotification(notification, options = {}) {
1023
- if (notification.method !== "rawResponseItem/completed" || !isJsonObject(notification.params)) return false;
1024
- const item = notification.params.item;
1025
- const role = isJsonObject(item) ? readString$6(item, "role") : void 0;
1026
- if (!isJsonObject(item) || role !== "user" && role !== "developer") return false;
1027
- const text = extractRawResponseItemText(item).trim();
1028
- const currentPromptTexts = [options.currentPromptText, ...options.currentPromptTexts ?? []].filter(isNonEmptyString$1).map((prompt) => prompt.trim());
1029
- if (role === "user" && currentPromptTexts.includes(text)) return false;
1030
- const markerBody = readCodexTurnAbortMarkerBody(text);
1031
- return markerBody === CODEX_INTERRUPTED_USER_GUIDANCE || markerBody === CODEX_INTERRUPTED_DEVELOPER_GUIDANCE;
1032
- }
1033
- function readCodexTurnAbortMarkerBody(text) {
1034
- if (!text.startsWith(CODEX_TURN_ABORT_MARKER_START) || !text.endsWith(CODEX_TURN_ABORT_MARKER_END)) return;
1035
- return text.slice(14, -15).trim();
1036
- }
1037
- function extractRawResponseItemText(item) {
1038
- const content = item.content;
1039
- if (!Array.isArray(content)) return "";
1040
- return content.flatMap((entry) => {
1041
- if (!isJsonObject(entry)) return [];
1042
- const type = readString$6(entry, "type");
1043
- if (type !== "input_text" && type !== "text") return [];
1044
- const text = readString$6(entry, "text");
1045
- return text ? [text] : [];
1046
- }).join("");
1047
- }
1048
- function readString$6(record, key) {
1049
- const value = record[key];
1050
- return typeof value === "string" ? value : void 0;
1051
- }
1052
- function readBoolean$1(record, key) {
1053
- return asBoolean(record[key]);
1054
- }
1055
- /** Reads a typed Codex item from notification params when id/type are present. */
1056
- function readCodexNotificationItem(params) {
1057
- if (!isJsonObject(params) || !isJsonObject(params.item)) return;
1058
- const item = params.item;
1059
- return typeof item.id === "string" && typeof item.type === "string" ? item : void 0;
1060
- }
1061
- /** Maps Codex item types to the tool name shown in execution progress. */
1062
- function codexExecutionToolName(item) {
1063
- if (item.type === "dynamicToolCall" && typeof item.tool === "string") return item.tool;
1064
- if (item.type === "mcpToolCall" && typeof item.tool === "string") {
1065
- const server = typeof item.server === "string" && item.server ? item.server : void 0;
1066
- return server ? `${server}.${item.tool}` : item.tool;
1067
- }
1068
- if (item.type === "commandExecution") return "bash";
1069
- if (item.type === "fileChange") return "apply_patch";
1070
- if (item.type === "webSearch") return "web_search";
1071
- }
1072
- function isNonEmptyString$1(value) {
1073
- return typeof value === "string" && value.length > 0;
1074
- }
1075
- //#endregion
1076
875
  //#region extensions/codex/src/app-server/attempt-notification-state.ts
1077
876
  /**
1078
877
  * State machine for Codex app-server turn notifications and idle-watch updates.
@@ -1224,6 +1023,7 @@ function buildCodexTurnStartFailureResult(params) {
1224
1023
  assistantTexts: [],
1225
1024
  toolMetas: [],
1226
1025
  lastAssistant: void 0,
1026
+ currentAttemptAssistant: void 0,
1227
1027
  didSendViaMessagingTool: false,
1228
1028
  messagingToolSentTexts: [],
1229
1029
  messagingToolSentMediaUrls: [],
@@ -1249,396 +1049,6 @@ function isInvalidCodexImagePayloadError(message) {
1249
1049
  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);
1250
1050
  }
1251
1051
  //#endregion
1252
- //#region extensions/codex/src/app-server/dynamic-tool-build.ts
1253
- /**
1254
- * Builds the Codex app-server dynamic tool list for one turn, including
1255
- * OpenClaw-owned tools, Codex native-tool fallback rules, sandbox shell shims,
1256
- * and provider allowlist normalization.
1257
- */
1258
- const CODEX_NATIVE_SANDBOX_TOOL_REQUIREMENTS = [
1259
- "exec",
1260
- "process",
1261
- "read",
1262
- "write",
1263
- "edit",
1264
- "apply_patch"
1265
- ];
1266
- const CODEX_MEMORY_FLUSH_DYNAMIC_TOOL_ALLOW = new Set(["read", "write"]);
1267
- /** Splits sandbox and run session keys so tool calls can bind to both scopes when needed. */
1268
- function resolveOpenClawCodingToolsSessionKeys(params, sandboxSessionKey) {
1269
- return {
1270
- sessionKey: sandboxSessionKey,
1271
- runSessionKey: params.sessionKey && params.sessionKey !== sandboxSessionKey ? params.sessionKey : void 0
1272
- };
1273
- }
1274
- /** Resolves the channel id that hook events should target for this Codex app-server turn. */
1275
- function resolveCodexAppServerHookChannelId(params, sandboxSessionKey) {
1276
- return buildAgentHookContextChannelFields({
1277
- sessionKey: sandboxSessionKey,
1278
- messageChannel: params.messageChannel,
1279
- messageProvider: params.messageProvider,
1280
- currentChannelId: params.currentChannelId,
1281
- messageTo: params.messageTo
1282
- }).channelId;
1283
- }
1284
- const CODEX_DYNAMIC_TOOL_BUILD_WARN_TOTAL_MS = 1e3;
1285
- const CODEX_DYNAMIC_TOOL_BUILD_WARN_STAGE_MS = 500;
1286
- /** Creates cheap optional timing instrumentation for the dynamic-tool hot path. */
1287
- function createCodexDynamicToolBuildStageTracker(options = {}) {
1288
- if (!options.enabled) return {
1289
- mark() {},
1290
- snapshot() {
1291
- return {
1292
- totalMs: 0,
1293
- stages: []
1294
- };
1295
- }
1296
- };
1297
- const startedAt = Date.now();
1298
- let previousAt = startedAt;
1299
- const stages = [];
1300
- const toMs = (value) => Math.max(0, Math.round(value));
1301
- return {
1302
- mark(name) {
1303
- const currentAt = Date.now();
1304
- stages.push({
1305
- name,
1306
- durationMs: toMs(currentAt - previousAt),
1307
- elapsedMs: toMs(currentAt - startedAt)
1308
- });
1309
- previousAt = currentAt;
1310
- },
1311
- snapshot() {
1312
- return {
1313
- totalMs: toMs(Date.now() - startedAt),
1314
- stages: stages.slice()
1315
- };
1316
- }
1317
- };
1318
- }
1319
- /** Returns true when dynamic-tool construction is slow enough to warrant a warning log. */
1320
- function shouldWarnCodexDynamicToolBuildStageSummary(summary) {
1321
- return summary.totalMs >= CODEX_DYNAMIC_TOOL_BUILD_WARN_TOTAL_MS || summary.stages.some((stage) => stage.durationMs >= CODEX_DYNAMIC_TOOL_BUILD_WARN_STAGE_MS);
1322
- }
1323
- /** Formats per-stage timings into the compact form used by Codex app-server logs. */
1324
- function formatCodexDynamicToolBuildStageSummary(summary) {
1325
- return summary.stages.length > 0 ? summary.stages.map((stage) => `${stage.name}:${stage.durationMs}ms@${stage.elapsedMs}ms`).join(",") : "none";
1326
- }
1327
- /** Builds, filters, and normalizes Codex-compatible runtime tools for a single turn. */
1328
- async function buildDynamicTools(input) {
1329
- const { params } = input;
1330
- if (params.disableTools || !supportsModelTools(params.model)) return [];
1331
- const toolBuildStages = createCodexDynamicToolBuildStageTracker({ enabled: input.profilerEnabled });
1332
- const modelHasVision = params.model.input?.includes("image") ?? false;
1333
- const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, input.sessionAgentId);
1334
- const createOpenClawCodingTools = (await import("openclaw/plugin-sdk/agent-harness")).createOpenClawCodingTools;
1335
- toolBuildStages.mark("load-agent-harness-tools");
1336
- const sessionKeys = resolveOpenClawCodingToolsSessionKeys(params, input.sandboxSessionKey);
1337
- const allTools = createOpenClawCodingTools({
1338
- agentId: input.sessionAgentId,
1339
- ...buildEmbeddedAttemptToolRunContext(params),
1340
- exec: {
1341
- ...params.execOverrides,
1342
- config: params.config,
1343
- elevated: params.bashElevated
1344
- },
1345
- sandbox: input.sandbox,
1346
- messageProvider: params.messageChannel ?? params.messageProvider,
1347
- agentAccountId: params.agentAccountId,
1348
- messageTo: params.messageTo,
1349
- messageThreadId: params.messageThreadId,
1350
- groupId: params.groupId,
1351
- groupChannel: params.groupChannel,
1352
- groupSpace: params.groupSpace,
1353
- spawnedBy: params.spawnedBy,
1354
- senderId: params.senderId,
1355
- senderName: params.senderName,
1356
- senderUsername: params.senderUsername,
1357
- senderE164: params.senderE164,
1358
- allowGatewaySubagentBinding: params.allowGatewaySubagentBinding || isForcedPrivateQaCodexRuntime(),
1359
- ...sessionKeys,
1360
- sessionId: params.sessionId,
1361
- runId: params.runId,
1362
- agentDir,
1363
- cwd: input.effectiveCwd ?? input.effectiveWorkspace,
1364
- workspaceDir: input.effectiveWorkspace,
1365
- spawnWorkspaceDir: input.effectiveCwd && input.effectiveCwd !== input.effectiveWorkspace ? input.resolvedWorkspace : resolveAttemptSpawnWorkspaceDir({
1366
- sandbox: input.sandbox,
1367
- resolvedWorkspace: input.resolvedWorkspace
1368
- }),
1369
- config: params.config,
1370
- authProfileStore: params.toolAuthProfileStore ?? params.authProfileStore,
1371
- abortSignal: input.runAbortController.signal,
1372
- emitBeforeToolCallDiagnostics: false,
1373
- modelProvider: params.model.provider,
1374
- modelId: params.modelId,
1375
- modelCompat: params.model.compat && typeof params.model.compat === "object" ? params.model.compat : void 0,
1376
- modelApi: params.model.api,
1377
- modelContextWindowTokens: params.model.contextWindow,
1378
- modelAuthMode: resolveModelAuthMode(params.model.provider, params.config, params.toolAuthProfileStore ?? params.authProfileStore, { workspaceDir: input.effectiveWorkspace }),
1379
- suppressManagedWebSearch: false,
1380
- currentChannelId: params.currentChannelId,
1381
- hookChannelId: resolveCodexAppServerHookChannelId(params, input.sandboxSessionKey),
1382
- currentThreadTs: params.currentThreadTs,
1383
- currentMessageId: params.currentMessageId,
1384
- replyToMode: params.replyToMode,
1385
- hasRepliedRef: params.hasRepliedRef,
1386
- modelHasVision,
1387
- requireExplicitMessageTarget: params.requireExplicitMessageTarget ?? isSubagentSessionKey(params.sessionKey),
1388
- sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
1389
- disableMessageTool: params.disableMessageTool,
1390
- forceMessageTool: shouldForceMessageTool(params),
1391
- enableHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
1392
- forceHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
1393
- onYield: (message) => {
1394
- input.onYieldDetected();
1395
- input.onCodexAppServerEvent?.({
1396
- stream: "codex_app_server.tool",
1397
- data: {
1398
- name: "sessions_yield",
1399
- message
1400
- }
1401
- });
1402
- },
1403
- recordToolPrepStage: (name) => {
1404
- toolBuildStages.mark(name);
1405
- }
1406
- });
1407
- toolBuildStages.mark("create-openclaw-coding-tools");
1408
- const preNormalizationDiagnostics = [];
1409
- const readableAllToolProjection = filterProviderNormalizableTools(allTools);
1410
- preNormalizationDiagnostics.push(...readableAllToolProjection.diagnostics);
1411
- const readableAllTools = [...readableAllToolProjection.tools];
1412
- const codexFilteredTools = addNodeShellDynamicToolsIfNeeded(addSandboxShellDynamicToolsIfAvailable(isCodexMemoryFlushRun(params) ? filterCodexMemoryFlushDynamicTools(readableAllTools) : filterCodexDynamicTools(readableAllTools, input.pluginConfig), readableAllTools, input), readableAllTools, input);
1413
- toolBuildStages.mark("codex-filtering");
1414
- const visionFilteredTools = filterToolsForVisionInputs(codexFilteredTools, {
1415
- modelHasVision,
1416
- hasInboundImages: (params.images?.length ?? 0) > 0
1417
- });
1418
- toolBuildStages.mark("vision-filtering");
1419
- const filteredTools = filterCodexDynamicToolsForAllowlist(visionFilteredTools, includeForcedCodexDynamicToolAllow(params.toolsAllow, params));
1420
- toolBuildStages.mark("allowlist-filter");
1421
- const normalizedTools = normalizeAgentRuntimeTools({
1422
- runtimePlan: input.ignoreRuntimePlan ? void 0 : params.runtimePlan,
1423
- tools: filteredTools,
1424
- provider: params.provider,
1425
- config: params.config,
1426
- workspaceDir: input.effectiveWorkspace,
1427
- env: process.env,
1428
- modelId: params.modelId,
1429
- modelApi: params.model.api,
1430
- model: params.model,
1431
- onPreNormalizationSchemaDiagnostics: (diagnostics) => preNormalizationDiagnostics.push(...diagnostics)
1432
- });
1433
- toolBuildStages.mark("runtime-normalization");
1434
- if (preNormalizationDiagnostics.length > 0) embeddedAgentLog.warn(`codex app-server quarantined ${preNormalizationDiagnostics.length} unsupported runtime tool schema${preNormalizationDiagnostics.length === 1 ? "" : "s"} before dynamic tool registration`, {
1435
- runId: params.runId,
1436
- sessionId: params.sessionId,
1437
- diagnostics: preNormalizationDiagnostics.map((diagnostic) => ({
1438
- index: diagnostic.toolIndex,
1439
- tool: diagnostic.toolName,
1440
- violations: diagnostic.violations.slice(0, 12),
1441
- violationCount: diagnostic.violations.length
1442
- }))
1443
- });
1444
- const summary = toolBuildStages.snapshot();
1445
- if (shouldWarnCodexDynamicToolBuildStageSummary(summary)) {
1446
- const phase = input.forceHeartbeatTool ? "registered-tools" : "runtime-tools";
1447
- embeddedAgentLog.warn(`codex app-server dynamic tool build timings runId=${params.runId} sessionId=${params.sessionId} phase=${phase} totalMs=${summary.totalMs} stages=${formatCodexDynamicToolBuildStageSummary(summary)}`, {
1448
- runId: params.runId,
1449
- sessionId: params.sessionId,
1450
- phase,
1451
- totalMs: summary.totalMs,
1452
- stages: summary.stages,
1453
- allToolCount: readableAllTools.length,
1454
- codexFilteredToolCount: codexFilteredTools.length,
1455
- visionFilteredToolCount: visionFilteredTools.length,
1456
- filteredToolCount: filteredTools.length,
1457
- normalizedToolCount: normalizedTools.length,
1458
- forceHeartbeatTool: input.forceHeartbeatTool === true,
1459
- ignoreRuntimePlan: input.ignoreRuntimePlan === true,
1460
- nativeToolSurfaceEnabled: input.nativeToolSurfaceEnabled === true
1461
- });
1462
- }
1463
- return normalizedTools;
1464
- }
1465
- /** Preserves delivery-critical tools when a narrow allowlist would otherwise hide them. */
1466
- function includeForcedCodexDynamicToolAllow(toolsAllow, params) {
1467
- if (toolsAllow === void 0 || hasWildcardCodexToolsAllow(toolsAllow)) return toolsAllow;
1468
- const forcedToolNames = shouldForceMessageTool(params) ? ["message"] : [];
1469
- if (forcedToolNames.length === 0) return toolsAllow;
1470
- if (toolsAllow.length === 0) return forcedToolNames;
1471
- const normalized = new Set(toolsAllow.map((name) => normalizeCodexDynamicToolName$1(name)));
1472
- const missingToolNames = forcedToolNames.filter((toolName) => !normalized.has(normalizeCodexDynamicToolName$1(toolName)));
1473
- return missingToolNames.length === 0 ? toolsAllow : [...toolsAllow, ...missingToolNames];
1474
- }
1475
- /** Decides whether Codex native code mode can own shell/file tools for this turn. */
1476
- function shouldEnableCodexAppServerNativeToolSurface(params, sandbox, options = {}) {
1477
- if (isCodexMemoryFlushRun(params)) return false;
1478
- if (isCodexNativeExecutionBlockedByNodeExecHost(params, {
1479
- agentId: options.agentId,
1480
- runtimeSessionKey: options.runtimeSessionKey,
1481
- sandbox
1482
- })) return false;
1483
- const toolsAllow = includeForcedCodexDynamicToolAllow(params.toolsAllow, params);
1484
- if (toolsAllow === void 0) return canCodexAppServerNativeToolSurfaceHonorSandbox(sandbox, options);
1485
- return hasWildcardCodexToolsAllow(toolsAllow) && canCodexAppServerNativeToolSurfaceHonorSandbox(sandbox, options);
1486
- }
1487
- /** Returns true when OpenClaw policy requires the Node-owned exec/process tools instead. */
1488
- function isCodexNativeExecutionBlockedByNodeExecHost(params, options = {}) {
1489
- return !resolveCodexNativeExecutionPolicy({
1490
- config: params.config,
1491
- sessionKey: resolveCodexRuntimePolicySessionKey(params, options.runtimeSessionKey),
1492
- sessionId: params.sessionId,
1493
- agentId: options.agentId,
1494
- execOverrides: params.execOverrides,
1495
- sandboxAvailable: options.sandbox?.enabled,
1496
- readRuntimeSessionEntry: true
1497
- }).nativeToolSurfaceAllowed;
1498
- }
1499
- function resolveCodexRuntimePolicySessionKey(params, runtimeSessionKey) {
1500
- return runtimeSessionKey?.trim() || params.sandboxSessionKey?.trim() || params.sessionKey?.trim() || params.sessionId;
1501
- }
1502
- function canCodexAppServerNativeToolSurfaceHonorSandbox(sandbox, options = {}) {
1503
- if (!sandbox?.enabled) return true;
1504
- if (options.sandboxExecServerEnabled === true && sandbox.backend && canSandboxToolPolicyExposeCodexNativeToolSurface(sandbox)) return true;
1505
- return false;
1506
- }
1507
- function canSandboxToolPolicyExposeCodexNativeToolSurface(sandbox) {
1508
- return CODEX_NATIVE_SANDBOX_TOOL_REQUIREMENTS.every((toolName) => isToolAllowed(sandbox.tools, toolName));
1509
- }
1510
- function isCodexMemoryFlushRun(params) {
1511
- return params?.trigger === "memory" && Boolean(params.memoryFlushWritePath?.trim());
1512
- }
1513
- function filterCodexMemoryFlushDynamicTools(tools) {
1514
- return tools.filter((tool) => CODEX_MEMORY_FLUSH_DYNAMIC_TOOL_ALLOW.has(normalizeCodexDynamicToolName$1(tool.name)));
1515
- }
1516
- /** Requires a Codex sandbox environment only when native tools must run inside OpenClaw sandboxing. */
1517
- function shouldRequireCodexSandboxExecServerEnvironment(params) {
1518
- return Boolean(params.sandbox?.enabled && params.nativeToolSurfaceEnabled && params.sandboxExecServerEnabled);
1519
- }
1520
- /** Selects the sandbox exec-server environment passed through the Codex app-server protocol. */
1521
- function resolveCodexSandboxEnvironmentSelection(environment, nativeToolSurfaceEnabled) {
1522
- return environment && nativeToolSurfaceEnabled ? [environment] : void 0;
1523
- }
1524
- /** Chooses the cwd visible to Codex native execution after sandbox exec-server setup. */
1525
- function resolveCodexAppServerExecutionCwd(params) {
1526
- return params.environment && params.nativeToolSurfaceEnabled ? params.environment.cwd : params.effectiveCwd;
1527
- }
1528
- /** Converts OpenClaw sandbox networking into Codex's external-sandbox policy shape. */
1529
- function resolveCodexExternalSandboxPolicyForOpenClawSandbox(sandbox) {
1530
- return {
1531
- type: "externalSandbox",
1532
- networkAccess: codexNetworkAccessForOpenClawSandbox(sandbox) ? "enabled" : "restricted"
1533
- };
1534
- }
1535
- function codexNetworkAccessForOpenClawSandbox(sandbox) {
1536
- if (sandbox?.backendId !== "docker") return true;
1537
- const network = sandbox?.docker?.network?.trim().toLowerCase();
1538
- return Boolean(network && network !== "none");
1539
- }
1540
- /** Returns a Codex config copy with app-server Codex plugin loading disabled for thread tools. */
1541
- function disableCodexPluginThreadConfig(pluginConfig) {
1542
- const config = readCodexPluginConfig(pluginConfig);
1543
- return {
1544
- ...config,
1545
- codexPlugins: {
1546
- ...config.codexPlugins,
1547
- enabled: false
1548
- }
1549
- };
1550
- }
1551
- /** Adds sandbox_exec/process aliases when native Code Mode cannot directly honor the sandbox. */
1552
- function addSandboxShellDynamicToolsIfAvailable(filteredTools, allTools, input) {
1553
- if (!shouldExposeSandboxExecDynamicTool(input) || isSandboxShellDynamicToolExcluded(input.pluginConfig)) return filteredTools;
1554
- const execTool = allTools.find((tool) => normalizeCodexDynamicToolName$1(tool.name) === "exec");
1555
- const processTool = allTools.find((tool) => normalizeCodexDynamicToolName$1(tool.name) === "process");
1556
- if (!execTool || !processTool) return filteredTools;
1557
- const sandboxExecTool = {
1558
- ...execTool,
1559
- name: "sandbox_exec",
1560
- description: "Run a shell command through OpenClaw's configured sandbox backend for this session. Use when OpenClaw sandboxing is active or when a command must execute in the sandbox backend, such as an SSH-backed sandbox or Docker container-path bind layout. Use Codex's native shell only when no OpenClaw sandbox is active and native Code Mode is available.",
1561
- execute: async (toolCallId, args, signal, onUpdate) => {
1562
- const result = await execTool.execute(toolCallId, args, signal, onUpdate);
1563
- return {
1564
- ...result,
1565
- content: result.content.map((item) => item.type === "text" ? Object.assign({}, item, { text: item.text.replace("Use process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.", "Use sandbox_process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.") }) : item)
1566
- };
1567
- }
1568
- };
1569
- const sandboxProcessTool = {
1570
- ...processTool,
1571
- name: "sandbox_process",
1572
- description: "Manage sandbox_exec sessions that were started through OpenClaw's configured sandbox backend for this session: list, poll, log, write, send-keys, submit, paste, kill, clear, or remove. Use only for sandbox_exec follow-up; use Codex's native shell session handling only when no OpenClaw sandbox is active and native Code Mode is available."
1573
- };
1574
- return [
1575
- ...filteredTools,
1576
- sandboxExecTool,
1577
- sandboxProcessTool
1578
- ];
1579
- }
1580
- function shouldExposeSandboxExecDynamicTool(input) {
1581
- if (isCodexMemoryFlushRun(input.params)) return false;
1582
- if (isCodexNativeExecutionBlockedByNodeExecHost(input.params, {
1583
- agentId: input.sessionAgentId,
1584
- runtimeSessionKey: input.sandboxSessionKey,
1585
- sandbox: input.sandbox
1586
- })) return false;
1587
- const backendId = input.sandbox?.enabled ? input.sandbox.backendId.trim().toLowerCase() : "";
1588
- return Boolean(backendId && input.nativeToolSurfaceEnabled === false);
1589
- }
1590
- function isCodexDynamicToolExcluded(config, names) {
1591
- const normalizedNames = new Set(names.map((name) => normalizeCodexDynamicToolName$1(name)));
1592
- return (config.codexDynamicToolsExclude ?? []).some((name) => {
1593
- const normalized = normalizeCodexDynamicToolName$1(name);
1594
- return normalizedNames.has(normalized);
1595
- });
1596
- }
1597
- function isSandboxShellDynamicToolExcluded(config) {
1598
- return isCodexDynamicToolExcluded(config, [
1599
- "exec",
1600
- "sandbox_exec",
1601
- "process",
1602
- "sandbox_process"
1603
- ]);
1604
- }
1605
- function addNodeShellDynamicToolsIfNeeded(filteredTools, allTools, input) {
1606
- if (isCodexMemoryFlushRun(input.params) || !isCodexNativeExecutionBlockedByNodeExecHost(input.params, {
1607
- agentId: input.sessionAgentId,
1608
- runtimeSessionKey: input.sandboxSessionKey,
1609
- sandbox: input.sandbox
1610
- })) return filteredTools;
1611
- let next = filteredTools;
1612
- for (const toolName of ["exec", "process"]) {
1613
- if (isCodexDynamicToolExcluded(input.pluginConfig, [toolName])) continue;
1614
- if (next.some((tool) => normalizeCodexDynamicToolName$1(tool.name) === toolName)) continue;
1615
- const tool = allTools.find((candidate) => normalizeCodexDynamicToolName$1(candidate.name) === toolName);
1616
- if (!tool) continue;
1617
- if (next === filteredTools) next = [...filteredTools];
1618
- next.push(tool);
1619
- }
1620
- return next;
1621
- }
1622
- /** Applies a normalized tool allowlist while preserving sandbox shell aliases for exec/process. */
1623
- function filterCodexDynamicToolsForAllowlist(tools, toolsAllow) {
1624
- if (!toolsAllow) return tools;
1625
- if (toolsAllow.length === 0) return [];
1626
- if (hasWildcardCodexToolsAllow(toolsAllow)) return tools;
1627
- const allowSet = new Set(toolsAllow.map((name) => normalizeCodexDynamicToolName$1(name)).filter(Boolean));
1628
- return tools.filter((tool) => {
1629
- const normalized = normalizeCodexDynamicToolName$1(tool.name);
1630
- return allowSet.has(normalized) || normalized === "sandbox_exec" && allowSet.has("exec") || normalized === "sandbox_process" && (allowSet.has("exec") || allowSet.has("process"));
1631
- });
1632
- }
1633
- /** Detects the wildcard allowlist marker after Codex tool-name normalization. */
1634
- function hasWildcardCodexToolsAllow(toolsAllow) {
1635
- return toolsAllow.some((name) => normalizeCodexDynamicToolName$1(name) === "*");
1636
- }
1637
- /** Forces message delivery through the message tool when the source channel requires it. */
1638
- function shouldForceMessageTool(params) {
1639
- return params.disableMessageTool !== true && params.sourceReplyDeliveryMode === "message_tool_only";
1640
- }
1641
- //#endregion
1642
1052
  //#region extensions/codex/src/app-server/sandbox-exec-server/json-rpc.ts
1643
1053
  /** JSON-RPC error code used when a sandbox exec-server method is unknown. */
1644
1054
  const JSON_RPC_NOT_FOUND = -32004;
@@ -3039,39 +2449,17 @@ async function startCodexAttemptThread(params) {
3039
2449
  await releaseStartupResourcesOnTimeout?.();
3040
2450
  releaseSharedClientLease?.();
3041
2451
  releaseSharedClientLease = void 0;
3042
- await closeAbandonedStartupClient(startupClientForAbandonedRequestCleanup);
2452
+ await closeCodexStartupClientBestEffort(startupClientForAbandonedRequestCleanup);
3043
2453
  startupClientForAbandonedRequestCleanup = void 0;
3044
2454
  },
3045
2455
  operation: async () => {
3046
2456
  const threadConfig = mergeCodexThreadConfigs(params.bundleMcpThreadConfig?.configPatch);
3047
2457
  const pluginThreadConfigRequired = !params.nativeToolSurfaceEnabled || shouldBuildCodexPluginThreadConfig(params.pluginConfig);
3048
2458
  const pluginThreadConfigPluginConfig = params.nativeToolSurfaceEnabled ? params.pluginConfig : disableCodexPluginThreadConfig(params.pluginConfig);
3049
- const pluginAppCacheKey = buildCodexPluginAppCacheKey({
3050
- appServer: params.appServer,
3051
- agentDir: params.agentDir,
3052
- authProfileId: params.startupAuthProfileId,
3053
- accountId: params.startupAuthAccountCacheKey,
3054
- envApiKeyFingerprint: params.startupEnvApiKeyCacheKey
3055
- });
3056
- const pluginThreadConfigInputFingerprint = pluginThreadConfigRequired ? buildCodexPluginThreadConfigInputFingerprint({
3057
- pluginConfig: pluginThreadConfigPluginConfig,
3058
- appCacheKey: pluginAppCacheKey
3059
- }) : void 0;
3060
2459
  const resolvedPluginPolicy = pluginThreadConfigRequired ? resolveCodexPluginsPolicy(pluginThreadConfigPluginConfig) : void 0;
3061
2460
  const computerUseMcpElicitationDelegationRequired = params.computerUseConfig.enabled;
3062
2461
  const mcpElicitationDelegationRequired = resolvedPluginPolicy?.enabled === true || computerUseMcpElicitationDelegationRequired;
3063
2462
  const enabledPluginConfigKeys = resolvedPluginPolicy ? resolvedPluginPolicy.pluginPolicies.filter((plugin) => plugin.enabled).map((plugin) => plugin.configKey).toSorted() : void 0;
3064
- const attemptParams = params.buildAttemptParams();
3065
- embeddedAgentLog.debug("codex plugin thread config eligibility", buildCodexPluginThreadConfigEligibilityLogData({
3066
- sessionId: attemptParams.sessionId,
3067
- sessionKey: attemptParams.sessionKey ?? "",
3068
- pluginThreadConfigRequired,
3069
- resolvedPluginPolicy,
3070
- enabledPluginConfigKeys,
3071
- pluginAppCacheKey,
3072
- startupAuthProfileId: params.startupAuthProfileId,
3073
- appServer: params.appServer
3074
- }));
3075
2463
  pluginAppServer = mcpElicitationDelegationRequired ? {
3076
2464
  ...params.appServer,
3077
2465
  approvalPolicy: withMcpElicitationsApprovalPolicy(params.appServer.approvalPolicy)
@@ -3086,7 +2474,7 @@ async function startCodexAttemptThread(params) {
3086
2474
  startupClient = await params.attemptClientFactory(params.appServer.start, params.startupAuthProfileId, params.agentDir, params.config, {
3087
2475
  onStartedClient: (client) => {
3088
2476
  startupClientForAbandonedRequestCleanup = client;
3089
- if (startupAbandoned || startupAbandonController.signal.aborted) closeAbandonedStartupClient(client);
2477
+ if (startupAbandoned || startupAbandonController.signal.aborted) closeCodexStartupClientBestEffort(client);
3090
2478
  },
3091
2479
  abandonSignal: startupAbandonController.signal
3092
2480
  });
@@ -3108,6 +2496,36 @@ async function startCodexAttemptThread(params) {
3108
2496
  timeoutMs: params.appServer.requestTimeoutMs,
3109
2497
  signal: startupAbandonController.signal
3110
2498
  });
2499
+ const startupRuntimeIdentity = activeStartupClient.getRuntimeIdentity();
2500
+ const pluginAppCacheKey = buildCodexPluginAppCacheKey({
2501
+ appServer: params.appServer,
2502
+ agentDir: params.agentDir,
2503
+ authProfileId: params.startupAuthProfileId,
2504
+ accountId: params.startupAuthAccountCacheKey,
2505
+ envApiKeyFingerprint: params.startupEnvApiKeyCacheKey,
2506
+ appServerVersion: activeStartupClient.getServerVersion(),
2507
+ runtimeIdentity: startupRuntimeIdentity
2508
+ });
2509
+ const appServerRuntimeFingerprint = buildCodexAppServerRuntimeFingerprint({
2510
+ appServer: params.appServer,
2511
+ appServerVersion: activeStartupClient.getServerVersion(),
2512
+ runtimeIdentity: startupRuntimeIdentity
2513
+ });
2514
+ const pluginThreadConfigInputFingerprint = pluginThreadConfigRequired ? buildCodexPluginThreadConfigInputFingerprint({
2515
+ pluginConfig: pluginThreadConfigPluginConfig,
2516
+ appCacheKey: pluginAppCacheKey
2517
+ }) : void 0;
2518
+ const attemptParams = params.buildAttemptParams();
2519
+ embeddedAgentLog.debug("codex plugin thread config eligibility", buildCodexPluginThreadConfigEligibilityLogData({
2520
+ sessionId: attemptParams.sessionId,
2521
+ sessionKey: attemptParams.sessionKey ?? "",
2522
+ pluginThreadConfigRequired,
2523
+ resolvedPluginPolicy,
2524
+ enabledPluginConfigKeys,
2525
+ pluginAppCacheKey,
2526
+ startupAuthProfileId: params.startupAuthProfileId,
2527
+ appServer: params.appServer
2528
+ }));
3111
2529
  let startupSandboxEnvironment;
3112
2530
  let startupSandboxEnvironmentAcquired = false;
3113
2531
  const releaseStartupSandboxEnvironment = async () => {
@@ -3142,8 +2560,10 @@ async function startCodexAttemptThread(params) {
3142
2560
  const startupEnvironmentSelection = resolveCodexSandboxEnvironmentSelection(startupSandboxEnvironment, params.nativeToolSurfaceEnabled);
3143
2561
  const startupExecutionCwd = resolveCodexAppServerExecutionCwd({
3144
2562
  effectiveCwd: params.effectiveCwd,
2563
+ localWorkspaceRoot: params.effectiveWorkspace,
3145
2564
  environment: startupSandboxEnvironment,
3146
- nativeToolSurfaceEnabled: params.nativeToolSurfaceEnabled
2565
+ nativeToolSurfaceEnabled: params.nativeToolSurfaceEnabled,
2566
+ remoteWorkspaceRoot: params.appServer.remoteWorkspaceRoot
3147
2567
  });
3148
2568
  const startupSandboxPolicy = startupSandboxEnvironment ? resolveCodexExternalSandboxPolicyForOpenClawSandbox(params.sandbox) : void 0;
3149
2569
  const buildThreadLifecycleParams = (signal) => ({
@@ -3152,6 +2572,8 @@ async function startCodexAttemptThread(params) {
3152
2572
  agentId: params.sessionAgentId,
3153
2573
  cwd: startupExecutionCwd,
3154
2574
  dynamicTools: params.dynamicTools,
2575
+ persistentWebSearchAllowed: params.persistentWebSearchAllowed,
2576
+ webSearchAllowed: params.webSearchAllowed,
3155
2577
  appServer: pluginAppServer,
3156
2578
  developerInstructions: params.developerInstructions,
3157
2579
  config: threadConfig,
@@ -3159,11 +2581,13 @@ async function startCodexAttemptThread(params) {
3159
2581
  buildFinalConfigPatch: params.buildFinalConfigPatch,
3160
2582
  nativeHookRelayGeneration: params.nativeHookRelayGeneration,
3161
2583
  nativeCodeModeEnabled: params.nativeToolSurfaceEnabled,
2584
+ nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
3162
2585
  nativeCodeModeOnlyEnabled: params.appServer.codeModeOnly,
3163
2586
  userMcpServersEnabled: params.nativeToolSurfaceEnabled,
3164
2587
  mcpServersFingerprint: params.bundleMcpThreadConfig.fingerprint,
3165
2588
  mcpServersFingerprintEvaluated: params.bundleMcpThreadConfig.evaluated,
3166
2589
  environmentSelection: startupEnvironmentSelection,
2590
+ appServerRuntimeFingerprint,
3167
2591
  contextEngineProjection: params.contextEngineProjection,
3168
2592
  signal,
3169
2593
  pluginThreadConfig: pluginThreadConfigRequired ? {
@@ -3213,13 +2637,13 @@ async function startCodexAttemptThread(params) {
3213
2637
  startupClientLease?.();
3214
2638
  if (startupAbandoned || params.signal.aborted) {
3215
2639
  if (startupClientForAbandonedRequestCleanup === startupClient) startupClientForAbandonedRequestCleanup = void 0;
3216
- await closeAbandonedStartupClient(startupClient);
2640
+ await closeCodexStartupClientBestEffort(startupClient);
3217
2641
  } else if (shouldClearSharedClientAfterStartupRace(startupAttemptError) || shouldClearSharedClientAfterStartupFailure({
3218
2642
  error: startupAttemptError,
3219
2643
  spawnedBy: params.spawnedBy
3220
2644
  })) {
3221
2645
  if (startupClientForAbandonedRequestCleanup === startupClient) startupClientForAbandonedRequestCleanup = void 0;
3222
- await evictFailedStartupClient(startupClient);
2646
+ await closeCodexStartupClientBestEffort(startupClient);
3223
2647
  }
3224
2648
  }
3225
2649
  }
@@ -3262,7 +2686,7 @@ async function startCodexAttemptThread(params) {
3262
2686
  if (params.signal.aborted || shouldClearSharedClientAfterStartupAbandon(error)) {
3263
2687
  releaseSharedClientLease?.();
3264
2688
  releaseSharedClientLease = void 0;
3265
- await closeAbandonedStartupClient(startupClientForAbandonedRequestCleanup);
2689
+ await closeCodexStartupClientBestEffort(startupClientForAbandonedRequestCleanup);
3266
2690
  startupClientForAbandonedRequestCleanup = void 0;
3267
2691
  } else if (shouldClearSharedClientAfterStartupRace(error) || shouldClearSharedClientAfterStartupFailure({
3268
2692
  error,
@@ -3270,7 +2694,7 @@ async function startCodexAttemptThread(params) {
3270
2694
  })) {
3271
2695
  releaseSharedClientLease?.();
3272
2696
  releaseSharedClientLease = void 0;
3273
- await evictFailedStartupClient(startupClientForAbandonedRequestCleanup);
2697
+ await closeCodexStartupClientBestEffort(startupClientForAbandonedRequestCleanup);
3274
2698
  startupClientForAbandonedRequestCleanup = void 0;
3275
2699
  }
3276
2700
  throw error;
@@ -3278,58 +2702,6 @@ async function startCodexAttemptThread(params) {
3278
2702
  params.signal.removeEventListener("abort", abandonStartupAcquire);
3279
2703
  }
3280
2704
  }
3281
- async function closeAbandonedStartupClient(client) {
3282
- if (!client) return;
3283
- const unclaimedSharedClient = clearSharedCodexAppServerClientIfCurrentAndUnclaimed(client);
3284
- if (unclaimedSharedClient.closed) {
3285
- await closeClientAndWaitIfAvailable(client);
3286
- return;
3287
- }
3288
- if (unclaimedSharedClient.found) {
3289
- if (retireSharedCodexAppServerClientIfCurrent(client)?.closed) await closeClientAndWaitIfAvailable(client);
3290
- return;
3291
- }
3292
- const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
3293
- if (retiredSharedClient) {
3294
- if (retiredSharedClient.closed) await closeClientAndWaitIfAvailable(client);
3295
- return;
3296
- }
3297
- if (clearSharedCodexAppServerClientIfCurrent(client)) {
3298
- await closeClientAndWaitIfAvailable(client);
3299
- return;
3300
- }
3301
- await closeClientAndWaitIfAvailable(client);
3302
- }
3303
- async function closeClientAndWaitIfAvailable(client) {
3304
- const closeable = client;
3305
- if (typeof closeable.closeAndWait === "function") {
3306
- await closeable.closeAndWait();
3307
- return;
3308
- }
3309
- closeable.close?.();
3310
- }
3311
- async function evictFailedStartupClient(client) {
3312
- if (!client) return;
3313
- const unclaimedSharedClient = clearSharedCodexAppServerClientIfCurrentAndUnclaimed(client);
3314
- if (unclaimedSharedClient.closed) {
3315
- await closeClientAndWaitIfAvailable(client);
3316
- return;
3317
- }
3318
- if (unclaimedSharedClient.found) {
3319
- if (retireSharedCodexAppServerClientIfCurrent(client)?.closed) await closeClientAndWaitIfAvailable(client);
3320
- return;
3321
- }
3322
- const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
3323
- if (retiredSharedClient) {
3324
- if (retiredSharedClient.closed) await closeClientAndWaitIfAvailable(client);
3325
- return;
3326
- }
3327
- if (clearSharedCodexAppServerClientIfCurrent(client)) {
3328
- await closeClientAndWaitIfAvailable(client);
3329
- return;
3330
- }
3331
- await closeClientAndWaitIfAvailable(client);
3332
- }
3333
2705
  function shouldClearSharedClientAfterStartupAbandon(error) {
3334
2706
  return error instanceof Error && (error.message === "codex app-server startup timed out" || error.message === "codex app-server startup aborted");
3335
2707
  }
@@ -3857,6 +3229,7 @@ async function handleDynamicToolCallWithTimeout(params) {
3857
3229
  };
3858
3230
  if (params.signal.aborted) {
3859
3231
  const message = "OpenClaw dynamic tool call aborted before execution.";
3232
+ params.onFallbackSelected?.();
3860
3233
  notifyFailedToolResult(message);
3861
3234
  return failedDynamicToolResponse(message);
3862
3235
  }
@@ -3866,6 +3239,7 @@ async function handleDynamicToolCallWithTimeout(params) {
3866
3239
  let resolveAbort;
3867
3240
  const abortFromRun = () => {
3868
3241
  const message = "OpenClaw dynamic tool call aborted.";
3242
+ params.onFallbackSelected?.();
3869
3243
  controller.abort(params.signal.reason ?? /* @__PURE__ */ new Error(message));
3870
3244
  notifyFailedToolResult(message);
3871
3245
  resolveAbort?.(failedDynamicToolResponse(message, { sideEffectEvidence: true }));
@@ -3881,6 +3255,7 @@ async function handleDynamicToolCallWithTimeout(params) {
3881
3255
  call: params.call,
3882
3256
  timeoutMs
3883
3257
  });
3258
+ params.onFallbackSelected?.();
3884
3259
  controller.abort(new Error(timeoutDetails.responseMessage));
3885
3260
  params.onTimeout?.();
3886
3261
  embeddedAgentLog.warn("codex dynamic tool call timed out", {
@@ -3898,7 +3273,8 @@ async function handleDynamicToolCallWithTimeout(params) {
3898
3273
  const response = await Promise.race([
3899
3274
  params.toolBridge.handleToolCall(params.call, {
3900
3275
  signal: controller.signal,
3901
- onAgentToolResult: notifyAgentToolResult
3276
+ onAgentToolResult: notifyAgentToolResult,
3277
+ toolCallOrdinal: params.toolCallOrdinal
3902
3278
  }),
3903
3279
  abortPromise,
3904
3280
  timeoutPromise
@@ -4406,6 +3782,7 @@ var CodexAppServerEventProjector = class {
4406
3782
  this.assistantItemOrder = [];
4407
3783
  this.assistantPhaseByItem = /* @__PURE__ */ new Map();
4408
3784
  this.lastCommentaryProgressTextByItem = /* @__PURE__ */ new Map();
3785
+ this.pendingRawCommentaryEchoes = 0;
4409
3786
  this.reasoningTextByGroup = /* @__PURE__ */ new Map();
4410
3787
  this.reasoningItemOrder = /* @__PURE__ */ new Map();
4411
3788
  this.planTextByItem = /* @__PURE__ */ new Map();
@@ -4421,6 +3798,8 @@ var CodexAppServerEventProjector = class {
4421
3798
  this.toolResultOutputDeltaState = /* @__PURE__ */ new Map();
4422
3799
  this.toolResultOutputTextByItem = /* @__PURE__ */ new Map();
4423
3800
  this.toolMetas = /* @__PURE__ */ new Map();
3801
+ this.terminalPresentationClearedItemIds = /* @__PURE__ */ new Set();
3802
+ this.nativeToolOutcomeOrdinals = /* @__PURE__ */ new Map();
4424
3803
  this.sideEffectingToolItemIds = /* @__PURE__ */ new Set();
4425
3804
  this.sideEffectingDynamicToolCallIds = /* @__PURE__ */ new Set();
4426
3805
  this.toolTranscriptMessages = [];
@@ -4453,6 +3832,12 @@ var CodexAppServerEventProjector = class {
4453
3832
  const finalItem = this.resolveFinalAssistantTextItem();
4454
3833
  return finalItem !== void 0 && this.completedItemIds.has(finalItem.itemId);
4455
3834
  }
3835
+ /** Resolves the shared model-order position for a native tool item. */
3836
+ recordNativeToolOutcome(item) {
3837
+ if (!item || this.nativeToolOutcomeOrdinals.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
3838
+ const ordinal = this.params.allocateToolOutcomeOrdinal?.(item.id);
3839
+ if (ordinal !== void 0) this.nativeToolOutcomeOrdinals.set(item.id, ordinal);
3840
+ }
4456
3841
  async handleNotification(notification) {
4457
3842
  const params = isJsonObject(notification.params) ? notification.params : void 0;
4458
3843
  if (!params) return;
@@ -4527,6 +3912,7 @@ var CodexAppServerEventProjector = class {
4527
3912
  recordPromptError: legacyFailClosed && !hasDeliverableAssistantOnCompletedTurn
4528
3913
  });
4529
3914
  const lastAssistant = assistantTexts.length > 0 ? this.createAssistantMessage(assistantTexts.join("\n\n")) : void 0;
3915
+ const currentAttemptAssistant = this.createCurrentAttemptAssistantMessage();
4530
3916
  const turnId = this.turnId;
4531
3917
  const messagesSnapshot = this.params.suppressNextUserMessagePersistence ? [] : [attachCodexMirrorIdentity(buildCodexUserPromptMessage(this.params), `${turnId}:prompt`)];
4532
3918
  if (reasoningText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex reasoning", reasoningText), `${turnId}:reasoning`));
@@ -4561,6 +3947,7 @@ var CodexAppServerEventProjector = class {
4561
3947
  assistantTexts,
4562
3948
  toolMetas,
4563
3949
  lastAssistant,
3950
+ currentAttemptAssistant,
4564
3951
  ...this.lastNativeToolError ? { lastToolError: this.lastNativeToolError } : {},
4565
3952
  didSendViaMessagingTool: toolTelemetry.didSendViaMessagingTool,
4566
3953
  messagingToolSentTexts: toolTelemetry.messagingToolSentTexts,
@@ -4596,6 +3983,7 @@ var CodexAppServerEventProjector = class {
4596
3983
  });
4597
3984
  }
4598
3985
  recordDynamicToolResult(params) {
3986
+ const resultText = collectDynamicToolContentText(params.contentItems);
4599
3987
  if (params.asyncStarted === true) {
4600
3988
  const existing = this.toolMetas.get(params.callId);
4601
3989
  this.toolMetas.set(params.callId, {
@@ -4607,10 +3995,15 @@ var CodexAppServerEventProjector = class {
4607
3995
  this.recordToolTranscriptResult({
4608
3996
  id: params.callId,
4609
3997
  name: params.tool,
4610
- text: collectDynamicToolContentText(params.contentItems),
3998
+ text: resultText,
4611
3999
  isError: !params.success
4612
4000
  });
4613
- if ((params.terminalType ?? (params.success ? "completed" : "error")) !== "blocked" && params.sideEffectEvidence === true) this.sideEffectingDynamicToolCallIds.add(params.callId);
4001
+ if (!params.success && params.terminalType === "blocked") this.lastNativeToolError = {
4002
+ toolName: params.tool,
4003
+ error: resultText || "codex dynamic tool blocked"
4004
+ };
4005
+ else if (params.success && this.lastNativeToolError && !this.lastNativeToolError.mutatingAction) this.lastNativeToolError = void 0;
4006
+ if (params.sideEffectEvidence === true) this.sideEffectingDynamicToolCallIds.add(params.callId);
4614
4007
  }
4615
4008
  markTimedOut() {
4616
4009
  this.aborted = true;
@@ -4695,6 +4088,7 @@ var CodexAppServerEventProjector = class {
4695
4088
  const itemId = item?.id ?? readString$5(params, "itemId") ?? readString$5(params, "id");
4696
4089
  this.rememberAssistantPhase(item);
4697
4090
  if (itemId) this.activeItemIds.add(itemId);
4091
+ this.recordNativeToolOutcome(item);
4698
4092
  if (item?.type === "contextCompaction" && itemId) {
4699
4093
  this.activeCompactionItemIds.add(itemId);
4700
4094
  await runAgentHarnessBeforeCompactionHook({
@@ -4744,19 +4138,24 @@ var CodexAppServerEventProjector = class {
4744
4138
  }
4745
4139
  async handleItemCompleted(params) {
4746
4140
  const item = readItem(params.item);
4141
+ this.recordNativeToolOutcome(item);
4142
+ this.clearTerminalPresentationForNativeItem(item);
4747
4143
  const itemId = item?.id ?? readString$5(params, "itemId") ?? readString$5(params, "id");
4748
4144
  if (itemId) {
4749
4145
  this.activeItemIds.delete(itemId);
4750
4146
  this.completedItemIds.add(itemId);
4751
4147
  }
4752
4148
  this.rememberAssistantPhase(item);
4753
- if (item?.type === "agentMessage" && typeof item.text === "string" && item.text) {
4149
+ if (item?.type === "agentMessage" && typeof item.text === "string") {
4754
4150
  this.rememberAssistantItem(item.id);
4755
4151
  this.assistantTextByItem.set(item.id, item.text);
4756
- if (this.isCommentaryAssistantItem(item.id)) this.emitCommentaryProgress({
4757
- itemId: item.id,
4758
- text: item.text
4759
- });
4152
+ if (item.text && this.isCommentaryAssistantItem(item.id)) {
4153
+ this.emitCommentaryProgress({
4154
+ itemId: item.id,
4155
+ text: item.text
4156
+ });
4157
+ this.pendingRawCommentaryEchoes += 1;
4158
+ }
4760
4159
  }
4761
4160
  this.recordNativeGeneratedMedia(item);
4762
4161
  if (item?.type === "plan" && typeof item.text === "string" && item.text) {
@@ -4883,9 +4282,19 @@ var CodexAppServerEventProjector = class {
4883
4282
  }) ?? turn.error?.message ?? "codex app-server turn failed";
4884
4283
  this.promptErrorSource = "prompt";
4885
4284
  }
4886
- for (const item of turn.items ?? []) {
4285
+ const turnItems = turn.items ?? [];
4286
+ for (let index = turnItems.length - 1; index >= 0; index -= 1) {
4287
+ const item = turnItems[index];
4288
+ if (!item || !this.isCurrentTurnSnapshotItem(item)) continue;
4289
+ if (item?.type === "dynamicToolCall") break;
4290
+ if (shouldClearTerminalPresentationForNativeItem(item)) {
4291
+ this.clearTerminalPresentationForNativeItem(item);
4292
+ break;
4293
+ }
4294
+ }
4295
+ for (const item of turnItems) {
4887
4296
  this.rememberAssistantPhase(item);
4888
- if (item.type === "agentMessage" && typeof item.text === "string" && item.text) {
4297
+ if (item.type === "agentMessage" && typeof item.text === "string") {
4889
4298
  this.rememberAssistantItem(item.id);
4890
4299
  this.assistantTextByItem.set(item.id, item.text);
4891
4300
  }
@@ -4976,10 +4385,14 @@ var CodexAppServerEventProjector = class {
4976
4385
  if (!item) return;
4977
4386
  await this.recordRawGeneratedImageMedia(item);
4978
4387
  if (readString$5(item, "role") !== "assistant") return;
4388
+ const phase = readString$5(item, "phase");
4389
+ if (phase === "commentary" && this.pendingRawCommentaryEchoes > 0) {
4390
+ this.pendingRawCommentaryEchoes -= 1;
4391
+ return;
4392
+ }
4979
4393
  const text = extractRawAssistantText(item);
4980
4394
  if (!text) return;
4981
4395
  const itemId = readString$5(item, "id") ?? `raw-assistant-${this.assistantItemOrder.length + 1}`;
4982
- const phase = readString$5(item, "phase");
4983
4396
  if (phase) this.assistantPhaseByItem.set(itemId, phase);
4984
4397
  this.rememberAssistantItem(itemId);
4985
4398
  this.assistantTextByItem.set(itemId, text);
@@ -5162,6 +4575,19 @@ var CodexAppServerEventProjector = class {
5162
4575
  });
5163
4576
  if (params.phase === "result") this.emitAfterToolCallObservation(item);
5164
4577
  }
4578
+ clearTerminalPresentationForNativeItem(item) {
4579
+ if (!item || this.terminalPresentationClearedItemIds.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
4580
+ const toolCallOrdinal = this.nativeToolOutcomeOrdinals.get(item.id);
4581
+ this.terminalPresentationClearedItemIds.add(item.id);
4582
+ this.params.onToolOutcome?.({
4583
+ toolName: itemName(item) ?? item.type,
4584
+ argsHash: "",
4585
+ resultHash: "",
4586
+ ...toolCallOrdinal !== void 0 ? { toolCallOrdinal } : {},
4587
+ terminalPresentation: void 0,
4588
+ presentationOnly: true
4589
+ });
4590
+ }
5165
4591
  recordNativeToolError(params) {
5166
4592
  if (!isNonSuccessItemStatus(params.status)) {
5167
4593
  if (!this.lastNativeToolError) return;
@@ -5333,6 +4759,8 @@ var CodexAppServerEventProjector = class {
5333
4759
  }
5334
4760
  recordToolMeta(item) {
5335
4761
  if (!item) return;
4762
+ if (isSideEffectingNativeToolItem(item)) this.sideEffectingToolItemIds.add(item.id);
4763
+ else this.sideEffectingToolItemIds.delete(item.id);
5336
4764
  const toolName = itemName(item);
5337
4765
  if (!toolName) return;
5338
4766
  const meta = itemMeta(item, this.toolProgressDetailMode());
@@ -5342,8 +4770,6 @@ var CodexAppServerEventProjector = class {
5342
4770
  ...meta ? { meta } : {},
5343
4771
  ...existing?.asyncStarted ? { asyncStarted: true } : {}
5344
4772
  });
5345
- if (isSideEffectingNativeToolItem(item)) this.sideEffectingToolItemIds.add(item.id);
5346
- else this.sideEffectingToolItemIds.delete(item.id);
5347
4773
  }
5348
4774
  recordNativeToolTranscriptCall(item) {
5349
4775
  if (!item || !shouldRecordNativeToolTranscript(item)) return;
@@ -5545,6 +4971,16 @@ var CodexAppServerEventProjector = class {
5545
4971
  if (!itemId || this.assistantItemOrder.includes(itemId)) return;
5546
4972
  this.assistantItemOrder.push(itemId);
5547
4973
  }
4974
+ createCurrentAttemptAssistantMessage() {
4975
+ for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
4976
+ const itemId = this.assistantItemOrder[i];
4977
+ if (!itemId || this.isCommentaryAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
4978
+ const text = this.assistantTextByItem.get(itemId) ?? "";
4979
+ const normalizedText = text.trim();
4980
+ if (normalizedText && this.toolProgressTexts.has(normalizedText)) continue;
4981
+ return this.createAssistantMessage(text);
4982
+ }
4983
+ }
5548
4984
  async readMirroredSessionMessages() {
5549
4985
  return await readCodexMirroredSessionHistoryMessages(this.params.sessionFile) ?? [];
5550
4986
  }
@@ -5869,7 +5305,20 @@ function shouldRecordNativeToolTranscript(item) {
5869
5305
  return shouldSynthesizeToolProgressForItem(item) && item.type !== "webSearch";
5870
5306
  }
5871
5307
  function isMutatingNativeToolItem(item) {
5872
- return item.type === "commandExecution" || item.type === "fileChange";
5308
+ if (item.type === "commandExecution") return true;
5309
+ return item.type === "fileChange" || item.type === "collabAgentToolCall" || item.type === "imageGeneration";
5310
+ }
5311
+ function shouldClearTerminalPresentationForNativeItem(item) {
5312
+ switch (item.type) {
5313
+ case "collabAgentToolCall":
5314
+ case "commandExecution":
5315
+ case "fileChange":
5316
+ case "imageGeneration":
5317
+ case "imageView":
5318
+ case "mcpToolCall":
5319
+ case "webSearch": return true;
5320
+ default: return false;
5321
+ }
5873
5322
  }
5874
5323
  function nativeToolActionFingerprint(item) {
5875
5324
  if (item.type === "commandExecution" && typeof item.command === "string") return JSON.stringify({
@@ -6199,8 +5648,18 @@ var CodexNativeSubagentTaskMirror = class {
6199
5648
  this.mirroredThreadIds = /* @__PURE__ */ new Set();
6200
5649
  this.failedMirrorThreadIds = /* @__PURE__ */ new Set();
6201
5650
  this.terminalRunIds = /* @__PURE__ */ new Set();
5651
+ this.authoritativeRunIds = /* @__PURE__ */ new Set();
5652
+ this.expectedAuthoritativeRunIds = /* @__PURE__ */ new Set();
6202
5653
  this.now = params.now ?? Date.now;
6203
5654
  }
5655
+ markAuthoritativeCompletion(childThreadId) {
5656
+ const runId = codexNativeSubagentRunId(childThreadId);
5657
+ this.authoritativeRunIds.add(runId);
5658
+ this.terminalRunIds.add(runId);
5659
+ }
5660
+ markAuthoritativeCompletionExpected(childThreadId) {
5661
+ this.expectedAuthoritativeRunIds.add(codexNativeSubagentRunId(childThreadId));
5662
+ }
6204
5663
  handleNotification(notification) {
6205
5664
  const params = isJsonObject(notification.params) ? notification.params : void 0;
6206
5665
  if (!params) return;
@@ -6246,6 +5705,7 @@ var CodexNativeSubagentTaskMirror = class {
6246
5705
  }
6247
5706
  this.failedMirrorThreadIds.delete(threadId);
6248
5707
  this.terminalRunIds.delete(runId);
5708
+ this.authoritativeRunIds.delete(runId);
6249
5709
  this.applyStatus(threadId, thread.status);
6250
5710
  }
6251
5711
  handleThreadStatusChanged(params) {
@@ -6258,6 +5718,7 @@ var CodexNativeSubagentTaskMirror = class {
6258
5718
  const statusType = status?.type;
6259
5719
  if (!statusType) return;
6260
5720
  const runId = codexNativeSubagentRunId(threadId);
5721
+ if (this.authoritativeRunIds.has(runId)) return;
6261
5722
  if (this.terminalRunIds.has(runId) && statusType !== "systemError") return;
6262
5723
  const eventAt = this.now();
6263
5724
  if (statusType === "active") {
@@ -6270,13 +5731,10 @@ var CodexNativeSubagentTaskMirror = class {
6270
5731
  }
6271
5732
  if (statusType === "idle") {
6272
5733
  this.terminalRunIds.add(runId);
6273
- this.runtime.finalizeTaskRunByRunId({
5734
+ this.runtime.recordTaskRunProgressByRunId({
6274
5735
  runId,
6275
- status: "succeeded",
6276
- endedAt: eventAt,
6277
5736
  lastEventAt: eventAt,
6278
- progressSummary: "Codex native subagent is idle.",
6279
- terminalSummary: "Codex native subagent finished."
5737
+ progressSummary: "Codex native subagent is idle."
6280
5738
  });
6281
5739
  return;
6282
5740
  }
@@ -6353,12 +5811,14 @@ var CodexNativeSubagentTaskMirror = class {
6353
5811
  }
6354
5812
  this.failedMirrorThreadIds.delete(normalizedThreadId);
6355
5813
  this.terminalRunIds.delete(runId);
5814
+ this.authoritativeRunIds.delete(runId);
6356
5815
  }
6357
5816
  applyCollabAgentStatus(threadId, status, message) {
6358
5817
  if (!this.mirroredThreadIds.has(threadId) && this.failedMirrorThreadIds.has(threadId)) return;
6359
5818
  const normalizedStatus = normalizeAgentStateStatus(status);
6360
5819
  if (!normalizedStatus) return;
6361
5820
  const runId = codexNativeSubagentRunId(threadId);
5821
+ if (this.authoritativeRunIds.has(runId)) return;
6362
5822
  if (this.terminalRunIds.has(runId) && isNonTerminalAgentStateStatus(normalizedStatus)) return;
6363
5823
  const eventAt = this.now();
6364
5824
  if (normalizedStatus === "pendingInit" || normalizedStatus === "running") {
@@ -6371,13 +5831,19 @@ var CodexNativeSubagentTaskMirror = class {
6371
5831
  }
6372
5832
  if (normalizedStatus === "completed") {
6373
5833
  this.terminalRunIds.add(runId);
6374
- this.runtime.finalizeTaskRunByRunId({
5834
+ const summary = trimOptional(message) ?? "Codex native subagent completed.";
5835
+ if (this.expectedAuthoritativeRunIds.has(runId)) this.runtime.recordTaskRunProgressByRunId({
5836
+ runId,
5837
+ lastEventAt: eventAt,
5838
+ progressSummary: summary
5839
+ });
5840
+ else this.runtime.finalizeTaskRunByRunId({
6375
5841
  runId,
6376
5842
  status: "succeeded",
6377
5843
  endedAt: eventAt,
6378
5844
  lastEventAt: eventAt,
6379
- progressSummary: trimOptional(message) ?? "Codex native subagent completed.",
6380
- terminalSummary: trimOptional(message) ?? "Codex native subagent finished."
5845
+ progressSummary: summary,
5846
+ terminalSummary: summary
6381
5847
  });
6382
5848
  return;
6383
5849
  }
@@ -6608,6 +6074,8 @@ var CodexNativeSubagentMonitor = class {
6608
6074
  error: formatErrorMessage(error)
6609
6075
  });
6610
6076
  }
6077
+ this.captureChildAssistantMessage(notification);
6078
+ await this.handleChildTurnCompletion(notification);
6611
6079
  await this.handleCompletionNotification(notification);
6612
6080
  }
6613
6081
  ensureParentTaskRuntime(state) {
@@ -6661,7 +6129,7 @@ var CodexNativeSubagentMonitor = class {
6661
6129
  for (const nativeCompletion of completions) {
6662
6130
  const childThreadId = this.resolveChildThreadIdForAgentPath(state.parentThreadId, nativeCompletion.agentPath);
6663
6131
  const childState = childThreadId ? this.childStates.get(childThreadId) : void 0;
6664
- if (!childState || childState.parentThreadId !== state.parentThreadId) {
6132
+ if (!childState || childState.parentThreadId !== state.parentThreadId || childState.transcriptTerminal) {
6665
6133
  embeddedAgentLog.warn("Ignoring Codex native subagent completion for unknown child thread", {
6666
6134
  parentThreadId: state.parentThreadId,
6667
6135
  agentPath: nativeCompletion.agentPath
@@ -6680,6 +6148,26 @@ var CodexNativeSubagentMonitor = class {
6680
6148
  await this.processCompletion(state, completion);
6681
6149
  }
6682
6150
  }
6151
+ captureChildAssistantMessage(notification) {
6152
+ if (notification.method !== "item/completed") return;
6153
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
6154
+ const childThreadId = readString$2(params, "threadId")?.trim();
6155
+ const childState = childThreadId ? this.childStates.get(childThreadId) : void 0;
6156
+ const item = isJsonObject(params?.item) ? params.item : void 0;
6157
+ const text = readString$2(item, "type") === "agentMessage" ? normalizeOptionalString(readString$2(item, "text")) : void 0;
6158
+ if (childState && text) childState.lastAssistantMessage = text;
6159
+ }
6160
+ async handleChildTurnCompletion(notification) {
6161
+ if (notification.method !== "turn/completed") return;
6162
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
6163
+ const childThreadId = readString$2(params, "threadId")?.trim();
6164
+ const childState = childThreadId ? this.childStates.get(childThreadId) : void 0;
6165
+ const state = childState ? this.parentStates.get(childState.parentThreadId) : void 0;
6166
+ const turn = isJsonObject(params?.turn) ? params.turn : void 0;
6167
+ const completion = childState && turn ? toChildTurnCompletion(childState, turn) : void 0;
6168
+ if (!state || !childState || childState.transcriptTerminal || !completion) return;
6169
+ await this.processCompletion(state, completion);
6170
+ }
6683
6171
  async reconcileChildTranscript(childThreadId, options = {}) {
6684
6172
  const childState = this.childStates.get(childThreadId.trim());
6685
6173
  const state = childState ? this.parentStates.get(childState.parentThreadId) : void 0;
@@ -6803,6 +6291,7 @@ var CodexNativeSubagentMonitor = class {
6803
6291
  finalizeCompletionTask(completion, eventAt) {
6804
6292
  const taskRuntime = this.getTaskRuntimeForChild(completion.childThreadId);
6805
6293
  if (!taskRuntime) return;
6294
+ this.getMirrorForChild(completion.childThreadId)?.markAuthoritativeCompletion(completion.childThreadId);
6806
6295
  taskRuntime.finalizeTaskRunByRunId({
6807
6296
  runId: codexNativeSubagentRunId(completion.childThreadId),
6808
6297
  status: completion.status,
@@ -6817,6 +6306,10 @@ var CodexNativeSubagentMonitor = class {
6817
6306
  const childState = this.childStates.get(childThreadId.trim());
6818
6307
  return (childState ? this.parentStates.get(childState.parentThreadId) : void 0)?.taskRuntime;
6819
6308
  }
6309
+ getMirrorForChild(childThreadId) {
6310
+ const childState = this.childStates.get(childThreadId.trim());
6311
+ return (childState ? this.parentStates.get(childState.parentThreadId) : void 0)?.mirror;
6312
+ }
6820
6313
  registerChildThread(parentThreadId, childThreadId, options = {}) {
6821
6314
  const normalizedParentThreadId = parentThreadId.trim();
6822
6315
  const normalizedChildThreadId = childThreadId.trim();
@@ -6824,6 +6317,8 @@ var CodexNativeSubagentMonitor = class {
6824
6317
  this.childThreadParents.set(normalizedChildThreadId, normalizedParentThreadId);
6825
6318
  this.childThreadIdsByAgentPath.set(buildParentAgentPathKey(normalizedParentThreadId, normalizedChildThreadId), normalizedChildThreadId);
6826
6319
  const agentPath = normalizeOptionalString(options.agentPath);
6320
+ const state = this.parentStates.get(normalizedParentThreadId);
6321
+ if (state?.mirror && (this.codexHome || agentPath)) state.mirror.markAuthoritativeCompletionExpected(normalizedChildThreadId);
6827
6322
  if (agentPath) this.childThreadIdsByAgentPath.set(buildParentAgentPathKey(normalizedParentThreadId, agentPath), normalizedChildThreadId);
6828
6323
  let childState = this.childStates.get(normalizedChildThreadId);
6829
6324
  if (!childState) {
@@ -7025,6 +6520,31 @@ function buildCompletionDedupeKey(parentThreadId, completion) {
7025
6520
  const hash = createHash("sha256").update(completion.result).digest("hex").slice(0, 16);
7026
6521
  return `${parentThreadId}:${completion.childThreadId}:${completion.status}:${hash}`;
7027
6522
  }
6523
+ function toChildTurnCompletion(childState, turn) {
6524
+ const status = readString$2(turn, "status");
6525
+ if (status === "completed") return {
6526
+ childThreadId: childState.childThreadId,
6527
+ status: "succeeded",
6528
+ statusLabel: "turn_completed",
6529
+ result: childState.lastAssistantMessage ?? "Codex native subagent completed without a final assistant message."
6530
+ };
6531
+ if (status === "interrupted") return {
6532
+ childThreadId: childState.childThreadId,
6533
+ status: "cancelled",
6534
+ statusLabel: "turn_interrupted",
6535
+ result: "Codex native subagent was interrupted."
6536
+ };
6537
+ if (status === "failed") return {
6538
+ childThreadId: childState.childThreadId,
6539
+ status: "failed",
6540
+ statusLabel: "turn_failed",
6541
+ result: readTurnErrorMessage(turn) ?? "Codex native subagent failed."
6542
+ };
6543
+ }
6544
+ function readTurnErrorMessage(turn) {
6545
+ const error = isJsonObject(turn.error) ? turn.error : void 0;
6546
+ return normalizeOptionalString(readString$2(error, "message")) ?? normalizeOptionalString(isJsonObject(error?.codexErrorInfo) ? readString$2(error.codexErrorInfo, "message") : void 0);
6547
+ }
7028
6548
  function buildParentAgentPathKey(parentThreadId, agentPath) {
7029
6549
  return `${parentThreadId}\0${agentPath}`;
7030
6550
  }
@@ -7620,7 +7140,7 @@ function resolveContainedPath(baseDir, fileName) {
7620
7140
  }
7621
7141
  function toTrajectoryToolDefinitions(tools) {
7622
7142
  if (!tools || tools.length === 0) return;
7623
- return tools.flatMap((tool) => {
7143
+ return flattenCodexDynamicToolFunctions(tools).flatMap((tool) => {
7624
7144
  const name = tool.name?.trim();
7625
7145
  if (!name) return [];
7626
7146
  return [{
@@ -8264,18 +7784,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
8264
7784
  });
8265
7785
  const startupEnvApiKeyCacheKey = startupAuthProfileId ? void 0 : resolveCodexAppServerFallbackApiKeyCacheKey({ startOptions: appServer.start });
8266
7786
  preDynamicStartupStages.mark("auth-cache");
8267
- const nodeExecBlocksNativeExecution = isCodexNativeExecutionBlockedByNodeExecHost(params, {
8268
- agentId: sessionAgentId,
8269
- runtimeSessionKey: sandboxSessionKey,
8270
- sandbox
8271
- });
8272
- preDynamicStartupStages.mark("native-exec-policy");
8273
7787
  const bundleMcpThreadConfig = await loadCodexBundleMcpThreadConfig({
8274
7788
  workspaceDir: effectiveWorkspace,
8275
7789
  cfg: params.config,
8276
7790
  toolsEnabled: supportsModelTools(params.model),
8277
7791
  disableTools: params.disableTools,
8278
- toolsAllow: nodeExecBlocksNativeExecution ? [] : params.toolsAllow
7792
+ toolsAllow: params.toolsAllow
8279
7793
  });
8280
7794
  preDynamicStartupStages.mark("bundle-mcp");
8281
7795
  const sandboxExecServerEnabled = isCodexSandboxExecServerEnabled(pluginConfig);
@@ -8285,6 +7799,28 @@ async function runCodexAppServerAttempt(params, options = {}) {
8285
7799
  sandboxExecServerEnabled
8286
7800
  });
8287
7801
  preDynamicStartupStages.mark("native-tool-surface");
7802
+ const nativeProviderWebSearchSupport = resolveCodexWebSearchPlan({
7803
+ config: params.config,
7804
+ disableTools: params.disableTools,
7805
+ nativeToolSurfaceEnabled
7806
+ }).kind === "native-hosted" ? await resolveCodexProviderWebSearchSupport({
7807
+ clientFactory: attemptClientFactory,
7808
+ appServer,
7809
+ authProfileId: startupAuthProfileId,
7810
+ agentDir,
7811
+ config: params.config,
7812
+ modelProviderOverride: resolveCodexAppServerThreadModelSelection({
7813
+ provider: params.provider,
7814
+ model: params.modelId,
7815
+ binding: startupBinding,
7816
+ authProfileId: startupAuthProfileId,
7817
+ authProfileStore: params.authProfileStore,
7818
+ agentDir,
7819
+ config: params.config
7820
+ }).modelProvider,
7821
+ signal: runAbortController.signal
7822
+ }) : "unsupported";
7823
+ preDynamicStartupStages.mark("provider-capabilities");
8288
7824
  for (const diagnostic of bundleMcpThreadConfig.diagnostics) embeddedAgentLog.warn(`bundle-mcp: ${diagnostic.pluginId}: ${diagnostic.message}`);
8289
7825
  if (activeContextEngine) assertContextEngineHostSupport({
8290
7826
  contextEngine: activeContextEngine,
@@ -8305,14 +7841,36 @@ async function runCodexAppServerAttempt(params, options = {}) {
8305
7841
  nativeToolSurfaceEnabled
8306
7842
  });
8307
7843
  let yieldDetected = false;
7844
+ const toolOutcomeOrdinals = /* @__PURE__ */ new Map();
7845
+ const suppressedDynamicToolOutcomeOrdinals = /* @__PURE__ */ new Set();
7846
+ const onCodexToolOutcome = params.onToolOutcome ? (observation) => {
7847
+ if (observation.toolCallOrdinal !== void 0 && suppressedDynamicToolOutcomeOrdinals.has(observation.toolCallOrdinal)) return;
7848
+ params.onToolOutcome?.(observation);
7849
+ } : void 0;
7850
+ const baseAllocateToolOutcomeOrdinal = params.allocateToolOutcomeOrdinal;
7851
+ const allocateCodexToolOutcomeOrdinal = baseAllocateToolOutcomeOrdinal ? (toolCallId) => {
7852
+ const reservedOrdinal = toolCallId ? toolOutcomeOrdinals.get(toolCallId) : void 0;
7853
+ if (reservedOrdinal !== void 0) return reservedOrdinal;
7854
+ const ordinal = baseAllocateToolOutcomeOrdinal(toolCallId);
7855
+ if (toolCallId) toolOutcomeOrdinals.set(toolCallId, ordinal);
7856
+ return ordinal;
7857
+ } : void 0;
7858
+ const dynamicToolParams = allocateCodexToolOutcomeOrdinal || onCodexToolOutcome ? {
7859
+ ...params,
7860
+ ...allocateCodexToolOutcomeOrdinal ? { allocateToolOutcomeOrdinal: allocateCodexToolOutcomeOrdinal } : {},
7861
+ ...onCodexToolOutcome ? { onToolOutcome: onCodexToolOutcome } : {}
7862
+ } : params;
7863
+ let persistentWebSearchAllowed;
7864
+ let webSearchAllowed = false;
8308
7865
  const tools = await buildDynamicTools({
8309
- params,
7866
+ params: dynamicToolParams,
8310
7867
  resolvedWorkspace,
8311
7868
  effectiveWorkspace,
8312
7869
  effectiveCwd,
8313
7870
  sandboxSessionKey,
8314
7871
  sandbox,
8315
7872
  nativeToolSurfaceEnabled,
7873
+ nativeProviderWebSearchSupport,
8316
7874
  runAbortController,
8317
7875
  sessionAgentId,
8318
7876
  pluginConfig,
@@ -8320,23 +7878,31 @@ async function runCodexAppServerAttempt(params, options = {}) {
8320
7878
  onYieldDetected: () => {
8321
7879
  yieldDetected = true;
8322
7880
  },
8323
- onCodexAppServerEvent: (event) => emitCodexAppServerEvent(params, event)
7881
+ onCodexAppServerEvent: (event) => emitCodexAppServerEvent(params, event),
7882
+ onPersistentWebSearchPolicyResolved: (allowed) => {
7883
+ persistentWebSearchAllowed = allowed;
7884
+ },
7885
+ onWebSearchPolicyResolved: (allowed) => {
7886
+ webSearchAllowed = allowed;
7887
+ }
8324
7888
  });
8325
7889
  const toolBridge = createCodexDynamicToolBridge({
8326
7890
  tools,
8327
7891
  registeredTools: await buildDynamicTools({
8328
- params,
7892
+ params: dynamicToolParams,
8329
7893
  resolvedWorkspace,
8330
7894
  effectiveWorkspace,
8331
7895
  effectiveCwd,
8332
7896
  sandboxSessionKey,
8333
7897
  sandbox,
8334
7898
  nativeToolSurfaceEnabled,
7899
+ nativeProviderWebSearchSupport,
8335
7900
  runAbortController,
8336
7901
  sessionAgentId,
8337
7902
  pluginConfig,
8338
7903
  profilerEnabled,
8339
7904
  forceHeartbeatTool: true,
7905
+ ignoreDisableMessageTool: true,
8340
7906
  ignoreRuntimePlan: true,
8341
7907
  onYieldDetected: () => {
8342
7908
  yieldDetected = true;
@@ -8344,15 +7910,23 @@ async function runCodexAppServerAttempt(params, options = {}) {
8344
7910
  onCodexAppServerEvent: (event) => emitCodexAppServerEvent(params, event)
8345
7911
  }),
8346
7912
  signal: runAbortController.signal,
8347
- loading: resolveCodexDynamicToolsLoadingForModel(pluginConfig, params.modelId),
8348
- directToolNames: shouldForceMessageTool(params) ? ["message"] : [],
7913
+ loading: resolveCodexDynamicToolsLoadingForRuntime(pluginConfig, params.modelId, { connectionClass: appServer.connectionClass }),
7914
+ directToolNames: resolveCodexDynamicToolDirectNames(params),
8349
7915
  hookContext: {
8350
7916
  agentId: sessionAgentId,
8351
7917
  config: params.config,
8352
7918
  sessionId: params.sessionId,
8353
7919
  sessionKey: sandboxSessionKey,
8354
7920
  runId: params.runId,
8355
- channelId: hookChannelId
7921
+ channelId: hookChannelId,
7922
+ currentChannelProvider: resolveCodexMessageToolProvider(params),
7923
+ currentChannelId: params.currentChannelId,
7924
+ currentMessagingTarget: params.currentMessagingTarget,
7925
+ currentThreadId: params.currentThreadTs,
7926
+ replyToMode: params.replyToMode,
7927
+ hasRepliedRef: params.hasRepliedRef,
7928
+ onToolOutcome: onCodexToolOutcome,
7929
+ allocateToolOutcomeOrdinal: allocateCodexToolOutcomeOrdinal
8356
7930
  }
8357
7931
  });
8358
7932
  const hadSessionFile = await pathExists(activeSessionFile);
@@ -8392,6 +7966,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
8392
7966
  sessionKey: contextSessionKey,
8393
7967
  sessionFile: activeSessionFile,
8394
7968
  runtimeContext: buildActiveContextEngineRuntimeContext(),
7969
+ contextEngineHostSupport: CODEX_APP_SERVER_CONTEXT_ENGINE_HOST,
7970
+ providerId: params.provider,
7971
+ requestedModelId: params.requestedModelId,
7972
+ modelId: params.modelId,
7973
+ fallbackReason: params.fallbackReason,
7974
+ degradedReason: params.degradedReason,
8395
7975
  runMaintenance: runHarnessContextEngineMaintenance,
8396
7976
  config: params.config,
8397
7977
  warn: (message) => embeddedAgentLog.warn(message)
@@ -8439,9 +8019,14 @@ async function runCodexAppServerAttempt(params, options = {}) {
8439
8019
  sessionKey: contextSessionKey,
8440
8020
  messages: historyMessages,
8441
8021
  tokenBudget: params.contextTokenBudget,
8442
- availableTools: new Set(toolBridge.availableSpecs.map((tool) => tool.name).filter(isNonEmptyString)),
8022
+ availableTools: new Set(flattenCodexDynamicToolFunctions(toolBridge.availableSpecs).map((tool) => tool.name).filter(isNonEmptyString)),
8443
8023
  citationsMode: params.config?.memory?.citations,
8444
8024
  modelId: params.modelId,
8025
+ contextEngineHostSupport: CODEX_APP_SERVER_CONTEXT_ENGINE_HOST,
8026
+ providerId: params.provider,
8027
+ requestedModelId: params.requestedModelId,
8028
+ fallbackReason: params.fallbackReason,
8029
+ degradedReason: params.degradedReason,
8445
8030
  prompt: params.prompt
8446
8031
  });
8447
8032
  if (!assembled) throw new Error("context engine assemble returned no result");
@@ -8635,12 +8220,35 @@ async function runCodexAppServerAttempt(params, options = {}) {
8635
8220
  };
8636
8221
  let nativeHookRelay;
8637
8222
  let releaseSharedClientLease;
8223
+ let sharedCodexClientRetiredForOneShotCleanup = false;
8638
8224
  const releaseSharedClientLeaseOnce = () => {
8639
8225
  const release = releaseSharedClientLease;
8640
8226
  if (!release) return;
8641
8227
  releaseSharedClientLease = void 0;
8642
8228
  release();
8643
8229
  };
8230
+ const retireSharedCodexClientForOneShotCleanup = async () => {
8231
+ if (params.cleanupBundleMcpOnRunEnd !== true) return;
8232
+ if (sharedCodexClientRetiredForOneShotCleanup) return;
8233
+ sharedCodexClientRetiredForOneShotCleanup = true;
8234
+ const retired = retireSharedCodexAppServerClientIfCurrent(client);
8235
+ embeddedAgentLog.info("codex app-server one-shot cleanup retired shared client", {
8236
+ runId: params.runId,
8237
+ sessionId: params.sessionId,
8238
+ sessionKey: params.sessionKey,
8239
+ activeLeases: retired?.activeLeases ?? null,
8240
+ closed: retired?.closed ?? false,
8241
+ matchedSharedClient: Boolean(retired)
8242
+ });
8243
+ if (retired?.closed) await client.closeAndWait({
8244
+ exitTimeoutMs: 2e3,
8245
+ forceKillDelayMs: 250
8246
+ });
8247
+ };
8248
+ const releaseSharedClientLeaseAndRetireOneShotClient = async () => {
8249
+ releaseSharedClientLeaseOnce();
8250
+ await retireSharedCodexClientForOneShotCleanup();
8251
+ };
8644
8252
  let sandboxExecEnvironmentAcquired = false;
8645
8253
  const releaseSandboxExecEnvironment = async () => {
8646
8254
  if (sandboxExecEnvironmentAcquired) {
@@ -8703,10 +8311,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
8703
8311
  effectiveWorkspace,
8704
8312
  effectiveCwd,
8705
8313
  dynamicTools: toolBridge.specs,
8314
+ persistentWebSearchAllowed,
8315
+ webSearchAllowed,
8706
8316
  developerInstructions: promptBuild.developerInstructions,
8707
8317
  buildFinalConfigPatch: buildNativeHookRelayFinalConfigPatch,
8708
8318
  bundleMcpThreadConfig,
8709
8319
  nativeToolSurfaceEnabled,
8320
+ nativeProviderWebSearchSupport,
8710
8321
  sandboxExecServerEnabled,
8711
8322
  sandbox,
8712
8323
  contextEngineProjection,
@@ -8745,7 +8356,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
8745
8356
  threadId: thread.threadId,
8746
8357
  authProfileId: startupAuthProfileId,
8747
8358
  workspaceDir: effectiveWorkspace,
8748
- toolCount: toolBridge.specs.length
8359
+ toolCount: flattenCodexDynamicToolFunctions(toolBridge.specs).length
8749
8360
  });
8750
8361
  recordCodexTrajectoryContext(trajectoryRecorder, {
8751
8362
  attempt: params,
@@ -8932,7 +8543,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
8932
8543
  data: {
8933
8544
  startedAt: attemptStartedAt,
8934
8545
  endedAt: Date.now(),
8935
- ...data
8546
+ ...data,
8547
+ ...params.deferTerminalLifecycle ?? params.deferTerminalLifecycleEnd ? { phase: "finishing" } : {}
8936
8548
  }
8937
8549
  });
8938
8550
  lifecycleTerminalEmitted = true;
@@ -9055,7 +8667,14 @@ async function runCodexAppServerAttempt(params, options = {}) {
9055
8667
  if (isTerminalTurnNotificationForTurn(notification, turnId)) terminalTurnNotificationQueued = true;
9056
8668
  const isNativeResponseStreamDelta = isNativeResponseStreamDeltaNotification(notification);
9057
8669
  const nativeResponseStreamDeltaMatchesActiveTurn = isNativeResponseStreamDelta && (correlation.matchesActiveTurn === true || isUnscopedCodexNotification(correlation) && canAttributeUnscopedNativeResponseDeltaToThisTurn(client));
9058
- if (correlation.matchesActiveTurn === true || !isNativeResponseStreamDelta && correlation.matchesActiveTurn !== false || nativeResponseStreamDeltaMatchesActiveTurn) turnWatches.noteNotificationReceived(notification.method, isNativeResponseStreamDelta ? {
8670
+ const notificationMatchesActiveTurn = correlation.matchesActiveTurn === true || !isNativeResponseStreamDelta && correlation.matchesActiveTurn !== false || nativeResponseStreamDeltaMatchesActiveTurn;
8671
+ if (correlation.matchesActiveTurn === true) {
8672
+ const modelToolCallId = readRawResponseToolCallId(notification);
8673
+ if (modelToolCallId) allocateCodexToolOutcomeOrdinal?.(modelToolCallId);
8674
+ const nativeItem = readCodexNotificationItem(notification.params);
8675
+ if (nativeItem?.type === "webSearch") projector.recordNativeToolOutcome(nativeItem);
8676
+ }
8677
+ if (notificationMatchesActiveTurn) turnWatches.noteNotificationReceived(notification.method, isNativeResponseStreamDelta ? {
9059
8678
  attemptProgress: true,
9060
8679
  ...turnCrossedToolHandoff ? { attemptTimeoutMs: postToolRawAssistantCompletionIdleTimeoutMs } : {},
9061
8680
  details: { lastNotificationMethod: notification.method }
@@ -9063,13 +8682,14 @@ async function runCodexAppServerAttempt(params, options = {}) {
9063
8682
  notificationQueue = notificationQueue.then(() => handleNotification(notification), () => handleNotification(notification));
9064
8683
  return notificationQueue;
9065
8684
  };
8685
+ const nativeSubagentCodexHome = appServer.start.transport === "stdio" ? appServer.start.env?.CODEX_HOME ?? resolveCodexAppServerHomeDir(agentDir) : void 0;
9066
8686
  registerCodexNativeSubagentMonitor({
9067
8687
  client,
9068
8688
  parentThreadId: thread.threadId,
9069
8689
  requesterSessionKey: params.sessionKey,
9070
8690
  taskRuntimeScope: params.agentHarnessTaskRuntimeScope,
9071
8691
  agentId: params.agentId,
9072
- codexHome: appServer.start.env?.CODEX_HOME ?? resolveCodexAppServerHomeDir(agentDir)
8692
+ codexHome: nativeSubagentCodexHome
9073
8693
  });
9074
8694
  const notificationCleanup = client.addNotificationHandler(enqueueNotification);
9075
8695
  const requestCleanup = client.addRequestHandler(async (request) => {
@@ -9141,6 +8761,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
9141
8761
  }
9142
8762
  const call = readDynamicToolCallParams(request.params);
9143
8763
  if (!call || call.threadId !== thread.threadId || call.turnId !== turnId) return;
8764
+ const toolCallOrdinal = allocateCodexToolOutcomeOrdinal?.(call.callId);
9144
8765
  armCompletionWatchOnResponse = true;
9145
8766
  markCurrentTurnRequestProgress();
9146
8767
  turnCrossedToolHandoff = true;
@@ -9204,7 +8825,11 @@ async function runCodexAppServerAttempt(params, options = {}) {
9204
8825
  toolBridge,
9205
8826
  signal: runAbortController.signal,
9206
8827
  timeoutMs: dynamicToolTimeoutMs,
8828
+ toolCallOrdinal,
9207
8829
  onAgentToolResult: params.onAgentToolResult,
8830
+ onFallbackSelected: () => {
8831
+ if (toolCallOrdinal !== void 0) suppressedDynamicToolOutcomeOrdinals.add(toolCallOrdinal);
8832
+ },
9208
8833
  onTimeout: () => {
9209
8834
  trajectoryRecorder?.recordEvent("tool.timeout", {
9210
8835
  threadId: call.threadId,
@@ -9216,6 +8841,17 @@ async function runCodexAppServerAttempt(params, options = {}) {
9216
8841
  }
9217
8842
  });
9218
8843
  const protocolResponse = toCodexDynamicToolProtocolResponse(response);
8844
+ if (!protocolResponse.success && toolCallOrdinal !== void 0) {
8845
+ suppressedDynamicToolOutcomeOrdinals.add(toolCallOrdinal);
8846
+ params.onToolOutcome?.({
8847
+ toolName: call.tool,
8848
+ argsHash: "",
8849
+ resultHash: "",
8850
+ toolCallOrdinal,
8851
+ terminalPresentation: void 0,
8852
+ presentationOnly: true
8853
+ });
8854
+ }
9219
8855
  const toolDurationMs = Math.max(0, Date.now() - toolStartedAt);
9220
8856
  trajectoryRecorder?.recordEvent("tool.result", {
9221
8857
  threadId: call.threadId,
@@ -9289,6 +8925,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
9289
8925
  });
9290
8926
  throw error;
9291
8927
  } finally {
8928
+ toolOutcomeOrdinals.delete(call.callId);
9292
8929
  unsubscribeToolDiagnosticObserver();
9293
8930
  }
9294
8931
  } finally {
@@ -9564,7 +9201,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
9564
9201
  }
9565
9202
  });
9566
9203
  params.abortSignal?.removeEventListener("abort", abortFromUpstream);
9567
- releaseSharedClientLeaseOnce();
9204
+ await releaseSharedClientLeaseAndRetireOneShotClient();
9568
9205
  if (usageLimitError) {
9569
9206
  await markCodexAuthProfileBlockedFromRateLimits({
9570
9207
  params,
@@ -9582,7 +9219,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
9582
9219
  }
9583
9220
  }
9584
9221
  if (!turn) {
9585
- releaseSharedClientLeaseOnce();
9222
+ await releaseSharedClientLeaseAndRetireOneShotClient();
9586
9223
  throw new Error("codex app-server turn/start failed without an error");
9587
9224
  }
9588
9225
  turnIdRef.current = turn.turn.id;
@@ -9600,7 +9237,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
9600
9237
  prompt: codexTurnPromptText,
9601
9238
  imagesCount: params.images?.length ?? 0
9602
9239
  });
9603
- projectorRef.current = new CodexAppServerEventProjector(params, thread.threadId, activeTurnId, {
9240
+ projectorRef.current = new CodexAppServerEventProjector(dynamicToolParams, thread.threadId, activeTurnId, {
9604
9241
  nativePostToolUseRelayEnabled: nativeHookRelay?.allowedEvents.includes("post_tool_use") === true && nativeHookRelay.shouldRelayEvent("post_tool_use"),
9605
9242
  trajectoryRecorder
9606
9243
  });
@@ -9815,6 +9452,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
9815
9452
  lastCallUsage: result.attemptUsage,
9816
9453
  promptCache: result.promptCache
9817
9454
  }),
9455
+ contextEngineHostSupport: CODEX_APP_SERVER_CONTEXT_ENGINE_HOST,
9456
+ providerId: params.provider,
9457
+ requestedModelId: params.requestedModelId,
9458
+ modelId: params.modelId,
9459
+ fallbackReason: params.fallbackReason,
9460
+ degradedReason: params.degradedReason,
9818
9461
  runMaintenance: runHarnessContextEngineMaintenance,
9819
9462
  config: params.config,
9820
9463
  warn: (message) => embeddedAgentLog.warn(message),
@@ -9906,7 +9549,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
9906
9549
  notificationCleanup();
9907
9550
  requestCleanup();
9908
9551
  closeCleanup?.();
9909
- releaseSharedClientLeaseOnce();
9552
+ await releaseSharedClientLeaseAndRetireOneShotClient();
9910
9553
  if (nativeHookRelay) if (shouldDelayNativeHookRelayUnregister) scheduleCodexNativeHookRelayUnregister({
9911
9554
  relay: nativeHookRelay,
9912
9555
  hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec
@@ -10036,5 +9679,9 @@ function handleApprovalRequest(params) {
10036
9679
  signal: params.signal
10037
9680
  });
10038
9681
  }
9682
+ function resolveCodexDynamicToolDirectNames(params) {
9683
+ if (params.sourceReplyDeliveryMode !== "message_tool_only") return [];
9684
+ return ["message"];
9685
+ }
10039
9686
  //#endregion
10040
9687
  export { runCodexAppServerAttempt };