@openclaw/codex 2026.7.2-beta.3 → 2026.7.2-beta.5

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 (43) hide show
  1. package/dist/{app-server-policy-DUMF2Xyr.js → app-server-policy-BmDgseo_.js} +1 -1
  2. package/dist/attempt-client-cleanup-Ia52yRWc.js +127 -0
  3. package/dist/{attempt-notifications-Ecjyzufe.js → attempt-notifications-CMR2_fmR.js} +1 -1
  4. package/dist/{media-understanding-provider-IPNNQre6.js → bounded-turn-FEbUXoYT.js} +71 -188
  5. package/dist/capabilities-BHRSI6Cn.js +36 -0
  6. package/dist/cli-metadata.js +1 -1
  7. package/dist/{command-formatters-NCTL7A2q.js → command-formatters-ico4ZFnk.js} +3 -37
  8. package/dist/{command-handlers-DtFbZiba.js → command-handlers-CRmnwI2N.js} +1233 -1221
  9. package/dist/{compact-Kgy26gIM.js → compact-dEN49UH6.js} +8 -7
  10. package/dist/{computer-use-C7x4ZaqR.js → computer-use-BjGylO-E.js} +16 -6
  11. package/dist/{conversation-control-B0s3aIjT.js → conversation-control-DE7d9DOj.js} +6 -5
  12. package/dist/doctor-contract-api.js +24 -86
  13. package/dist/{dynamic-tools-D_vSVmv9.js → dynamic-tools-LIt9ojep.js} +135 -2696
  14. package/dist/event-projector-assistant-message-DTgnPPVF.js +101 -0
  15. package/dist/event-projector-usage-tvgb1NCX.js +69 -0
  16. package/dist/harness.js +31 -11
  17. package/dist/incognito-session-ET_ufDH-.js +2 -0
  18. package/dist/index.js +250 -275
  19. package/dist/media-understanding-provider.js +173 -1
  20. package/dist/{models-DD5zBdX5.js → models-BA01NEez.js} +2 -2
  21. package/dist/{notification-correlation-C8DVQ_Bq.js → notification-correlation-Bj_Ak_jI.js} +3 -3
  22. package/dist/{plugin-app-cache-key-DJbhLYSK.js → plugin-app-cache-key-DSXX-rXO.js} +5 -5
  23. package/dist/{transcript-mirror-D9rTxx2P.js → protocol-validators-Dm4eXZmt.js} +335 -595
  24. package/dist/{rate-limits-CqhWQ2R9.js → rate-limits-S_PXs0ul.js} +1 -1
  25. package/dist/realtime-voice-api-BPcA1uN9.js +494 -0
  26. package/dist/realtime-voice-api.js +2 -0
  27. package/dist/{request-9yf9pZjf.js → request-CXpPennK.js} +1 -1
  28. package/dist/{run-attempt-BIoO8ISr.js → run-attempt-C5GTPJOE.js} +3111 -387
  29. package/dist/{session-binding-Bmt20HfB.js → session-binding-DlVQ-Pxs.js} +957 -890
  30. package/dist/{session-catalog-CA7R-hX2.js → session-catalog-CP2oCcEb.js} +98 -87
  31. package/dist/{session-cli-Bxk3-Wqz.js → session-cli-CqZ0Fp2a.js} +2 -2
  32. package/dist/session-upstream-marker-QE-m5QFG.js +82 -0
  33. package/dist/settled-turn-finalizer-CA_2QXIE.js +303 -0
  34. package/dist/{shared-client-CSXLtCLE.js → shared-client-BVKE40c-.js} +1870 -1694
  35. package/dist/{side-question-DtzquMOb.js → side-question-CO12sHxo.js} +24 -16
  36. package/dist/{thread-lifecycle-CQ_9mAa1.js → thread-lifecycle-DRg_FdzC.js} +127 -153
  37. package/dist/transcript-mirror-uKA-A4tB.js +550 -0
  38. package/dist/upstream-session-fork-_Vrz4WJr.js +320 -0
  39. package/dist/{usage-CuPpFgTc.js → usage-UpVbdX5G.js} +3 -3
  40. package/dist/{web-search-provider.runtime-_x5XTeqh.js → web-search-provider.runtime-CaQMYDbp.js} +3 -3
  41. package/package.json +7 -8
  42. package/npm-shrinkwrap.json +0 -202
  43. package/dist/{auth-binding-C1czuHPA.js → auth-binding-B4FfbcCg.js} +1 -1
package/dist/index.js CHANGED
@@ -1,20 +1,24 @@
1
1
  import { registerCodexCliMetadata } from "./cli-metadata.js";
2
2
  import { createCodexAppServerAgentHarness } from "./harness.js";
