@openclaw/codex 2026.9.1-beta.1 → 2026.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/api.js +8 -9
  2. package/dist/{attempt-client-cleanup-bfsmcQ6m.js → attempt-client-cleanup-Bp1ZOnk2.js} +105 -75
  3. package/dist/attempt-context-CSazkWns.js +634 -0
  4. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  5. package/dist/{bounded-turn-B7iBUcuP.js → bounded-turn-DxGJRYbY.js} +53 -57
  6. package/dist/{capabilities-BRMh0aIq.js → capabilities-B1mgvpP8.js} +2 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-DEsIufkt.js → command-formatters-DSbGMXA0.js} +5 -4
  9. package/dist/{command-handlers-D_-1_qRp.js → command-handlers-DOJvGv6-.js} +243 -148
  10. package/dist/{compact-BglNFZrQ.js → compact-Y_xBqrHN.js} +109 -65
  11. package/dist/{config-giW8X0QA.js → config-CyapYSji.js} +328 -374
  12. package/dist/config-utils-C57JiJrU.js +69 -0
  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 +144 -14
  16. package/dist/{dynamic-tools-Dnlfa--w.js → dynamic-tools-BwpvTxaX.js} +1119 -1135
  17. package/dist/{effective-mcp-catalog-CTeGrnfO.js → effective-mcp-catalog-CY9Jn4bA.js} +4 -3
  18. package/dist/{event-projector-assistant-message-C5-Ds8mA.js → event-projector-assistant-message-B9kp6iGC.js} +15 -6
  19. package/dist/event-projector-usage-ZqEVbAEY.js +212 -0
  20. package/dist/{harness-Csz0F-DK.js → harness-Czh2Sdoc.js} +65 -52
  21. package/dist/harness.js +1 -1
  22. package/dist/{image-payload-sanitizer-CnfoUPCE.js → image-payload-sanitizer-tupAr-2o.js} +29 -15
  23. package/dist/index.js +495 -457
  24. package/dist/{isolated-completion-Cv6FqygM.js → isolated-completion-CRh-51AK.js} +9 -21
  25. package/dist/{managed-binary-DFYK4DWq.js → managed-binary-DcpnJF9x.js} +117 -80
  26. package/dist/media-understanding-provider.js +1 -1
  27. package/dist/model-catalog-DT5vo6qo.js +101 -0
  28. package/dist/{models-TS3HBU-q.js → models-CcrgSMQR.js} +26 -29
  29. package/dist/{native-subagent-monitor-4Mfq2HDs.js → native-subagent-monitor-B3bvQGcg.js} +63 -122
  30. package/dist/{node-exec-server.runtime-BpWqJO1V.js → node-exec-server.runtime-nfbUOtUB.js} +10 -7
  31. package/dist/{plugin-approval-roundtrip-bUm777tR.js → plugin-approval-roundtrip-aQVdyL5g.js} +30 -3
  32. package/dist/{plugin-marketplace-discovery-B9DvaAG-.js → plugin-marketplace-discovery-B7gHXJ3d.js} +44 -30
  33. package/dist/{plugin-metadata-cache-CTCT3w5L.js → plugin-metadata-cache-BwCLTY_U.js} +6 -6
  34. package/dist/provider-capabilities-BpG9Ltc5.js +101 -0
  35. package/dist/{rate-limits-C5_D8Gmb.js → rate-limits-Fx_l4eNa.js} +3 -2
  36. package/dist/{sandbox-guard-kuWTn9C5.js → request-C8H22rPi.js} +195 -2
  37. package/dist/{run-attempt-DYc0QCBF.js → run-attempt-BzthFQSM.js} +1616 -2634
  38. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-Bh_gf6-t.js} +22 -15
  39. package/dist/{session-binding-Bcynfd-_.js → session-binding-4Ki04sW-.js} +282 -399
  40. package/dist/session-binding-record-CyaXZvTe.js +270 -0
  41. package/dist/{session-catalog-DaLKVtpH.js → session-catalog-B47uwekb.js} +812 -504
  42. package/dist/{session-cli-DMc4oS2w.js → session-cli-BjLhpmS1.js} +1 -1
  43. package/dist/session-history-DWmMqiqW.js +93 -0
  44. package/dist/session-history-read-LHldwvyh.js +36 -0
  45. package/dist/session-history-worker-runtime.js +48 -0
  46. package/dist/session-history.worker-zZVcx0ME.js +267 -0
  47. package/dist/session-history.worker.js +2 -0
  48. package/dist/session-initialization-BCFVnLL4.js +75 -0
  49. package/dist/{session-permission-policy-BrNH1bjy.js → session-permission-policy-BaueHmSj.js} +2 -2
  50. package/dist/session-retirement-DNdmYdFc.js +97 -0
  51. package/dist/{session-upstream-marker-BIkusb95.js → session-upstream-marker-DReTpGz6.js} +20 -4
  52. package/dist/settled-turn-context-BOM3Y1pl.js +45 -0
  53. package/dist/settled-turn-finalizer-BI-b1sry.js +115 -0
  54. package/dist/{shared-client-DKz23hHY.js → shared-client-CKhX2kFt.js} +3659 -847
  55. package/dist/{side-question-DjA_jsNe.js → side-question-B6DWAw1A.js} +168 -81
  56. package/dist/{thread-lifecycle-L7oEkTlq.js → thread-lifecycle-DPK30jad.js} +2412 -2145
  57. package/dist/{thread-ownership-DZeuKH1j.js → thread-ownership-CbucAmmp.js} +27 -16
  58. package/dist/thread-policy-CTalhWFy.js +105 -0
  59. package/dist/{plugin-app-cache-key-C1CAdfcP.js → thread-resume-BLgqkUc1.js} +61 -9
  60. package/dist/{transcript-mirror-DsCMWBca.js → transcript-mirror-_njEB84z.js} +114 -110
  61. package/dist/transcript-mirror-attestation-Byl34PC-.js +76 -0
  62. package/dist/transport-stdio-CzTngV0j.js +728 -0
  63. package/dist/{upstream-prompt-provenance-CFmsKwr9.js → upstream-prompt-provenance-LphB8slG.js} +8 -4
  64. package/dist/upstream-session-fork-1fwbNKzQ.js +886 -0
  65. package/dist/{usage-ROIaW3wo.js → usage-i9fv5-4m.js} +3 -3
  66. package/dist/{web-search-provider.runtime-ClaNPV7N.js → web-search-provider.runtime-DKQn67A7.js} +3 -3
  67. package/openclaw.plugin.json +1 -30
  68. package/package.json +6 -6
  69. package/dist/binding-connection-BMvPX73o.js +0 -45
  70. package/dist/command-rpc-CCeCkf_v.js +0 -90
  71. package/dist/event-projector-usage-Bk1I90Bg.js +0 -328
  72. package/dist/incognito-session-ET_ufDH-.js +0 -2
  73. package/dist/model-catalog-i9rNBthX.js +0 -48
  74. package/dist/protocol-validators-VeJxxJ0U.js +0 -2421
  75. package/dist/request-DXXGHko0.js +0 -157
  76. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  77. package/dist/session-retirement-CcNC3YSY.js +0 -36
  78. package/dist/settled-turn-finalizer-Dc3ApjU4.js +0 -328
  79. package/dist/thread-resume-BE9qNEW0.js +0 -30
  80. package/dist/transport-stdio-DhoGy33Z.js +0 -99
  81. package/dist/upstream-session-fork-DlaHxSLC.js +0 -326
