@openclaw/codex 2026.9.1-beta.1 → 2026.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/api.js +5 -6
  2. package/dist/{attempt-client-cleanup-bfsmcQ6m.js → attempt-client-cleanup-DYHcRQAa.js} +31 -45
  3. package/dist/attempt-context-DmQUNWwT.js +634 -0
  4. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  5. package/dist/{binding-connection-BMvPX73o.js → binding-connection-E6uDsPlx.js} +2 -2
  6. package/dist/{bounded-turn-B7iBUcuP.js → bounded-turn-wzJrwKKe.js} +15 -20
  7. package/dist/{capabilities-BRMh0aIq.js → capabilities-dIjNVCYH.js} +1 -1
  8. package/dist/cli-metadata.js +1 -1
  9. package/dist/{command-formatters-DEsIufkt.js → command-formatters-CLHhEB1Q.js} +5 -4
  10. package/dist/{command-handlers-D_-1_qRp.js → command-handlers-CTGb2csu.js} +124 -101
  11. package/dist/{compact-BglNFZrQ.js → compact-_t0HQrUS.js} +38 -29
  12. package/dist/{config-giW8X0QA.js → config-BhmfC1s6.js} +294 -282
  13. package/dist/{context-compaction-activity-BOeJf7yo.js → context-compaction-activity-CMla5FPJ.js} +4 -1
  14. package/dist/{conversation-binding-data-CW2GuupV.js → conversation-binding-data-BiodpIzq.js} +2 -1
  15. package/dist/doctor-contract-api.js +143 -12
  16. package/dist/{dynamic-tools-Dnlfa--w.js → dynamic-tools-BU9tmlYm.js} +599 -655
  17. package/dist/{effective-mcp-catalog-CTeGrnfO.js → effective-mcp-catalog-DzYBW4h1.js} +2 -2
  18. package/dist/{event-projector-assistant-message-C5-Ds8mA.js → event-projector-assistant-message-DHRFXKbS.js} +4 -4
  19. package/dist/{event-projector-usage-Bk1I90Bg.js → event-projector-usage-C5mcrGjp.js} +15 -148
  20. package/dist/{harness-Csz0F-DK.js → harness-DXVZ5II5.js} +42 -52
  21. package/dist/harness.js +1 -1
  22. package/dist/index.js +458 -404
  23. package/dist/{isolated-completion-Cv6FqygM.js → isolated-completion-kg9kv4iH.js} +4 -4
  24. package/dist/launch-args-RXQwn8zV.js +90 -0
  25. package/dist/{managed-binary-DFYK4DWq.js → managed-binary-4-Vjz5GH.js} +14 -9
  26. package/dist/media-understanding-provider.js +1 -1
  27. package/dist/model-catalog-CUM68Hkl.js +101 -0
  28. package/dist/{models-TS3HBU-q.js → models-sYnEc0gd.js} +25 -28
  29. package/dist/{native-subagent-monitor-4Mfq2HDs.js → native-subagent-monitor-CnH147jH.js} +55 -115
  30. package/dist/{node-exec-server.runtime-BpWqJO1V.js → node-exec-server.runtime-CstNHCmC.js} +9 -6
  31. package/dist/{plugin-approval-roundtrip-bUm777tR.js → plugin-approval-roundtrip-CNe-Xrjx.js} +30 -3
  32. package/dist/{plugin-marketplace-discovery-B9DvaAG-.js → plugin-marketplace-discovery-CfFlKqDl.js} +15 -14
  33. package/dist/{plugin-metadata-cache-CTCT3w5L.js → plugin-metadata-cache-BFrPhb2c.js} +6 -6
  34. package/dist/provider-capabilities-DW5p4k9w.js +101 -0
  35. package/dist/{rate-limits-C5_D8Gmb.js → rate-limits-CawQJfUS.js} +3 -2
  36. package/dist/{sandbox-guard-kuWTn9C5.js → request-DYAI5FQE.js} +195 -2
  37. package/dist/{run-attempt-DYc0QCBF.js → run-attempt-D1hJO4fX.js} +828 -2100
  38. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-C3jVimFn.js} +21 -14
  39. package/dist/{session-binding-Bcynfd-_.js → session-binding-BHuTCRZQ.js} +198 -127
  40. package/dist/{session-catalog-DaLKVtpH.js → session-catalog-yyvTWFOw.js} +564 -408
  41. package/dist/{session-cli-DMc4oS2w.js → session-cli-CmFEXMAM.js} +1 -1
  42. package/dist/session-history-C5J1AKzG.js +87 -0
  43. package/dist/session-initialization-BeWzjGiR.js +74 -0
  44. package/dist/{session-permission-policy-BrNH1bjy.js → session-permission-policy-DeWVTCMj.js} +2 -2
  45. package/dist/session-retirement-BujDjXVF.js +97 -0
  46. package/dist/{session-upstream-marker-BIkusb95.js → session-upstream-marker-C351-5Qw.js} +19 -4
  47. package/dist/settled-turn-context-D3SNWtUn.js +268 -0
  48. package/dist/settled-turn-finalizer-BVJx4yoc.js +104 -0
  49. package/dist/{shared-client-DKz23hHY.js → shared-client-DxcfF9KX.js} +3480 -679
  50. package/dist/{side-question-DjA_jsNe.js → side-question-BTZAII4l.js} +108 -70
  51. package/dist/{thread-lifecycle-L7oEkTlq.js → thread-lifecycle-DAcEJ_Om.js} +2188 -1915
  52. package/dist/{thread-ownership-DZeuKH1j.js → thread-ownership-zGokd8i1.js} +25 -14
  53. package/dist/thread-policy-g4KbJPzp.js +54 -0
  54. package/dist/{plugin-app-cache-key-C1CAdfcP.js → thread-resume-C7pyVKeW.js} +59 -7
  55. package/dist/{transcript-mirror-DsCMWBca.js → transcript-mirror-DTUxFWa_.js} +79 -17
  56. package/dist/transport-stdio-CyPldczm.js +726 -0
  57. package/dist/{upstream-prompt-provenance-CFmsKwr9.js → upstream-prompt-provenance-LphB8slG.js} +8 -4
  58. package/dist/upstream-session-fork-CYlBIGVt.js +966 -0
  59. package/dist/{usage-ROIaW3wo.js → usage-Bj-rrzkR.js} +3 -3
  60. package/dist/{web-search-provider.runtime-ClaNPV7N.js → web-search-provider.runtime-D2FWSK4w.js} +3 -3
  61. package/openclaw.plugin.json +1 -30
  62. package/package.json +6 -6
  63. package/dist/command-rpc-CCeCkf_v.js +0 -90
  64. package/dist/incognito-session-ET_ufDH-.js +0 -2
  65. package/dist/model-catalog-i9rNBthX.js +0 -48
  66. package/dist/protocol-validators-VeJxxJ0U.js +0 -2421
  67. package/dist/request-DXXGHko0.js +0 -157
  68. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  69. package/dist/session-retirement-CcNC3YSY.js +0 -36
  70. package/dist/settled-turn-finalizer-Dc3ApjU4.js +0 -328
  71. package/dist/thread-resume-BE9qNEW0.js +0 -30
  72. package/dist/transport-stdio-DhoGy33Z.js +0 -99
  73. package/dist/upstream-session-fork-DlaHxSLC.js +0 -326
package/dist/index.js CHANGED
@@ -1,30 +1,26 @@
1
1
  import { registerCodexCliMetadata } from "./cli-metadata.js";
2
- import { n as createCodexAppServerNativeCompaction, t as createCodexAppServerAgentHarness } from "./harness-Csz0F-DK.js";
3
- import { D as readCodexRequirementsToml, _ as CODEX_PLUGINS_MARKETPLACE_NAME, c as assertCodexAppServerConnectionSecurity, h as readCodexPluginConfig, i as resolveCodexAppServerRuntimeOptions, j as resolveOpenClawExecPolicyForCodexAppServer, n as codexSandboxPolicyForTurn, s as resolveCodexSupervisionAppServerRuntimeOptions, t as codexAppServerStartOptionsKey, x as canUseCodexModelBackedApprovalsReviewerForModel } from "./config-giW8X0QA.js";
4
- import { $ as releaseCodexAppServerLiveThread, C as createCodexDesktopGenerationService, H as isUnsupportedCodexAppServerVersionError, J as consumeCodexAppServerLiveThread, R as isCodexAppServerIndeterminateRequestCancellationError, X as isCodexAppServerClientRuntimeLive, Z as isCodexAppServerLiveThreadClaimed, ct as resolveCodexAppServerAuthAccountCacheKey, dt as resolveCodexAppServerFallbackApiKeyCacheKey, f as releaseCodexAppServerClientLease, h as retainSharedCodexAppServerClientByInstanceId, ht as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS, i as clearSharedCodexAppServerClientIfCurrentAndWait, o as getLeasedSharedCodexAppServerClient, p as releaseLeasedSharedCodexAppServerClient, ut as resolveCodexAppServerAuthProfileIdForAgent, v as retireSharedCodexAppServerClientsBeforeDesktopGeneration, vt as isJsonObject, x as withLeasedCodexAppServerClientStartSelectionRetry } from "./shared-client-DKz23hHY.js";
5
- import { i as setManagedCodexPluginRoot } from "./managed-binary-DFYK4DWq.js";
2
+ import { n as createCodexAppServerNativeCompaction, t as createCodexAppServerAgentHarness } from "./harness-DXVZ5II5.js";
3
+ import { $t as isJsonObject, B as isCodexAppServerIndeterminateRequestCancellationError, Bt as resolveCodexAppServerAuthProfileIdForAgent, C as withLeasedCodexAppServerClientStartSelectionRetry, Et as isCodexAppServerLiveThreadClaimed, G as isUnsupportedCodexAppServerVersionError, H as isCodexAppServerOverloadError, Ht as resolveCodexAppServerFallbackApiKeyCacheKey, J as CodexThreadDirectInputError, O as createCodexDesktopGenerationService, Rt as resolveCodexAppServerAuthAccountCacheKey, St as consumeCodexAppServerLiveThread, Tt as isCodexAppServerClientRuntimeLive, X as assertCodexThreadAcceptsDirectInput, Xt as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS, _ as retainSharedCodexAppServerClientByInstanceId, b as retireSharedCodexAppServerClientsBeforeDesktopGeneration, c as getLeasedSharedCodexAppServerClient, et as assertCodexThreadStartResponse, h as releaseLeasedSharedCodexAppServerClient, kt as releaseCodexAppServerLiveThread, m as releaseCodexAppServerClientLease, o as clearSharedCodexAppServerClientIfCurrentAndWait, wt as hasCodexAppServerLiveThread } from "./shared-client-DxcfF9KX.js";
4
+ import { E as readCodexPluginConfig, M as CODEX_PLUGINS_MARKETPLACE_NAME, O as resolveOpenClawExecPolicyForCodexAppServer, c as assertCodexAppServerConnectionSecurity, i as resolveCodexAppServerRuntimeOptions, m as canUseCodexModelBackedApprovalsReviewerForModel, n as codexSandboxPolicyForTurn, s as resolveCodexSupervisionAppServerRuntimeOptions, t as codexAppServerStartOptionsKey, y as readCodexRequirementsToml } from "./config-BhmfC1s6.js";
5
+ import { a as setManagedCodexPluginRoot } from "./managed-binary-4-Vjz5GH.js";
6
6
  import { n as CODEX_APP_SERVER_BINDING_NAMESPACE, t as CODEX_APP_SERVER_BINDING_MAX_ENTRIES } from "./session-binding-meta-B7aEMU7g.js";