3
- import { C as resolveCodexAppServerFallbackApiKeyCacheKey, J as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS, S as resolveCodexAppServerAuthProfileIdForAgent, Z as isJsonObject, a as getLeasedSharedCodexAppServerClient, b as resolveCodexAppServerAuthAccountCacheKey, c as releaseLeasedSharedCodexAppServerClient, p as withLeasedCodexAppServerClientStartSelectionRetry, r as clearSharedCodexAppServerClientIfCurrentAndWait, s as releaseCodexAppServerClientLease } from "./shared-client-CSXLtCLE.js";
4
- import { D as resolveOpenClawExecPolicyForCodexAppServer, E as resolveCodexSupervisionAppServerRuntimeOptions, a as isCodexAppServerNativeAuthProfile, b as resolveCodexAppServerRuntimeOptions, c as sessionBindingIdentity, f as assertCodexAppServerConnectionSecurity, h as codexSandboxPolicyForTurn, m as codexAppServerStartOptionsKey, n as assertCodexBindingMayBeReplaced, o as normalizeCodexAppServerBindingModelProvider, p as canUseCodexModelBackedApprovalsReviewerForModel, u as CODEX_PLUGINS_MARKETPLACE_NAME, y as readCodexPluginConfig } from "./session-binding-Bmt20HfB.js";
5
- import { d as assertCodexThreadStartResponse } from "./transcript-mirror-D9rTxx2P.js";
6
- import { i as defaultCodexAppInventoryCache, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-DJbhLYSK.js";
7
- import { X as ensureCodexPluginActivation, Z as pluginReadParams, c as resumeCodexAppServerThread, h as resolveCodexAppServerRequestModelSelection, p as CODEX_NATIVE_PERSONALITY_NONE } from "./thread-lifecycle-CQ_9mAa1.js";
3
+ import { N as resolveCodexAppServerAuthProfileIdForAgent, P as resolveCodexAppServerFallbackApiKeyCacheKey, R as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS, V as isJsonObject, a as getLeasedSharedCodexAppServerClient, c as releaseCodexAppServerClientLease, j as resolveCodexAppServerAuthAccountCacheKey, l as releaseLeasedSharedCodexAppServerClient, m as withLeasedCodexAppServerClientStartSelectionRetry, r as clearSharedCodexAppServerClientIfCurrentAndWait } from "./shared-client-BVKE40c-.js";
4
+ import { D as resolveOpenClawExecPolicyForCodexAppServer, T as readCodexPluginConfig, _ as assertCodexAppServerConnectionSecurity, b as canUseCodexModelBackedApprovalsReviewerForModel, d as codexAppServerStartOptionsKey, f as codexSandboxPolicyForTurn, g as resolveCodexSupervisionAppServerRuntimeOptions, k as CODEX_PLUGINS_MARKETPLACE_NAME, l as sessionBindingIdentity, n as assertCodexBindingMayBeReplaced, o as isCodexAppServerNativeAuthProfile, p as resolveCodexAppServerRuntimeOptions, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-DlVQ-Pxs.js";
8
5
  import { n as CODEX_APP_SERVER_BINDING_NAMESPACE, t as CODEX_APP_SERVER_BINDING_MAX_ENTRIES } from "./session-binding-meta-B7aEMU7g.js";
9
- import { t as buildCodexMediaUnderstandingProvider } from "./media-understanding-provider-IPNNQre6.js";
10
- import { d as CODEX_CONTROL_METHODS, f as describeControlFailure, r as formatCodexDisplayText } from "./command-formatters-NCTL7A2q.js";
11
- import { t as resolveCodexAppServerForModelProvider } from "./app-server-policy-DUMF2Xyr.js";
6
+ import { n as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, u as unsubscribeCodexThreadBestEffort } from "./attempt-client-cleanup-Ia52yRWc.js";
7
+ import { J as pluginReadParams, _ as CODEX_NATIVE_PERSONALITY_NONE, c as resumeCodexAppServerThread, q as ensureCodexPluginActivation, y as resolveCodexAppServerRequestModelSelection } from "./thread-lifecycle-DRg_FdzC.js";
8
+ import { i as assertCodexThreadStartResponse } from "./protocol-validators-Dm4eXZmt.js";
9
+ import { i as defaultCodexAppInventoryCache, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-DSXX-rXO.js";
10
+ import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
11
+ import { buildCodexMediaUnderstandingProvider } from "./media-understanding-provider.js";
12
+ import { n as CODEX_REALTIME_OFFER_PATH, t as configureCodexRealtimeBrowserSession } from "./realtime-voice-api-BPcA1uN9.js";
13
+ import { n as describeControlFailure, t as CODEX_CONTROL_METHODS } from "./capabilities-BHRSI6Cn.js";
14
+ import { r as formatCodexDisplayText } from "./command-formatters-ico4ZFnk.js";
15
+ import { t as resolveCodexAppServerForModelProvider } from "./app-server-policy-BmDgseo_.js";
12
16
  import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "./sandbox-guard-D1Wky__S.js";
13
- import { d as CODEX_NATIVE_EXECUTION_AUTH_ERROR, f as canMutateCodexHost, u as trackCodexConversationActiveTurn } from "./conversation-control-B0s3aIjT.js";
17
+ import { d as CODEX_NATIVE_EXECUTION_AUTH_ERROR, f as canMutateCodexHost, u as trackCodexConversationActiveTurn } from "./conversation-control-DE7d9DOj.js";
14
18
  import { a as readCodexConversationBindingDataRecord, i as readCodexConversationBindingData, o as resolveCodexDefaultWorkspaceDir, r as createCodexConversationBindingData } from "./conversation-binding-data-BHRpYbX3.js";
15
- import { a as resolveCodexBindingAppServerConnection, n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./notification-correlation-C8DVQ_Bq.js";
16
- import { d as createCodexCliSessionNodeHostCommands, f as createCodexCliSessionNodeInvokePolicies, g as resumeCodexCliSessionOnNode, h as resolveCodexCliSessionForBindingOnNode, i as createCodexSessionCatalogNodeInvokePolicies, m as listCodexCliSessionsOnNode, n as createCodexSessionCatalogControl, o as codexControlRequest, r as createCodexSessionCatalogNodeHostCommands, t as codexSessionCatalogRuntime, u as assertCodexArchiveDescendantsUnowned } from "./session-catalog-CA7R-hX2.js";
17
- import { r as requestCodexAppServerJson } from "./request-9yf9pZjf.js";
19
+ import { a as resolveCodexBindingAppServerConnection, n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./notification-correlation-Bj_Ak_jI.js";
20
+ import { d as createCodexCliSessionNodeHostCommands, f as createCodexCliSessionNodeInvokePolicies, g as resumeCodexCliSessionOnNode, h as resolveCodexCliSessionForBindingOnNode, i as createCodexSessionCatalogNodeInvokePolicies, m as listCodexCliSessionsOnNode, n as createCodexSessionCatalogControl, o as codexControlRequest, r as createCodexSessionCatalogNodeHostCommands, t as codexSessionCatalogRuntime, u as assertCodexArchiveDescendantsUnowned } from "./session-catalog-CP2oCcEb.js";
21
+ import { r as requestCodexAppServerJson } from "./request-CXpPennK.js";
18
22
  import { t as createCodexWebSearchProviderBase } from "./web-search-provider.shared-BrZmlqyR.js";
19
23
  import { mutateConfigFile } from "openclaw/plugin-sdk/config-mutation";
20
24
  import { normalizePluginsConfig, resolveEffectiveEnableState, resolveLivePluginConfigObject, resolvePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
@@ -22,19 +26,19 @@ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
22
26
  import fs from "node:fs/promises";
23
27
  import path from "node:path";
24
28
  import { parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
29
+ import { formatErrorMessage, resolveSandboxContext } from "openclaw/plugin-sdk/agent-harness-runtime";
30
+ import { loadAuthProfileStoreWithoutExternalProfiles, resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentDir, resolveDefaultAgentId, resolveSessionAgentIds as resolveSessionAgentIds$1 } from "openclaw/plugin-sdk/agent-runtime";
25
31
  import os from "node:os";
26
- import { loadAuthProfileStoreWithoutExternalProfiles, resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentDir, resolveDefaultAgentId, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
32
+ import { applyAuthProfileConfig, buildApiKeyCredential, buildOauthProviderAuthResult, buildOpenAICodexCredentialExtra, readCodexCliCredentialsCached, resolveOpenAICodexAuthIdentity, resolveOpenAICodexImportProfileName, updateAuthProfileStoreWithLock } from "openclaw/plugin-sdk/provider-auth";
27
33
  import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
28
34
  import { asBoolean, asOptionalRecord, isRecord, normalizeOptionalString, normalizeSingleOrTrimmedStringList, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
29
- import { formatErrorMessage, resolveSandboxContext } from "openclaw/plugin-sdk/agent-harness-runtime";
30
- import { applyAuthProfileConfig, buildApiKeyCredential, buildOauthProviderAuthResult, buildOpenAICodexCredentialExtra, readCodexCliCredentialsCached, resolveOpenAICodexAuthIdentity, resolveOpenAICodexImportProfileName, updateAuthProfileStoreWithLock } from "openclaw/plugin-sdk/provider-auth";
31
35
  import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
32
36
  import { fileURLToPath } from "node:url";
33
37
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
34
- import { canonicalPathFromExistingAncestor, isPathInside, pathExists } from "openclaw/plugin-sdk/security-runtime";
38
+ import { canonicalPathFromExistingAncestor, extractErrorCode, isPathInside, pathExists } from "openclaw/plugin-sdk/security-runtime";
35
39
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
36
40
  import { ModelSelectionLockedError, isModelSelectionLocked } from "openclaw/plugin-sdk/model-session-runtime";
37
- import { MIGRATION_REASON_TARGET_EXISTS, applyMigrationManualItem, createMigrationItem, createMigrationManualItem, hasMigrationConfigPatchConflict, markMigrationItemConflict, markMigrationItemError, markMigrationItemSkipped, readMigrationConfigPath, summarizeMigrationItems, writeMigrationConfigPath } from "openclaw/plugin-sdk/migration";
41
+ import { MIGRATION_REASON_TARGET_EXISTS, applyMigrationManualItem, createMigrationItem, createMigrationManualItem, hasMigrationConfigPatchConflict, markMigrationItemConflict, markMigrationItemError, markMigrationItemSkipped, mergeMigrationConfigValue, readMigrationConfigPath, resolveMigrationConfigRuntime, summarizeMigrationItems, writeMigrationConfigPath } from "openclaw/plugin-sdk/migration";
38
42
  import { archiveMigrationItem, copyMemoryMigrationFileItem, copyMigrationFileItem, withCachedMigrationConfigRuntime, writeMigrationReport } from "openclaw/plugin-sdk/migration-runtime";
39
43
  import { sleep } from "openclaw/plugin-sdk/runtime-env";
40
44
  import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
@@ -45,13 +49,14 @@ import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
45
49
  /** Defers schema compilation and auth loading until the first binding operation. */
46
50
  function createLazyCodexAppServerBindingStore(state) {
47
51
  let resolved;
48
- const store = () => resolved ??= import("./session-binding-Bmt20HfB.js").then((n) => n.l).then(({ createCodexAppServerBindingStore }) => createCodexAppServerBindingStore(state));
52
+ const store = () => resolved ??= import("./session-binding-DlVQ-Pxs.js").then((n) => n.u).then(({ createCodexAppServerBindingStore }) => createCodexAppServerBindingStore(state));
49
53
  return {
50
54
  read: async (identity) => (await store()).read(identity),
51
55
  hasOtherThreadOwner: async (threadId, currentIdentity) => (await store()).hasOtherThreadOwner(threadId, currentIdentity),
52
56
  mutate: async (identity, mutation) => (await store()).mutate(identity, mutation),
53
57
  prepareSessionGenerationReclaim: async (identity) => (await store()).prepareSessionGenerationReclaim(identity),
54
58
  adoptSessionGeneration: async (identity, previousSessionId) => (await store()).adoptSessionGeneration(identity, previousSessionId),
59
+ resetSessionGeneration: async (identity) => (await store()).resetSessionGeneration(identity),
55
60
  retireSessionGeneration: async (identity) => (await store()).retireSessionGeneration(identity),
56
61
  withThreadArchiveFence: async (run) => (await store()).withThreadArchiveFence(run),
57
62
  withLease: async (identity, run) => (await store()).withLease(identity, run)
@@ -72,7 +77,7 @@ async function handleCodexCommand(ctx, options) {
72
77
  }
73
78
  }
74
79
  async function loadDefaultCodexSubcommandHandler() {
75
- const { handleCodexSubcommand } = await import("./command-handlers-DtFbZiba.js");
80
+ const { handleCodexSubcommand } = await import("./command-handlers-CRmnwI2N.js");
76
81
  return handleCodexSubcommand;
77
82
  }
78
83
  //#endregion
@@ -569,7 +574,8 @@ async function requestNewConversationBindingThread(params, resolved) {
569
574
  personality: CODEX_NATIVE_PERSONALITY_NONE,
570
575
  ...buildThreadRequestRuntimeOptions(params, resolved),
571
576
  developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
572
- experimentalRawEvents: true
577
+ experimentalRawEvents: true,
578
+ ...isIncognitoSessionKey(params.sessionKey) ? { ephemeral: true } : {}
573
579
  }, requestOptions),
574
580
  onClientChange: (client) => {
575
581
  resolved.client = client;
@@ -717,7 +723,8 @@ async function runBoundTurn(params) {
717
723
  ...modelScopedRuntime.networkProxy ? { config: modelScopedRuntime.networkProxy.configPatch } : { sandbox },
718
724
  ...serviceTier ? { serviceTier } : {},
719
725
  developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
720
- experimentalRawEvents: true
726
+ experimentalRawEvents: true,
727
+ ...isIncognitoSessionKey(params.sessionKey) ? { ephemeral: true } : {}
721
728
  }, requestOptions),
722
729
  onClientChange: (nextClient) => {
723
730
  client = nextClient;
@@ -834,6 +841,19 @@ async function runBoundTurn(params) {
834
841
  });
835
842
  collector.setTurnId(turnId);
836
843
  return { reply: { text: (await collector.wait({ timeoutMs: params.timeoutMs ?? DEFAULT_BOUND_TURN_TIMEOUT_MS }).finally(activeCleanup)).replyText.trim() || "Codex completed without a text reply." } };
844
+ } catch (error) {
845
+ if (isIncognitoSessionKey(params.sessionKey)) {
846
+ const bindingReleased = await params.bindingStore.mutate(identity, {
847
+ kind: "clear",
848
+ threadId
849
+ });
850
+ const timedOut = error instanceof Error && error.message === "codex app-server bound turn timed out";
851
+ if (bindingReleased && !timedOut) await unsubscribeCodexThreadBestEffort(client, {
852
+ threadId,
853
+ timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
854
+ });
855
+ }
856
+ throw error;
837
857
  } finally {
838
858
  notificationCleanup();
839
859
  requestCleanup();
@@ -920,7 +940,7 @@ async function prepareConversationBinding(params, options = {}) {
920
940
  });
921
941
  }
922
942
  function resolveConversationExecPolicy(params) {
923
- const agentId = params.agentId ?? (params.config ? resolveSessionAgentIds({
943
+ const agentId = params.agentId ?? (params.config ? resolveSessionAgentIds$1({
924
944
  sessionKey: params.sessionKey,
925
945
  config: params.config
926
946
  }).sessionAgentId : void 0);
@@ -1121,11 +1141,6 @@ async function readModelRefs(source) {
1121
1141
  refs.add(OPENAI_CODEX_DEFAULT_MODEL);
1122
1142
  return [...refs].toSorted();
1123
1143
  }
1124
- function readProviderAuthModelConfigs(result) {
1125
- const models = result.configPatch?.agents?.defaults?.models;
1126
- if (isRecord(models)) return { ...models };
1127
- return { [normalizeOptionalString(result.defaultModel) ?? OPENAI_CODEX_DEFAULT_MODEL]: {} };
1128
- }
1129
1144
  async function buildCodexOAuthCredential(source) {
1130
1145
  const credential = readCodexCliCredentialsCached({
1131
1146
  codexHome: source.codexHome,
@@ -1160,8 +1175,7 @@ async function buildCodexOAuthCredential(source) {
1160
1175
  kind: "oauth",
1161
1176
  provider: OPENAI_PROVIDER_ID,
1162
1177
  profileId: profile.profileId,
1163
- result,
1164
- modelConfigs: readProviderAuthModelConfigs(result)
1178
+ result
1165
1179
  } : null;
1166
1180
  }
1167
1181
  async function buildCodexApiKeyCredential(source) {
@@ -1219,7 +1233,7 @@ function hasAuthProfileConfigConflict(config, profile, overwrite) {
1219
1233
  function hasCurrentAuthProfileConfigConflict(ctx, profile) {
1220
1234
  let config = ctx.config;
1221
1235
  try {
1222
- config = ctx.runtime?.config?.current?.() ?? config;
1236
+ config = resolveMigrationConfigRuntime(ctx)?.current?.() ?? config;
1223
1237
  } catch {}
1224
1238
  return hasAuthProfileConfigConflict(config, profile, Boolean(ctx.overwrite));
1225
1239
  }
@@ -1240,30 +1254,8 @@ function applyDefaultModelIfMissing(cfg) {
1240
1254
  }
1241
1255
  };
1242
1256
  }
1243
- function mergeModelConfigEntry(existing, patch) {
1244
- if (existing && isRecord(existing) && isRecord(patch)) return {
1245
- ...existing,
1246
- ...patch
1247
- };
1248
- return existing ?? patch;
1249
- }
1250
- function applyOAuthModelConfigsToConfig(cfg, credential) {
1251
- const existingModels = cfg.agents?.defaults?.models ?? {};
1252
- const models = credential.result.replaceDefaultModels ? { ...credential.modelConfigs } : { ...existingModels };
1253
- if (!credential.result.replaceDefaultModels) for (const [modelRef, modelConfig] of Object.entries(credential.modelConfigs)) models[modelRef] = mergeModelConfigEntry(models[modelRef], modelConfig);
1254
- return {
1255
- ...cfg,
1256
- agents: {
1257
- ...cfg.agents,
1258
- defaults: {
1259
- ...cfg.agents?.defaults,
1260
- models
1261
- }
1262
- }
1263
- };
1264
- }
1265
1257
  function applyOAuthConfigToConfig(cfg, credential, profileId) {
1266
- let next = applyOAuthModelConfigsToConfig(cfg, credential);
1258
+ let next = mergeMigrationConfigValue(cfg, credential.result.configPatch);
1267
1259
  const profile = credential.result.profiles[0];
1268
1260
  if (profile) next = applyAuthProfileConfig(next, {
1269
1261
  profileId,
@@ -1287,30 +1279,25 @@ function applyApiKeyConfigToConfig(cfg, credential, profileId) {
1287
1279
  function shouldReturnAuthConfigPatch(ctx) {
1288
1280
  return ctx.providerOptions?.configPatchMode === CODEX_CONFIG_PATCH_MODE_RETURN$1;
1289
1281
  }
1290
- function oauthAuthProfileConfig(credential, profileId) {
1282
+ function authProfileConfigForCredential(credential, profileId) {
1283
+ if (credential.kind === "api_key") return {
1284
+ profileId,
1285
+ provider: credential.provider,
1286
+ mode: "api_key",
1287
+ displayName: CODEX_IMPORT_DISPLAY_NAME
1288
+ };
1291
1289
  const profile = credential.result.profiles[0];
1292
1290
  if (!profile || profile.credential.type !== "oauth") return null;
1293
1291
  return {
1294
1292
  profileId,
1295
1293
  provider: profile.credential.provider,
1296
1294
  mode: "oauth",
1297
- ..."email" in profile.credential && profile.credential.email ? { email: profile.credential.email } : {},
1298
- ..."displayName" in profile.credential && profile.credential.displayName ? { displayName: profile.credential.displayName } : {}
1299
- };
1300
- }
1301
- function apiKeyAuthProfileConfig(credential, profileId) {
1302
- return {
1303
- profileId,
1304
- provider: credential.provider,
1305
- mode: "api_key",
1306
- displayName: CODEX_IMPORT_DISPLAY_NAME
1295
+ ...profile.credential.email ? { email: profile.credential.email } : {},
1296
+ ...profile.credential.displayName ? { displayName: profile.credential.displayName } : {}
1307
1297
  };
1308
1298
  }
1309
- function authProfileConfigForCredential(credential, profileId) {
1310
- return credential.kind === "oauth" ? oauthAuthProfileConfig(credential, profileId) : apiKeyAuthProfileConfig(credential, profileId);
1311
- }
1312
1299
  async function applyCodexAuthProfileConfig(ctx, profile, applyConfig) {
1313
- const configApi = ctx.runtime?.config;
1300
+ const configApi = resolveMigrationConfigRuntime(ctx);
1314
1301
  if (!configApi?.current || !configApi.mutateConfigFile) return "unavailable";
1315
1302
  try {
1316
1303
  await configApi.mutateConfigFile({
@@ -1327,13 +1314,13 @@ async function applyCodexAuthProfileConfig(ctx, profile, applyConfig) {
1327
1314
  return error instanceof CodexAuthConfigConflict ? "conflict" : "unavailable";
1328
1315
  }
1329
1316
  }
1330
- async function applyOAuthConfig(ctx, credential, profileId) {
1331
- const profile = oauthAuthProfileConfig(credential, profileId);
1317
+ async function applyCodexAuthConfig(ctx, credential, profileId) {
1318
+ const profile = authProfileConfigForCredential(credential, profileId);
1332
1319
  if (!profile) return "unavailable";
1333
- return applyCodexAuthProfileConfig(ctx, profile, (config) => applyOAuthConfigToConfig(config, credential, profileId));
1320
+ return applyCodexAuthProfileConfig(ctx, profile, (config) => applyCredentialConfig(config, credential, profileId));
1334
1321
  }
1335
- async function applyApiKeyConfig(ctx, credential, profileId) {
1336
- return applyCodexAuthProfileConfig(ctx, apiKeyAuthProfileConfig(credential, profileId), (config) => applyApiKeyConfigToConfig(config, credential, profileId));
1322
+ function applyCredentialConfig(config, credential, profileId) {
1323
+ return credential.kind === "oauth" ? applyOAuthConfigToConfig(config, credential, profileId) : applyApiKeyConfigToConfig(config, credential, profileId);
1337
1324
  }
1338
1325
  async function buildCodexAuthItems(params) {
1339
1326
  const credentials = await readCodexAuthCredentials(params.source);
@@ -1366,26 +1353,27 @@ async function buildCodexAuthItems(params) {
1366
1353
  });
1367
1354
  });
1368
1355
  }
1369
- async function applyCodexAuthItem(params) {
1356
+ async function applyCodexAuthItems(params) {
1370
1357
  const { ctx, item, source, targets } = params;
1371
- if (item.status !== "planned") return item;
1358
+ if (item.status !== "planned") return [item];
1372
1359
  const profileId = typeof item.details?.profileId === "string" ? item.details.profileId : "";
1373
1360
  const provider = typeof item.details?.provider === "string" ? item.details.provider : "";
1374
1361
  const sourceProfileId = typeof item.details?.sourceProfileId === "string" ? item.details.sourceProfileId : void 0;
1375
- if (!profileId || !provider) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
1362
+ if (!profileId || !provider) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
1376
1363
  const credential = (await readCodexAuthCredentials(source)).find((candidate) => candidate.provider === provider);
1377
- if (!credential) return markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT);
1378
- if (credential.kind === "oauth" && sourceProfileId && credential.profileId !== sourceProfileId) return markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT);
1364
+ if (!credential) return [markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT)];
1365
+ if (credential.kind === "oauth" && sourceProfileId && credential.profileId !== sourceProfileId) return [markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT)];
1379
1366
  const oauthProfile = credential.kind === "oauth" ? credential.result.profiles[0] : void 0;
1380
1367
  const oauthCredential = oauthProfile?.credential.type === "oauth" ? oauthProfile.credential : void 0;
1381
- if (credential.kind === "oauth" && !oauthCredential) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
1368
+ if (credential.kind === "oauth" && !oauthCredential) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
1382
1369
  const configProfile = authProfileConfigForCredential(credential, profileId);
1383
- if (!configProfile) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
1384
- if (hasCurrentAuthProfileConfigConflict(ctx, configProfile)) return markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS);
1370
+ if (!configProfile) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
1371
+ if (hasCurrentAuthProfileConfigConflict(ctx, configProfile)) return [markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS)];
1385
1372
  let conflicted = false;
1386
1373
  let wrote = false;
1387
1374
  const store = await updateAuthProfileStoreWithLock({
1388
1375
  agentDir: targets.agentDir,
1376
+ stateDir: ctx.stateDir,
1389
1377
  updater: (freshStore) => {
1390
1378
  const existing = freshStore.profiles[profileId];
1391
1379
  if (!ctx.overwrite && existing) {
@@ -1404,11 +1392,11 @@ async function applyCodexAuthItem(params) {
1404
1392
  return true;
1405
1393
  }
1406
1394
  });
1407
- if (conflicted) return markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS);
1408
- if (!store?.profiles[profileId]) return markMigrationItemError(item, CODEX_REASON_AUTH_PROFILE_WRITE_FAILED);
1409
- const configResult = shouldReturnAuthConfigPatch(ctx) ? "unavailable" : credential.kind === "oauth" ? await applyOAuthConfig(ctx, credential, profileId) : await applyApiKeyConfig(ctx, credential, profileId);
1410
- if (configResult === "conflict") return markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS);
1411
- return {
1395
+ if (conflicted) return [markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS)];
1396
+ if (!store?.profiles[profileId]) return [markMigrationItemError(item, CODEX_REASON_AUTH_PROFILE_WRITE_FAILED)];
1397
+ const configResult = shouldReturnAuthConfigPatch(ctx) ? "unavailable" : await applyCodexAuthConfig(ctx, credential, profileId);
1398
+ if (configResult === "conflict") return [markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS)];
1399
+ const migratedItem = {
1412
1400
  ...item,
1413
1401
  status: "migrated",
1414
1402
  details: {
@@ -1418,18 +1406,10 @@ async function applyCodexAuthItem(params) {
1418
1406
  ...shouldReturnAuthConfigPatch(ctx) ? { configPatchReturned: true } : {}
1419
1407
  }
1420
1408
  };
1409
+ return [migratedItem, ...shouldReturnAuthConfigPatch(ctx) ? buildCodexAuthConfigPatchItems(ctx, migratedItem, credential, profileId) : []];
1421
1410
  }
1422
- async function buildCodexAuthConfigPatchItems(params) {
1423
- const { ctx, item, source } = params;
1424
- if (item.status !== "migrated" || !shouldReturnAuthConfigPatch(ctx)) return [];
1425
- const profileId = typeof item.details?.profileId === "string" ? item.details.profileId : "";
1426
- const provider = typeof item.details?.provider === "string" ? item.details.provider : "";
1427
- const sourceProfileId = typeof item.details?.sourceProfileId === "string" ? item.details.sourceProfileId : void 0;
1428
- if (!profileId || !provider) return [];
1429
- const credential = (await readCodexAuthCredentials(source)).find((candidate) => candidate.provider === provider);
1430
- if (!credential) return [];
1431
- if (credential.kind === "oauth" && sourceProfileId && credential.profileId !== sourceProfileId) return [];
1432
- const next = credential.kind === "oauth" ? applyOAuthConfigToConfig(ctx.config, credential, profileId) : applyApiKeyConfigToConfig(ctx.config, credential, profileId);
1411
+ function buildCodexAuthConfigPatchItems(ctx, item, credential, profileId) {
1412
+ const next = applyCredentialConfig(ctx.config, credential, profileId);
1433
1413
  const items = [];
1434
1414
  if (next.auth) items.push(createMigrationItem({
1435
1415
  id: `${item.id}:config:auth`,
@@ -1458,108 +1438,6 @@ async function buildCodexAuthConfigPatchItems(params) {
1458
1438
  return items;
1459
1439
  }
1460
1440
  //#endregion
1461
- //#region extensions/codex/src/migration/memory-plan.ts
1462
- const MIGRATION_REASON_TARGET_NOT_REGULAR = "target is not a regular file";
1463
- async function lstatIfExists(filePath) {
1464
- try {
1465
- return await fs.lstat(filePath);
1466
- } catch (error) {
1467
- const code = error && typeof error === "object" && "code" in error ? String(error.code) : void 0;
1468
- if (code === "ENOENT" || code === "ENOTDIR") return;
1469
- throw error;
1470
- }
1471
- }
1472
- async function assertSafeMemoryDestination(params) {
1473
- const [canonicalSource, canonicalWorkspace, canonicalTarget] = await Promise.all([
1474
- fs.realpath(path.dirname(params.source)),
1475
- canonicalPathFromExistingAncestor(params.workspaceDir),
1476
- canonicalPathFromExistingAncestor(params.target)
1477
- ]);
1478
- if (!isPathInside(canonicalWorkspace, canonicalTarget)) throw new Error("Codex memory import destination must stay in the selected workspace.");
1479
- if (isPathInside(canonicalSource, canonicalTarget) || isPathInside(canonicalTarget, canonicalSource)) throw new Error("Codex memory source and OpenClaw import destination must be separate paths.");
1480
- }
1481
- async function buildCodexMemoryItems(params) {
1482
- const items = [];
1483
- for (const memory of params.memoryFiles) {
1484
- const target = path.join(params.workspaceDir, "memory", "imports", "codex", path.basename(memory.path));
1485
- const targetStat = await lstatIfExists(target);
1486
- const targetExists = targetStat !== void 0;
1487
- const targetNotRegular = targetExists && !targetStat.isFile();
1488
- if (!targetNotRegular) await assertSafeMemoryDestination({
1489
- source: memory.path,
1490
- workspaceDir: params.workspaceDir,
1491
- target
1492
- });
1493
- const targetConflict = targetNotRegular || targetExists && !params.overwrite;
1494
- items.push(createMigrationItem({
1495
- id: memory.id,
1496
- kind: "memory",
1497
- action: "copy",
1498
- source: memory.path,
1499
- target,
1500
- status: targetConflict ? "conflict" : "planned",
1501
- reason: targetNotRegular ? MIGRATION_REASON_TARGET_NOT_REGULAR : targetConflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
1502
- message: "Copy consolidated Codex memory into the OpenClaw memory index.",
1503
- details: {
1504
- sourceType: "codex-memory",
1505
- sourceLabel: memory.label,
1506
- collectionId: "codex",
1507
- collectionLabel: "Codex",
1508
- relativePath: path.basename(memory.path)
1509
- }
1510
- }));
1511
- }
1512
- return items;
1513
- }
1514
- //#endregion
1515
- //#region extensions/codex/src/migration/skill-plan.ts
1516
- function uniqueSkillName(skill, counts) {
1517
- const base = sanitizeName(skill.name) || "codex-skill";
1518
- if ((counts.get(base) ?? 0) <= 1) return base;
1519
- return sanitizeName([
1520
- "codex",
1521
- sanitizeName(path.basename(path.dirname(skill.source))),
1522
- base
1523
- ].filter(Boolean).join("-")) || base;
1524
- }
1525
- async function buildCodexSkillItems(params) {
1526
- const baseCounts = /* @__PURE__ */ new Map();
1527
- for (const skill of params.skills) {
1528
- const base = sanitizeName(skill.name) || "codex-skill";
1529
- baseCounts.set(base, (baseCounts.get(base) ?? 0) + 1);
1530
- }
1531
- const resolvedCounts = /* @__PURE__ */ new Map();
1532
- const planned = params.skills.map((skill) => {
1533
- const name = uniqueSkillName(skill, baseCounts);
1534
- resolvedCounts.set(name, (resolvedCounts.get(name) ?? 0) + 1);
1535
- return {
1536
- skill,
1537
- name,
1538
- target: path.join(params.workspaceDir, "skills", name)
1539
- };
1540
- });
1541
- const items = [];
1542
- for (const item of planned) {
1543
- const collides = (resolvedCounts.get(item.name) ?? 0) > 1;
1544
- const targetExists = await exists(item.target);
1545
- items.push(createMigrationItem({
1546
- id: `skill:${item.name}`,
1547
- kind: "skill",
1548
- action: "copy",
1549
- source: item.skill.source,
1550
- target: item.target,
1551
- status: collides ? "conflict" : targetExists && !params.overwrite ? "conflict" : "planned",
1552
- reason: collides ? `multiple Codex skills normalize to "${item.name}"` : targetExists && !params.overwrite ? MIGRATION_REASON_TARGET_EXISTS : void 0,
1553
- message: `Copy ${item.skill.sourceLabel} into this OpenClaw agent workspace.`,
1554
- details: {
1555
- skillName: item.name,
1556
- sourceLabel: item.skill.sourceLabel
1557
- }
1558
- }));
1559
- }
1560
- return items;
1561
- }
1562
- //#endregion
1563
1441
  //#region extensions/codex/src/migration/source-files.ts
1564
1442
  const SKILL_FILENAME = "SKILL.md";
1565
1443
  const MAX_SCAN_DEPTH = 6;
@@ -1600,8 +1478,6 @@ async function discoverPluginDirs(codexHome) {
1600
1478
  discovered.set(dir, {
1601
1479
  name: manifestName || path.basename(dir),
1602
1480
  source: dir,
1603
- manifestPath,
1604
- sourceKind: "cache",
1605
1481
  migratable: false,
1606
1482
  message: "Cached Codex plugin bundle found. Review manually unless the plugin is also installed in the source Codex app-server inventory"
1607
1483
  });
@@ -1625,7 +1501,7 @@ async function discoverCodexMemoryFile(candidate) {
1625
1501
  }
1626
1502
  async function discoverCodexMemorySources(codexHome) {
1627
1503
  const memoriesDir = path.join(codexHome, "memories");
1628
- const memoryFiles = (await Promise.all([{
1504
+ return (await Promise.all([{
1629
1505
  id: "memory:codex:MEMORY.md",
1630
1506
  label: "Codex consolidated memory",
1631
1507
  name: "MEMORY.md"
@@ -1638,10 +1514,6 @@ async function discoverCodexMemorySources(codexHome) {
1638
1514
  label: candidate.label,
1639
1515
  path: path.join(memoriesDir, candidate.name)
1640
1516
  })))).filter((entry) => entry !== void 0);
1641
- return {
1642
- ...await isDirectory(memoriesDir) ? { memoriesDir } : {},
1643
- memoryFiles
1644
- };
1645
1517
  }
1646
1518
  //#endregion
1647
1519
  //#region extensions/codex/src/migration/source.ts
@@ -1713,7 +1585,6 @@ function buildInstalledPluginSource(plugin) {
1713
1585
  pluginName,
1714
1586
  marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
1715
1587
  source: `${CODEX_PLUGINS_MARKETPLACE_NAME}/${pluginName}`,
1716
- sourceKind: "app-server",
1717
1588
  migratable: true,
1718
1589
  installed: plugin.installed,
1719
1590
  enabled: plugin.enabled
@@ -1920,8 +1791,7 @@ function pluginNameFromSummary(summary) {
1920
1791
  if (normalized) return normalized;
1921
1792
  }
1922
1793
  }
1923
- async function discoverCodexSource(inputOrOptions) {
1924
- const options = typeof inputOrOptions === "string" || inputOrOptions === void 0 ? { input: inputOrOptions } : inputOrOptions;
1794
+ async function discoverCodexSource(options = {}) {
1925
1795
  const codexHome = resolveHomePath(options.input?.trim() || defaultCodexHome());
1926
1796
  const codexSkillsDir = path.join(codexHome, "skills");
1927
1797
  const agentsSkillsDir = personalAgentsSkillsDir();
@@ -1929,7 +1799,7 @@ async function discoverCodexSource(inputOrOptions) {
1929
1799
  const authPath = path.join(codexHome, "auth.json");
1930
1800
  const modelsCachePath = path.join(codexHome, "models_cache.json");
1931
1801
  const hooksPath = path.join(codexHome, "hooks", "hooks.json");
1932
- const { memoriesDir, memoryFiles } = await discoverCodexMemorySources(codexHome);
1802
+ const memoryFiles = await discoverCodexMemorySources(codexHome);
1933
1803
  const codexSkills = options.memoryOnly ? [] : await discoverSkillDirs({
1934
1804
  root: codexSkillsDir,
1935
1805
  sourceLabel: "Codex skill",
@@ -1969,11 +1839,8 @@ async function discoverCodexSource(inputOrOptions) {
1969
1839
  codexHome,
1970
1840
  ...await isDirectory(codexSkillsDir) ? { codexSkillsDir } : {},
1971
1841
  ...await isDirectory(agentsSkillsDir) ? { personalAgentsSkillsDir: agentsSkillsDir } : {},
1972
- ...await exists(configPath) ? { configPath } : {},
1973
1842
  ...hasAuth ? { authPath } : {},
1974
1843
  ...await exists(modelsCachePath) ? { modelsCachePath } : {},
1975
- ...await exists(hooksPath) ? { hooksPath } : {},
1976
- ...memoriesDir ? { memoriesDir } : {},
1977
1844
  memoryFiles,
1978
1845
  skills,
1979
1846
  plugins,
@@ -2022,12 +1889,98 @@ const CODEX_PLUGIN_NATIVE_CONFIG_PATH = [
2022
1889
  ];
2023
1890
  const MIGRATION_REASON_PLUGIN_EXISTS = "plugin exists";
2024
1891
  const CODEX_PLUGIN_SOURCE_APP_VERIFICATION_UNVERIFIED = "not_run";
2025
- function uniquePluginConfigKey(plugin, counts, usedCounts) {
2026
- const base = sanitizeName(plugin.pluginName ?? plugin.name) || "codex-plugin";
1892
+ const MIGRATION_REASON_TARGET_NOT_REGULAR = "target is not a regular file";
1893
+ async function lstatIfExists(filePath) {
1894
+ try {
1895
+ return await fs.lstat(filePath);
1896
+ } catch (error) {
1897
+ const code = extractErrorCode(error);
1898
+ if (code === "ENOENT" || code === "ENOTDIR") return;
1899
+ throw error;
1900
+ }
1901
+ }
1902
+ async function buildCodexMemoryItems(params) {
1903
+ const items = [];
1904
+ for (const memory of params.memoryFiles) {
1905
+ const target = path.join(params.workspaceDir, "memory", "imports", "codex", path.basename(memory.path));
1906
+ const targetStat = await lstatIfExists(target);
1907
+ const targetNotRegular = targetStat !== void 0 && !targetStat.isFile();
1908
+ if (!targetNotRegular) {
1909
+ const [source, workspace, destination] = await Promise.all([
1910
+ fs.realpath(path.dirname(memory.path)),
1911
+ canonicalPathFromExistingAncestor(params.workspaceDir),
1912
+ canonicalPathFromExistingAncestor(target)
1913
+ ]);
1914
+ if (!isPathInside(workspace, destination)) throw new Error("Codex memory import destination must stay in the selected workspace.");
1915
+ if (isPathInside(source, destination) || isPathInside(destination, source)) throw new Error("Codex memory source and OpenClaw import destination must be separate paths.");
1916
+ }
1917
+ const targetConflict = targetStat !== void 0 && !params.overwrite;
1918
+ items.push(createMigrationItem({
1919
+ id: memory.id,
1920
+ kind: "memory",
1921
+ action: "copy",
1922
+ source: memory.path,
1923
+ target,
1924
+ status: targetNotRegular || targetConflict ? "conflict" : "planned",
1925
+ reason: targetNotRegular ? MIGRATION_REASON_TARGET_NOT_REGULAR : targetConflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
1926
+ message: "Copy consolidated Codex memory into the OpenClaw memory index.",
1927
+ details: {
1928
+ sourceType: "codex-memory",
1929
+ sourceLabel: memory.label,
1930
+ collectionId: "codex",
1931
+ collectionLabel: "Codex",
1932
+ relativePath: path.basename(memory.path)
1933
+ }
1934
+ }));
1935
+ }
1936
+ return items;
1937
+ }
1938
+ function uniqueSkillName(skill, counts) {
1939
+ const base = sanitizeName(skill.name) || "codex-skill";
2027
1940
  if ((counts.get(base) ?? 0) <= 1) return base;
2028
- const next = (usedCounts.get(base) ?? 0) + 1;
2029
- usedCounts.set(base, next);
2030
- return sanitizeName(`${base}-${next}`) || base;
1941
+ return sanitizeName([
1942
+ "codex",
1943
+ sanitizeName(path.basename(path.dirname(skill.source))),
1944
+ base
1945
+ ].filter(Boolean).join("-")) || base;
1946
+ }
1947
+ async function buildCodexSkillItems(params) {
1948
+ const counts = /* @__PURE__ */ new Map();
1949
+ for (const skill of params.skills) {
1950
+ const base = sanitizeName(skill.name) || "codex-skill";
1951
+ counts.set(base, (counts.get(base) ?? 0) + 1);
1952
+ }
1953
+ const planned = params.skills.map((skill) => {
1954
+ const name = uniqueSkillName(skill, counts);
1955
+ return {
1956
+ skill,
1957
+ name,
1958
+ target: path.join(params.workspaceDir, "skills", name)
1959
+ };
1960
+ });
1961
+ const resolvedCounts = planned.reduce((resolved, item) => {
1962
+ resolved.set(item.name, (resolved.get(item.name) ?? 0) + 1);
1963
+ return resolved;
1964
+ }, /* @__PURE__ */ new Map());
1965
+ return await Promise.all(planned.map(async (item) => {
1966
+ const collision = (resolvedCounts.get(item.name) ?? 0) > 1;
1967
+ const targetExists = await exists(item.target);
1968
+ const conflict = collision || targetExists && !params.overwrite;
1969
+ return createMigrationItem({
1970
+ id: `skill:${item.name}`,
1971
+ kind: "skill",
1972
+ action: "copy",
1973
+ source: item.skill.source,
1974
+ target: item.target,
1975
+ status: conflict ? "conflict" : "planned",
1976
+ reason: collision ? `multiple Codex skills normalize to "${item.name}"` : conflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
1977
+ message: `Copy ${item.skill.sourceLabel} into this OpenClaw agent workspace.`,
1978
+ details: {
1979
+ skillName: item.name,
1980
+ sourceLabel: item.skill.sourceLabel
1981
+ }
1982
+ });
1983
+ }));
2031
1984
  }
2032
1985
  function readExistingCodexPluginEntries(config) {
2033
1986
  const entries = readMigrationConfigPath(config, [...CODEX_PLUGIN_NATIVE_CONFIG_PATH, "plugins"]);
@@ -2058,18 +2011,12 @@ function readExistingPluginAllowDestructiveActions(existing, pluginName) {
2058
2011
  return normalized === "auto" || normalized === "ask" ? normalized : void 0;
2059
2012
  }
2060
2013
  function buildPluginItems(ctx, plugins) {
2061
- const baseCounts = /* @__PURE__ */ new Map();
2062
- for (const plugin of plugins.filter((entry) => entry.migratable)) {
2063
- const base = sanitizeName(plugin.pluginName ?? plugin.name) || "codex-plugin";
2064
- baseCounts.set(base, (baseCounts.get(base) ?? 0) + 1);
2065
- }
2066
2014
  const existingPluginEntries = readExistingCodexPluginEntries(ctx.config);
2067
- const usedCounts = /* @__PURE__ */ new Map();
2068
2015
  let manualIndex = 0;
2069
2016
  const items = [];
2070
2017
  for (const plugin of plugins) {
2071
2018
  if (plugin.migratable && plugin.marketplaceName === "openai-curated" && plugin.pluginName) {
2072
- const configKey = uniquePluginConfigKey(plugin, baseCounts, usedCounts);
2019
+ const configKey = plugin.pluginName;
2073
2020
  const plannedEntry = {
2074
2021
  enabled: true,
2075
2022
  marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
@@ -2086,6 +2033,7 @@ function buildPluginItems(ctx, plugins) {
2086
2033
  action: "install",
2087
2034
  status: conflict ? "conflict" : "planned",
2088
2035
  reason: conflict ? MIGRATION_REASON_PLUGIN_EXISTS : void 0,
2036
+ applyPhase: "after-promotion",
2089
2037
  source: plugin.source,
2090
2038
  target: `plugins.entries.codex.config.codexPlugins.plugins.${configKey}`,
2091
2039
  message: `Install Codex plugin "${plugin.pluginName}" in the OpenClaw-managed Codex app-server runtime.`,
@@ -2103,12 +2051,6 @@ function buildPluginItems(ctx, plugins) {
2103
2051
  }
2104
2052
  manualIndex += 1;
2105
2053
  if (plugin.migrationBlock && plugin.pluginName) {
2106
- const details = {
2107
- pluginName: plugin.pluginName,
2108
- marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
2109
- ...plugin.migrationBlock.apps ? { apps: plugin.migrationBlock.apps } : {},
2110
- ...plugin.migrationBlock.error ? { error: plugin.migrationBlock.error } : {}
2111
- };
2112
2054
  items.push(createMigrationItem({
2113
2055
  id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${manualIndex}`,
2114
2056
  kind: "manual",
@@ -2117,7 +2059,12 @@ function buildPluginItems(ctx, plugins) {
2117
2059
  status: "skipped",
2118
2060
  reason: plugin.migrationBlock.code,
2119
2061
  message: plugin.message ?? `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
2120
- details: { ...details }
2062
+ details: {
2063
+ pluginName: plugin.pluginName,
2064
+ marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
2065
+ ...plugin.migrationBlock.apps ? { apps: plugin.migrationBlock.apps } : {},
2066
+ ...plugin.migrationBlock.error ? { error: plugin.migrationBlock.error } : {}
2067
+ }
2121
2068
  }));
2122
2069
  continue;
2123
2070
  }
@@ -2155,7 +2102,6 @@ function normalizeExistingAllowDestructiveActions(value) {
2155
2102
  return asBoolean(value);
2156
2103
  }
2157
2104
  function readExistingPluginPolicyRepairs(config) {
2158
- if (config === void 0) return {};
2159
2105
  return Object.fromEntries(Object.entries(readExistingCodexPluginEntries(config)).flatMap(([configKey, entry]) => {
2160
2106
  const pluginEntry = isRecord(entry) ? entry : void 0;
2161
2107
  if (pluginEntry?.allow_destructive_actions !== "on-request") return [];
@@ -2165,9 +2111,9 @@ function readExistingPluginPolicyRepairs(config) {
2165
2111
  }]];
2166
2112
  }));
2167
2113
  }
2168
- function buildCodexPluginsConfigValue(entries, params = {}) {
2114
+ function buildCodexPluginsConfigValue(entries, config) {
2169
2115
  const plugins = {
2170
- ...readExistingPluginPolicyRepairs(params.config),
2116
+ ...readExistingPluginPolicyRepairs(config),
2171
2117
  ...Object.fromEntries(entries.toSorted((a, b) => a.configKey.localeCompare(b.configKey)).map((entry) => [entry.configKey, {
2172
2118
  enabled: entry.enabled,
2173
2119
  marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
@@ -2179,7 +2125,7 @@ function buildCodexPluginsConfigValue(entries, params = {}) {
2179
2125
  enabled: true,
2180
2126
  config: { codexPlugins: {
2181
2127
  enabled: true,
2182
- allow_destructive_actions: params.config === void 0 ? true : readExistingAllowDestructiveActions(params.config) ?? true,
2128
+ allow_destructive_actions: readExistingAllowDestructiveActions(config) ?? true,
2183
2129
  plugins
2184
2130
  } }
2185
2131
  };
@@ -2206,7 +2152,7 @@ function hasCodexPluginConfigConflict(config, value) {
2206
2152
  function buildPluginConfigItem(ctx, pluginItems) {
2207
2153
  const entries = pluginItems.filter((item) => item.status === "planned").map((item) => readCodexPluginMigrationConfigEntry(item, true)).filter((entry) => entry !== void 0);
2208
2154
  if (entries.length === 0) return;
2209
- const value = buildCodexPluginsConfigValue(entries, { config: ctx.config });
2155
+ const value = buildCodexPluginsConfigValue(entries, ctx.config);
2210
2156
  const conflict = !ctx.overwrite && hasCodexPluginConfigConflict(ctx.config, value);
2211
2157
  return createMigrationItem({
2212
2158
  id: CODEX_PLUGIN_CONFIG_ITEM_ID,
@@ -2215,6 +2161,7 @@ function buildPluginConfigItem(ctx, pluginItems) {
2215
2161
  target: "plugins.entries.codex.config.codexPlugins",
2216
2162
  status: conflict ? "conflict" : "planned",
2217
2163
  reason: conflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
2164
+ applyPhase: "after-promotion",
2218
2165
  message: "Enable OpenClaw's Codex plugin integration and record migrated source-installed curated plugins.",
2219
2166
  details: {
2220
2167
  path: [...CODEX_PLUGIN_CONFIG_PATH],
@@ -2331,15 +2278,9 @@ async function applyCodexMigrationPlan(params) {
2331
2278
  const targets = resolveCodexMigrationTargets(params.ctx);
2332
2279
  const codexHome = typeof plan.metadata?.codexHome === "string" && plan.metadata.codexHome.trim() ? plan.metadata.codexHome : plan.source;
2333
2280
  const authSource = {
2334
- root: plan.source,
2335
- confidence: "high",
2336
2281
  codexHome,
2337
2282
  authPath: path.join(codexHome, "auth.json"),
2338
- modelsCachePath: path.join(codexHome, "models_cache.json"),
2339
- memoryFiles: [],
2340
- skills: [],
2341
- plugins: [],
2342
- archivePaths: []
2283
+ modelsCachePath: path.join(codexHome, "models_cache.json")
2343
2284
  };
2344
2285
  const runtime = withCachedMigrationConfigRuntime(params.ctx.runtime ?? params.runtime, params.ctx.config);
2345
2286
  const applyCtx = {
@@ -2352,20 +2293,13 @@ async function applyCodexMigrationPlan(params) {
2352
2293
  continue;
2353
2294
  }
2354
2295
  if (item.id === "config:codex-plugins") items.push(await applyCodexPluginConfigItem(applyCtx, item, items));
2355
- else if (item.kind === "auth") {
2356
- const authItem = await applyCodexAuthItem({
2357
- ctx: applyCtx,
2358
- item,
2359
- source: authSource,
2360
- targets
2361
- });
2362
- items.push(authItem);
2363
- items.push(...await buildCodexAuthConfigPatchItems({
2364
- ctx: applyCtx,
2365
- item: authItem,
2366
- source: authSource
2367
- }));
2368
- } else if (item.kind === "plugin" && item.action === "install") items.push(await applyCodexPluginInstallItem(applyCtx, item));
2296
+ else if (item.kind === "auth") items.push(...await applyCodexAuthItems({
2297
+ ctx: applyCtx,
2298
+ item,
2299
+ source: authSource,
2300
+ targets
2301
+ }));
2302
+ else if (item.kind === "plugin" && item.action === "install") items.push(await applyCodexPluginInstallItem(applyCtx, item));
2369
2303
  else if (item.kind === "manual") items.push(applyMigrationManualItem(item));
2370
2304
  else if (item.action === "archive") items.push(await archiveMigrationItem(item, reportDir));
2371
2305
  else if (item.kind === "memory") items.push(await copyMemoryMigrationFileItem(item, reportDir, {
@@ -2542,13 +2476,21 @@ async function buildTargetCodexPluginAppCacheKey(ctx) {
2542
2476
  });
2543
2477
  }
2544
2478
  async function applyCodexPluginConfigItem(ctx, item, appliedItems) {
2479
+ if (appliedItems.filter((candidate) => candidate.kind === "plugin" && candidate.action === "install" && readCodexPluginPolicy(candidate) !== void 0 && !isCodexPluginConfigTerminal(candidate)).length > 0) return {
2480
+ ...item,
2481
+ status: "warning",
2482
+ reason: "selected Codex plugin activation is incomplete"
2483
+ };
2545
2484
  const entries = appliedItems.map(readAppliedPluginConfigEntry).filter((entry) => entry !== void 0);
2546
- if (entries.length === 0) return markMigrationItemSkipped(item, "no selected Codex plugins");
2485
+ if (entries.length === 0) return {
2486
+ ...markMigrationItemSkipped(item, "no selected Codex plugins"),
2487
+ deferredCompletion: true
2488
+ };
2547
2489
  const returnPatch = shouldReturnCodexPluginConfigPatch(ctx);
2548
- const configApi = ctx.runtime?.config;
2490
+ const configApi = resolveMigrationConfigRuntime(ctx);
2549
2491
  const currentConfig = returnPatch ? ctx.config : configApi?.current?.();
2550
2492
  if (!currentConfig) return markMigrationItemError(item, "config runtime unavailable");
2551
- const value = buildCodexPluginsConfigValue(entries, { config: currentConfig });
2493
+ const value = buildCodexPluginsConfigValue(entries, currentConfig);
2552
2494
  if (!ctx.overwrite && hasCodexPluginConfigConflict(currentConfig, value)) return markMigrationItemConflict(item, MIGRATION_REASON_TARGET_EXISTS);
2553
2495
  const migratedItem = {
2554
2496
  ...item,
@@ -2576,19 +2518,20 @@ async function applyCodexPluginConfigItem(ctx, item, appliedItems) {
2576
2518
  return markMigrationItemError(item, error instanceof Error ? error.message : String(error));
2577
2519
  }
2578
2520
  }
2521
+ function isCodexPluginConfigTerminal(item) {
2522
+ return item.status === "migrated" || item.status === "skipped" && (item.deferredCompletion === true || item.reason === CODEX_PLUGIN_NOT_SELECTED_REASON || item.reason === CODEX_PLUGIN_AUTH_REQUIRED_REASON);
2523
+ }
2579
2524
  function readAppliedPluginConfigEntry(item) {
2580
- if (item.status === "migrated") return readCodexPluginMigrationConfigEntry(item, true);
2581
- if (item.status === "skipped" && item.reason !== CODEX_PLUGIN_NOT_SELECTED_REASON && item.reason === CODEX_PLUGIN_AUTH_REQUIRED_REASON) return readCodexPluginMigrationConfigEntry(item, false);
2525
+ if (item.status === "migrated" || item.deferredCompletion === true) return readCodexPluginMigrationConfigEntry(item, true);
2526
+ if (item.status === "skipped" && item.reason === CODEX_PLUGIN_AUTH_REQUIRED_REASON) return readCodexPluginMigrationConfigEntry(item, false);
2582
2527
  }
2583
2528
  function readCodexPluginPolicy(item) {
2584
- const configKey = item.details?.configKey;
2585
- const marketplaceName = item.details?.marketplaceName;
2586
- const pluginName = item.details?.pluginName;
2587
- if (typeof configKey !== "string" || marketplaceName !== "openai-curated" || typeof pluginName !== "string") return;
2529
+ const entry = readCodexPluginMigrationConfigEntry(item, true);
2530
+ if (!entry) return;
2588
2531
  return {
2589
- configKey,
2532
+ configKey: entry.configKey,
2590
2533
  marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
2591
- pluginName,
2534
+ pluginName: entry.pluginName,
2592
2535
  enabled: true,
2593
2536
  allowDestructiveActions: true,
2594
2537
  destructiveApprovalMode: "allow"
@@ -2620,6 +2563,7 @@ function buildCodexMigrationProvider(params = {}) {
2620
2563
  };
2621
2564
  },
2622
2565
  plan: buildCodexMigrationPlan,
2566
+ deferredApply: { retrySafe: true },
2623
2567
  prepareApply(ctx) {
2624
2568
  if (isMemoryOnlyMigration(ctx)) return;
2625
2569
  return prepareTargetCodexAppServer(ctx);
@@ -2801,7 +2745,7 @@ function createCodexThreadsTool(options) {
2801
2745
  return {
2802
2746
  name: "codex_threads",
2803
2747
  label: "Codex Threads",
2804
- description: "List and inspect native Codex threads. When supervision is enabled, raw transcript reads and every mutation require their matching supervision policy option.",
2748
+ description: "Manage native Codex threads: list, read, fork, rename, archive (confirm:true), unarchive. When supervision is enabled, raw transcript reads and every mutation require their matching supervision policy option.",
2805
2749
  parameters: CodexThreadsParamsSchema,
2806
2750
  async execute(_toolCallId, rawParams) {
2807
2751
  const params = asRecord$1(rawParams);
@@ -3726,7 +3670,7 @@ function createCodexSupervisionTools(options) {
3726
3670
  }
3727
3671
  //#endregion
3728
3672
  //#region extensions/codex/src/web-search-provider.ts
3729
- const loadCodexWebSearchRuntime = createLazyRuntimeModule(() => import("./web-search-provider.runtime-_x5XTeqh.js"));
3673
+ const loadCodexWebSearchRuntime = createLazyRuntimeModule(() => import("./web-search-provider.runtime-CaQMYDbp.js"));
3730
3674
  const CodexWebSearchSchema = {
3731
3675
  type: "object",
3732
3676
  properties: { query: {
@@ -3780,11 +3724,39 @@ var codex_default = definePluginEntry({
3780
3724
  origin: "bundled",
3781
3725
  config: normalizePluginsConfig(liveConfig.plugins),
3782
3726
  rootConfig: liveConfig,
3783
- enabledByDefault: readCodexPluginConfig(livePluginConfig).supervision?.enabled === true
3727
+ enabledByDefault: livePluginConfig !== void 0
3784
3728
  }).enabled) return;
3785
3729
  return livePluginConfig;
3786
3730
  };
3787
3731
  const resolveCurrentPluginConfig = () => resolvePluginConfig(resolveCurrentConfig);
3732
+ if (api.registrationMode === "full") {
3733
+ const realtimeBrowserSession = configureCodexRealtimeBrowserSession({
3734
+ getConfig: resolveCurrentConfig,
3735
+ getPluginConfig: resolveCurrentPluginConfig
3736
+ });
3737
+ api.registerHttpRoute({
3738
+ path: CODEX_REALTIME_OFFER_PATH,
3739
+ auth: "plugin",
3740
+ match: "exact",
3741
+ handler: realtimeBrowserSession.handler
3742
+ });
3743
+ api.registerService({
3744
+ id: "codex-oauth-realtime-browser-session-warmup",
3745
+ start: () => {
3746
+ realtimeBrowserSession.warmup().catch((error) => {
3747
+ api.logger.debug?.(`Codex OAuth realtime warmup unavailable: ${error instanceof Error ? error.message : String(error)}`);
3748
+ });
3749
+ }
3750
+ });
3751
+ api.lifecycle.registerRuntimeLifecycle({
3752
+ id: "codex-oauth-realtime-browser-session",
3753
+ description: "Release Codex OAuth realtime browser sessions when the plugin stops",
3754
+ cleanup: async ({ reason }) => {
3755
+ if (reason === "reset" || reason === "delete") return;
3756
+ await realtimeBrowserSession.cleanup();
3757
+ }
3758
+ });
3759
+ }
3788
3760
  let bindingStateStore;
3789
3761
  const openBindingStateStore = () => bindingStateStore ??= api.runtime.state.openSyncKeyedStore({
3790
3762
  namespace: CODEX_APP_SERVER_BINDING_NAMESPACE,
@@ -3825,8 +3797,10 @@ var codex_default = definePluginEntry({
3825
3797
  }, { names: [...CODEX_SUPERVISION_COMPAT_TOOL_NAMES] });
3826
3798
  api.registerAgentHarness(createCodexAppServerAgentHarness({
3827
3799
  bindingStore,
3800
+ sessionCatalogControl,
3828
3801
  resolveConfig: resolveCurrentConfig,
3829
- resolvePluginConfig: resolveCurrentPluginConfig
3802
+ resolvePluginConfig: resolveCurrentPluginConfig,
3803
+ runtime: api.runtime
3830
3804
  }));
3831
3805
  api.registerMediaUnderstandingProvider(buildCodexMediaUnderstandingProvider({ pluginConfig: api.pluginConfig }));
3832
3806
  api.registerWebSearchProvider(createCodexWebSearchProvider({ resolvePluginConfig: resolveCurrentPluginConfig }));
@@ -3914,7 +3888,7 @@ var codex_default = definePluginEntry({
3914
3888
  if (!previousSessionId || !sessionId || previousSessionId === sessionId) return;
3915
3889
  const config = resolveCurrentConfig();
3916
3890
  const sessionKey = ctx.sessionKey?.trim();
3917
- const { sessionBindingIdentity } = await import("./session-binding-Bmt20HfB.js").then((n) => n.l);
3891
+ const { sessionBindingIdentity } = await import("./session-binding-DlVQ-Pxs.js").then((n) => n.u);
3918
3892
  const identity = sessionBindingIdentity({
3919
3893
  sessionId,
3920
3894
  ...sessionKey ? { sessionKey } : {},
@@ -3930,8 +3904,9 @@ var codex_default = definePluginEntry({
3930
3904
  const endedSessionKey = sessionKey?.trim();
3931
3905
  const nextSessionKey = event.nextSessionKey?.trim();
3932
3906
  if (endedSessionKey && nextSessionKey && nextSessionKey !== endedSessionKey) return;
3907
+ if (event.nextSessionId?.trim() === event.sessionId.trim()) return;
3933
3908
  const config = resolveCurrentConfig();
3934
- const { sessionBindingIdentity } = await import("./session-binding-Bmt20HfB.js").then((n) => n.l);
3909
+ const { sessionBindingIdentity } = await import("./session-binding-DlVQ-Pxs.js").then((n) => n.u);
3935
3910
  await bindingStore.retireSessionGeneration(sessionBindingIdentity({
3936
3911
  sessionId: event.sessionId,
3937
3912
  ...sessionKey ? { sessionKey } : {},