package/dist/index.js CHANGED
@@ -1,54 +1,52 @@
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-Czh2Sdoc.js";
3
+ import { m as CODEX_PLUGINS_MARKETPLACE_NAME } from "./config-utils-C57JiJrU.js";
4
+ import { l as sessionBindingIdentity, s as readCurrentCodexAppServerBinding } from "./session-binding-record-CyaXZvTe.js";
5
+ import { A as resolveOpenClawExecPolicyForCodexAppServer, B as isJsonObject, L as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS, O as readCodexPluginConfig, c as assertCodexAppServerConnectionSecurity, i as resolveCodexAppServerRuntimeOptions, m as canUseCodexModelBackedApprovalsReviewerForModel, n as codexSandboxPolicyForTurn, s as resolveCodexSupervisionAppServerRuntimeOptions, t as codexAppServerStartOptionsKey, y as readCodexRequirementsToml } from "./config-CyapYSji.js";
6
+ import { B as isCodexAppServerIndeterminateRequestCancellationError, Bt as resolveCodexAppServerAuthAccountCacheKey, C as withLeasedCodexAppServerClientStartSelectionRetry, Dt as isCodexAppServerClientRuntimeLive, Et as hasCodexAppServerLiveThread, G as isUnsupportedCodexAppServerVersionError, H as isCodexAppServerOverloadError, Ht as resolveCodexAppServerAuthProfileIdForAgent, O as createCodexDesktopGenerationService, Ot as isCodexAppServerLiveThreadClaimed, Q as assertCodexThreadAcceptsDirectInput, Wt as resolveCodexAppServerFallbackApiKeyCacheKey, Y as CodexThreadDirectInputError, _ as retainSharedCodexAppServerClientByInstanceId, b as retireSharedCodexAppServerClientsBeforeDesktopGeneration, c as getLeasedSharedCodexAppServerClient, h as releaseLeasedSharedCodexAppServerClient, jt as releaseCodexAppServerLiveThread, m as releaseCodexAppServerClientLease, nt as assertCodexThreadStartResponse, o as clearSharedCodexAppServerClientIfCurrentAndWait, wt as consumeCodexAppServerLiveThread } from "./shared-client-CKhX2kFt.js";
7
+ import { a as setManagedCodexPluginRoot } from "./managed-binary-DcpnJF9x.js";
6
8
  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";