7
- import { d as sessionBindingIdentity, n as assertCodexBindingMayBeReplaced, o as isCodexAppServerNativeAuthProfile, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-Bcynfd-_.js";
8
- import { a as closeCodexStartupClientBestEffort, d as unsubscribeCodexThreadBestEffort, h as isCodexNotificationForTurn, o as interruptCodexTurnAndWaitBestEffort, p as getCodexAppServerTurnRouter, u as retireUnsafeCodexTurnClientBestEffort } from "./attempt-client-cleanup-bfsmcQ6m.js";
9
- import { s as isAssistantCommentaryCompletionNotification } from "./event-projector-usage-Bk1I90Bg.js";
10
- import { a as assertCodexThreadStartResponse } from "./protocol-validators-VeJxxJ0U.js";
11
- import { o as defaultCodexAppInventoryCache, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-C1CAdfcP.js";
12
- import { B as resolveCodexAppServerRequestModelSelection, Et as pluginReadParams, G as CODEX_MANAGED_THREAD_MAX_ENTRIES, K as CODEX_MANAGED_THREAD_NAMESPACE, L as buildCodexProjectDocThreadConfig, R as CODEX_NATIVE_PERSONALITY_NONE, St as ensureCodexPluginActivation, bt as mergeCodexThreadConfigs, q as createCodexManagedThreadStore, vt as buildDisabledAppsConfigPatch, wt as isOpenAiCuratedMarketplace } from "./thread-lifecycle-L7oEkTlq.js";
13
- import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
14
- import { t as resumeCodexAppServerThread } from "./thread-resume-BE9qNEW0.js";
15
- import { d as projectBoundedCodexVisibleSessionHistory } from "./transcript-mirror-DsCMWBca.js";
7
+ import { d as sessionBindingIdentity, n as assertCodexBindingMayBeReplaced, o as isCodexAppServerNativeAuthProfile, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-BHuTCRZQ.js";
8
+ import { o as createCodexAppServerProcessReaperService } from "./transport-stdio-CyPldczm.js";
9
+ import { a as closeCodexStartupClientBestEffort, c as isCodexAppServerUnsafeSubscriptionError, d as unsubscribeCodexThreadBestEffort, h as isCodexNotificationForTurn, l as retireUnsafeCodexTurnClientBestEffort, o as interruptCodexTurnAndWaitBestEffort, p as getCodexAppServerTurnRouter } from "./attempt-client-cleanup-DYHcRQAa.js";
10
+ import { o as isAssistantCommentaryCompletionNotification } from "./event-projector-usage-C5mcrGjp.js";
11
+ import { i as buildCodexPluginAppCacheKey, s as defaultCodexAppInventoryCache, t as resumeCodexAppServerThread } from "./thread-resume-C7pyVKeW.js";
12
+ import { Dt as mergeCodexThreadConfigs, E as resolveCodexAppServerRequestModelSelection, Et as buildDisabledAppsConfigPatch, J as createCodexManagedThreadStore, K as CODEX_MANAGED_THREAD_MAX_ENTRIES, Nt as pluginReadParams, jt as isOpenAiCuratedMarketplace, k as buildCodexProjectDocThreadConfig, kt as ensureCodexPluginActivation, q as CODEX_MANAGED_THREAD_NAMESPACE, w as CODEX_NATIVE_PERSONALITY_NONE } from "./thread-lifecycle-DAcEJ_Om.js";
13
+ import { a as rollbackCodexAppServerBindingSubscription, c as withExclusiveCodexAppServerThread, i as retireCodexConversationThreadBinding, l as isIncognitoSessionKey, n as releaseCodexAppServerBindingSubscription, r as retainCodexAppServerBindingSubscription, s as withCodexConversationThreadActivity, t as isSameCodexAppServerThreadOwner } from "./thread-ownership-zGokd8i1.js";
14
+ import { p as projectBoundedCodexVisibleSessionHistory } from "./transcript-mirror-DTUxFWa_.js";
15
+ import { c as resolveCodexNativeSandboxBlock, i as requestCodexAppServerJson, o as withCodexAppServerJsonClient, s as resolveCodexNativeExecutionBlock } from "./request-DYAI5FQE.js";
16
16
  import { buildCodexMediaUnderstandingProvider } from "./media-understanding-provider.js";
17
- import { n as describeControlFailure, t as CODEX_CONTROL_METHODS } from "./capabilities-BRMh0aIq.js";
18
- import { r as formatCodexDisplayText } from "./command-formatters-DEsIufkt.js";
19
- import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, t as CODEX_SESSION_PERMISSION_EXEC_MODES } from "./session-permission-policy-BrNH1bjy.js";
20
- import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "./sandbox-guard-kuWTn9C5.js";
21
- import { a as rollbackCodexAppServerBindingSubscription, i as retireCodexConversationThreadBinding, n as releaseCodexAppServerBindingSubscription, o as withCodexConversationThreadActivity, r as retainCodexAppServerBindingSubscription, s as withExclusiveCodexAppServerThread, t as isSameCodexAppServerThreadOwner } from "./thread-ownership-DZeuKH1j.js";
22
- import { f as trackCodexConversationActiveTurn, g as canMutateCodexHost, h as CODEX_NATIVE_EXECUTION_AUTH_ERROR, t as discoverCodexMarketplacePlugins } from "./plugin-marketplace-discovery-B9DvaAG-.js";
23
- import { a as readCodexConversationBindingDataRecord, i as readCodexConversationBindingData, o as resolveCodexDefaultWorkspaceDir, r as createCodexConversationBindingData } from "./conversation-binding-data-CW2GuupV.js";
24
- import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-BMvPX73o.js";
25
- import { c as createCodexCliSessionNodeInvokePolicies, d as resolveCodexCliSessionForBindingOnNode, f as resumeCodexCliSessionOnNode, i as createCodexSessionCatalogNodeHostCommands, n as createCodexSessionCatalogNodeInvokePolicies, o as assertCodexArchiveDescendantsUnowned, r as createCodexSessionCatalogControl, s as createCodexCliSessionNodeHostCommands, t as codexSessionCatalogRuntime, u as listCodexCliSessionsOnNode } from "./session-catalog-DaLKVtpH.js";
26
- import { i as withCodexAppServerJsonClient, r as requestCodexAppServerJson } from "./request-DXXGHko0.js";
27
- import { t as codexControlRequest } from "./command-rpc-CCeCkf_v.js";
17
+ import { n as describeControlFailure, t as CODEX_CONTROL_METHODS } from "./capabilities-dIjNVCYH.js";
18
+ import { a as formatCodexDisplayText } from "./command-formatters-CLHhEB1Q.js";
19
+ import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, t as CODEX_SESSION_PERMISSION_EXEC_MODES } from "./session-permission-policy-DeWVTCMj.js";
20
+ import { f as trackCodexConversationActiveTurn, g as canMutateCodexHost, h as CODEX_NATIVE_EXECUTION_AUTH_ERROR, t as discoverCodexMarketplacePlugins } from "./plugin-marketplace-discovery-CfFlKqDl.js";
21
+ import { a as readCodexConversationBindingDataRecord, i as readCodexConversationBindingData, o as resolveCodexDefaultWorkspaceDir, r as createCodexConversationBindingData } from "./conversation-binding-data-BiodpIzq.js";
22
+ import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-E6uDsPlx.js";
23
+ import { d as createCodexCliSessionNodeHostCommands, f as createCodexCliSessionNodeInvokePolicies, g as resumeCodexCliSessionOnNode, h as resolveCodexCliSessionForBindingOnNode, i as createCodexSessionCatalogNodeHostCommands, m as listCodexCliSessionsOnNode, n as createCodexSessionCatalogNodeInvokePolicies, o as assertCodexArchiveDescendantsUnowned, r as createCodexSessionCatalogControl, s as codexControlRequest, t as codexSessionCatalogRuntime } from "./session-catalog-yyvTWFOw.js";
28
24
  import { t as createCodexWebSearchProviderBase } from "./web-search-provider.shared-BrZmlqyR.js";
29
25
  import { mutateConfigFile } from "openclaw/plugin-sdk/config-mutation";
30
26
  import { normalizePluginsConfig, resolveEffectiveEnableState, resolveLivePluginConfigObject, resolvePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
@@ -34,20 +30,20 @@ import fs from "node:fs/promises";
34
30
  import path from "node:path";
35
31
  import { parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
36
32
  import { embeddedAgentLog, formatErrorMessage, resolveActiveEmbeddedRunSessionId, resolveSandboxContext } from "openclaw/plugin-sdk/agent-harness-runtime";
33
+ import { canonicalPathFromExistingAncestor, isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
37
34
  import os from "node:os";
38
35
  import { loadAuthProfileStoreWithoutExternalProfiles, resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentDir, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
39
36
  import { applyAuthProfileConfig, buildApiKeyCredential, buildOauthProviderAuthResult, buildOpenAICodexCredentialExtra, readCodexCliCredentialsCached, resolveOpenAICodexAuthIdentity, resolveOpenAICodexImportProfileName, updateAuthProfileStoreWithLock } from "openclaw/plugin-sdk/provider-auth";
40
- import { resolveSessionAgentIds as resolveSessionAgentIds$2 } from "openclaw/plugin-sdk/agent-scope-runtime";
41
- import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
42
37
  import { extractErrorCode, pathExists } from "openclaw/plugin-sdk/security-runtime";
38
+ import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
39
+ import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
43
40
  import { getSessionEntry, resolveStorePath, resolveTranscriptSessionKeyBySessionId } from "openclaw/plugin-sdk/session-store-runtime";
44
41
  import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
45
42
  import { fileURLToPath } from "node:url";
46
43
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
47
- import { canonicalPathFromExistingAncestor, isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
44
+ import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
48
45
  import { readVisibleSessionTranscriptMessageEntries } from "openclaw/plugin-sdk/session-transcript-runtime";
49
46
  import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-binding-runtime";
50
- import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
51
47
  import { ModelSelectionLockedError, isModelSelectionLocked } from "openclaw/plugin-sdk/model-session-runtime";
52
48
  import { MIGRATION_REASON_TARGET_EXISTS, applyMigrationManualItem, createMigrationItem, createMigrationManualItem, hasMigrationConfigPatchConflict, markMigrationItemConflict, markMigrationItemError, markMigrationItemSkipped, mergeMigrationConfigValue, readMigrationConfigPath, resolveMigrationConfigRuntime, summarizeMigrationItems, writeMigrationConfigPath } from "openclaw/plugin-sdk/migration";
53
49
  import { archiveMigrationItem, copyMemoryMigrationFileItem, copyMigrationFileItem, withCachedMigrationConfigRuntime, writeMigrationReport } from "openclaw/plugin-sdk/migration-runtime";
@@ -184,17 +180,18 @@ function waitForReconnect(delayMs, signal) {
184
180
  /** Defers schema compilation and auth loading until the first binding operation. */
185
181
  function createLazyCodexAppServerBindingStore(state, managedThreadState) {
186
182
  let resolved;
187
- const store = () => resolved ??= import("./session-binding-Bcynfd-_.js").then((n) => n.f).then(({ createCodexAppServerBindingStore }) => createCodexAppServerBindingStore(state));
183
+ const store = () => resolved ??= import("./session-binding-BHuTCRZQ.js").then((n) => n.f).then(({ createCodexAppServerBindingStore }) => createCodexAppServerBindingStore(state));
188
184
  const managedThreads = managedThreadState ? createCodexManagedThreadStore(managedThreadState) : void 0;
189
185
  return {
190
186
  ...managedThreads ? { managedThreads } : {},
191
187
  read: async (identity) => (await store()).read(identity),
192
188
  hasOtherThreadOwner: async (threadId, currentIdentity) => (await store()).hasOtherThreadOwner(threadId, currentIdentity),
193
- mutate: async (identity, mutation) => (await store()).mutate(identity, mutation),
189
+ mutate: async (identity, mutation, assertCurrent) => (await store()).mutate(identity, mutation, assertCurrent),
194
190
  prepareSessionGenerationReclaim: async (identity) => (await store()).prepareSessionGenerationReclaim(identity),
195
191
  adoptSessionGeneration: async (identity, previousSessionId) => (await store()).adoptSessionGeneration(identity, previousSessionId),
196
192
  resetSessionGeneration: async (identity) => (await store()).resetSessionGeneration(identity),
197
193
  retireSessionGeneration: async (identity) => (await store()).retireSessionGeneration(identity),
194
+ withSessionDeletion: async (identity, assertCurrent, run) => (await store()).withSessionDeletion(identity, assertCurrent, run),
198
195
  withThreadArchiveFence: async (run) => (await store()).withThreadArchiveFence(run),
199
196
  withLease: async (identity, run) => (await store()).withLease(identity, run)
200
197
  };
@@ -214,7 +211,7 @@ async function handleCodexCommand(ctx, options) {
214
211
  }
215
212
  }
216
213
  async function loadDefaultCodexSubcommandHandler() {
217
- const { handleCodexSubcommand } = await import("./command-handlers-D_-1_qRp.js");
214
+ const { handleCodexSubcommand } = await import("./command-handlers-CTGb2csu.js");
218
215
  return handleCodexSubcommand;
219
216
  }
220
217
  //#endregion
@@ -396,7 +393,7 @@ function isImageMedia(media) {
396
393
  return IMAGE_EXTENSIONS.has(path.extname(candidate.split(/[?#]/, 1)[0] ?? "").toLowerCase());
397
394
  }
398
395
  function normalizeFileUrl(value) {
399
- if (!value.startsWith("file://")) return value;
396
+ if (!/^file:\/\//iu.test(value)) return value;
400
397
  try {
401
398
  return fileURLToPath(value);
402
399
  } catch {
@@ -405,18 +402,18 @@ function normalizeFileUrl(value) {
405
402
  }
406
403
  function readLocalMediaPath(value) {
407
404
  if (!value) return;
408
- if (value.startsWith("file://")) return value;
405
+ if (/^file:\/\//iu.test(value)) return value;
409
406
  if (value.startsWith("//")) return;
410
407
  if (path.isAbsolute(value) || path.win32.isAbsolute(value)) return value;
411
408
  return /^[a-z][a-z0-9+.-]*:/i.test(value) ? void 0 : value;
412
409
  }
413
410
  //#endregion
414
411
  //#region extensions/codex/src/conversation-binding.ts
415
- const DEFAULT_BOUND_TURN_TIMEOUT_MS = 20 * 6e4;
412
+ const DEFAULT_BOUND_TURN_TIMEOUT_MS = 12e5;
416
413
  const NATIVE_CONVERSATION_INTERACTIVE_APPROVALS_UNAVAILABLE = "OpenClaw native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow.";
417
414
  async function resolveConversationAppServerRuntime(params) {
418
415
  const source = params.source;
419
- const agentId = source?.agentId ?? params.agentId ?? (params.config ? resolveSessionAgentIds$2({
416
+ const agentId = source?.agentId ?? params.agentId ?? (params.config ? resolveSessionAgentIdsStrict({
420
417
  sessionKey: params.sessionKey,
421
418
  config: params.config
422
419
  }).sessionAgentId : void 0);
@@ -511,12 +508,10 @@ async function startCodexConversationThread(params) {
511
508
  authProfileId: params.authProfileId ?? existingBinding?.authProfileId,
512
509
  ...agentLookup
513
510
  });
514
- const incognito = isIncognitoSessionKey(params.sessionKey);
515
- if (params.threadId?.trim()) await attachExistingThread({
511
+ const bindingParams = {
516
512
  pluginConfig: params.pluginConfig,
517
513
  bindingStore: params.bindingStore,
518
514
  identity,
519
- threadId: params.threadId.trim(),
520
515
  workspaceDir,
521
516
  ...agentDir ? { agentDir } : {},
522
517
  model: params.model,
@@ -525,24 +520,19 @@ async function startCodexConversationThread(params) {
525
520
  serviceTier: params.serviceTier,
526
521
  config: params.config,
527
522
  sessionKey: params.sessionKey,
528
- incognito,
523
+ incognito: isIncognitoSessionKey(params.sessionKey),
529
524
  agentId: params.agentId
530
- });
531
- else await createThread({
532
- pluginConfig: params.pluginConfig,
525
+ };
526
+ const threadId = params.threadId?.trim();
527
+ const bind = () => params.bindingStore.withLease(identity, () => bindThread(bindingParams, threadId));
528
+ const ownedThreadId = threadId ?? existingBinding?.threadId;
529
+ if (ownedThreadId) await withExclusiveCodexAppServerThread({
533
530
  bindingStore: params.bindingStore,
534
531
  identity,
535
- workspaceDir,
536
- ...agentDir ? { agentDir } : {},
537
- model: params.model,
538
- modelProvider: params.modelProvider,
539
- authProfileId,
540
- serviceTier: params.serviceTier,
541
- config: params.config,
542
- sessionKey: params.sessionKey,
543
- incognito,
544
- agentId: params.agentId
532
+ threadId: ownedThreadId,
533
+ run: bind
545
534
  });
535
+ else await bind();
546
536
  const storedBinding = await params.bindingStore.read(identity);
547
537
  if (!storedBinding) throw new Error("Codex session binding disappeared while starting its conversation thread.");
548
538
  return createCodexConversationBindingData({
@@ -707,21 +697,21 @@ async function resolveThreadBindingRuntime(params) {
707
697
  authProfileId: params.authProfileId,
708
698
  ...agentLookup
709
699
  };
710
- const client = await getLeasedSharedCodexAppServerClient(clientOptions);
711
700
  return {
712
701
  runtime: modelScopedRuntime,
713
702
  workspaceDir,
714
703
  agentLookup,
715
704
  model: modelSelection?.model,
716
705
  modelProvider: modelSelection?.modelProvider ?? modelProvider,
717
- client,
718
- clientLease: { client },
719
706
  clientOptions
720
707
  };
721
708
  }
722
- function buildThreadRequestRuntimeOptions(params, resolved) {
723
- const serviceTier = params.serviceTier ?? resolved.runtime.serviceTier;
709
+ function buildConversationThreadRequest(resolved, serviceTier) {
724
710
  return {
711
+ cwd: resolved.workspaceDir,
712
+ ...resolved.model ? { model: resolved.model } : {},
713
+ ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
714
+ personality: CODEX_NATIVE_PERSONALITY_NONE,
725
715
  approvalPolicy: resolved.runtime.approvalPolicy,
726
716
  approvalsReviewer: resolved.runtime.approvalsReviewer,
727
717
  ...resolved.runtime.sessionRoot ? { runtimeWorkspaceRoots: [resolved.runtime.sessionRoot] } : {},
@@ -738,45 +728,33 @@ function codexConversationSandboxOrPermissions(runtime, sandbox) {
738
728
  config
739
729
  };
740
730
  }
741
- async function requestNewConversationBindingThread(params, resolved) {
742
- return await withLeasedCodexAppServerClientStartSelectionRetry({
743
- lease: resolved.clientLease,
744
- options: resolved.clientOptions,
745
- run: async (client, requestOptions) => await client.request("thread/start", {
746
- cwd: resolved.workspaceDir,
747
- ...resolved.model ? { model: resolved.model } : {},
748
- ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
749
- personality: CODEX_NATIVE_PERSONALITY_NONE,
750
- ...buildThreadRequestRuntimeOptions(params, resolved),
751
- developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
752
- experimentalRawEvents: true,
753
- ...params.incognito ? { ephemeral: true } : {}
754
- }, requestOptions),
755
- onClientChange: (client) => {
756
- resolved.client = client;
757
- }
758
- });
759
- }
760
- async function writeThreadBindingFromResponse(params, resolved, response) {
761
- const current = await params.bindingStore.read(params.identity);
762
- assertCodexBindingMayBeReplaced(current, "storing a conversation-bound Codex thread");
763
- const trackSubscription = !params.incognito && isCodexAppServerClientRuntimeLive(resolved.client);
764
- const sameOwner = isSameCodexAppServerThreadOwner(current, {
765
- threadId: response.thread.id,
766
- clientId: resolved.client.getInstanceId()
767
- });
731
+ async function writeThreadBindingFromResponse(params, resolved, client, response, requestOptions) {
768
732
  let retained = false;
733
+ let sameOwner = false;
769
734
  try {
735
+ const current = await params.bindingStore.read(params.identity);
736
+ assertCodexBindingMayBeReplaced(current, "storing a conversation-bound Codex thread");
737
+ const trackSubscription = !params.incognito && isCodexAppServerClientRuntimeLive(client);
738
+ sameOwner = isSameCodexAppServerThreadOwner(current, {
739
+ threadId: response.thread.id,
740
+ clientId: client.getInstanceId()
741
+ });
742
+ requestOptions();
743
+ assertCodexThreadAcceptsDirectInput(response.thread);
770
744
  if (trackSubscription) {
771
- retained = await retainCodexAppServerBindingSubscription(resolved.client, response.thread.id);
745
+ retained = await retainCodexAppServerBindingSubscription(client, response.thread.id);
772
746
  if (!retained) throw new Error("Codex conversation thread lost its native subscription owner.");
773
747
  }
774
- if (current && !sameOwner) await releaseCodexAppServerBindingSubscription(current);
748
+ if (current && !sameOwner) {
749
+ const { assertCurrent } = requestOptions();
750
+ await releaseCodexAppServerBindingSubscription(current, { assertCurrent });
751
+ }
752
+ requestOptions();
775
753
  if (!await params.bindingStore.mutate(params.identity, {
776
754
  kind: "set",
777
755
  binding: {
778
756
  threadId: response.thread.id,
779
- clientId: resolved.client.getInstanceId(),
757
+ clientId: client.getInstanceId(),
780
758
  cwd: resolved.workspaceDir,
781
759
  authProfileId: params.authProfileId,
782
760
  model: response.model ?? resolved.model ?? params.model,
@@ -789,56 +767,54 @@ async function writeThreadBindingFromResponse(params, resolved, response) {
789
767
  networkProxyProfileName: resolved.runtime.networkProxy?.profileName,
790
768
  networkProxyConfigFingerprint: resolved.runtime.networkProxy?.configFingerprint
791
769
  }
792
- })) throw new Error("Codex conversation binding changed while storing its thread.");
770
+ }, requestOptions)) throw new Error("Codex conversation binding changed while storing its thread.");
793
771
  } catch (error) {
794
- if (trackSubscription && !sameOwner) await rollbackCodexAppServerBindingSubscription(resolved.client, response.thread.id, retained);
772
+ if (retained && !sameOwner || !hasCodexAppServerLiveThread(client, response.thread.id)) await rollbackCodexAppServerBindingSubscription(client, response.thread.id, retained);
795
773
  throw error;
796
774
  }
797
775
  }
798
- async function attachExistingThread(params) {
799
- await withExclusiveCodexAppServerThread({
800
- bindingStore: params.bindingStore,
801
- identity: params.identity,
802
- threadId: params.threadId,
803
- run: async () => {
804
- assertCodexBindingMayBeReplaced(await params.bindingStore.read(params.identity), "attaching a conversation-bound Codex thread");
805
- const resolved = await resolveThreadBindingRuntime(params);
806
- try {
807
- const response = resolved.runtime.networkProxy ? await requestNewConversationBindingThread(params, resolved) : await withLeasedCodexAppServerClientStartSelectionRetry({
808
- lease: resolved.clientLease,
809
- options: resolved.clientOptions,
810
- run: async (client, requestOptions) => {
811
- if (isCodexAppServerLiveThreadClaimed(client, params.threadId)) throw new Error(`Codex thread ${params.threadId} has an active run; stop it before binding its conversation.`);
812
- await releaseCodexAppServerLiveThread(client, params.threadId);
813
- if (isCodexAppServerLiveThreadClaimed(client, params.threadId)) throw new Error(`Codex thread ${params.threadId} has an active run; stop it before binding its conversation.`);
814
- return await client.request(CODEX_CONTROL_METHODS.resumeThread, {
815
- threadId: params.threadId,
816
- cwd: resolved.workspaceDir,
817
- ...resolved.model ? { model: resolved.model } : {},
818
- ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
819
- personality: CODEX_NATIVE_PERSONALITY_NONE,
820
- ...buildThreadRequestRuntimeOptions(params, resolved)
821
- }, requestOptions);
822
- },
823
- onClientChange: (client) => {
824
- resolved.client = client;
825
- }
826
- });
827
- if (!resolved.runtime.networkProxy && response.thread.id !== params.threadId) throw new Error(`Codex conversation resume returned ${response.thread.id} for ${params.threadId}.`);
828
- await writeThreadBindingFromResponse(params, resolved, response);
829
- } finally {
830
- releaseCodexAppServerClientLease(resolved.clientLease);
831
- }
832
- }
833
- });
834
- }
835
- async function createThread(params) {
836
- assertCodexBindingMayBeReplaced(await params.bindingStore.read(params.identity), "creating a conversation-bound Codex thread");
776
+ async function bindThread(params, threadId) {
777
+ const current = await params.bindingStore.read(params.identity);
778
+ assertCodexBindingMayBeReplaced(current, "binding a conversation-bound Codex thread");
837
779
  const resolved = await resolveThreadBindingRuntime(params);
780
+ const clientLease = { client: await getLeasedSharedCodexAppServerClient(resolved.clientOptions) };
838
781
  try {
839
- await writeThreadBindingFromResponse(params, resolved, await requestNewConversationBindingThread(params, resolved));
782
+ await withLeasedCodexAppServerClientStartSelectionRetry({
783
+ lease: clientLease,
784
+ options: resolved.clientOptions,
785
+ run: async (client, requestOptions) => {
786
+ const request = buildConversationThreadRequest(resolved, params.serviceTier ?? resolved.runtime.serviceTier);
787
+ let response;
788
+ if (threadId && !resolved.runtime.networkProxy) {
789
+ if (isCodexAppServerLiveThreadClaimed(client, threadId)) throw new Error(`Codex thread ${threadId} has an active run; stop it before binding its conversation.`);
790
+ const { thread } = await client.request("thread/read", {
791
+ threadId,
792
+ includeTurns: false
793
+ }, requestOptions());
794
+ assertCodexThreadAcceptsDirectInput(thread);
795
+ const { assertCurrent } = requestOptions();
796
+ await releaseCodexAppServerLiveThread(client, threadId, assertCurrent);
797
+ if (isCodexAppServerLiveThreadClaimed(client, threadId)) throw new Error(`Codex thread ${threadId} has an active run; stop it before binding its conversation.`);
798
+ response = await resumeCodexAppServerThread({
799
+ client,
800
+ abandonClient: () => closeCodexStartupClientBestEffort(client),
801
+ request: {
802
+ ...request,
803
+ threadId
804
+ },
805
+ requestResume: (resumeRequest) => client.request("thread/resume", resumeRequest, requestOptions())
806
+ });
807
+ } else response = await client.request("thread/start", {
808
+ ...request,
809
+ developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
810
+ experimentalRawEvents: true,
811
+ ...params.incognito ? { ephemeral: true } : {}
812
+ }, requestOptions());
813
+ await writeThreadBindingFromResponse(params, resolved, client, response, requestOptions);
814
+ }
815
+ });
840
816
  } finally {
841
- releaseCodexAppServerClientLease(resolved.clientLease);
817
+ releaseCodexAppServerClientLease(clientLease);
842
818
  }
843
819
  }
844
820
  async function runBoundTurn(params) {
@@ -849,264 +825,286 @@ async function runBoundTurn(params) {
849
825
  const identity = conversationBindingIdentity(params.data);
850
826
  const binding = await params.bindingStore.read(identity);
851
827
  if (!binding?.threadId) throw new Error("bound Codex conversation has no thread binding");
852
- assertCodexBindingMayBeReplaced(binding, "running a conversation-bound Codex thread");
853
- let threadId = binding.threadId;
854
- const requestedWorkspaceDir = binding.cwd || params.data.workspaceDir;
855
- const reviewerModelProvider = resolveModelBackedReviewerPolicyProvider({
856
- authProfileId: binding.authProfileId,
857
- modelProvider: binding.modelProvider,
858
- ...agentLookup
859
- });
860
- const { runtime, workspaceDir } = await resolveConversationAppServerRuntime({
861
- pluginConfig: params.pluginConfig,
862
- config: params.config,
863
- agentId: params.data.source?.agentId ?? params.data.agentId,
864
- sessionKey: params.data.legacyBinding ? params.sessionKey : params.data.source?.sessionKey,
865
- source: params.data.source,
866
- workspaceDir: requestedWorkspaceDir,
867
- modelProvider: reviewerModelProvider,
868
- model: binding.model,
869
- agentDir: params.data.agentDir
870
- });
871
- const modelScopedRuntime = resolveCodexAppServerForModelProvider({
872
- appServer: runtime,
873
- provider: reviewerModelProvider,
874
- model: binding.model,
875
- config: params.config,
876
- env: process.env,
877
- agentDir: params.data.agentDir
878
- });
879
- const sessionRoot = modelScopedRuntime.sessionRoot;
880
- const approvalPolicy = modelScopedRuntime.approvalPolicy;
881
- const sandbox = modelScopedRuntime.sandbox;
882
- const permissionProfile = modelScopedRuntime.networkProxy?.profileName;
883
- const networkProxyConfigFingerprint = modelScopedRuntime.networkProxy?.configFingerprint;
884
- const networkProxyBindingChanged = binding.networkProxyProfileName !== permissionProfile || binding.networkProxyConfigFingerprint !== networkProxyConfigFingerprint;
885
- const serviceTier = binding.serviceTier ?? runtime.serviceTier;
886
- let useStickyNetworkProfile = permissionProfile !== void 0 && binding.networkProxyProfileName === permissionProfile && binding.networkProxyConfigFingerprint === networkProxyConfigFingerprint;
887
- assertNativeConversationApprovalPolicySupported(modelScopedRuntime);
888
- const modelSelection = binding.model ? resolveCodexAppServerRequestModelSelection({
889
- model: binding.model,
890
- modelProvider: binding.modelProvider,
891
- authProfileId: binding.authProfileId,
892
- ...agentLookup
893
- }) : void 0;
894
- const clientOptions = {
895
- startOptions: runtime.start,
896
- timeoutMs: runtime.requestTimeoutMs,
897
- authProfileId: binding.authProfileId,
898
- ...agentLookup
899
- };
900
- let client = await getLeasedSharedCodexAppServerClient(clientOptions);
901
- const clientLease = { client };
902
- let activeTurnId;
903
- let activeTurnCleanup = () => void 0;
904
- let retiredUnsafeClient;
905
- let turnRoute;
906
- let liveThreadOwnership;
907
- let ownsNativeSubscription = false;
908
- let turnSucceeded = false;
909
- try {
910
- if (!params.incognito && isCodexAppServerClientRuntimeLive(client)) {
911
- const ownership = await consumeCodexAppServerLiveThread(client, threadId);
912
- if (ownership) {
913
- liveThreadOwnership = {
914
- client,
828
+ return await withExclusiveCodexAppServerThread({
829
+ bindingStore: params.bindingStore,
830
+ identity,
831
+ threadId: binding.threadId,
832
+ run: async () => {
833
+ const current = await params.bindingStore.read(identity);
834
+ if (!isSameCodexAppServerThreadOwner(current, binding)) throw new Error("Codex conversation binding changed before its turn.");
835
+ assertCodexBindingMayBeReplaced(binding, "running a conversation-bound Codex thread");
836
+ let threadId = binding.threadId;
837
+ const requestedWorkspaceDir = binding.cwd || params.data.workspaceDir;
838
+ const reviewerModelProvider = resolveModelBackedReviewerPolicyProvider({
839
+ authProfileId: binding.authProfileId,
840
+ modelProvider: binding.modelProvider,
841
+ ...agentLookup
842
+ });
843
+ const { runtime, workspaceDir } = await resolveConversationAppServerRuntime({
844
+ pluginConfig: params.pluginConfig,
845
+ config: params.config,
846
+ agentId: params.data.source?.agentId ?? params.data.agentId,
847
+ sessionKey: params.data.legacyBinding ? params.sessionKey : params.data.source?.sessionKey,
848
+ source: params.data.source,
849
+ workspaceDir: requestedWorkspaceDir,
850
+ modelProvider: reviewerModelProvider,
851
+ model: binding.model,
852
+ agentDir: params.data.agentDir
853
+ });
854
+ const modelScopedRuntime = resolveCodexAppServerForModelProvider({
855
+ appServer: runtime,
856
+ provider: reviewerModelProvider,
857
+ model: binding.model,
858
+ config: params.config,
859
+ env: process.env,
860
+ agentDir: params.data.agentDir
861
+ });
862
+ const sessionRoot = modelScopedRuntime.sessionRoot;
863
+ const approvalPolicy = modelScopedRuntime.approvalPolicy;
864
+ const sandbox = modelScopedRuntime.sandbox;
865
+ const permissionProfile = modelScopedRuntime.networkProxy?.profileName;
866
+ const networkProxyConfigFingerprint = modelScopedRuntime.networkProxy?.configFingerprint;
867
+ const networkProxyBindingChanged = binding.networkProxyProfileName !== permissionProfile || binding.networkProxyConfigFingerprint !== networkProxyConfigFingerprint;
868
+ const serviceTier = binding.serviceTier ?? runtime.serviceTier;
869
+ let useStickyNetworkProfile = permissionProfile !== void 0 && binding.networkProxyProfileName === permissionProfile && binding.networkProxyConfigFingerprint === networkProxyConfigFingerprint;
870
+ assertNativeConversationApprovalPolicySupported(modelScopedRuntime);
871
+ const modelSelection = binding.model ? resolveCodexAppServerRequestModelSelection({
872
+ model: binding.model,
873
+ modelProvider: binding.modelProvider,
874
+ authProfileId: binding.authProfileId,
875
+ ...agentLookup
876
+ }) : void 0;
877
+ const threadRequestRuntime = {
878
+ runtime: modelScopedRuntime,
879
+ workspaceDir,
880
+ ...modelSelection
881
+ };
882
+ const clientOptions = {
883
+ startOptions: runtime.start,
884
+ timeoutMs: runtime.requestTimeoutMs,
885
+ authProfileId: binding.authProfileId,
886
+ ...agentLookup
887
+ };
888
+ let client = await getLeasedSharedCodexAppServerClient(clientOptions);
889
+ const clientLease = { client };
890
+ let activeTurnId;
891
+ let activeTurnCleanup = () => void 0;
892
+ let isolatedSubscriptionClient;
893
+ let turnRoute;
894
+ let liveThreadOwnership;
895
+ let ownsNativeSubscription = false;
896
+ let turnSucceeded = false;
897
+ const assertResumeInputAllowed = async () => {
898
+ const { thread } = await client.request("thread/read", {
915
899
  threadId,
916
- ownership
917
- };
918
- ownsNativeSubscription = true;
919
- }
920
- }
921
- if (networkProxyBindingChanged) {
922
- const response = assertCodexThreadStartResponse(await withLeasedCodexAppServerClientStartSelectionRetry({
923
- lease: clientLease,
924
- options: clientOptions,
925
- run: async (requestClient, requestOptions) => await requestClient.request("thread/start", {
900
+ includeTurns: false
901
+ }, { timeoutMs: runtime.requestTimeoutMs });
902
+ assertCodexThreadAcceptsDirectInput(thread);
903
+ };
904
+ try {
905
+ if (!networkProxyBindingChanged && binding.clientId !== client.getInstanceId()) await assertResumeInputAllowed();
906
+ if (!params.incognito && isCodexAppServerClientRuntimeLive(client)) {
907
+ const ownership = await consumeCodexAppServerLiveThread(client, threadId);
908
+ if (ownership) {
909
+ liveThreadOwnership = {
910
+ client,
911
+ threadId,
912
+ ownership
913
+ };
914
+ ownsNativeSubscription = true;
915
+ }
916
+ }
917
+ if (networkProxyBindingChanged) {
918
+ const response = assertCodexThreadStartResponse(await withLeasedCodexAppServerClientStartSelectionRetry({
919
+ lease: clientLease,
920
+ options: clientOptions,
921
+ run: async (requestClient, requestOptions) => await requestClient.request("thread/start", {
922
+ ...buildConversationThreadRequest(threadRequestRuntime, serviceTier),
923
+ developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
924
+ experimentalRawEvents: true,
925
+ ...params.incognito ? { ephemeral: true } : {}
926
+ }, requestOptions()),
927
+ onClientChange: (nextClient) => {
928
+ client = nextClient;
929
+ }
930
+ }));
931
+ threadId = response.thread.id;
932
+ ownsNativeSubscription = true;
933
+ assertCodexThreadAcceptsDirectInput(response.thread);
934
+ if (liveThreadOwnership && (liveThreadOwnership.threadId !== threadId || liveThreadOwnership.client !== client)) {
935
+ const previousOwnership = liveThreadOwnership;
936
+ try {
937
+ await previousOwnership.ownership.release(previousOwnership.threadId);
938
+ } catch (error) {
939
+ if (!(isCodexAppServerClientRuntimeLive(previousOwnership.client) && await retainCodexAppServerBindingSubscription(previousOwnership.client, previousOwnership.threadId, previousOwnership.ownership).catch(() => false))) await closeCodexStartupClientBestEffort(previousOwnership.client);
940
+ liveThreadOwnership = void 0;
941
+ throw error;
942
+ }
943
+ liveThreadOwnership = void 0;
944
+ } else if (binding.threadId !== threadId) await releaseCodexAppServerBindingSubscription(binding);
945
+ if (!await params.bindingStore.mutate(identity, {
946
+ kind: "set",
947
+ binding: {
948
+ threadId,
949
+ clientId: client.getInstanceId(),
950
+ cwd: response.thread.cwd ?? workspaceDir,
951
+ authProfileId: binding.authProfileId,
952
+ model: response.model ?? modelSelection?.model ?? binding.model,
953
+ modelProvider: normalizeCodexAppServerBindingModelProvider({
954
+ authProfileId: binding.authProfileId,
955
+ modelProvider: response.modelProvider ?? modelSelection?.modelProvider ?? binding.modelProvider,
956
+ ...agentLookup
957
+ }),
958
+ serviceTier: serviceTier ?? void 0,
959
+ networkProxyProfileName: modelScopedRuntime.networkProxy?.profileName,
960
+ networkProxyConfigFingerprint: modelScopedRuntime.networkProxy?.configFingerprint,
961
+ conversationStartId: binding.conversationStartId,
962
+ conversationSourceTransferComplete: binding.conversationSourceTransferComplete,
963
+ historyCoveredThrough: binding.historyCoveredThrough
964
+ }
965
+ })) throw new Error("Codex conversation binding changed while rotating its thread.");
966
+ useStickyNetworkProfile = modelScopedRuntime.networkProxy !== void 0;
967
+ } else if (binding.clientId !== client.getInstanceId() || isCodexAppServerClientRuntimeLive(client) && !params.incognito && !liveThreadOwnership) {
968
+ if (binding.clientId === client.getInstanceId()) await assertResumeInputAllowed();
969
+ const response = await withLeasedCodexAppServerClientStartSelectionRetry({
970
+ lease: clientLease,
971
+ options: clientOptions,
972
+ run: async (requestClient, requestOptions) => await resumeCodexAppServerThread({
973
+ client: requestClient,
974
+ onSubscriptionReleased: () => {
975
+ isolatedSubscriptionClient = requestClient;
976
+ },
977
+ abandonClient: async () => {
978
+ await closeCodexStartupClientBestEffort(requestClient);
979
+ isolatedSubscriptionClient = requestClient;
980
+ },
981
+ request: {
982
+ threadId,
983
+ ...buildConversationThreadRequest(threadRequestRuntime, serviceTier)
984
+ },
985
+ requestResume: (request) => requestClient.request("thread/resume", request, requestOptions())
986
+ }),
987
+ onClientChange: (nextClient) => {
988
+ client = nextClient;
989
+ }
990
+ });
991
+ threadId = response.thread.id;
992
+ ownsNativeSubscription = true;
993
+ assertCodexThreadAcceptsDirectInput(response.thread);
994
+ if (!isSameCodexAppServerThreadOwner(binding, {
995
+ threadId,
996
+ clientId: client.getInstanceId()
997
+ })) await releaseCodexAppServerBindingSubscription(binding);
998
+ if (!await params.bindingStore.mutate(identity, {
999
+ kind: "patch",
1000
+ threadId: binding.threadId,
1001
+ patch: {
1002
+ clientId: client.getInstanceId(),
1003
+ cwd: response.thread.cwd ?? binding.cwd,
1004
+ model: response.model ?? modelSelection?.model ?? binding.model,
1005
+ modelProvider: normalizeCodexAppServerBindingModelProvider({
1006
+ authProfileId: binding.authProfileId,
1007
+ modelProvider: response.modelProvider ?? modelSelection?.modelProvider ?? binding.modelProvider,
1008
+ ...agentLookup
1009
+ })
1010
+ }
1011
+ })) throw new Error("Codex conversation binding changed while resuming on a new client.");
1012
+ }
1013
+ const turnCollector = createCodexConversationTurnCollector(threadId);
1014
+ turnRoute = getCodexAppServerTurnRouter(client).reserveThread({
1015
+ threadId,
1016
+ onNotification: turnCollector.handleNotification
1017
+ });
1018
+ turnRoute.armTurn();
1019
+ activeTurnId = (await client.request("turn/start", {
1020
+ threadId,
1021
+ input: buildCodexConversationTurnInput({
1022
+ prompt: params.prompt,
1023
+ event: params.event
1024
+ }),
926
1025
  cwd: workspaceDir,
927
1026
  ...sessionRoot ? { runtimeWorkspaceRoots: [sessionRoot] } : {},
928
- ...modelSelection?.model ? { model: modelSelection.model } : {},
929
- ...modelSelection?.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
930
- personality: CODEX_NATIVE_PERSONALITY_NONE,
931
1027
  approvalPolicy,
932
1028
  approvalsReviewer: modelScopedRuntime.approvalsReviewer,
933
- ...codexConversationSandboxOrPermissions(modelScopedRuntime, sandbox),
934
- ...serviceTier ? { serviceTier } : {},
935
- developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
936
- experimentalRawEvents: true,
937
- ...params.incognito ? { ephemeral: true } : {}
938
- }, requestOptions),
939
- onClientChange: (nextClient) => {
940
- client = nextClient;
941
- }
942
- }));
943
- threadId = response.thread.id;
944
- ownsNativeSubscription = true;
945
- if (liveThreadOwnership && (liveThreadOwnership.threadId !== threadId || liveThreadOwnership.client !== client)) {
946
- const previousOwnership = liveThreadOwnership;
947
- try {
948
- await previousOwnership.ownership.release(previousOwnership.threadId);
949
- } catch (error) {
950
- if (!(isCodexAppServerClientRuntimeLive(previousOwnership.client) && await retainCodexAppServerBindingSubscription(previousOwnership.client, previousOwnership.threadId, previousOwnership.ownership).catch(() => false))) await closeCodexStartupClientBestEffort(previousOwnership.client);
951
- liveThreadOwnership = void 0;
952
- throw error;
953
- }
954
- liveThreadOwnership = void 0;
955
- } else if (binding.threadId !== threadId) await releaseCodexAppServerBindingSubscription(binding);
956
- if (!await params.bindingStore.mutate(identity, {
957
- kind: "set",
958
- binding: {
1029
+ ...useStickyNetworkProfile ? {} : { sandboxPolicy: codexSandboxPolicyForTurn(sandbox, sessionRoot ?? workspaceDir) },
1030
+ ...modelSelection?.model ? { model: modelSelection.model } : {},
1031
+ personality: CODEX_NATIVE_PERSONALITY_NONE,
1032
+ ...serviceTier ? { serviceTier } : {}
1033
+ }, { timeoutMs: runtime.requestTimeoutMs })).turn.id;
1034
+ activeTurnCleanup = trackCodexConversationActiveTurn({
1035
+ identity,
1036
+ client,
959
1037
  threadId,
960
- clientId: client.getInstanceId(),
961
- cwd: response.thread.cwd ?? workspaceDir,
962
- authProfileId: binding.authProfileId,
963
- model: response.model ?? modelSelection?.model ?? binding.model,
964
- modelProvider: normalizeCodexAppServerBindingModelProvider({
965
- authProfileId: binding.authProfileId,
966
- modelProvider: response.modelProvider ?? modelSelection?.modelProvider ?? binding.modelProvider,
967
- ...agentLookup
968
- }),
969
- serviceTier: serviceTier ?? void 0,
970
- networkProxyProfileName: modelScopedRuntime.networkProxy?.profileName,
971
- networkProxyConfigFingerprint: modelScopedRuntime.networkProxy?.configFingerprint,
972
- conversationStartId: binding.conversationStartId,
973
- conversationSourceTransferComplete: binding.conversationSourceTransferComplete,
974
- historyCoveredThrough: binding.historyCoveredThrough
1038
+ turnId: activeTurnId
1039
+ });
1040
+ turnCollector.setTurnId(activeTurnId);
1041
+ await turnRoute.bindTurn(activeTurnId);
1042
+ const replyText = (await turnCollector.wait({ timeoutMs: params.timeoutMs ?? DEFAULT_BOUND_TURN_TIMEOUT_MS })).replyText.trim();
1043
+ turnSucceeded = true;
1044
+ return { reply: { text: replyText || "Codex completed without a text reply." } };
1045
+ } catch (error) {
1046
+ if (isCodexAppServerOverloadError(error) && error.method === "thread/resume") throw error;
1047
+ if (error instanceof CodexThreadDirectInputError) {
1048
+ if (params.incognito && ownsNativeSubscription) {
1049
+ if (!await unsubscribeCodexThreadBestEffort(client, {
1050
+ threadId,
1051
+ timeoutMs: 5e3
1052
+ })) await retireUnsafeCodexTurnClientBestEffort(client, "parent-owned thread unsubscribe");
1053
+ }
1054
+ throw error;
975
1055
  }
976
- })) throw new Error("Codex conversation binding changed while rotating its thread.");
977
- useStickyNetworkProfile = modelScopedRuntime.networkProxy !== void 0;
978
- } else if (binding.clientId !== client.getInstanceId() || isCodexAppServerClientRuntimeLive(client) && !params.incognito && !liveThreadOwnership) {
979
- const response = await withLeasedCodexAppServerClientStartSelectionRetry({
980
- lease: clientLease,
981
- options: clientOptions,
982
- run: async (requestClient, requestOptions) => await resumeCodexAppServerThread({
983
- client: requestClient,
984
- abandonClient: async () => {
985
- retiredUnsafeClient = requestClient;
986
- await closeCodexStartupClientBestEffort(requestClient);
987
- },
988
- request: {
1056
+ if (error instanceof CodexConversationTurnTimeoutError && activeTurnId || turnRoute && isCodexAppServerIndeterminateRequestCancellationError(error)) {
1057
+ if (!await interruptCodexTurnAndWaitBestEffort(client, {
989
1058
  threadId,
990
- cwd: workspaceDir,
991
- ...sessionRoot ? { runtimeWorkspaceRoots: [sessionRoot] } : {},
992
- ...modelSelection?.model ? { model: modelSelection.model } : {},
993
- ...modelSelection?.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
994
- personality: CODEX_NATIVE_PERSONALITY_NONE,
995
- approvalPolicy,
996
- approvalsReviewer: modelScopedRuntime.approvalsReviewer,
997
- ...codexConversationSandboxOrPermissions(modelScopedRuntime, sandbox),
998
- ...serviceTier ? { serviceTier } : {}
999
- },
1000
- ...requestOptions
1001
- }),
1002
- onClientChange: (nextClient) => {
1003
- client = nextClient;
1059
+ turnId: activeTurnId ?? ""
1060
+ })) {
1061
+ await retireUnsafeCodexTurnClientBestEffort(client, "turn interrupt");
1062
+ isolatedSubscriptionClient = client;
1063
+ }
1004
1064
  }
1005
- });
1006
- threadId = response.thread.id;
1007
- ownsNativeSubscription = true;
1008
- if (!isSameCodexAppServerThreadOwner(binding, {
1009
- threadId,
1010
- clientId: client.getInstanceId()
1011
- })) await releaseCodexAppServerBindingSubscription(binding);
1012
- if (!await params.bindingStore.mutate(identity, {
1013
- kind: "patch",
1014
- threadId: binding.threadId,
1015
- patch: {
1016
- clientId: client.getInstanceId(),
1017
- cwd: response.thread.cwd ?? binding.cwd,
1018
- model: response.model ?? modelSelection?.model ?? binding.model,
1019
- modelProvider: normalizeCodexAppServerBindingModelProvider({
1020
- authProfileId: binding.authProfileId,
1021
- modelProvider: response.modelProvider ?? modelSelection?.modelProvider ?? binding.modelProvider,
1022
- ...agentLookup
1023
- })
1065
+ if (params.incognito) {
1066
+ if (await params.bindingStore.mutate(identity, {
1067
+ kind: "clear",
1068
+ threadId
1069
+ }) && isolatedSubscriptionClient !== client) {
1070
+ if (!await unsubscribeCodexThreadBestEffort(client, {
1071
+ threadId,
1072
+ timeoutMs: 5e3
1073
+ })) await retireUnsafeCodexTurnClientBestEffort(client, "thread unsubscribe");
1074
+ }
1024
1075
  }
1025
- })) throw new Error("Codex conversation binding changed while resuming on a new client.");
1026
- }
1027
- const turnCollector = createCodexConversationTurnCollector(threadId);
1028
- turnRoute = getCodexAppServerTurnRouter(client).reserveThread({
1029
- threadId,
1030
- onNotification: turnCollector.handleNotification
1031
- });
1032
- turnRoute.armTurn();
1033
- activeTurnId = (await client.request("turn/start", {
1034
- threadId,
1035
- input: buildCodexConversationTurnInput({
1036
- prompt: params.prompt,
1037
- event: params.event
1038
- }),
1039
- cwd: workspaceDir,
1040
- ...sessionRoot ? { runtimeWorkspaceRoots: [sessionRoot] } : {},
1041
- approvalPolicy,
1042
- approvalsReviewer: modelScopedRuntime.approvalsReviewer,
1043
- ...useStickyNetworkProfile ? {} : { sandboxPolicy: codexSandboxPolicyForTurn(sandbox, sessionRoot ?? workspaceDir) },
1044
- ...modelSelection?.model ? { model: modelSelection.model } : {},
1045
- personality: CODEX_NATIVE_PERSONALITY_NONE,
1046
- ...serviceTier ? { serviceTier } : {}
1047
- }, { timeoutMs: runtime.requestTimeoutMs })).turn.id;
1048
- activeTurnCleanup = trackCodexConversationActiveTurn({
1049
- identity,
1050
- client,
1051
- threadId,
1052
- turnId: activeTurnId
1053
- });
1054
- turnCollector.setTurnId(activeTurnId);
1055
- await turnRoute.bindTurn(activeTurnId);
1056
- const replyText = (await turnCollector.wait({ timeoutMs: params.timeoutMs ?? DEFAULT_BOUND_TURN_TIMEOUT_MS })).replyText.trim();
1057
- turnSucceeded = true;
1058
- return { reply: { text: replyText || "Codex completed without a text reply." } };
1059
- } catch (error) {
1060
- if (error instanceof CodexConversationTurnTimeoutError && activeTurnId || turnRoute && isCodexAppServerIndeterminateRequestCancellationError(error)) {
1061
- if (!await interruptCodexTurnAndWaitBestEffort(client, {
1062
- threadId,
1063
- turnId: activeTurnId ?? ""
1064
- })) {
1065
- retiredUnsafeClient = client;
1066
- await retireUnsafeCodexTurnClientBestEffort(client, "turn interrupt");
1067
- }
1068
- }
1069
- if (params.incognito) {
1070
- if (await params.bindingStore.mutate(identity, {
1071
- kind: "clear",
1072
- threadId
1073
- }) && retiredUnsafeClient !== client) {
1074
- if (!await unsubscribeCodexThreadBestEffort(client, {
1075
- threadId,
1076
- timeoutMs: 5e3
1077
- })) await retireUnsafeCodexTurnClientBestEffort(client, "thread unsubscribe");
1078
- }
1079
- }
1080
- throw error;
1081
- } finally {
1082
- activeTurnCleanup();
1083
- turnRoute?.release();
1084
- try {
1085
- if (ownsNativeSubscription && retiredUnsafeClient !== client && !params.incognito && isCodexAppServerClientRuntimeLive(client)) {
1086
- const currentLiveThreadOwnership = liveThreadOwnership?.client === client && liveThreadOwnership.threadId === threadId ? liveThreadOwnership.ownership : void 0;
1087
- let retained = false;
1088
- if (turnSucceeded) retained = await params.bindingStore.withLease(identity, async () => {
1089
- const current = await params.bindingStore.read(identity);
1090
- if (current?.threadId !== threadId || current.clientId !== client.getInstanceId()) return false;
1091
- return await retainCodexAppServerBindingSubscription(client, threadId, currentLiveThreadOwnership);
1092
- });
1093
- if (!retained) {
1094
- if (!(currentLiveThreadOwnership ? await currentLiveThreadOwnership.release(threadId).then(() => true) : await unsubscribeCodexThreadBestEffort(client, {
1076
+ throw error;
1077
+ } finally {
1078
+ activeTurnCleanup();
1079
+ turnRoute?.release();
1080
+ try {
1081
+ if (ownsNativeSubscription && isolatedSubscriptionClient !== client && !params.incognito && isCodexAppServerClientRuntimeLive(client)) {
1082
+ const currentLiveThreadOwnership = liveThreadOwnership?.client === client && liveThreadOwnership.threadId === threadId ? liveThreadOwnership.ownership : void 0;
1083
+ let retained = false;
1084
+ if (turnSucceeded) retained = await params.bindingStore.withLease(identity, async () => {
1085
+ const latest = await params.bindingStore.read(identity);
1086
+ if (latest?.threadId !== threadId || latest.clientId !== client.getInstanceId()) return false;
1087
+ return await retainCodexAppServerBindingSubscription(client, threadId, currentLiveThreadOwnership);
1088
+ });
1089
+ if (!retained) {
1090
+ if (!(currentLiveThreadOwnership ? await currentLiveThreadOwnership.release(threadId).then(() => true) : await unsubscribeCodexThreadBestEffort(client, {
1091
+ threadId,
1092
+ timeoutMs: 5e3
1093
+ }))) await closeCodexStartupClientBestEffort(client);
1094
+ }
1095
+ }
1096
+ } catch (error) {
1097
+ embeddedAgentLog.warn("codex conversation subscription cleanup failed", {
1095
1098
  threadId,
1096
- timeoutMs: 5e3
1097
- }))) await closeCodexStartupClientBestEffort(client);
1099
+ reason: formatErrorMessage(error)
1100
+ });
1101
+ await closeCodexStartupClientBestEffort(client);
1102
+ } finally {
1103
+ releaseCodexAppServerClientLease(clientLease);
1098
1104
  }
1099
1105
  }
1100
- } catch (error) {
1101
- embeddedAgentLog.warn("codex conversation subscription cleanup failed", {
1102
- threadId,
1103
- reason: formatErrorMessage(error)
1104
- });
1105
- await closeCodexStartupClientBestEffort(client);
1106
- } finally {
1107
- releaseCodexAppServerClientLease(clientLease);
1108
1106
  }
1109
- }
1107
+ });
1110
1108
  }
1111
1109
  function assertNativeConversationApprovalPolicySupported(runtime) {
1112
1110
  if (runtime.approvalPolicy !== "never" && runtime.approvalsReviewer === "user") throw new Error(NATIVE_CONVERSATION_INTERACTIVE_APPROVALS_UNAVAILABLE);
@@ -1123,8 +1121,10 @@ async function runBoundTurnWithMissingThreadRecovery(params) {
1123
1121
  }
1124
1122
  async function prepareConversationBinding(params, options = {}) {
1125
1123
  const identity = conversationBindingIdentity(params.data);
1126
- await params.bindingStore.withLease(identity, async () => {
1124
+ const snapshot = await params.bindingStore.read(identity);
1125
+ const run = () => params.bindingStore.withLease(identity, async () => {
1127
1126
  const current = await params.bindingStore.read(identity);
1127
+ if (current?.threadId !== snapshot?.threadId || current?.clientId !== snapshot?.clientId) throw new Error("Codex conversation binding changed before preparation.");
1128
1128
  const requested = params.data.start && current?.conversationStartId !== params.data.start.id ? params.data.start : void 0;
1129
1129
  if (current && !requested && !options.forceNew) return;
1130
1130
  const sourceIdentity = params.data.source ? sessionBindingIdentity({
@@ -1158,11 +1158,7 @@ async function prepareConversationBinding(params, options = {}) {
1158
1158
  agentId: params.data.source?.agentId ?? params.data.agentId
1159
1159
  };
1160
1160
  const threadId = requested?.threadId;
1161
- if (threadId && !options.forceNew) await attachExistingThread({
1162
- ...bindingParams,
1163
- threadId
1164
- });
1165
- else await createThread(bindingParams);
1161
+ await bindThread(bindingParams, options.forceNew ? void 0 : threadId);
1166
1162
  const stored = await params.bindingStore.read(identity);
1167
1163
  if (!stored) throw new Error("Codex conversation binding disappeared while initializing its thread.");
1168
1164
  if (sourceIdentity && params.data.source && !current?.conversationSourceTransferComplete) await params.bindingStore.withLease(sourceIdentity, async () => {
@@ -1193,6 +1189,14 @@ async function prepareConversationBinding(params, options = {}) {
1193
1189
  }
1194
1190
  })) throw new Error("Codex conversation binding changed while initializing its thread.");
1195
1191
  });
1192
+ const threadId = params.data.start?.threadId ?? snapshot?.threadId;
1193
+ if (threadId) await withExclusiveCodexAppServerThread({
1194
+ bindingStore: params.bindingStore,
1195
+ identity,
1196
+ threadId,
1197
+ run
1198
+ });
1199
+ else await run();
1196
1200
  }
1197
1201
  async function projectConversationSourceHistory(source, target, config) {
1198
1202
  const storePath = resolveStorePath(config?.session?.store, { agentId: source.agentId });
@@ -1202,12 +1206,13 @@ async function projectConversationSourceHistory(source, target, config) {
1202
1206
  storePath
1203
1207
  });
1204
1208
  if (!sessionKey) return;
1205
- const history = projectBoundedCodexVisibleSessionHistory(await readVisibleSessionTranscriptMessageEntries({
1209
+ const entries = await readVisibleSessionTranscriptMessageEntries({
1206
1210
  agentId: source.agentId,
1207
1211
  sessionId: source.sessionId,
1208
1212
  sessionKey,
1209
1213
  storePath
1210
- }));
1214
+ });
1215
+ const history = projectBoundedCodexVisibleSessionHistory(entries);
1211
1216
  if (history.length === 0) return;
1212
1217
  const clientLease = retainSharedCodexAppServerClientByInstanceId(target.clientId);
1213
1218
  if (!clientLease) throw new Error("Codex conversation source history lost its bound client owner.");
@@ -1221,6 +1226,7 @@ async function projectConversationSourceHistory(source, target, config) {
1221
1226
  }
1222
1227
  }
1223
1228
  function isCodexThreadNotFoundError(error) {
1229
+ if (isCodexAppServerOverloadError(error) || isCodexAppServerUnsafeSubscriptionError(error)) return false;
1224
1230
  const message = formatErrorMessage(error);
1225
1231
  return /\bthread not found:/iu.test(message) || /\bbound Codex conversation has no thread binding\b/u.test(message);
1226
1232
  }
@@ -1391,7 +1397,8 @@ async function buildCodexOAuthCredential(source) {
1391
1397
  } : null;
1392
1398
  }
1393
1399
  async function buildCodexApiKeyCredential(source) {
1394
- const key = normalizeOptionalString((await readJsonObject(source.authPath)).OPENAI_API_KEY);
1400
+ const raw = await readJsonObject(source.authPath);
1401
+ const key = normalizeOptionalString(raw.OPENAI_API_KEY);
1395
1402
  if (!key) return null;
1396
1403
  return {
1397
1404
  kind: "api_key",
@@ -2699,7 +2706,8 @@ async function requestTargetCodexAppServerJson(params) {
2699
2706
  });
2700
2707
  if (hasOpenAiCuratedMarketplace(lastResponse)) return lastResponse;
2701
2708
  if (Date.now() >= discoveryDeadline) return lastResponse;
2702
- await sleep(Math.min(TARGET_CODEX_MARKETPLACE_DISCOVERY_POLL_MS, discoveryDeadline - Date.now()));
2709
+ const waitMs = Math.min(TARGET_CODEX_MARKETPLACE_DISCOVERY_POLL_MS, discoveryDeadline - Date.now());
2710
+ await sleep(waitMs);
2703
2711
  } while (Date.now() < discoveryDeadline);
2704
2712
  return lastResponse;
2705
2713
  }
@@ -3254,7 +3262,8 @@ function createCodexNodeExecServerCommand() {
3254
3262
  } catch {
3255
3263
  throw new Error("Codex node exec-server requires a valid workspace request.");
3256
3264
  }
3257
- const placement = parseCodexNodePlacementWorkspace(request);
3265
+ if (!isRecord(request) || Object.keys(request).length !== 2 || request.authorization !== "human-approved" && request.authorization !== "session-full") throw new Error("Codex node exec-server requires an authorized managed placement workspace launch.");
3266
+ const placement = parseCodexNodePlacementWorkspace(request.placement);
3258
3267
  if (!context?.acquireManagedWorkspace || context.sessionKey !== placement.sessionKey || io.signal.aborted) throw new Error("Codex node exec-server requires active managed placement authority.");
3259
3268
  const workspace = context.acquireManagedWorkspace({
3260
3269
  workspaceDir: placement.cwd,
@@ -3266,11 +3275,14 @@ function createCodexNodeExecServerCommand() {
3266
3275
  const frames = io.frames;
3267
3276
  let unsubscribe;
3268
3277
  try {
3269
- const { runCodexNodeExecServer } = await import("./node-exec-server.runtime-BpWqJO1V.js");
3278
+ if (!context.prepareExecAuthorization) throw new Error("Codex node execution requires node-local exec policy support; update the node.");
3279
+ const assertExecAuthorized = context.prepareExecAuthorization(request.authorization);
3280
+ const { runCodexNodeExecServer } = await import("./node-exec-server.runtime-CstNHCmC.js");
3270
3281
  return await runCodexNodeExecServer({
3271
3282
  workspaceDir: workspace.workspaceDir,
3272
3283
  io,
3273
3284
  activeProcesses,
3285
+ assertExecAuthorized,
3274
3286
  onFrameReceiver: (receiver) => {
3275
3287
  unsubscribe = frames.onMessage(receiver);
3276
3288
  }
@@ -3285,17 +3297,21 @@ function createCodexNodeExecServerCommand() {
3285
3297
  }
3286
3298
  };
3287
3299
  }
3288
- /** Keeps node exec-server launch behind explicit arming and one-time approval. */
3300
+ /** Keeps node launch behind command opt-in and a live Full owner or human decision. */
3289
3301
  function createCodexNodeExecServerInvokePolicy() {
3290
3302
  return {
3291
3303
  commands: [CODEX_NODE_EXEC_SERVER_COMMAND],
3292
3304
  dangerous: true,
3305
+ standingApproval: {
3306
+ kind: "placement",
3307
+ scope: CODEX_NODE_EXEC_SERVER_CAPABILITY
3308
+ },
3293
3309
  classifyRisk: () => ({
3294
3310
  level: "high",
3295
3311
  family: CODEX_NODE_EXEC_SERVER_CAPABILITY
3296
3312
  }),
3297
3313
  handle: async (context) => {
3298
- if (!context.approvals || context.risk?.level !== "high") return {
3314
+ if (context.risk?.level !== "high") return {
3299
3315
  ok: false,
3300
3316
  code: "CODEX_NODE_EXEC_APPROVAL_REQUIRED",
3301
3317
  message: "Codex node execution requires an available approval reviewer."
@@ -3310,18 +3326,52 @@ function createCodexNodeExecServerInvokePolicy() {
3310
3326
  message: "Codex node execution requires an exact managed placement workspace."
3311
3327
  };
3312
3328
  }
3313
- const nodeName = context.node?.displayName ?? context.nodeId;
3314
- if ((await context.approvals.request({
3315
- title: "Run Codex execution on node",
3316
- description: `${nodeName}: ${placement.cwd}; allows arbitrary processes and filesystem access across the node account, not only this workspace.`,
3317
- severity: "critical",
3318
- allowedDecisions: ["allow-once"]
3319
- })).decision !== "allow-once") return {
3329
+ const workspace = {
3330
+ workspaceDir: placement.cwd,
3331
+ environmentId: placement.environmentId,
3332
+ sessionId: placement.sessionId,
3333
+ ownerEpoch: placement.ownerEpoch,
3334
+ sessionKey: placement.sessionKey
3335
+ };
3336
+ const fullLaunch = await context.invokeNodeWithSessionFull?.({
3337
+ workspace,
3338
+ createParams: () => ({
3339
+ placement,
3340
+ authorization: "session-full"
3341
+ })
3342
+ });
3343
+ if (fullLaunch) return fullLaunch;
3344
+ if (!context.approvals) return {
3320
3345
  ok: false,
3321
- code: "CODEX_NODE_EXEC_APPROVAL_DENIED",
3322
- message: "Codex node execution requires one-time approval."
3346
+ code: "CODEX_NODE_EXEC_APPROVAL_REQUIRED",
3347
+ message: "Codex node execution requires an available approval reviewer."
3323
3348
  };
3324
- return await context.invokeNode({ params: placement });
3349
+ const nodeName = context.node?.displayName ?? context.nodeId;
3350
+ const approval = await context.approvals.request({
3351
+ title: "Run Codex on this node placement",
3352
+ description: `Allows arbitrary processes and filesystem access across the node account, not only this workspace. Allow always applies only while this exact placement remains active. ${nodeName}: ${placement.cwd}`,
3353
+ severity: "critical",
3354
+ allowedDecisions: ["allow-once", "allow-always"]
3355
+ });
3356
+ if (approval.decision !== "allow-once" && approval.decision !== "allow-always") {
3357
+ if (approval.decision === "deny") return {
3358
+ ok: false,
3359
+ code: "CODEX_NODE_EXEC_APPROVAL_DENIED",
3360
+ message: "Codex node execution was denied. Retry the action and choose Allow once or Allow always to continue."
3361
+ };
3362
+ return {
3363
+ ok: false,
3364
+ code: "CODEX_NODE_EXEC_APPROVAL_EXPIRED",
3365
+ message: "Codex node execution approval expired before a decision. Retry the action and approve the new request."
3366
+ };
3367
+ }
3368
+ return await context.invokeNode({
3369
+ workspace,
3370
+ params: {
3371
+ placement,
3372
+ authorization: "human-approved"
3373
+ }
3374
+ });
3325
3375
  }
3326
3376
  };
3327
3377
  }
@@ -4107,7 +4157,7 @@ function createCodexSupervisionTools(options) {
4107
4157
  }
4108
4158
  //#endregion
4109
4159
  //#region extensions/codex/src/web-search-provider.ts
4110
- const loadCodexWebSearchRuntime = createLazyRuntimeModule(() => import("./web-search-provider.runtime-ClaNPV7N.js"));
4160
+ const loadCodexWebSearchRuntime = createLazyRuntimeModule(() => import("./web-search-provider.runtime-D2FWSK4w.js"));
4111
4161
  const CodexWebSearchSchema = {
4112
4162
  type: "object",
4113
4163
  properties: { query: {
@@ -4143,13 +4193,13 @@ const ENDED_SESSION_REASONS = /* @__PURE__ */ new Set([
4143
4193
  "new",
4144
4194
  "reset",
4145
4195
  "idle",
4146
- "daily",
4147
- "deleted"
4196
+ "daily"
4148
4197
  ]);
4149
4198
  var codex_default = definePluginEntry({
4150
4199
  id: "codex",
4151
4200
  name: "Codex",
4152
4201
  description: "Codex app-server harness and native session supervision.",
4202
+ reload: { noopPrefixes: ["plugins.entries.codex.config.codexPlugins"] },
4153
4203
  register(api) {
4154
4204
  setManagedCodexPluginRoot(api.rootDir);
4155
4205
  const resolveCurrentConfig = () => api.runtime.config?.current ? api.runtime.config.current() : void 0;
@@ -4169,6 +4219,7 @@ var codex_default = definePluginEntry({
4169
4219
  const resolveCurrentPluginConfig = () => resolvePluginConfig(resolveCurrentConfig);
4170
4220
  const appServerConfig = readCodexPluginConfig(resolveCurrentPluginConfig()).appServer;
4171
4221
  api.registerService(createCodexDesktopGenerationService({ onGenerationChange: retireSharedCodexAppServerClientsBeforeDesktopGeneration }));
4222
+ api.registerService(createCodexAppServerProcessReaperService());
4172
4223
  if (appServerConfig?.transport === "websocket") api.registerService(createCodexAppServerConnectionHealthService({
4173
4224
  getPluginConfig: resolveCurrentPluginConfig,
4174
4225
  getRuntimeConfig: resolveCurrentConfig
@@ -4181,9 +4232,10 @@ var codex_default = definePluginEntry({
4181
4232
  overflowPolicy: "reject-new"
4182
4233
  });
4183
4234
  const lazyBindingStateStore = {
4184
- delete: (key) => openBindingStateStore().delete(key),
4235
+ deleteIf: (key, predicate) => openBindingStateStore().deleteIf(key, predicate),
4185
4236
  entries: () => openBindingStateStore().entries(),
4186
4237
  lookup: (key) => openBindingStateStore().lookup(key),
4238
+ registerIfAbsent: (key, value, options) => openBindingStateStore().registerIfAbsent(key, value, options),
4187
4239
  get update() {
4188
4240
  const store = openBindingStateStore();
4189
4241
  return store.update?.bind(store);
@@ -4196,10 +4248,12 @@ var codex_default = definePluginEntry({
4196
4248
  });
4197
4249
  const bindingStore = createLazyCodexAppServerBindingStore(lazyBindingStateStore, {
4198
4250
  entries: () => openManagedThreadStateStore().entries(),
4251
+ lookup: (key) => openManagedThreadStateStore().lookup(key),
4199
4252
  registerIfAbsent: (key, value) => openManagedThreadStateStore().registerIfAbsent(key, value)
4200
4253
  });
4201
4254
  registerCodexCliMetadata(api);
4202
4255
  const sessionCatalogControlFactory = createCodexSessionCatalogControl({
4256
+ managedThreads: bindingStore.managedThreads,
4203
4257
  config: api.config,
4204
4258
  getPluginConfig: resolveCurrentPluginConfig,
4205
4259
  getRuntimeConfig: resolveCurrentConfig
@@ -4339,7 +4393,7 @@ var codex_default = definePluginEntry({
4339
4393
  if (!previousSessionId || !sessionId || previousSessionId === sessionId) return;
4340
4394
  const config = resolveCurrentConfig();
4341
4395
  const sessionKey = ctx.sessionKey?.trim();
4342
- const { sessionBindingIdentity } = await import("./session-binding-Bcynfd-_.js").then((n) => n.f);
4396
+ const { sessionBindingIdentity } = await import("./session-binding-BHuTCRZQ.js").then((n) => n.f);
4343
4397
  const identity = sessionBindingIdentity({
4344
4398
  sessionId,
4345
4399
  ...sessionKey ? { sessionKey } : {},
@@ -4357,7 +4411,7 @@ var codex_default = definePluginEntry({
4357
4411
  if (endedSessionKey && nextSessionKey && nextSessionKey !== endedSessionKey) return;
4358
4412
  if (event.nextSessionId?.trim() === event.sessionId.trim()) return;
4359
4413
  const config = resolveCurrentConfig();
4360
- const [{ sessionBindingIdentity }, { retireCodexAppServerSessionGeneration }] = await Promise.all([import("./session-binding-Bcynfd-_.js").then((n) => n.f), import("./session-retirement-CcNC3YSY.js")]);
4414
+ const [{ sessionBindingIdentity }, { retireCodexAppServerSessionGeneration }] = await Promise.all([import("./session-binding-BHuTCRZQ.js").then((n) => n.f), import("./session-retirement-BujDjXVF.js")]);
4361
4415
  await retireCodexAppServerSessionGeneration({
4362
4416
  bindingStore,
4363
4417
  identity: sessionBindingIdentity({