9
+ import { a as isCodexAppServerNativeAuthProfile, n as assertCodexBindingMayBeReplaced, o as normalizeCodexAppServerBindingModelProvider } from "./session-binding-4Ki04sW-.js";
10
+ import { o as createCodexAppServerProcessReaperService } from "./transport-stdio-CzTngV0j.js";
11
+ import { a as closeCodexStartupClientBestEffort, d as unsubscribeCodexThreadBestEffort, h as isCodexNotificationForTurn, l as retireUnsafeCodexTurnClientBestEffort, o as interruptCodexTurnAndWaitBestEffort, p as getCodexAppServerTurnRouter, s as isCodexAppServerUnsafeSubscriptionError } from "./attempt-client-cleanup-Bp1ZOnk2.js";
12
+ import { i as isAssistantCommentaryCompletionNotification } from "./event-projector-usage-ZqEVbAEY.js";
13
+ import { c as defaultCodexAppInventoryCache, i as buildCodexPluginAppCacheKey, t as resumeCodexAppServerThread } from "./thread-resume-BLgqkUc1.js";
14
+ import { E as resolveCodexAppServerRequestModelSelection, Et as mergeCodexThreadConfigs, J as createCodexManagedThreadStore, K as CODEX_MANAGED_THREAD_MAX_ENTRIES, Nt as pluginReadParams, Tt as buildDisabledAppsConfigPatch, jt as isOpenAiCuratedMarketplace, k as buildCodexProjectDocThreadConfig, kt as ensureCodexPluginActivation, q as CODEX_MANAGED_THREAD_NAMESPACE, w as CODEX_NATIVE_PERSONALITY_NONE } from "./thread-lifecycle-DPK30jad.js";
15
+ 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-CbucAmmp.js";
16
+ import { s as resolveCodexBindingAppServerConnection } from "./thread-policy-CTalhWFy.js";
17
+ import { c as projectBoundedCodexVisibleSessionHistory } from "./transcript-mirror-_njEB84z.js";
18
+ import { c as resolveCodexNativeSandboxBlock, i as requestCodexAppServerJson, o as withCodexAppServerJsonClient, s as resolveCodexNativeExecutionBlock } from "./request-C8H22rPi.js";
16
19
  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";
20
+ import { n as describeControlFailure, t as CODEX_CONTROL_METHODS } from "./capabilities-B1mgvpP8.js";
21
+ import { a as formatCodexDisplayText } from "./command-formatters-DSbGMXA0.js";
22
+ import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, t as CODEX_SESSION_PERMISSION_EXEC_MODES } from "./session-permission-policy-BaueHmSj.js";
23
+ import { f as trackCodexConversationActiveTurn, g as canMutateCodexHost, h as CODEX_NATIVE_EXECUTION_AUTH_ERROR, t as discoverCodexMarketplacePlugins } from "./plugin-marketplace-discovery-B7gHXJ3d.js";
24
+ import { a as readCodexConversationBindingDataRecord, i as readCodexConversationBindingData, o as resolveCodexDefaultWorkspaceDir, r as createCodexConversationBindingData } from "./conversation-binding-data-BiodpIzq.js";
25
+ 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-B47uwekb.js";
28
26
  import { t as createCodexWebSearchProviderBase } from "./web-search-provider.shared-BrZmlqyR.js";
29
27
  import { mutateConfigFile } from "openclaw/plugin-sdk/config-mutation";
30
28
  import { normalizePluginsConfig, resolveEffectiveEnableState, resolveLivePluginConfigObject, resolvePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
31
29
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
32
30
  import { asBoolean, asOptionalRecord, asSafeIntegerInRange, isRecord, normalizeOptionalString, normalizeSingleOrTrimmedStringList, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
31
+ import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
32
+ import { getSessionEntry, resolveStorePath, resolveTranscriptSessionKeyBySessionId } from "openclaw/plugin-sdk/session-store-runtime";
33
+ import { parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
33
34
  import fs from "node:fs/promises";
34
35
  import path from "node:path";
35
- import { parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
36
36
  import { embeddedAgentLog, formatErrorMessage, resolveActiveEmbeddedRunSessionId, resolveSandboxContext } from "openclaw/plugin-sdk/agent-harness-runtime";
37
+ import { canonicalPathFromExistingAncestor, isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
37
38
  import os from "node:os";
38
- import { loadAuthProfileStoreWithoutExternalProfiles, resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentDir, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
39
+ import { loadAuthProfileStoreWithoutExternalProfiles, resolveAgentConfig as resolveAgentConfig$1, resolveAgentWorkspaceDir, resolveDefaultAgentDir, resolveDefaultAgentId as resolveDefaultAgentId$1 } from "openclaw/plugin-sdk/agent-runtime";
39
40
  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
41
  import { extractErrorCode, pathExists } from "openclaw/plugin-sdk/security-runtime";
43
- import { getSessionEntry, resolveStorePath, resolveTranscriptSessionKeyBySessionId } from "openclaw/plugin-sdk/session-store-runtime";
42
+ import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
44
43
  import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
45
- import { fileURLToPath } from "node:url";
46
44
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
47
- import { canonicalPathFromExistingAncestor, isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
45
+ import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
48
46
  import { readVisibleSessionTranscriptMessageEntries } from "openclaw/plugin-sdk/session-transcript-runtime";
49
47
  import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-binding-runtime";
50
- import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
51
48
  import { ModelSelectionLockedError, isModelSelectionLocked } from "openclaw/plugin-sdk/model-session-runtime";
49
+ import { fileURLToPath } from "node:url";
52
50
  import { MIGRATION_REASON_TARGET_EXISTS, applyMigrationManualItem, createMigrationItem, createMigrationManualItem, hasMigrationConfigPatchConflict, markMigrationItemConflict, markMigrationItemError, markMigrationItemSkipped, mergeMigrationConfigValue, readMigrationConfigPath, resolveMigrationConfigRuntime, summarizeMigrationItems, writeMigrationConfigPath } from "openclaw/plugin-sdk/migration";
53
51
  import { archiveMigrationItem, copyMemoryMigrationFileItem, copyMigrationFileItem, withCachedMigrationConfigRuntime, writeMigrationReport } from "openclaw/plugin-sdk/migration-runtime";
54
52
  import { sleep } from "openclaw/plugin-sdk/runtime-env";
@@ -181,20 +179,21 @@ function waitForReconnect(delayMs, signal) {
181
179
  }
182
180
  //#endregion
183
181
  //#region extensions/codex/src/app-server/session-binding-store.ts
184
- /** Defers schema compilation and auth loading until the first binding operation. */
182
+ /** Keeps lifecycle/auth loading behind mutations while sharing the canonical read codec. */
185
183
  function createLazyCodexAppServerBindingStore(state, managedThreadState) {
186
184
  let resolved;
187
- const store = () => resolved ??= import("./session-binding-Bcynfd-_.js").then((n) => n.f).then(({ createCodexAppServerBindingStore }) => createCodexAppServerBindingStore(state));
185
+ const store = () => resolved ??= import("./session-binding-4Ki04sW-.js").then((n) => n.d).then(({ createCodexAppServerBindingStore }) => createCodexAppServerBindingStore(state));
188
186
  const managedThreads = managedThreadState ? createCodexManagedThreadStore(managedThreadState) : void 0;
189
187
  return {
190
188
  ...managedThreads ? { managedThreads } : {},
191
- read: async (identity) => (await store()).read(identity),
189
+ read: (identity) => readCurrentCodexAppServerBinding(state, identity),
192
190
  hasOtherThreadOwner: async (threadId, currentIdentity) => (await store()).hasOtherThreadOwner(threadId, currentIdentity),
193
- mutate: async (identity, mutation) => (await store()).mutate(identity, mutation),
191
+ mutate: async (identity, mutation, assertCurrent) => (await store()).mutate(identity, mutation, assertCurrent),
194
192
  prepareSessionGenerationReclaim: async (identity) => (await store()).prepareSessionGenerationReclaim(identity),
195
- adoptSessionGeneration: async (identity, previousSessionId) => (await store()).adoptSessionGeneration(identity, previousSessionId),
193
+ adoptSessionGeneration: async (identity, previousSessionId, assertCurrent) => (await store()).adoptSessionGeneration(identity, previousSessionId, assertCurrent),
196
194
  resetSessionGeneration: async (identity) => (await store()).resetSessionGeneration(identity),
197
195
  retireSessionGeneration: async (identity) => (await store()).retireSessionGeneration(identity),
196
+ withSessionDeletion: async (identity, assertCurrent, run) => (await store()).withSessionDeletion(identity, assertCurrent, run),
198
197
  withThreadArchiveFence: async (run) => (await store()).withThreadArchiveFence(run),
199
198
  withLease: async (identity, run) => (await store()).withLease(identity, run)
200
199
  };
@@ -214,7 +213,7 @@ async function handleCodexCommand(ctx, options) {
214
213
  }
215
214
  }
216
215
  async function loadDefaultCodexSubcommandHandler() {
217
- const { handleCodexSubcommand } = await import("./command-handlers-D_-1_qRp.js");
216
+ const { handleCodexSubcommand } = await import("./command-handlers-DOJvGv6-.js");
218
217
  return handleCodexSubcommand;
219
218
  }
220
219
  //#endregion
@@ -396,7 +395,7 @@ function isImageMedia(media) {
396
395
  return IMAGE_EXTENSIONS.has(path.extname(candidate.split(/[?#]/, 1)[0] ?? "").toLowerCase());
397
396
  }
398
397
  function normalizeFileUrl(value) {
399
- if (!value.startsWith("file://")) return value;
398
+ if (!/^file:\/\//iu.test(value)) return value;
400
399
  try {
401
400
  return fileURLToPath(value);
402
401
  } catch {
@@ -405,18 +404,18 @@ function normalizeFileUrl(value) {
405
404
  }
406
405
  function readLocalMediaPath(value) {
407
406
  if (!value) return;
408
- if (value.startsWith("file://")) return value;
407
+ if (/^file:\/\//iu.test(value)) return value;
409
408
  if (value.startsWith("//")) return;
410
409
  if (path.isAbsolute(value) || path.win32.isAbsolute(value)) return value;
411
410
  return /^[a-z][a-z0-9+.-]*:/i.test(value) ? void 0 : value;
412
411
  }
413
412
  //#endregion
414
413
  //#region extensions/codex/src/conversation-binding.ts
415
- const DEFAULT_BOUND_TURN_TIMEOUT_MS = 20 * 6e4;
414
+ const DEFAULT_BOUND_TURN_TIMEOUT_MS = 12e5;
416
415
  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
416
  async function resolveConversationAppServerRuntime(params) {
418
417
  const source = params.source;
419
- const agentId = source?.agentId ?? params.agentId ?? (params.config ? resolveSessionAgentIds$2({
418
+ const agentId = source?.agentId ?? params.agentId ?? (params.config ? resolveSessionAgentIdsStrict({
420
419
  sessionKey: params.sessionKey,
421
420
  config: params.config
422
421
  }).sessionAgentId : void 0);
@@ -505,18 +504,16 @@ async function startCodexConversationThread(params) {
505
504
  sessionKey: params.sessionKey,
506
505
  config: params.config
507
506
  });
508
- const existingBinding = await params.bindingStore.read(identity);
507
+ const existingBinding = params.bindingStore.read(identity);
509
508
  assertCodexBindingMayBeReplaced(existingBinding, "starting a conversation-bound Codex thread");
510
509
  const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
511
510
  authProfileId: params.authProfileId ?? existingBinding?.authProfileId,
512
511
  ...agentLookup
513
512
  });
514
- const incognito = isIncognitoSessionKey(params.sessionKey);
515
- if (params.threadId?.trim()) await attachExistingThread({
513
+ const bindingParams = {
516
514
  pluginConfig: params.pluginConfig,
517
515
  bindingStore: params.bindingStore,
518
516
  identity,
519
- threadId: params.threadId.trim(),
520
517
  workspaceDir,
521
518
  ...agentDir ? { agentDir } : {},
522
519
  model: params.model,
@@ -525,25 +522,20 @@ async function startCodexConversationThread(params) {
525
522
  serviceTier: params.serviceTier,
526
523
  config: params.config,
527
524
  sessionKey: params.sessionKey,
528
- incognito,
525
+ incognito: isIncognitoSessionKey(params.sessionKey),
529
526
  agentId: params.agentId
530
- });
531
- else await createThread({
532
- pluginConfig: params.pluginConfig,
527
+ };
528
+ const threadId = params.threadId?.trim();
529
+ const bind = () => params.bindingStore.withLease(identity, () => bindThread(bindingParams, threadId));
530
+ const ownedThreadId = threadId ?? existingBinding?.threadId;
531
+ if (ownedThreadId) await withExclusiveCodexAppServerThread({
533
532
  bindingStore: params.bindingStore,
534
533
  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
534
+ threadId: ownedThreadId,
535
+ run: bind
545
536
  });
546
- const storedBinding = await params.bindingStore.read(identity);
537
+ else await bind();
538
+ const storedBinding = params.bindingStore.read(identity);
547
539
  if (!storedBinding) throw new Error("Codex session binding disappeared while starting its conversation thread.");
548
540
  return createCodexConversationBindingData({
549
541
  source: {
@@ -612,7 +604,7 @@ async function handleCodexConversationInboundClaim(event, ctx, options) {
612
604
  const identity = conversationBindingIdentity(data);
613
605
  const sessionKey = event.sessionKey ?? ctx.sessionKey;
614
606
  const incognito = isIncognitoSessionKey(data.source?.sessionKey ?? (data.legacyBinding ? sessionKey : void 0));
615
- const queuedOwner = options.bindingStore.read(identity);
607
+ const expected = options.bindingStore.read(identity);
616
608
  return {
617
609
  handled: true,
618
610
  reply: (await withCodexConversationThreadActivity(data.bindingId, async () => {
@@ -622,8 +614,7 @@ async function handleCodexConversationInboundClaim(event, ctx, options) {
622
614
  conversationId: publicBinding.conversationId,
623
615
  ...publicBinding.parentConversationId ? { parentConversationId: publicBinding.parentConversationId } : {}
624
616
  });
625
- const expected = await queuedOwner;
626
- const current = await options.bindingStore.read(identity);
617
+ const current = options.bindingStore.read(identity);
627
618
  if (currentPublicBinding?.bindingId !== publicBinding.bindingId || expected && (!current || current.threadId !== expected.threadId || current.conversationStartId !== expected.conversationStartId) || !expected && current && data.start?.id && current.conversationStartId !== data.start.id) return { reply: { text: "This Codex conversation was detached or changed before its message could run." } };
628
619
  return await runBoundTurnWithMissingThreadRecovery({
629
620
  bindingStore: options.bindingStore,
@@ -650,7 +641,7 @@ async function handleCodexConversationBindingResolved(event, options) {
650
641
  const data = readCodexConversationBindingDataRecord(event.request.data ?? {});
651
642
  if (!data || data.kind !== "codex-app-server-session") return;
652
643
  const identity = conversationBindingIdentity(data);
653
- const binding = await options.bindingStore.read(identity);
644
+ const binding = options.bindingStore.read(identity);
654
645
  assertCodexBindingMayBeReplaced(binding, "clearing a denied conversation binding");
655
646
  if (binding && (!data.start?.id || binding.conversationStartId === data.start.id)) await withCodexConversationThreadActivity(identity.bindingId, () => retireCodexConversationThreadBinding({
656
647
  bindingStore: options.bindingStore,
@@ -707,21 +698,21 @@ async function resolveThreadBindingRuntime(params) {
707
698
  authProfileId: params.authProfileId,
708
699
  ...agentLookup
709
700
  };
710
- const client = await getLeasedSharedCodexAppServerClient(clientOptions);
711
701
  return {
712
702
  runtime: modelScopedRuntime,
713
703
  workspaceDir,
714
704
  agentLookup,
715
705
  model: modelSelection?.model,
716
706
  modelProvider: modelSelection?.modelProvider ?? modelProvider,
717
- client,
718
- clientLease: { client },
719
707
  clientOptions
720
708
  };
721
709
  }
722
- function buildThreadRequestRuntimeOptions(params, resolved) {
723
- const serviceTier = params.serviceTier ?? resolved.runtime.serviceTier;
710
+ function buildConversationThreadRequest(resolved, serviceTier) {
724
711
  return {
712
+ cwd: resolved.workspaceDir,
713
+ ...resolved.model ? { model: resolved.model } : {},
714
+ ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
715
+ personality: CODEX_NATIVE_PERSONALITY_NONE,
725
716
  approvalPolicy: resolved.runtime.approvalPolicy,
726
717
  approvalsReviewer: resolved.runtime.approvalsReviewer,
727
718
  ...resolved.runtime.sessionRoot ? { runtimeWorkspaceRoots: [resolved.runtime.sessionRoot] } : {},
@@ -731,52 +722,39 @@ function buildThreadRequestRuntimeOptions(params, resolved) {
731
722
  }
732
723
  function codexConversationSandboxOrPermissions(runtime, sandbox) {
733
724
  const networkProxy = runtime.networkProxy;
734
- const disabledApps = mergeCodexThreadConfigs(buildDisabledAppsConfigPatch(), { "features.apps": false });
735
- const config = buildCodexProjectDocThreadConfig(mergeCodexThreadConfigs(networkProxy?.configPatch, disabledApps));
725
+ const config = buildCodexProjectDocThreadConfig(mergeCodexThreadConfigs(networkProxy?.configPatch, buildDisabledAppsConfigPatch()));
736
726
  return networkProxy ? { config } : {
737
727
  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 = 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 = 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) {
@@ -847,266 +823,288 @@ async function runBoundTurn(params) {
847
823
  config: params.config
848
824
  });
849
825
  const identity = conversationBindingIdentity(params.data);
850
- const binding = await params.bindingStore.read(identity);
826
+ const binding = 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 = 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 = 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 () => {
1127
- const current = await params.bindingStore.read(identity);
1124
+ const snapshot = params.bindingStore.read(identity);
1125
+ const run = () => params.bindingStore.withLease(identity, async () => {
1126
+ const current = 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({
@@ -1133,7 +1133,7 @@ async function prepareConversationBinding(params, options = {}) {
1133
1133
  sessionKey: params.data.source.sessionKey,
1134
1134
  config: params.config
1135
1135
  }) : void 0;
1136
- const sourceBinding = sourceIdentity ? await params.bindingStore.read(sourceIdentity) : void 0;
1136
+ const sourceBinding = sourceIdentity ? params.bindingStore.read(sourceIdentity) : void 0;
1137
1137
  assertCodexBindingMayBeReplaced(current, "initializing a conversation-bound Codex thread");
1138
1138
  assertCodexBindingMayBeReplaced(sourceBinding, "transferring a session into a conversation-bound Codex thread");
1139
1139
  const inherited = current ?? sourceBinding;
@@ -1158,15 +1158,11 @@ 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);
1166
- const stored = await params.bindingStore.read(identity);
1161
+ await bindThread(bindingParams, options.forceNew ? void 0 : threadId);
1162
+ const stored = 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 () => {
1169
- const source = await params.bindingStore.read(sourceIdentity);
1165
+ const source = params.bindingStore.read(sourceIdentity);
1170
1166
  if (source && source.threadId === params.data.source?.threadId) {
1171
1167
  const sourceSessionKey = sourceIdentity.sessionKey ?? resolveTranscriptSessionKeyBySessionId({
1172
1168
  agentId: sourceIdentity.agentId,
@@ -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",
@@ -2123,9 +2130,9 @@ function sanitizePluginName(value) {
2123
2130
  //#region extensions/codex/src/migration/targets.ts
2124
2131
  function resolveCodexMigrationTargets(ctx) {
2125
2132
  const cfg = ctx.config;
2126
- const agentId = ctx.targetAgentId ?? resolveDefaultAgentId(cfg);
2133
+ const agentId = ctx.targetAgentId ?? resolveDefaultAgentId$1(cfg);
2127
2134
  const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
2128
- const configuredAgentDir = resolveAgentConfig(cfg, agentId)?.agentDir?.trim();
2135
+ const configuredAgentDir = resolveAgentConfig$1(cfg, agentId)?.agentDir?.trim();
2129
2136
  return {
2130
2137
  workspaceDir,
2131
2138
  agentDir: ctx.runtime?.agent?.resolveAgentDir(cfg, agentId) ?? (configuredAgentDir ? resolveHomePath(configuredAgentDir) : void 0) ?? path.join(ctx.stateDir, "agents", agentId, "agent")
@@ -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
  }
@@ -2866,7 +2874,7 @@ function createCodexPluginsTool(options) {
2866
2874
  const marketplace = typeof params.marketplace === "string" ? params.marketplace.trim() : void 0;
2867
2875
  const limit = typeof params.limit === "number" && Number.isInteger(params.limit) ? Math.max(1, Math.min(params.limit, 20)) : 12;
2868
2876
  const pluginConfig = options.getPluginConfig();
2869
- const binding = options.context.sessionId ? await options.bindingStore.read(sessionBindingIdentity({
2877
+ const binding = options.context.sessionId ? options.bindingStore.read(sessionBindingIdentity({
2870
2878
  sessionId: options.context.sessionId,
2871
2879
  sessionKey: options.context.sessionKey,
2872
2880
  agentId: options.context.agentId,
@@ -3041,11 +3049,11 @@ function createCodexThreadsTool(options) {
3041
3049
  agentId: options.context.agentId,
3042
3050
  config: runtimeConfig()
3043
3051
  });
3044
- const currentBinding = async (session) => session ? await options.bindingStore.read(currentIdentity(session.sessionId)) : void 0;
3045
- const requestOptions = async (pluginConfig) => {
3052
+ const currentBinding = (session) => session ? options.bindingStore.read(currentIdentity(session.sessionId)) : void 0;
3053
+ const requestOptions = (pluginConfig) => {
3046
3054
  const plugin = readCodexPluginConfig(pluginConfig);
3047
3055
  const session = currentSession();
3048
- const binding = await currentBinding(session);
3056
+ const binding = currentBinding(session);
3049
3057
  if (binding?.connectionScope === "supervision") {
3050
3058
  const connection = resolveCodexBindingAppServerConnection({
3051
3059
  binding,
@@ -3106,7 +3114,7 @@ function createCodexThreadsTool(options) {
3106
3114
  sourceKinds: [...CODEX_INTERACTIVE_THREAD_SOURCE_KINDS],
3107
3115
  ...cursor ? { cursor } : {},
3108
3116
  ...searchTerm ? { searchTerm } : {}
3109
- }, await requestOptions(pluginConfig));
3117
+ }, requestOptions(pluginConfig));
3110
3118
  return jsonResult(mayReadRawTranscripts ? response : redactNativeThreadResponse(response));
3111
3119
  }
3112
3120
  const threadId = readThreadId(params);
@@ -3116,7 +3124,7 @@ function createCodexThreadsTool(options) {
3116
3124
  const response = await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
3117
3125
  threadId,
3118
3126
  includeTurns
3119
- }, await requestOptions(pluginConfig));
3127
+ }, requestOptions(pluginConfig));
3120
3128
  return jsonResult(mayReadRawTranscripts ? response : redactNativeThreadResponse(response));
3121
3129
  }
3122
3130
  if ((action === "fork" || action === "rename" || action === "archive" || action === "unarchive") && supervision?.enabled === true && supervision.allowWriteControls !== true) throw new Error("Codex native thread mutations are disabled for this codex plugin supervision config.");
@@ -3128,7 +3136,7 @@ function createCodexThreadsTool(options) {
3128
3136
  await request(pluginConfig, CODEX_CONTROL_METHODS.renameThread, {
3129
3137
  threadId,
3130
3138
  name
3131
- }, await requestOptions(pluginConfig));
3139
+ }, requestOptions(pluginConfig));
3132
3140
  return jsonResult({
3133
3141
  action,
3134
3142
  threadId,
@@ -3136,17 +3144,17 @@ function createCodexThreadsTool(options) {
3136
3144
  });
3137
3145
  }
3138
3146
  if (action === "unarchive") {
3139
- const response = await request(pluginConfig, CODEX_CONTROL_METHODS.unarchiveThread, { threadId }, await requestOptions(pluginConfig));
3147
+ const response = await request(pluginConfig, CODEX_CONTROL_METHODS.unarchiveThread, { threadId }, requestOptions(pluginConfig));
3140
3148
  return jsonResult(mayReadRawTranscripts ? response : redactNativeThreadResponse(response));
3141
3149
  }
3142
3150
  const session = currentSession();
3143
- const binding = await currentBinding(session);
3151
+ const binding = currentBinding(session);
3144
3152
  if (action === "archive") {
3145
3153
  if (params.confirm !== true) throw new Error("confirm=true is required to archive a native Codex thread");
3146
3154
  if (!session) throw new Error("cannot safely archive a native Codex thread without a session identity");
3147
3155
  const identity = currentIdentity(session.sessionId);
3148
3156
  await options.bindingStore.withThreadArchiveFence(async () => {
3149
- const archivedBinding = await currentBinding(session);
3157
+ const archivedBinding = currentBinding(session);
3150
3158
  if (archivedBinding?.threadId === threadId) {
3151
3159
  if (session.modelSelectionLocked) throw new ModelSelectionLockedError();
3152
3160
  assertCodexBindingMayBeReplaced(archivedBinding, "archiving its bound native thread");
@@ -3154,20 +3162,20 @@ function createCodexThreadsTool(options) {
3154
3162
  assertThreadMayBeArchived(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
3155
3163
  threadId,
3156
3164
  includeTurns: false
3157
- }, await requestOptions(pluginConfig)), threadId);
3165
+ }, requestOptions(pluginConfig)), threadId);
3158
3166
  if (await options.bindingStore.hasOtherThreadOwner(threadId, identity)) throw new Error("cannot archive a native Codex thread owned by another OpenClaw session");
3159
3167
  await assertCodexArchiveDescendantsUnowned({
3160
3168
  bindingStore: options.bindingStore,
3161
3169
  threadId,
3162
- listPage: async (listParams) => await request(pluginConfig, CODEX_CONTROL_METHODS.listThreads, listParams, await requestOptions(pluginConfig)),
3170
+ listPage: async (listParams) => await request(pluginConfig, CODEX_CONTROL_METHODS.listThreads, listParams, requestOptions(pluginConfig)),
3163
3171
  assertDescendantIdle: async (descendantThreadId) => {
3164
3172
  assertThreadMayBeArchived(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
3165
3173
  threadId: descendantThreadId,
3166
3174
  includeTurns: false
3167
- }, await requestOptions(pluginConfig)), descendantThreadId);
3175
+ }, requestOptions(pluginConfig)), descendantThreadId);
3168
3176
  }
3169
3177
  });
3170
- await request(pluginConfig, CODEX_CONTROL_METHODS.archiveThread, { threadId }, await requestOptions(pluginConfig));
3178
+ await request(pluginConfig, CODEX_CONTROL_METHODS.archiveThread, { threadId }, requestOptions(pluginConfig));
3171
3179
  if (archivedBinding?.threadId === threadId) await options.bindingStore.mutate(identity, {
3172
3180
  kind: "clear",
3173
3181
  threadId
@@ -3189,13 +3197,13 @@ function createCodexThreadsTool(options) {
3189
3197
  assertThreadMayBeForked(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
3190
3198
  threadId,
3191
3199
  includeTurns: false
3192
- }, await requestOptions(pluginConfig)), threadId);
3200
+ }, requestOptions(pluginConfig)), threadId);
3193
3201
  }
3194
3202
  const response = await request(pluginConfig, CODEX_CONTROL_METHODS.forkThread, {
3195
3203
  threadId,
3196
3204
  threadSource: "user",
3197
3205
  excludeTurns: true
3198
- }, await requestOptions(pluginConfig));
3206
+ }, requestOptions(pluginConfig));
3199
3207
  if (!isJsonObject(response) || !isJsonObject(response.thread)) throw new Error("Codex app-server returned an invalid thread/fork response");
3200
3208
  const forkThreadId = typeof response.thread.id === "string" && response.thread.id.trim() ? response.thread.id : void 0;
3201
3209
  if (!forkThreadId) throw new Error("Codex app-server thread/fork response did not include a thread id");
@@ -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-nfbUOtUB.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-DKQn67A7.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
@@ -4333,22 +4387,6 @@ var codex_default = definePluginEntry({
4333
4387
  })
4334
4388
  }));
4335
4389
  api.onConversationBindingResolved?.((event) => codexConversationBindingRuntime.handleBindingResolved(event, { bindingStore }));
4336
- api.on("after_compaction", async (event, ctx) => {
4337
- const previousSessionId = event.previousSessionId?.trim();
4338
- const sessionId = ctx.sessionId?.trim();
4339
- if (!previousSessionId || !sessionId || previousSessionId === sessionId) return;
4340
- const config = resolveCurrentConfig();
4341
- const sessionKey = ctx.sessionKey?.trim();
4342
- const { sessionBindingIdentity } = await import("./session-binding-Bcynfd-_.js").then((n) => n.f);
4343
- const identity = sessionBindingIdentity({
4344
- sessionId,
4345
- ...sessionKey ? { sessionKey } : {},
4346
- ...ctx.agentId ? { agentId: ctx.agentId } : {},
4347
- ...config ? { config } : {}
4348
- });
4349
- const adopted = await bindingStore.adoptSessionGeneration(identity, previousSessionId);
4350
- if (adopted === "conflict") api.logger.warn?.(`codex: could not adopt compacted session generation ${sessionId} (${adopted}); secondary native compaction will skip`);
4351
- });
4352
4390
  api.on("session_end", async (event, ctx) => {
4353
4391
  if (!event.reason || !ENDED_SESSION_REASONS.has(event.reason)) return;
4354
4392
  const sessionKey = event.sessionKey ?? ctx.sessionKey;
@@ -4357,7 +4395,7 @@ var codex_default = definePluginEntry({
4357
4395
  if (endedSessionKey && nextSessionKey && nextSessionKey !== endedSessionKey) return;
4358
4396
  if (event.nextSessionId?.trim() === event.sessionId.trim()) return;
4359
4397
  const config = resolveCurrentConfig();
4360
- const [{ sessionBindingIdentity }, { retireCodexAppServerSessionGeneration }] = await Promise.all([import("./session-binding-Bcynfd-_.js").then((n) => n.f), import("./session-retirement-CcNC3YSY.js")]);
4398
+ const [{ sessionBindingIdentity }, { retireCodexAppServerSessionGeneration }] = await Promise.all([import("./session-binding-4Ki04sW-.js").then((n) => n.d), import("./session-retirement-DNdmYdFc.js")]);
4361
4399
  await retireCodexAppServerSessionGeneration({
4362
4400
  bindingStore,
4363
4401
  identity: sessionBindingIdentity({