@openclaw/codex 2026.7.2-beta.5 → 2026.7.33

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 (66) hide show
  1. package/README.md +3 -27
  2. package/dist/{command-formatters-ico4ZFnk.js → .setup/app-server-policy-C_qTMEMy.mjs} +51 -9
  3. package/dist/{attempt-notifications-CMR2_fmR.js → .setup/attempt-notifications-F8krwWL4.mjs} +6 -3
  4. package/dist/{command-handlers-CRmnwI2N.js → .setup/command-handlers-DSRFfor4.mjs} +1156 -1319
  5. package/dist/.setup/command-rpc-BvYsfQ7G.mjs +786 -0
  6. package/dist/{compact-dEN49UH6.js → .setup/compact-BNgh7I4t.mjs} +10 -40
  7. package/dist/.setup/computer-use-C3QkmSO3.mjs +377 -0
  8. package/dist/.setup/config-CYEDnLJ2.mjs +1138 -0
  9. package/dist/{conversation-binding-data-BHRpYbX3.js → .setup/conversation-binding-data-CrRxtDBu.mjs} +1 -1
  10. package/dist/{bounded-turn-FEbUXoYT.js → .setup/media-understanding-provider-EbJCyDqe.mjs} +193 -92
  11. package/dist/{models-BA01NEez.js → .setup/models-C_aZk03A.mjs} +11 -6
  12. package/dist/.setup/notification-correlation-iYs4WieS.mjs +35 -0
  13. package/dist/.setup/plugin-app-cache-key-DR8jV9oS.mjs +99 -0
  14. package/dist/.setup/protocol-2POPqAY4.mjs +13 -0
  15. package/dist/{protocol-validators-Dm4eXZmt.js → .setup/protocol-validators-dZQ-UTOa.mjs} +111 -334
  16. package/dist/{rate-limits-S_PXs0ul.js → .setup/provider-DGxDs6g8.mjs} +269 -13
  17. package/dist/{dynamic-tools-LIt9ojep.js → .setup/provider-capabilities-DS2N_zc1.mjs} +5465 -3399
  18. package/dist/.setup/request-DFR0JyoV.mjs +36 -0
  19. package/dist/.setup/run-attempt-C7xEKFVB.mjs +8180 -0
  20. package/dist/{sandbox-guard-D1Wky__S.js → .setup/sandbox-guard-DA2TQfZW.mjs} +1 -10
  21. package/dist/.setup/session-binding-DuihlMJc.mjs +628 -0
  22. package/dist/.setup/shared-client-B2KmLPRw.mjs +1854 -0
  23. package/dist/{side-question-CO12sHxo.js → .setup/side-question-Cv6JXWCS.mjs} +84 -159
  24. package/dist/{thread-lifecycle-DRg_FdzC.js → .setup/thread-lifecycle-u6CZdOOH.mjs} +1159 -1799
  25. package/dist/{web-search-provider.runtime-CaQMYDbp.js → .setup/web-search-provider.runtime-Ce2zOHxW.mjs} +3 -3
  26. package/dist/doctor-contract-api.js +122 -88
  27. package/dist/harness.js +20 -85
  28. package/dist/index.js +381 -1537
  29. package/dist/media-understanding-provider.js +1 -173
  30. package/dist/prompt-overlay.js +15 -0
  31. package/dist/provider-catalog.js +79 -0
  32. package/dist/provider-discovery.js +34 -0
  33. package/dist/provider.js +2 -0
  34. package/dist/web-search-contract-api.js +1 -1
  35. package/openclaw.plugin.json +178 -221
  36. package/package.json +8 -10
  37. package/dist/app-server-policy-BmDgseo_.js +0 -49
  38. package/dist/attempt-client-cleanup-Ia52yRWc.js +0 -127
  39. package/dist/auth-binding-B4FfbcCg.js +0 -62
  40. package/dist/capabilities-BHRSI6Cn.js +0 -36
  41. package/dist/cli-metadata.js +0 -20
  42. package/dist/computer-use-BjGylO-E.js +0 -695
  43. package/dist/conversation-control-DE7d9DOj.js +0 -287
  44. package/dist/event-projector-assistant-message-DTgnPPVF.js +0 -101
  45. package/dist/event-projector-usage-tvgb1NCX.js +0 -69
  46. package/dist/incognito-session-ET_ufDH-.js +0 -2
  47. package/dist/notification-correlation-Bj_Ak_jI.js +0 -67
  48. package/dist/plugin-app-cache-key-DSXX-rXO.js +0 -315
  49. package/dist/realtime-voice-api-BPcA1uN9.js +0 -494
  50. package/dist/realtime-voice-api.js +0 -2
  51. package/dist/request-CXpPennK.js +0 -157
  52. package/dist/run-attempt-C5GTPJOE.js +0 -11935
  53. package/dist/runtime-artifact-C2ITXdhM.js +0 -488
  54. package/dist/session-binding-DlVQ-Pxs.js +0 -2161
  55. package/dist/session-catalog-CP2oCcEb.js +0 -2444
  56. package/dist/session-cli-CqZ0Fp2a.js +0 -175
  57. package/dist/session-upstream-marker-QE-m5QFG.js +0 -82
  58. package/dist/settled-turn-finalizer-CA_2QXIE.js +0 -303
  59. package/dist/shared-client-BVKE40c-.js +0 -2952
  60. package/dist/transcript-mirror-uKA-A4tB.js +0 -550
  61. package/dist/transport-stdio-DXgGZ7Ib.js +0 -88
  62. package/dist/upstream-session-fork-_Vrz4WJr.js +0 -320
  63. package/dist/usage-UpVbdX5G.js +0 -25
  64. /package/dist/{rolldown-runtime-8H4AJuhK.js → .setup/rolldown-runtime-8H4AJuhK.mjs} +0 -0
  65. /package/dist/{session-binding-meta-B7aEMU7g.js → .setup/session-binding-meta-B7aEMU7g.mjs} +0 -0
  66. /package/dist/{web-search-provider.shared-BrZmlqyR.js → .setup/web-search-provider.shared-BrZmlqyR.mjs} +0 -0
package/dist/index.js CHANGED
@@ -1,86 +1,59 @@
1
- import { registerCodexCliMetadata } from "./cli-metadata.js";
2
1
  import { createCodexAppServerAgentHarness } from "./harness.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";
5
- import { n as CODEX_APP_SERVER_BINDING_NAMESPACE, t as CODEX_APP_SERVER_BINDING_MAX_ENTRIES } from "./session-binding-meta-B7aEMU7g.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";
16
- import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "./sandbox-guard-D1Wky__S.js";
17
- import { d as CODEX_NATIVE_EXECUTION_AUTH_ERROR, f as canMutateCodexHost, u as trackCodexConversationActiveTurn } from "./conversation-control-DE7d9DOj.js";
18
- import { a as readCodexConversationBindingDataRecord, i as readCodexConversationBindingData, o as resolveCodexDefaultWorkspaceDir, r as createCodexConversationBindingData } from "./conversation-binding-data-BHRpYbX3.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";
22
- import { t as createCodexWebSearchProviderBase } from "./web-search-provider.shared-BrZmlqyR.js";
2
+ import { r as isJsonObject, t as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS } from "./.setup/protocol-2POPqAY4.mjs";
3
+ import { d as resolveCodexAppServerRuntimeOptions, g as resolveOpenClawExecPolicyForCodexAppServer, i as codexSandboxPolicyForTurn, n as canUseCodexModelBackedApprovalsReviewerForModel, t as CODEX_PLUGINS_MARKETPLACE_NAME, u as readCodexPluginConfig } from "./.setup/config-CYEDnLJ2.mjs";
4
+ import { r as assertCodexThreadStartResponse } from "./.setup/protocol-validators-dZQ-UTOa.mjs";
5
+ import { G as pluginReadParams, W as ensureCodexPluginActivation, f as resolveCodexAppServerRequestModelSelection, q as defaultCodexAppInventoryCache, t as CODEX_NATIVE_PERSONALITY_NONE } from "./.setup/thread-lifecycle-u6CZdOOH.mjs";
6
+ import { t as buildCodexProvider } from "./.setup/provider-DGxDs6g8.mjs";
7
+ import { _ as resolveCodexAppServerAuthAccountCacheKey, a as getLeasedSharedCodexAppServerClient, b as resolveCodexAppServerFallbackApiKeyCacheKey, o as releaseLeasedSharedCodexAppServerClient, r as clearSharedCodexAppServerClientIfCurrentAndWait, y as resolveCodexAppServerAuthProfileIdForAgent } from "./.setup/shared-client-B2KmLPRw.mjs";
8
+ import { n as CODEX_APP_SERVER_BINDING_NAMESPACE, t as CODEX_APP_SERVER_BINDING_MAX_ENTRIES } from "./.setup/session-binding-meta-B7aEMU7g.mjs";
9
+ import { a as normalizeCodexAppServerBindingModelProvider, i as isCodexAppServerNativeAuthProfile, s as sessionBindingIdentity } from "./.setup/session-binding-DuihlMJc.mjs";
10
+ import { t as buildCodexMediaUnderstandingProvider } from "./.setup/media-understanding-provider-EbJCyDqe.mjs";
11
+ import { i as describeControlFailure, n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS } from "./.setup/plugin-app-cache-key-DR8jV9oS.mjs";
12
+ import { a as formatCodexDisplayText, t as resolveCodexAppServerForModelProvider } from "./.setup/app-server-policy-C_qTMEMy.mjs";
13
+ import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "./.setup/sandbox-guard-DA2TQfZW.mjs";
14
+ import { S as canMutateCodexHost, a as createCodexCliSessionNodeHostCommands, b as trackCodexConversationActiveTurn, c as listCodexCliSessionsOnNode, l as resolveCodexCliSessionForBindingOnNode, o as createCodexCliSessionNodeInvokePolicies, t as codexControlRequest, u as resumeCodexCliSessionOnNode, x as CODEX_NATIVE_EXECUTION_AUTH_ERROR } from "./.setup/command-rpc-BvYsfQ7G.mjs";
15
+ import { a as readCodexConversationBindingDataRecord, i as readCodexConversationBindingData } from "./.setup/conversation-binding-data-CrRxtDBu.mjs";
16
+ import { n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./.setup/notification-correlation-iYs4WieS.mjs";
17
+ import { t as requestCodexAppServerJson } from "./.setup/request-DFR0JyoV.mjs";
18
+ import { t as createCodexWebSearchProviderBase } from "./.setup/web-search-provider.shared-BrZmlqyR.mjs";
23
19
  import { mutateConfigFile } from "openclaw/plugin-sdk/config-mutation";
24
- import { normalizePluginsConfig, resolveEffectiveEnableState, resolveLivePluginConfigObject, resolvePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
20
+ import { resolveLivePluginConfigObject, resolvePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
25
21
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
26
22
  import fs from "node:fs/promises";
27
23
  import path from "node:path";
28
24
  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";
31
25
  import os from "node:os";
32
- import { applyAuthProfileConfig, buildApiKeyCredential, buildOauthProviderAuthResult, buildOpenAICodexCredentialExtra, readCodexCliCredentialsCached, resolveOpenAICodexAuthIdentity, resolveOpenAICodexImportProfileName, updateAuthProfileStoreWithLock } from "openclaw/plugin-sdk/provider-auth";
26
+ import { loadAuthProfileStoreWithoutExternalProfiles, resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentId, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
33
27
  import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
34
28
  import { asBoolean, asOptionalRecord, isRecord, normalizeOptionalString, normalizeSingleOrTrimmedStringList, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
35
- import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
29
+ import { formatErrorMessage, resolveSandboxContext } from "openclaw/plugin-sdk/agent-harness-runtime";
30
+ import { jsonResult, readStringParam } from "openclaw/plugin-sdk/core";
31
+ import { applyAuthProfileConfig, buildApiKeyCredential, buildOauthProviderAuthResult, buildOpenAICodexCredentialExtra, readCodexCliCredentialsCached, resolveOpenAICodexAuthIdentity, resolveOpenAICodexImportProfileName, updateAuthProfileStoreWithLock } from "openclaw/plugin-sdk/provider-auth";
36
32
  import { fileURLToPath } from "node:url";
37
- import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
38
- import { canonicalPathFromExistingAncestor, extractErrorCode, isPathInside, pathExists } from "openclaw/plugin-sdk/security-runtime";
33
+ import { pathExists } from "openclaw/plugin-sdk/security-runtime";
34
+ import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
39
35
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
40
- import { ModelSelectionLockedError, isModelSelectionLocked } from "openclaw/plugin-sdk/model-session-runtime";
41
- import { MIGRATION_REASON_TARGET_EXISTS, applyMigrationManualItem, createMigrationItem, createMigrationManualItem, hasMigrationConfigPatchConflict, markMigrationItemConflict, markMigrationItemError, markMigrationItemSkipped, mergeMigrationConfigValue, readMigrationConfigPath, resolveMigrationConfigRuntime, summarizeMigrationItems, writeMigrationConfigPath } from "openclaw/plugin-sdk/migration";
42
- import { archiveMigrationItem, copyMemoryMigrationFileItem, copyMigrationFileItem, withCachedMigrationConfigRuntime, writeMigrationReport } from "openclaw/plugin-sdk/migration-runtime";
36
+ import { MIGRATION_REASON_TARGET_EXISTS, applyMigrationManualItem, createMigrationItem, createMigrationManualItem, hasMigrationConfigPatchConflict, markMigrationItemConflict, markMigrationItemError, markMigrationItemSkipped, readMigrationConfigPath, summarizeMigrationItems, writeMigrationConfigPath } from "openclaw/plugin-sdk/migration";
37
+ import { archiveMigrationItem, copyMigrationFileItem, withCachedMigrationConfigRuntime, writeMigrationReport } from "openclaw/plugin-sdk/migration-runtime";
43
38
  import { sleep } from "openclaw/plugin-sdk/runtime-env";
44
39
  import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
45
- import { jsonResult, readStringParam } from "openclaw/plugin-sdk/core";
46
40
  import { Type } from "typebox";
47
41
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
48
42
  //#region extensions/codex/src/app-server/session-binding-store.ts
49
43
  /** Defers schema compilation and auth loading until the first binding operation. */
50
44
  function createLazyCodexAppServerBindingStore(state) {
51
45
  let resolved;
52
- const store = () => resolved ??= import("./session-binding-DlVQ-Pxs.js").then((n) => n.u).then(({ createCodexAppServerBindingStore }) => createCodexAppServerBindingStore(state));
46
+ const store = () => resolved ??= import("./.setup/session-binding-DuihlMJc.mjs").then((n) => n.c).then(({ createCodexAppServerBindingStore }) => createCodexAppServerBindingStore(state));
53
47
  return {
54
48
  read: async (identity) => (await store()).read(identity),
55
- hasOtherThreadOwner: async (threadId, currentIdentity) => (await store()).hasOtherThreadOwner(threadId, currentIdentity),
56
49
  mutate: async (identity, mutation) => (await store()).mutate(identity, mutation),
57
50
  prepareSessionGenerationReclaim: async (identity) => (await store()).prepareSessionGenerationReclaim(identity),
58
51
  adoptSessionGeneration: async (identity, previousSessionId) => (await store()).adoptSessionGeneration(identity, previousSessionId),
59
- resetSessionGeneration: async (identity) => (await store()).resetSessionGeneration(identity),
60
52
  retireSessionGeneration: async (identity) => (await store()).retireSessionGeneration(identity),
61
- withThreadArchiveFence: async (run) => (await store()).withThreadArchiveFence(run),
62
53
  withLease: async (identity, run) => (await store()).withLease(identity, run)
63
54
  };
64
55
  }
65
56
  //#endregion
66
- //#region extensions/codex/src/command-dispatch.ts
67
- /** Dispatches a `/codex` command to the lazily loaded handler. */
68
- async function handleCodexCommand(ctx, options) {
69
- const { loadSubcommandHandler, resolvePluginConfig, ...subcommandOptions } = options;
70
- try {
71
- return await (loadSubcommandHandler ? await loadSubcommandHandler() : await loadDefaultCodexSubcommandHandler())(ctx, {
72
- ...subcommandOptions,
73
- pluginConfig: resolvePluginConfig?.() ?? subcommandOptions.pluginConfig
74
- });
75
- } catch (error) {
76
- return { text: `Codex command failed: ${formatCodexDisplayText(describeControlFailure(error))}` };
77
- }
78
- }
79
- async function loadDefaultCodexSubcommandHandler() {
80
- const { handleCodexSubcommand } = await import("./command-handlers-CRmnwI2N.js");
81
- return handleCodexSubcommand;
82
- }
83
- //#endregion
84
57
  //#region extensions/codex/src/commands.ts
85
58
  /** Creates the reserved `/codex` command definition exposed by the plugin. */
86
59
  function createCodexCommand(options) {
@@ -100,6 +73,22 @@ function createCodexCommand(options) {
100
73
  handler: (ctx) => handleCodexCommand(ctx, options)
101
74
  };
102
75
  }
76
+ /** Dispatches a `/codex` command to the subcommand handler and formats failures for chat. */
77
+ async function handleCodexCommand(ctx, options) {
78
+ const { loadSubcommandHandler, resolvePluginConfig, ...subcommandOptions } = options;
79
+ try {
80
+ return await (loadSubcommandHandler ? await loadSubcommandHandler() : await loadDefaultCodexSubcommandHandler())(ctx, {
81
+ ...subcommandOptions,
82
+ pluginConfig: resolvePluginConfig?.() ?? subcommandOptions.pluginConfig
83
+ });
84
+ } catch (error) {
85
+ return { text: `Codex command failed: ${formatCodexDisplayText(describeControlFailure(error))}` };
86
+ }
87
+ }
88
+ async function loadDefaultCodexSubcommandHandler() {
89
+ const { handleCodexSubcommand } = await import("./.setup/command-handlers-DSRFfor4.mjs");
90
+ return handleCodexSubcommand;
91
+ }
103
92
  //#endregion
104
93
  //#region extensions/codex/src/conversation-turn-collector.ts
105
94
  const MAX_PENDING_NOTIFICATIONS_PER_TURN = 100;
@@ -295,7 +284,7 @@ function readLocalMediaPath(value) {
295
284
  }
296
285
  //#endregion
297
286
  //#region extensions/codex/src/conversation-binding.ts
298
- const DEFAULT_BOUND_TURN_TIMEOUT_MS = 20 * 6e4;
287
+ const DEFAULT_BOUND_TURN_TIMEOUT_MS = 12e5;
299
288
  const DEFAULT_AGENT_ID = "main";
300
289
  const VALID_AGENT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
301
290
  const INVALID_AGENT_ID_CHARS_PATTERN = /[^a-z0-9_-]+/g;
@@ -333,72 +322,6 @@ function getGlobalState() {
333
322
  globalState[CODEX_CONVERSATION_GLOBAL_STATE] ??= { queue: new KeyedAsyncQueue() };
334
323
  return globalState[CODEX_CONVERSATION_GLOBAL_STATE];
335
324
  }
336
- async function startCodexConversationThread(params) {
337
- const workspaceDir = params.workspaceDir?.trim() || resolveCodexDefaultWorkspaceDir(params.pluginConfig);
338
- const agentDir = params.agentDir?.trim();
339
- const agentLookup = buildAgentLookup({
340
- agentDir,
341
- config: params.config
342
- });
343
- const identity = sessionBindingIdentity({
344
- agentId: params.agentId,
345
- sessionId: params.sessionFile,
346
- sessionKey: params.sessionKey,
347
- config: params.config
348
- });
349
- const existingBinding = await params.bindingStore.read(identity);
350
- assertCodexBindingMayBeReplaced(existingBinding, "starting a conversation-bound Codex thread");
351
- const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
352
- authProfileId: params.authProfileId ?? existingBinding?.authProfileId,
353
- ...agentLookup
354
- });
355
- if (params.threadId?.trim()) await attachExistingThread({
356
- pluginConfig: params.pluginConfig,
357
- bindingStore: params.bindingStore,
358
- identity,
359
- threadId: params.threadId.trim(),
360
- workspaceDir,
361
- ...agentDir ? { agentDir } : {},
362
- model: params.model,
363
- modelProvider: params.modelProvider,
364
- authProfileId,
365
- approvalPolicy: params.approvalPolicy,
366
- sandbox: params.sandbox,
367
- serviceTier: params.serviceTier,
368
- config: params.config,
369
- sessionKey: params.sessionKey,
370
- agentId: params.agentId
371
- });
372
- else await createThread({
373
- pluginConfig: params.pluginConfig,
374
- bindingStore: params.bindingStore,
375
- identity,
376
- workspaceDir,
377
- ...agentDir ? { agentDir } : {},
378
- model: params.model,
379
- modelProvider: params.modelProvider,
380
- authProfileId,
381
- approvalPolicy: params.approvalPolicy,
382
- sandbox: params.sandbox,
383
- serviceTier: params.serviceTier,
384
- config: params.config,
385
- sessionKey: params.sessionKey,
386
- agentId: params.agentId
387
- });
388
- const storedBinding = await params.bindingStore.read(identity);
389
- if (!storedBinding) throw new Error("Codex session binding disappeared while starting its conversation thread.");
390
- return createCodexConversationBindingData({
391
- source: {
392
- agentId: identity.agentId,
393
- sessionId: identity.sessionId,
394
- threadId: storedBinding.threadId,
395
- ...identity.sessionKey ? { sessionKey: identity.sessionKey } : {}
396
- },
397
- workspaceDir,
398
- ...agentDir ? { agentDir } : {},
399
- agentId: params.agentId
400
- });
401
- }
402
325
  async function handleCodexConversationInboundClaim(event, ctx, options) {
403
326
  const data = readCodexConversationBindingData(ctx.pluginBinding);
404
327
  if (!data) return;
@@ -476,7 +399,6 @@ async function handleCodexConversationBindingResolved(event, options) {
476
399
  if (!data || data.kind !== "codex-app-server-session") return;
477
400
  const identity = conversationBindingIdentity(data);
478
401
  const binding = await options.bindingStore.read(identity);
479
- assertCodexBindingMayBeReplaced(binding, "clearing a denied conversation binding");
480
402
  if (!data.start?.id || binding?.conversationStartId === data.start.id) await options.bindingStore.mutate(identity, { kind: "clear" });
481
403
  }
482
404
  async function resolveThreadBindingRuntime(params) {
@@ -530,22 +452,19 @@ async function resolveThreadBindingRuntime(params) {
530
452
  agentDir: params.agentDir
531
453
  })
532
454
  });
533
- const clientOptions = {
455
+ const client = await getLeasedSharedCodexAppServerClient({
534
456
  startOptions: runtime.start,
535
457
  timeoutMs: runtime.requestTimeoutMs,
536
458
  authProfileId: params.authProfileId,
537
459
  ...agentLookup
538
- };
539
- const client = await getLeasedSharedCodexAppServerClient(clientOptions);
460
+ });
540
461
  return {
541
462
  execPolicy,
542
463
  runtime: modelScopedRuntime,
543
464
  agentLookup,
544
465
  model: modelSelection?.model,
545
466
  modelProvider: modelSelection?.modelProvider ?? modelProvider,
546
- client,
547
- clientLease: { client },
548
- clientOptions
467
+ client
549
468
  };
550
469
  }
551
470
  function buildThreadRequestRuntimeOptions(params, resolved) {
@@ -564,32 +483,22 @@ function codexConversationSandboxOrPermissions(runtime, sandbox) {
564
483
  return { sandbox };
565
484
  }
566
485
  async function requestNewConversationBindingThread(params, resolved) {
567
- return await withLeasedCodexAppServerClientStartSelectionRetry({
568
- lease: resolved.clientLease,
569
- options: resolved.clientOptions,
570
- run: async (client, requestOptions) => await client.request("thread/start", {
571
- cwd: params.workspaceDir,
572
- ...resolved.model ? { model: resolved.model } : {},
573
- ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
574
- personality: CODEX_NATIVE_PERSONALITY_NONE,
575
- ...buildThreadRequestRuntimeOptions(params, resolved),
576
- developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
577
- experimentalRawEvents: true,
578
- ...isIncognitoSessionKey(params.sessionKey) ? { ephemeral: true } : {}
579
- }, requestOptions),
580
- onClientChange: (client) => {
581
- resolved.client = client;
582
- }
583
- });
486
+ return await resolved.client.request("thread/start", {
487
+ cwd: params.workspaceDir,
488
+ ...resolved.model ? { model: resolved.model } : {},
489
+ ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
490
+ personality: CODEX_NATIVE_PERSONALITY_NONE,
491
+ ...buildThreadRequestRuntimeOptions(params, resolved),
492
+ developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
493
+ experimentalRawEvents: true
494
+ }, { timeoutMs: resolved.runtime.requestTimeoutMs });
584
495
  }
585
496
  async function writeThreadBindingFromResponse(params, resolved, response) {
586
- assertCodexBindingMayBeReplaced(await params.bindingStore.read(params.identity), "storing a conversation-bound Codex thread");
587
497
  const runtimeApprovalPolicy = typeof resolved.runtime.approvalPolicy === "string" ? resolved.runtime.approvalPolicy : void 0;
588
498
  if (!await params.bindingStore.mutate(params.identity, {
589
499
  kind: "set",
590
500
  binding: {
591
501
  threadId: response.thread.id,
592
- clientId: resolved.client.getInstanceId(),
593
502
  cwd: response.thread.cwd ?? params.workspaceDir,
594
503
  authProfileId: params.authProfileId,
595
504
  model: response.model ?? resolved.model ?? params.model,
@@ -607,34 +516,25 @@ async function writeThreadBindingFromResponse(params, resolved, response) {
607
516
  })) throw new Error("Codex conversation binding changed while storing its thread.");
608
517
  }
609
518
  async function attachExistingThread(params) {
610
- assertCodexBindingMayBeReplaced(await params.bindingStore.read(params.identity), "attaching a conversation-bound Codex thread");
611
519
  const resolved = await resolveThreadBindingRuntime(params);
612
520
  try {
613
- await writeThreadBindingFromResponse(params, resolved, resolved.runtime.networkProxy ? await requestNewConversationBindingThread(params, resolved) : await withLeasedCodexAppServerClientStartSelectionRetry({
614
- lease: resolved.clientLease,
615
- options: resolved.clientOptions,
616
- run: async (client, requestOptions) => await client.request(CODEX_CONTROL_METHODS.resumeThread, {
617
- threadId: params.threadId,
618
- ...resolved.model ? { model: resolved.model } : {},
619
- ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
620
- personality: CODEX_NATIVE_PERSONALITY_NONE,
621
- ...buildThreadRequestRuntimeOptions(params, resolved)
622
- }, requestOptions),
623
- onClientChange: (client) => {
624
- resolved.client = client;
625
- }
626
- }));
521
+ await writeThreadBindingFromResponse(params, resolved, resolved.runtime.networkProxy ? await requestNewConversationBindingThread(params, resolved) : await resolved.client.request(CODEX_CONTROL_METHODS.resumeThread, {
522
+ threadId: params.threadId,
523
+ ...resolved.model ? { model: resolved.model } : {},
524
+ ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
525
+ personality: CODEX_NATIVE_PERSONALITY_NONE,
526
+ ...buildThreadRequestRuntimeOptions(params, resolved)
527
+ }, { timeoutMs: resolved.runtime.requestTimeoutMs }));
627
528
  } finally {
628
- releaseCodexAppServerClientLease(resolved.clientLease);
529
+ releaseLeasedSharedCodexAppServerClient(resolved.client);
629
530
  }
630
531
  }
631
532
  async function createThread(params) {
632
- assertCodexBindingMayBeReplaced(await params.bindingStore.read(params.identity), "creating a conversation-bound Codex thread");
633
533
  const resolved = await resolveThreadBindingRuntime(params);
634
534
  try {
635
535
  await writeThreadBindingFromResponse(params, resolved, await requestNewConversationBindingThread(params, resolved));
636
536
  } finally {
637
- releaseCodexAppServerClientLease(resolved.clientLease);
537
+ releaseLeasedSharedCodexAppServerClient(resolved.client);
638
538
  }
639
539
  }
640
540
  async function runBoundTurn(params) {
@@ -645,7 +545,6 @@ async function runBoundTurn(params) {
645
545
  const identity = conversationBindingIdentity(params.data);
646
546
  const binding = await params.bindingStore.read(identity);
647
547
  if (!binding?.threadId) throw new Error("bound Codex conversation has no thread binding");
648
- assertCodexBindingMayBeReplaced(binding, "running a conversation-bound Codex thread");
649
548
  let threadId = binding.threadId;
650
549
  const workspaceDir = binding.cwd || params.data.workspaceDir;
651
550
  const reviewerModelProvider = resolveModelBackedReviewerPolicyProvider({
@@ -698,44 +597,33 @@ async function runBoundTurn(params) {
698
597
  authProfileId: binding.authProfileId,
699
598
  ...agentLookup
700
599
  }) : void 0;
701
- const clientOptions = {
600
+ const client = await getLeasedSharedCodexAppServerClient({
702
601
  startOptions: runtime.start,
703
602
  timeoutMs: runtime.requestTimeoutMs,
704
603
  authProfileId: binding.authProfileId,
705
604
  ...agentLookup
706
- };
707
- let client = await getLeasedSharedCodexAppServerClient(clientOptions);
708
- const clientLease = { client };
605
+ });
709
606
  let notificationCleanup = () => void 0;
710
607
  let requestCleanup = () => void 0;
711
608
  try {
712
609
  if (networkProxyBindingChanged) {
713
- const response = assertCodexThreadStartResponse(await withLeasedCodexAppServerClientStartSelectionRetry({
714
- lease: clientLease,
715
- options: clientOptions,
716
- run: async (requestClient, requestOptions) => await requestClient.request("thread/start", {
717
- cwd: workspaceDir,
718
- ...modelSelection?.model ? { model: modelSelection.model } : {},
719
- ...modelSelection?.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
720
- personality: CODEX_NATIVE_PERSONALITY_NONE,
721
- approvalPolicy,
722
- approvalsReviewer: modelScopedRuntime.approvalsReviewer,
723
- ...modelScopedRuntime.networkProxy ? { config: modelScopedRuntime.networkProxy.configPatch } : { sandbox },
724
- ...serviceTier ? { serviceTier } : {},
725
- developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
726
- experimentalRawEvents: true,
727
- ...isIncognitoSessionKey(params.sessionKey) ? { ephemeral: true } : {}
728
- }, requestOptions),
729
- onClientChange: (nextClient) => {
730
- client = nextClient;
731
- }
732
- }));
610
+ const response = assertCodexThreadStartResponse(await client.request("thread/start", {
611
+ cwd: workspaceDir,
612
+ ...modelSelection?.model ? { model: modelSelection.model } : {},
613
+ ...modelSelection?.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
614
+ personality: CODEX_NATIVE_PERSONALITY_NONE,
615
+ approvalPolicy,
616
+ approvalsReviewer: modelScopedRuntime.approvalsReviewer,
617
+ ...modelScopedRuntime.networkProxy ? { config: modelScopedRuntime.networkProxy.configPatch } : { sandbox },
618
+ ...serviceTier ? { serviceTier } : {},
619
+ developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
620
+ experimentalRawEvents: true
621
+ }, { timeoutMs: runtime.requestTimeoutMs }));
733
622
  threadId = response.thread.id;
734
623
  if (!await params.bindingStore.mutate(identity, {
735
624
  kind: "set",
736
625
  binding: {
737
626
  threadId,
738
- clientId: client.getInstanceId(),
739
627
  cwd: response.thread.cwd ?? workspaceDir,
740
628
  authProfileId: binding.authProfileId,
741
629
  model: response.model ?? modelSelection?.model ?? binding.model,
@@ -755,46 +643,6 @@ async function runBoundTurn(params) {
755
643
  }
756
644
  })) throw new Error("Codex conversation binding changed while rotating its thread.");
757
645
  useStickyNetworkProfile = modelScopedRuntime.networkProxy !== void 0;
758
- } else if (binding.clientId !== client.getInstanceId()) {
759
- const response = await withLeasedCodexAppServerClientStartSelectionRetry({
760
- lease: clientLease,
761
- options: clientOptions,
762
- run: async (requestClient, requestOptions) => await resumeCodexAppServerThread({
763
- client: requestClient,
764
- abandonClient: async () => {
765
- await requestClient.closeAndWait();
766
- },
767
- request: {
768
- threadId,
769
- ...modelSelection?.model ? { model: modelSelection.model } : {},
770
- ...modelSelection?.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
771
- personality: CODEX_NATIVE_PERSONALITY_NONE,
772
- approvalPolicy,
773
- approvalsReviewer: modelScopedRuntime.approvalsReviewer,
774
- ...codexConversationSandboxOrPermissions(modelScopedRuntime, sandbox),
775
- ...serviceTier ? { serviceTier } : {}
776
- },
777
- ...requestOptions
778
- }),
779
- onClientChange: (nextClient) => {
780
- client = nextClient;
781
- }
782
- });
783
- threadId = response.thread.id;
784
- if (!await params.bindingStore.mutate(identity, {
785
- kind: "patch",
786
- threadId: binding.threadId,
787
- patch: {
788
- clientId: client.getInstanceId(),
789
- cwd: response.thread.cwd ?? binding.cwd,
790
- model: response.model ?? modelSelection?.model ?? binding.model,
791
- modelProvider: normalizeCodexAppServerBindingModelProvider({
792
- authProfileId: binding.authProfileId,
793
- modelProvider: response.modelProvider ?? modelSelection?.modelProvider ?? binding.modelProvider,
794
- ...agentLookup
795
- })
796
- }
797
- })) throw new Error("Codex conversation binding changed while resuming on a new client.");
798
646
  }
799
647
  const collector = createCodexConversationTurnCollector(threadId);
800
648
  notificationCleanup = client.addNotificationHandler((notification) => collector.handleNotification(notification));
@@ -835,29 +683,15 @@ async function runBoundTurn(params) {
835
683
  }, { timeoutMs: runtime.requestTimeoutMs })).turn.id;
836
684
  const activeCleanup = trackCodexConversationActiveTurn({
837
685
  identity,
838
- client,
839
686
  threadId,
840
687
  turnId
841
688
  });
842
689
  collector.setTurnId(turnId);
843
690
  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;
857
691
  } finally {
858
692
  notificationCleanup();
859
693
  requestCleanup();
860
- releaseCodexAppServerClientLease(clientLease);
694
+ releaseLeasedSharedCodexAppServerClient(client);
861
695
  }
862
696
  }
863
697
  function assertNativeConversationApprovalPolicySupported(params) {
@@ -886,8 +720,6 @@ async function prepareConversationBinding(params, options = {}) {
886
720
  config: params.config
887
721
  }) : void 0;
888
722
  const sourceBinding = sourceIdentity ? await params.bindingStore.read(sourceIdentity) : void 0;
889
- assertCodexBindingMayBeReplaced(current, "initializing a conversation-bound Codex thread");
890
- assertCodexBindingMayBeReplaced(sourceBinding, "transferring a session into a conversation-bound Codex thread");
891
723
  const inherited = current ?? sourceBinding;
892
724
  const execPolicy = resolveConversationExecPolicy({
893
725
  config: params.config,
@@ -940,7 +772,7 @@ async function prepareConversationBinding(params, options = {}) {
940
772
  });
941
773
  }
942
774
  function resolveConversationExecPolicy(params) {
943
- const agentId = params.agentId ?? (params.config ? resolveSessionAgentIds$1({
775
+ const agentId = params.agentId ?? (params.config ? resolveSessionAgentIds({
944
776
  sessionKey: params.sessionKey,
945
777
  config: params.config
946
778
  }).sessionAgentId : void 0);
@@ -963,8 +795,9 @@ function readSessionExecOverrides(params) {
963
795
  agentId: params.agentId,
964
796
  sessionKey
965
797
  })) return;
798
+ const storePath = resolveStorePath(params.config.session?.store, { agentId: params.agentId });
966
799
  const entry = getSessionEntry({
967
- storePath: resolveStorePath(params.config.session?.store, { agentId: params.agentId }),
800
+ storePath,
968
801
  sessionKey,
969
802
  readConsistency: "latest"
970
803
  });
@@ -1050,44 +883,6 @@ function conversationBindingIdentity(data) {
1050
883
  bindingId: data.bindingId
1051
884
  };
1052
885
  }
1053
- const codexConversationBindingRuntime = {
1054
- startThread: startCodexConversationThread,
1055
- handleInboundClaim: handleCodexConversationInboundClaim,
1056
- handleBindingResolved: handleCodexConversationBindingResolved
1057
- };
1058
- //#endregion
1059
- //#region extensions/codex/src/migration/apply-report.ts
1060
- function codexPluginActivationReportState(result) {
1061
- switch (result.reason) {
1062
- case "already_active":
1063
- case "installed": return {
1064
- installed: true,
1065
- enabled: true
1066
- };
1067
- case "auth_required": return {
1068
- installed: true,
1069
- enabled: false
1070
- };
1071
- case "disabled":
1072
- case "marketplace_missing":
1073
- case "plugin_missing": return {
1074
- installed: false,
1075
- enabled: false
1076
- };
1077
- case "refresh_failed": return {
1078
- installed: true,
1079
- enabled: false
1080
- };
1081
- }
1082
- return result.reason;
1083
- }
1084
- function sanitizeAppsNeedingAuth(apps) {
1085
- return apps.map((app) => ({
1086
- id: app.id,
1087
- name: app.name,
1088
- needsAuth: app.needsAuth
1089
- }));
1090
- }
1091
886
  //#endregion
1092
887
  //#region extensions/codex/src/migration/helpers.ts
1093
888
  async function exists(filePath) {
@@ -1141,6 +936,11 @@ async function readModelRefs(source) {
1141
936
  refs.add(OPENAI_CODEX_DEFAULT_MODEL);
1142
937
  return [...refs].toSorted();
1143
938
  }
939
+ function readProviderAuthModelConfigs(result) {
940
+ const models = result.configPatch?.agents?.defaults?.models;
941
+ if (isRecord(models)) return { ...models };
942
+ return { [normalizeOptionalString(result.defaultModel) ?? OPENAI_CODEX_DEFAULT_MODEL]: {} };
943
+ }
1144
944
  async function buildCodexOAuthCredential(source) {
1145
945
  const credential = readCodexCliCredentialsCached({
1146
946
  codexHome: source.codexHome,
@@ -1175,11 +975,13 @@ async function buildCodexOAuthCredential(source) {
1175
975
  kind: "oauth",
1176
976
  provider: OPENAI_PROVIDER_ID,
1177
977
  profileId: profile.profileId,
1178
- result
978
+ result,
979
+ modelConfigs: readProviderAuthModelConfigs(result)
1179
980
  } : null;
1180
981
  }
1181
982
  async function buildCodexApiKeyCredential(source) {
1182
- const key = normalizeOptionalString((await readJsonObject(source.authPath)).OPENAI_API_KEY);
983
+ const raw = await readJsonObject(source.authPath);
984
+ const key = normalizeOptionalString(raw.OPENAI_API_KEY);
1183
985
  if (!key) return null;
1184
986
  return {
1185
987
  kind: "api_key",
@@ -1233,7 +1035,7 @@ function hasAuthProfileConfigConflict(config, profile, overwrite) {
1233
1035
  function hasCurrentAuthProfileConfigConflict(ctx, profile) {
1234
1036
  let config = ctx.config;
1235
1037
  try {
1236
- config = resolveMigrationConfigRuntime(ctx)?.current?.() ?? config;
1038
+ config = ctx.runtime?.config?.current?.() ?? config;
1237
1039
  } catch {}
1238
1040
  return hasAuthProfileConfigConflict(config, profile, Boolean(ctx.overwrite));
1239
1041
  }
@@ -1254,8 +1056,30 @@ function applyDefaultModelIfMissing(cfg) {
1254
1056
  }
1255
1057
  };
1256
1058
  }
1059
+ function mergeModelConfigEntry(existing, patch) {
1060
+ if (existing && isRecord(existing) && isRecord(patch)) return {
1061
+ ...existing,
1062
+ ...patch
1063
+ };
1064
+ return existing ?? patch;
1065
+ }
1066
+ function applyOAuthModelConfigsToConfig(cfg, credential) {
1067
+ const existingModels = cfg.agents?.defaults?.models ?? {};
1068
+ const models = credential.result.replaceDefaultModels ? { ...credential.modelConfigs } : { ...existingModels };
1069
+ if (!credential.result.replaceDefaultModels) for (const [modelRef, modelConfig] of Object.entries(credential.modelConfigs)) models[modelRef] = mergeModelConfigEntry(models[modelRef], modelConfig);
1070
+ return {
1071
+ ...cfg,
1072
+ agents: {
1073
+ ...cfg.agents,
1074
+ defaults: {
1075
+ ...cfg.agents?.defaults,
1076
+ models
1077
+ }
1078
+ }
1079
+ };
1080
+ }
1257
1081
  function applyOAuthConfigToConfig(cfg, credential, profileId) {
1258
- let next = mergeMigrationConfigValue(cfg, credential.result.configPatch);
1082
+ let next = applyOAuthModelConfigsToConfig(cfg, credential);
1259
1083
  const profile = credential.result.profiles[0];
1260
1084
  if (profile) next = applyAuthProfileConfig(next, {
1261
1085
  profileId,
@@ -1279,25 +1103,30 @@ function applyApiKeyConfigToConfig(cfg, credential, profileId) {
1279
1103
  function shouldReturnAuthConfigPatch(ctx) {
1280
1104
  return ctx.providerOptions?.configPatchMode === CODEX_CONFIG_PATCH_MODE_RETURN$1;
1281
1105
  }
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
- };
1106
+ function oauthAuthProfileConfig(credential, profileId) {
1289
1107
  const profile = credential.result.profiles[0];
1290
1108
  if (!profile || profile.credential.type !== "oauth") return null;
1291
1109
  return {
1292
1110
  profileId,
1293
1111
  provider: profile.credential.provider,
1294
1112
  mode: "oauth",
1295
- ...profile.credential.email ? { email: profile.credential.email } : {},
1296
- ...profile.credential.displayName ? { displayName: profile.credential.displayName } : {}
1113
+ ..."email" in profile.credential && profile.credential.email ? { email: profile.credential.email } : {},
1114
+ ..."displayName" in profile.credential && profile.credential.displayName ? { displayName: profile.credential.displayName } : {}
1115
+ };
1116
+ }
1117
+ function apiKeyAuthProfileConfig(credential, profileId) {
1118
+ return {
1119
+ profileId,
1120
+ provider: credential.provider,
1121
+ mode: "api_key",
1122
+ displayName: CODEX_IMPORT_DISPLAY_NAME
1297
1123
  };
1298
1124
  }
1125
+ function authProfileConfigForCredential(credential, profileId) {
1126
+ return credential.kind === "oauth" ? oauthAuthProfileConfig(credential, profileId) : apiKeyAuthProfileConfig(credential, profileId);
1127
+ }
1299
1128
  async function applyCodexAuthProfileConfig(ctx, profile, applyConfig) {
1300
- const configApi = resolveMigrationConfigRuntime(ctx);
1129
+ const configApi = ctx.runtime?.config;
1301
1130
  if (!configApi?.current || !configApi.mutateConfigFile) return "unavailable";
1302
1131
  try {
1303
1132
  await configApi.mutateConfigFile({
@@ -1314,13 +1143,13 @@ async function applyCodexAuthProfileConfig(ctx, profile, applyConfig) {
1314
1143
  return error instanceof CodexAuthConfigConflict ? "conflict" : "unavailable";
1315
1144
  }
1316
1145
  }
1317
- async function applyCodexAuthConfig(ctx, credential, profileId) {
1318
- const profile = authProfileConfigForCredential(credential, profileId);
1146
+ async function applyOAuthConfig(ctx, credential, profileId) {
1147
+ const profile = oauthAuthProfileConfig(credential, profileId);
1319
1148
  if (!profile) return "unavailable";
1320
- return applyCodexAuthProfileConfig(ctx, profile, (config) => applyCredentialConfig(config, credential, profileId));
1149
+ return applyCodexAuthProfileConfig(ctx, profile, (config) => applyOAuthConfigToConfig(config, credential, profileId));
1321
1150
  }
1322
- function applyCredentialConfig(config, credential, profileId) {
1323
- return credential.kind === "oauth" ? applyOAuthConfigToConfig(config, credential, profileId) : applyApiKeyConfigToConfig(config, credential, profileId);
1151
+ async function applyApiKeyConfig(ctx, credential, profileId) {
1152
+ return applyCodexAuthProfileConfig(ctx, apiKeyAuthProfileConfig(credential, profileId), (config) => applyApiKeyConfigToConfig(config, credential, profileId));
1324
1153
  }
1325
1154
  async function buildCodexAuthItems(params) {
1326
1155
  const credentials = await readCodexAuthCredentials(params.source);
@@ -1353,27 +1182,26 @@ async function buildCodexAuthItems(params) {
1353
1182
  });
1354
1183
  });
1355
1184
  }
1356
- async function applyCodexAuthItems(params) {
1185
+ async function applyCodexAuthItem(params) {
1357
1186
  const { ctx, item, source, targets } = params;
1358
- if (item.status !== "planned") return [item];
1187
+ if (item.status !== "planned") return item;
1359
1188
  const profileId = typeof item.details?.profileId === "string" ? item.details.profileId : "";
1360
1189
  const provider = typeof item.details?.provider === "string" ? item.details.provider : "";
1361
1190
  const sourceProfileId = typeof item.details?.sourceProfileId === "string" ? item.details.sourceProfileId : void 0;
1362
- if (!profileId || !provider) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
1191
+ if (!profileId || !provider) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
1363
1192
  const credential = (await readCodexAuthCredentials(source)).find((candidate) => candidate.provider === provider);
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)];
1193
+ if (!credential) return markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT);
1194
+ if (credential.kind === "oauth" && sourceProfileId && credential.profileId !== sourceProfileId) return markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT);
1366
1195
  const oauthProfile = credential.kind === "oauth" ? credential.result.profiles[0] : void 0;
1367
1196
  const oauthCredential = oauthProfile?.credential.type === "oauth" ? oauthProfile.credential : void 0;
1368
- if (credential.kind === "oauth" && !oauthCredential) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
1197
+ if (credential.kind === "oauth" && !oauthCredential) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
1369
1198
  const configProfile = authProfileConfigForCredential(credential, profileId);
1370
- if (!configProfile) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
1371
- if (hasCurrentAuthProfileConfigConflict(ctx, configProfile)) return [markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS)];
1199
+ if (!configProfile) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
1200
+ if (hasCurrentAuthProfileConfigConflict(ctx, configProfile)) return markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS);
1372
1201
  let conflicted = false;
1373
1202
  let wrote = false;
1374
1203
  const store = await updateAuthProfileStoreWithLock({
1375
1204
  agentDir: targets.agentDir,
1376
- stateDir: ctx.stateDir,
1377
1205
  updater: (freshStore) => {
1378
1206
  const existing = freshStore.profiles[profileId];
1379
1207
  if (!ctx.overwrite && existing) {
@@ -1392,11 +1220,11 @@ async function applyCodexAuthItems(params) {
1392
1220
  return true;
1393
1221
  }
1394
1222
  });
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 = {
1223
+ if (conflicted) return markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS);
1224
+ if (!store?.profiles[profileId]) return markMigrationItemError(item, CODEX_REASON_AUTH_PROFILE_WRITE_FAILED);
1225
+ const configResult = shouldReturnAuthConfigPatch(ctx) ? "unavailable" : credential.kind === "oauth" ? await applyOAuthConfig(ctx, credential, profileId) : await applyApiKeyConfig(ctx, credential, profileId);
1226
+ if (configResult === "conflict") return markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS);
1227
+ return {
1400
1228
  ...item,
1401
1229
  status: "migrated",
1402
1230
  details: {
@@ -1406,10 +1234,18 @@ async function applyCodexAuthItems(params) {
1406
1234
  ...shouldReturnAuthConfigPatch(ctx) ? { configPatchReturned: true } : {}
1407
1235
  }
1408
1236
  };
1409
- return [migratedItem, ...shouldReturnAuthConfigPatch(ctx) ? buildCodexAuthConfigPatchItems(ctx, migratedItem, credential, profileId) : []];
1410
1237
  }
1411
- function buildCodexAuthConfigPatchItems(ctx, item, credential, profileId) {
1412
- const next = applyCredentialConfig(ctx.config, credential, profileId);
1238
+ async function buildCodexAuthConfigPatchItems(params) {
1239
+ const { ctx, item, source } = params;
1240
+ if (item.status !== "migrated" || !shouldReturnAuthConfigPatch(ctx)) return [];
1241
+ const profileId = typeof item.details?.profileId === "string" ? item.details.profileId : "";
1242
+ const provider = typeof item.details?.provider === "string" ? item.details.provider : "";
1243
+ const sourceProfileId = typeof item.details?.sourceProfileId === "string" ? item.details.sourceProfileId : void 0;
1244
+ if (!profileId || !provider) return [];
1245
+ const credential = (await readCodexAuthCredentials(source)).find((candidate) => candidate.provider === provider);
1246
+ if (!credential) return [];
1247
+ if (credential.kind === "oauth" && sourceProfileId && credential.profileId !== sourceProfileId) return [];
1248
+ const next = credential.kind === "oauth" ? applyOAuthConfigToConfig(ctx.config, credential, profileId) : applyApiKeyConfigToConfig(ctx.config, credential, profileId);
1413
1249
  const items = [];
1414
1250
  if (next.auth) items.push(createMigrationItem({
1415
1251
  id: `${item.id}:config:auth`,
@@ -1438,10 +1274,16 @@ function buildCodexAuthConfigPatchItems(ctx, item, credential, profileId) {
1438
1274
  return items;
1439
1275
  }
1440
1276
  //#endregion
1441
- //#region extensions/codex/src/migration/source-files.ts
1277
+ //#region extensions/codex/src/migration/source.ts
1442
1278
  const SKILL_FILENAME = "SKILL.md";
1443
1279
  const MAX_SCAN_DEPTH = 6;
1444
1280
  const MAX_DISCOVERED_DIRS = 2e3;
1281
+ function defaultCodexHome() {
1282
+ return resolveHomePath(process.env.CODEX_HOME?.trim() || "~/.codex");
1283
+ }
1284
+ function personalAgentsSkillsDir() {
1285
+ return path.join(resolveUserHomeDir(), ".agents", "skills");
1286
+ }
1445
1287
  async function safeReadDir(dir) {
1446
1288
  return await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
1447
1289
  }
@@ -1460,7 +1302,10 @@ async function discoverSkillDirs(params) {
1460
1302
  });
1461
1303
  return;
1462
1304
  }
1463
- for (const entry of await safeReadDir(dir)) if (entry.isDirectory()) await visit(path.join(dir, entry.name), depth + 1);
1305
+ for (const entry of await safeReadDir(dir)) {
1306
+ if (!entry.isDirectory()) continue;
1307
+ await visit(path.join(dir, entry.name), depth + 1);
1308
+ }
1464
1309
  }
1465
1310
  await visit(params.root, 0);
1466
1311
  return discovered;
@@ -1474,56 +1319,25 @@ async function discoverPluginDirs(codexHome) {
1474
1319
  const manifestPath = path.join(dir, ".codex-plugin", "plugin.json");
1475
1320
  if (await exists(manifestPath)) {
1476
1321
  const manifest = await readJsonObject(manifestPath);
1477
- const manifestName = typeof manifest.name === "string" ? manifest.name.trim() : "";
1322
+ const name = (typeof manifest.name === "string" ? manifest.name.trim() : "") || path.basename(dir);
1478
1323
  discovered.set(dir, {
1479
- name: manifestName || path.basename(dir),
1324
+ name,
1480
1325
  source: dir,
1326
+ manifestPath,
1327
+ sourceKind: "cache",
1481
1328
  migratable: false,
1482
1329
  message: "Cached Codex plugin bundle found. Review manually unless the plugin is also installed in the source Codex app-server inventory"
1483
1330
  });
1484
1331
  return;
1485
1332
  }
1486
- for (const entry of await safeReadDir(dir)) if (entry.isDirectory()) await visit(path.join(dir, entry.name), depth + 1);
1333
+ for (const entry of await safeReadDir(dir)) {
1334
+ if (!entry.isDirectory()) continue;
1335
+ await visit(path.join(dir, entry.name), depth + 1);
1336
+ }
1487
1337
  }
1488
1338
  await visit(root, 0);
1489
1339
  return [...discovered.values()].toSorted((a, b) => a.source.localeCompare(b.source));
1490
1340
  }
1491
- async function discoverCodexMemoryFile(candidate) {
1492
- try {
1493
- const stat = await fs.lstat(candidate.path);
1494
- if (stat.isSymbolicLink()) throw new Error(`Codex memory source must not be a symbolic link: ${candidate.path}`);
1495
- if (!stat.isFile()) throw new Error(`Codex memory source must be a regular file: ${candidate.path}`);
1496
- return candidate;
1497
- } catch (error) {
1498
- if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return;
1499
- throw error;
1500
- }
1501
- }
1502
- async function discoverCodexMemorySources(codexHome) {
1503
- const memoriesDir = path.join(codexHome, "memories");
1504
- return (await Promise.all([{
1505
- id: "memory:codex:MEMORY.md",
1506
- label: "Codex consolidated memory",
1507
- name: "MEMORY.md"
1508
- }, {
1509
- id: "memory:codex:memory_summary.md",
1510
- label: "Codex memory summary",
1511
- name: "memory_summary.md"
1512
- }].map(async (candidate) => await discoverCodexMemoryFile({
1513
- id: candidate.id,
1514
- label: candidate.label,
1515
- path: path.join(memoriesDir, candidate.name)
1516
- })))).filter((entry) => entry !== void 0);
1517
- }
1518
- //#endregion
1519
- //#region extensions/codex/src/migration/source.ts
1520
- function defaultCodexHome() {
1521
- const configuredHome = process.env.CODEX_HOME;
1522
- return resolveHomePath(configuredHome !== void 0 && configuredHome.length > 0 ? configuredHome : "~/.codex");
1523
- }
1524
- function personalAgentsSkillsDir() {
1525
- return path.join(resolveUserHomeDir(), ".agents", "skills");
1526
- }
1527
1341
  async function discoverInstalledCuratedPlugins(codexHome, options = {}) {
1528
1342
  const requestOptions = { startOptions: sourceCodexAppServerStartOptions(codexHome) };
1529
1343
  try {
@@ -1554,7 +1368,6 @@ function sourceCodexAppServerStartOptions(codexHome) {
1554
1368
  transport: "stdio",
1555
1369
  command: "codex",
1556
1370
  commandSource: "managed",
1557
- managedCommandOrder: "desktop-first",
1558
1371
  args: [
1559
1372
  "app-server",
1560
1373
  "--listen",
@@ -1585,6 +1398,7 @@ function buildInstalledPluginSource(plugin) {
1585
1398
  pluginName,
1586
1399
  marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
1587
1400
  source: `${CODEX_PLUGINS_MARKETPLACE_NAME}/${pluginName}`,
1401
+ sourceKind: "app-server",
1588
1402
  migratable: true,
1589
1403
  installed: plugin.installed,
1590
1404
  enabled: plugin.enabled
@@ -1791,7 +1605,8 @@ function pluginNameFromSummary(summary) {
1791
1605
  if (normalized) return normalized;
1792
1606
  }
1793
1607
  }
1794
- async function discoverCodexSource(options = {}) {
1608
+ async function discoverCodexSource(inputOrOptions) {
1609
+ const options = typeof inputOrOptions === "string" || inputOrOptions === void 0 ? { input: inputOrOptions } : inputOrOptions;
1795
1610
  const codexHome = resolveHomePath(options.input?.trim() || defaultCodexHome());
1796
1611
  const codexSkillsDir = path.join(codexHome, "skills");
1797
1612
  const agentsSkillsDir = personalAgentsSkillsDir();
@@ -1799,39 +1614,38 @@ async function discoverCodexSource(options = {}) {
1799
1614
  const authPath = path.join(codexHome, "auth.json");
1800
1615
  const modelsCachePath = path.join(codexHome, "models_cache.json");
1801
1616
  const hooksPath = path.join(codexHome, "hooks", "hooks.json");
1802
- const memoryFiles = await discoverCodexMemorySources(codexHome);
1803
- const codexSkills = options.memoryOnly ? [] : await discoverSkillDirs({
1617
+ const codexSkills = await discoverSkillDirs({
1804
1618
  root: codexSkillsDir,
1805
1619
  sourceLabel: "Codex skill",
1806
1620
  excludeSystem: true
1807
1621
  });
1808
- const personalAgentSkills = options.memoryOnly ? [] : await discoverSkillDirs({
1622
+ const personalAgentSkills = await discoverSkillDirs({
1809
1623
  root: agentsSkillsDir,
1810
1624
  sourceLabel: "personal AgentSkill"
1811
1625
  });
1812
- const sourcePluginDiscovery = options.memoryOnly ? { plugins: [] } : await discoverInstalledCuratedPlugins(codexHome, options);
1626
+ const sourcePluginDiscovery = await discoverInstalledCuratedPlugins(codexHome, options);
1813
1627
  const sourcePluginNames = new Set(sourcePluginDiscovery.plugins.flatMap((plugin) => plugin.pluginName ? [plugin.pluginName] : []));
1814
- const cachedPlugins = (options.memoryOnly ? [] : await discoverPluginDirs(codexHome)).filter((plugin) => {
1628
+ const cachedPlugins = (await discoverPluginDirs(codexHome)).filter((plugin) => {
1815
1629
  const normalizedName = sanitizePluginName(plugin.name);
1816
1630
  return !sourcePluginNames.has(normalizedName);
1817
1631
  });
1818
1632
  const plugins = [...sourcePluginDiscovery.plugins, ...cachedPlugins].toSorted((a, b) => a.source.localeCompare(b.source));
1819
1633
  const archivePaths = [];
1820
- if (!options.memoryOnly && await exists(configPath)) archivePaths.push({
1634
+ if (await exists(configPath)) archivePaths.push({
1821
1635
  id: "archive:config.toml",
1822
1636
  path: configPath,
1823
1637
  relativePath: "config.toml",
1824
1638
  message: "Codex config is archived for manual review; it is not activated automatically"
1825
1639
  });
1826
- if (!options.memoryOnly && await exists(hooksPath)) archivePaths.push({
1640
+ if (await exists(hooksPath)) archivePaths.push({
1827
1641
  id: "archive:hooks/hooks.json",
1828
1642
  path: hooksPath,
1829
1643
  relativePath: "hooks/hooks.json",
1830
1644
  message: "Codex native hooks are archived for manual review because they can execute commands"
1831
1645
  });
1832
1646
  const skills = [...codexSkills, ...personalAgentSkills].toSorted((a, b) => a.source.localeCompare(b.source));
1833
- const hasAuth = !options.memoryOnly && await exists(authPath);
1834
- const high = Boolean(memoryFiles.length || codexSkills.length || plugins.length || archivePaths.length || hasAuth);
1647
+ const hasAuth = await exists(authPath);
1648
+ const high = Boolean(codexSkills.length || plugins.length || archivePaths.length || hasAuth);
1835
1649
  const medium = personalAgentSkills.length > 0;
1836
1650
  return {
1837
1651
  root: codexHome,
@@ -1839,9 +1653,10 @@ async function discoverCodexSource(options = {}) {
1839
1653
  codexHome,
1840
1654
  ...await isDirectory(codexSkillsDir) ? { codexSkillsDir } : {},
1841
1655
  ...await isDirectory(agentsSkillsDir) ? { personalAgentsSkillsDir: agentsSkillsDir } : {},
1656
+ ...await exists(configPath) ? { configPath } : {},
1842
1657
  ...hasAuth ? { authPath } : {},
1843
1658
  ...await exists(modelsCachePath) ? { modelsCachePath } : {},
1844
- memoryFiles,
1659
+ ...await exists(hooksPath) ? { hooksPath } : {},
1845
1660
  skills,
1846
1661
  plugins,
1847
1662
  ...sourcePluginDiscovery.error ? { pluginDiscoveryError: sourcePluginDiscovery.error } : {},
@@ -1858,7 +1673,7 @@ function sanitizePluginName(value) {
1858
1673
  //#region extensions/codex/src/migration/targets.ts
1859
1674
  function resolveCodexMigrationTargets(ctx) {
1860
1675
  const cfg = ctx.config;
1861
- const agentId = ctx.targetAgentId ?? resolveDefaultAgentId(cfg);
1676
+ const agentId = resolveDefaultAgentId(cfg);
1862
1677
  const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
1863
1678
  const configuredAgentDir = resolveAgentConfig(cfg, agentId)?.agentDir?.trim();
1864
1679
  return {
@@ -1889,52 +1704,6 @@ const CODEX_PLUGIN_NATIVE_CONFIG_PATH = [
1889
1704
  ];
1890
1705
  const MIGRATION_REASON_PLUGIN_EXISTS = "plugin exists";
1891
1706
  const CODEX_PLUGIN_SOURCE_APP_VERIFICATION_UNVERIFIED = "not_run";
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
1707
  function uniqueSkillName(skill, counts) {
1939
1708
  const base = sanitizeName(skill.name) || "codex-skill";
1940
1709
  if ((counts.get(base) ?? 0) <= 1) return base;
@@ -1944,43 +1713,49 @@ function uniqueSkillName(skill, counts) {
1944
1713
  base
1945
1714
  ].filter(Boolean).join("-")) || base;
1946
1715
  }
1947
- async function buildCodexSkillItems(params) {
1948
- const counts = /* @__PURE__ */ new Map();
1716
+ async function buildSkillItems(params) {
1717
+ const baseCounts = /* @__PURE__ */ new Map();
1949
1718
  for (const skill of params.skills) {
1950
1719
  const base = sanitizeName(skill.name) || "codex-skill";
1951
- counts.set(base, (counts.get(base) ?? 0) + 1);
1720
+ baseCounts.set(base, (baseCounts.get(base) ?? 0) + 1);
1952
1721
  }
1722
+ const resolvedCounts = /* @__PURE__ */ new Map();
1953
1723
  const planned = params.skills.map((skill) => {
1954
- const name = uniqueSkillName(skill, counts);
1724
+ const name = uniqueSkillName(skill, baseCounts);
1725
+ resolvedCounts.set(name, (resolvedCounts.get(name) ?? 0) + 1);
1955
1726
  return {
1956
1727
  skill,
1957
1728
  name,
1958
1729
  target: path.join(params.workspaceDir, "skills", name)
1959
1730
  };
1960
1731
  });
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;
1732
+ const items = [];
1733
+ for (const item of planned) {
1734
+ const collides = (resolvedCounts.get(item.name) ?? 0) > 1;
1967
1735
  const targetExists = await exists(item.target);
1968
- const conflict = collision || targetExists && !params.overwrite;
1969
- return createMigrationItem({
1736
+ items.push(createMigrationItem({
1970
1737
  id: `skill:${item.name}`,
1971
1738
  kind: "skill",
1972
1739
  action: "copy",
1973
1740
  source: item.skill.source,
1974
1741
  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,
1742
+ status: collides ? "conflict" : targetExists && !params.overwrite ? "conflict" : "planned",
1743
+ reason: collides ? `multiple Codex skills normalize to "${item.name}"` : targetExists && !params.overwrite ? MIGRATION_REASON_TARGET_EXISTS : void 0,
1977
1744
  message: `Copy ${item.skill.sourceLabel} into this OpenClaw agent workspace.`,
1978
1745
  details: {
1979
1746
  skillName: item.name,
1980
1747
  sourceLabel: item.skill.sourceLabel
1981
1748
  }
1982
- });
1983
- }));
1749
+ }));
1750
+ }
1751
+ return items;
1752
+ }
1753
+ function uniquePluginConfigKey(plugin, counts, usedCounts) {
1754
+ const base = sanitizeName(plugin.pluginName ?? plugin.name) || "codex-plugin";
1755
+ if ((counts.get(base) ?? 0) <= 1) return base;
1756
+ const next = (usedCounts.get(base) ?? 0) + 1;
1757
+ usedCounts.set(base, next);
1758
+ return sanitizeName(`${base}-${next}`) || base;
1984
1759
  }
1985
1760
  function readExistingCodexPluginEntries(config) {
1986
1761
  const entries = readMigrationConfigPath(config, [...CODEX_PLUGIN_NATIVE_CONFIG_PATH, "plugins"]);
@@ -2011,12 +1786,18 @@ function readExistingPluginAllowDestructiveActions(existing, pluginName) {
2011
1786
  return normalized === "auto" || normalized === "ask" ? normalized : void 0;
2012
1787
  }
2013
1788
  function buildPluginItems(ctx, plugins) {
1789
+ const baseCounts = /* @__PURE__ */ new Map();
1790
+ for (const plugin of plugins.filter((entry) => entry.migratable)) {
1791
+ const base = sanitizeName(plugin.pluginName ?? plugin.name) || "codex-plugin";
1792
+ baseCounts.set(base, (baseCounts.get(base) ?? 0) + 1);
1793
+ }
2014
1794
  const existingPluginEntries = readExistingCodexPluginEntries(ctx.config);
1795
+ const usedCounts = /* @__PURE__ */ new Map();
2015
1796
  let manualIndex = 0;
2016
1797
  const items = [];
2017
1798
  for (const plugin of plugins) {
2018
1799
  if (plugin.migratable && plugin.marketplaceName === "openai-curated" && plugin.pluginName) {
2019
- const configKey = plugin.pluginName;
1800
+ const configKey = uniquePluginConfigKey(plugin, baseCounts, usedCounts);
2020
1801
  const plannedEntry = {
2021
1802
  enabled: true,
2022
1803
  marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
@@ -2033,7 +1814,6 @@ function buildPluginItems(ctx, plugins) {
2033
1814
  action: "install",
2034
1815
  status: conflict ? "conflict" : "planned",
2035
1816
  reason: conflict ? MIGRATION_REASON_PLUGIN_EXISTS : void 0,
2036
- applyPhase: "after-promotion",
2037
1817
  source: plugin.source,
2038
1818
  target: `plugins.entries.codex.config.codexPlugins.plugins.${configKey}`,
2039
1819
  message: `Install Codex plugin "${plugin.pluginName}" in the OpenClaw-managed Codex app-server runtime.`,
@@ -2051,6 +1831,12 @@ function buildPluginItems(ctx, plugins) {
2051
1831
  }
2052
1832
  manualIndex += 1;
2053
1833
  if (plugin.migrationBlock && plugin.pluginName) {
1834
+ const details = {
1835
+ pluginName: plugin.pluginName,
1836
+ marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
1837
+ ...plugin.migrationBlock.apps ? { apps: plugin.migrationBlock.apps } : {},
1838
+ ...plugin.migrationBlock.error ? { error: plugin.migrationBlock.error } : {}
1839
+ };
2054
1840
  items.push(createMigrationItem({
2055
1841
  id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${manualIndex}`,
2056
1842
  kind: "manual",
@@ -2059,12 +1845,7 @@ function buildPluginItems(ctx, plugins) {
2059
1845
  status: "skipped",
2060
1846
  reason: plugin.migrationBlock.code,
2061
1847
  message: plugin.message ?? `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
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
- }
1848
+ details: { ...details }
2068
1849
  }));
2069
1850
  continue;
2070
1851
  }
@@ -2072,7 +1853,7 @@ function buildPluginItems(ctx, plugins) {
2072
1853
  id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${manualIndex}`,
2073
1854
  source: plugin.source,
2074
1855
  message: plugin.message ?? `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
2075
- recommendation: "Review the plugin bundle first, then install trusted compatible plugins with openclaw plugins install <path> --force."
1856
+ recommendation: "Review the plugin bundle first, then install trusted compatible plugins with openclaw plugins install <path>."
2076
1857
  }));
2077
1858
  }
2078
1859
  return items;
@@ -2102,6 +1883,7 @@ function normalizeExistingAllowDestructiveActions(value) {
2102
1883
  return asBoolean(value);
2103
1884
  }
2104
1885
  function readExistingPluginPolicyRepairs(config) {
1886
+ if (config === void 0) return {};
2105
1887
  return Object.fromEntries(Object.entries(readExistingCodexPluginEntries(config)).flatMap(([configKey, entry]) => {
2106
1888
  const pluginEntry = isRecord(entry) ? entry : void 0;
2107
1889
  if (pluginEntry?.allow_destructive_actions !== "on-request") return [];
@@ -2111,9 +1893,9 @@ function readExistingPluginPolicyRepairs(config) {
2111
1893
  }]];
2112
1894
  }));
2113
1895
  }
2114
- function buildCodexPluginsConfigValue(entries, config) {
1896
+ function buildCodexPluginsConfigValue(entries, params = {}) {
2115
1897
  const plugins = {
2116
- ...readExistingPluginPolicyRepairs(config),
1898
+ ...readExistingPluginPolicyRepairs(params.config),
2117
1899
  ...Object.fromEntries(entries.toSorted((a, b) => a.configKey.localeCompare(b.configKey)).map((entry) => [entry.configKey, {
2118
1900
  enabled: entry.enabled,
2119
1901
  marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
@@ -2125,7 +1907,7 @@ function buildCodexPluginsConfigValue(entries, config) {
2125
1907
  enabled: true,
2126
1908
  config: { codexPlugins: {
2127
1909
  enabled: true,
2128
- allow_destructive_actions: readExistingAllowDestructiveActions(config) ?? true,
1910
+ allow_destructive_actions: params.config === void 0 ? true : readExistingAllowDestructiveActions(params.config) ?? true,
2129
1911
  plugins
2130
1912
  } }
2131
1913
  };
@@ -2152,7 +1934,7 @@ function hasCodexPluginConfigConflict(config, value) {
2152
1934
  function buildPluginConfigItem(ctx, pluginItems) {
2153
1935
  const entries = pluginItems.filter((item) => item.status === "planned").map((item) => readCodexPluginMigrationConfigEntry(item, true)).filter((entry) => entry !== void 0);
2154
1936
  if (entries.length === 0) return;
2155
- const value = buildCodexPluginsConfigValue(entries, ctx.config);
1937
+ const value = buildCodexPluginsConfigValue(entries, { config: ctx.config });
2156
1938
  const conflict = !ctx.overwrite && hasCodexPluginConfigConflict(ctx.config, value);
2157
1939
  return createMigrationItem({
2158
1940
  id: CODEX_PLUGIN_CONFIG_ITEM_ID,
@@ -2161,7 +1943,6 @@ function buildPluginConfigItem(ctx, pluginItems) {
2161
1943
  target: "plugins.entries.codex.config.codexPlugins",
2162
1944
  status: conflict ? "conflict" : "planned",
2163
1945
  reason: conflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
2164
- applyPhase: "after-promotion",
2165
1946
  message: "Enable OpenClaw's Codex plugin integration and record migrated source-installed curated plugins.",
2166
1947
  details: {
2167
1948
  path: [...CODEX_PLUGIN_CONFIG_PATH],
@@ -2171,44 +1952,35 @@ function buildPluginConfigItem(ctx, pluginItems) {
2171
1952
  }
2172
1953
  async function buildCodexMigrationPlan(ctx) {
2173
1954
  const targets = resolveCodexMigrationTargets(ctx);
2174
- const memoryOnly = ctx.itemKinds !== void 0 && ctx.itemKinds.length > 0 && ctx.itemKinds.every((kind) => kind === "memory");
2175
1955
  const source = await discoverCodexSource({
2176
1956
  input: ctx.source,
2177
- memoryOnly,
2178
- evaluatePluginMigrationEligibility: !memoryOnly,
1957
+ evaluatePluginMigrationEligibility: true,
2179
1958
  verifyPluginApps: shouldVerifyPluginApps(ctx)
2180
1959
  });
2181
1960
  if (!hasCodexSource(source)) throw new Error(`Codex state was not found at ${source.root}. Pass --from <path> if it lives elsewhere.`);
2182
1961
  const items = [];
2183
- items.push(...await buildCodexMemoryItems({
2184
- memoryFiles: source.memoryFiles,
1962
+ items.push(...await buildCodexAuthItems({
1963
+ ctx,
1964
+ source,
1965
+ targets
1966
+ }));
1967
+ items.push(...await buildSkillItems({
1968
+ skills: source.skills,
2185
1969
  workspaceDir: targets.workspaceDir,
2186
1970
  overwrite: ctx.overwrite
2187
1971
  }));
2188
- if (!memoryOnly) {
2189
- items.push(...await buildCodexAuthItems({
2190
- ctx,
2191
- source,
2192
- targets
2193
- }));
2194
- items.push(...await buildCodexSkillItems({
2195
- skills: source.skills,
2196
- workspaceDir: targets.workspaceDir,
2197
- overwrite: ctx.overwrite
2198
- }));
2199
- const pluginItems = buildPluginItems(ctx, source.plugins);
2200
- items.push(...pluginItems);
2201
- const pluginConfigItem = buildPluginConfigItem(ctx, pluginItems);
2202
- if (pluginConfigItem) items.push(pluginConfigItem);
2203
- for (const archivePath of source.archivePaths) items.push(createMigrationItem({
2204
- id: archivePath.id,
2205
- kind: "archive",
2206
- action: "archive",
2207
- source: archivePath.path,
2208
- message: archivePath.message ?? "Archived in the migration report for manual review; not imported into live config.",
2209
- details: { archiveRelativePath: archivePath.relativePath }
2210
- }));
2211
- }
1972
+ const pluginItems = buildPluginItems(ctx, source.plugins);
1973
+ items.push(...pluginItems);
1974
+ const pluginConfigItem = buildPluginConfigItem(ctx, pluginItems);
1975
+ if (pluginConfigItem) items.push(pluginConfigItem);
1976
+ for (const archivePath of source.archivePaths) items.push(createMigrationItem({
1977
+ id: archivePath.id,
1978
+ kind: "archive",
1979
+ action: "archive",
1980
+ source: archivePath.path,
1981
+ message: archivePath.message ?? "Archived in the migration report for manual review; not imported into live config.",
1982
+ details: { archiveRelativePath: archivePath.relativePath }
1983
+ }));
2212
1984
  const warnings = [
2213
1985
  ...!ctx.includeSecrets && items.some((item) => item.kind === "auth") ? ["Auth credentials were detected but skipped. Re-run interactively or pass --include-secrets to import supported credentials."] : [],
2214
1986
  ...items.some((item) => item.status === "conflict") ? ["Conflicts were found. Re-run with --overwrite to replace conflicting migration targets after item-level backups."] : [],
@@ -2222,7 +1994,7 @@ async function buildCodexMigrationPlan(ctx) {
2222
1994
  summary: summarizeMigrationItems(items),
2223
1995
  items,
2224
1996
  warnings,
2225
- nextSteps: memoryOnly ? [] : ["Run openclaw doctor after applying the migration.", "Review skipped or auth-required Codex plugin/config/hook items before exposing them in OpenClaw sessions."],
1997
+ nextSteps: ["Run openclaw doctor after applying the migration.", "Review skipped or auth-required Codex plugin/config/hook items before exposing them in OpenClaw sessions."],
2226
1998
  metadata: {
2227
1999
  agentDir: targets.agentDir,
2228
2000
  codexHome: source.codexHome,
@@ -2278,9 +2050,14 @@ async function applyCodexMigrationPlan(params) {
2278
2050
  const targets = resolveCodexMigrationTargets(params.ctx);
2279
2051
  const codexHome = typeof plan.metadata?.codexHome === "string" && plan.metadata.codexHome.trim() ? plan.metadata.codexHome : plan.source;
2280
2052
  const authSource = {
2053
+ root: plan.source,
2054
+ confidence: "high",
2281
2055
  codexHome,
2282
2056
  authPath: path.join(codexHome, "auth.json"),
2283
- modelsCachePath: path.join(codexHome, "models_cache.json")
2057
+ modelsCachePath: path.join(codexHome, "models_cache.json"),
2058
+ skills: [],
2059
+ plugins: [],
2060
+ archivePaths: []
2284
2061
  };
2285
2062
  const runtime = withCachedMigrationConfigRuntime(params.ctx.runtime ?? params.runtime, params.ctx.config);
2286
2063
  const applyCtx = {
@@ -2293,19 +2070,22 @@ async function applyCodexMigrationPlan(params) {
2293
2070
  continue;
2294
2071
  }
2295
2072
  if (item.id === "config:codex-plugins") items.push(await applyCodexPluginConfigItem(applyCtx, item, items));
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));
2073
+ else if (item.kind === "auth") {
2074
+ const authItem = await applyCodexAuthItem({
2075
+ ctx: applyCtx,
2076
+ item,
2077
+ source: authSource,
2078
+ targets
2079
+ });
2080
+ items.push(authItem);
2081
+ items.push(...await buildCodexAuthConfigPatchItems({
2082
+ ctx: applyCtx,
2083
+ item: authItem,
2084
+ source: authSource
2085
+ }));
2086
+ } else if (item.kind === "plugin" && item.action === "install") items.push(await applyCodexPluginInstallItem(applyCtx, item));
2303
2087
  else if (item.kind === "manual") items.push(applyMigrationManualItem(item));
2304
2088
  else if (item.action === "archive") items.push(await archiveMigrationItem(item, reportDir));
2305
- else if (item.kind === "memory") items.push(await copyMemoryMigrationFileItem(item, reportDir, {
2306
- workspaceDir: targets.workspaceDir,
2307
- overwrite: params.ctx.overwrite
2308
- }));
2309
2089
  else items.push(await copyMigrationFileItem(item, reportDir, { overwrite: params.ctx.overwrite }));
2310
2090
  }
2311
2091
  const result = {
@@ -2437,7 +2217,8 @@ async function requestTargetCodexAppServerJson(params) {
2437
2217
  });
2438
2218
  if (hasOpenAiCuratedMarketplace(lastResponse)) return lastResponse;
2439
2219
  if (Date.now() >= discoveryDeadline) return lastResponse;
2440
- await sleep(Math.min(TARGET_CODEX_MARKETPLACE_DISCOVERY_POLL_MS, discoveryDeadline - Date.now()));
2220
+ const waitMs = Math.min(TARGET_CODEX_MARKETPLACE_DISCOVERY_POLL_MS, discoveryDeadline - Date.now());
2221
+ await sleep(waitMs);
2441
2222
  } while (Date.now() < discoveryDeadline);
2442
2223
  return lastResponse;
2443
2224
  }
@@ -2476,21 +2257,13 @@ async function buildTargetCodexPluginAppCacheKey(ctx) {
2476
2257
  });
2477
2258
  }
2478
2259
  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
- };
2484
2260
  const entries = appliedItems.map(readAppliedPluginConfigEntry).filter((entry) => entry !== void 0);
2485
- if (entries.length === 0) return {
2486
- ...markMigrationItemSkipped(item, "no selected Codex plugins"),
2487
- deferredCompletion: true
2488
- };
2261
+ if (entries.length === 0) return markMigrationItemSkipped(item, "no selected Codex plugins");
2489
2262
  const returnPatch = shouldReturnCodexPluginConfigPatch(ctx);
2490
- const configApi = resolveMigrationConfigRuntime(ctx);
2263
+ const configApi = ctx.runtime?.config;
2491
2264
  const currentConfig = returnPatch ? ctx.config : configApi?.current?.();
2492
2265
  if (!currentConfig) return markMigrationItemError(item, "config runtime unavailable");
2493
- const value = buildCodexPluginsConfigValue(entries, currentConfig);
2266
+ const value = buildCodexPluginsConfigValue(entries, { config: currentConfig });
2494
2267
  if (!ctx.overwrite && hasCodexPluginConfigConflict(currentConfig, value)) return markMigrationItemConflict(item, MIGRATION_REASON_TARGET_EXISTS);
2495
2268
  const migratedItem = {
2496
2269
  ...item,
@@ -2518,42 +2291,65 @@ async function applyCodexPluginConfigItem(ctx, item, appliedItems) {
2518
2291
  return markMigrationItemError(item, error instanceof Error ? error.message : String(error));
2519
2292
  }
2520
2293
  }
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
- }
2524
2294
  function readAppliedPluginConfigEntry(item) {
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);
2295
+ if (item.status === "migrated") return readCodexPluginMigrationConfigEntry(item, true);
2296
+ if (item.status === "skipped" && item.reason !== CODEX_PLUGIN_NOT_SELECTED_REASON && item.reason === CODEX_PLUGIN_AUTH_REQUIRED_REASON) return readCodexPluginMigrationConfigEntry(item, false);
2527
2297
  }
2528
2298
  function readCodexPluginPolicy(item) {
2529
- const entry = readCodexPluginMigrationConfigEntry(item, true);
2530
- if (!entry) return;
2299
+ const configKey = item.details?.configKey;
2300
+ const marketplaceName = item.details?.marketplaceName;
2301
+ const pluginName = item.details?.pluginName;
2302
+ if (typeof configKey !== "string" || marketplaceName !== "openai-curated" || typeof pluginName !== "string") return;
2531
2303
  return {
2532
- configKey: entry.configKey,
2304
+ configKey,
2533
2305
  marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
2534
- pluginName: entry.pluginName,
2306
+ pluginName,
2535
2307
  enabled: true,
2536
2308
  allowDestructiveActions: true,
2537
2309
  destructiveApprovalMode: "allow"
2538
2310
  };
2539
2311
  }
2540
- //#endregion
2541
- //#region extensions/codex/src/migration/provider.ts
2542
- function isMemoryOnlyMigration(ctx) {
2543
- return Boolean(ctx.itemKinds && ctx.itemKinds.length > 0 && ctx.itemKinds.every((kind) => kind === "memory"));
2544
- }
2545
- function buildCodexMigrationProvider(params = {}) {
2546
- return {
2547
- id: "codex",
2312
+ function codexPluginActivationReportState(result) {
2313
+ switch (result.reason) {
2314
+ case "already_active":
2315
+ case "installed": return {
2316
+ installed: true,
2317
+ enabled: true
2318
+ };
2319
+ case "auth_required": return {
2320
+ installed: true,
2321
+ enabled: false
2322
+ };
2323
+ case "disabled":
2324
+ case "marketplace_missing":
2325
+ case "plugin_missing": return {
2326
+ installed: false,
2327
+ enabled: false
2328
+ };
2329
+ case "refresh_failed": return {
2330
+ installed: true,
2331
+ enabled: false
2332
+ };
2333
+ }
2334
+ return result.reason;
2335
+ }
2336
+ function sanitizeAppsNeedingAuth(apps) {
2337
+ return apps.map((app) => ({
2338
+ id: app.id,
2339
+ name: app.name,
2340
+ needsAuth: app.needsAuth
2341
+ }));
2342
+ }
2343
+ //#endregion
2344
+ //#region extensions/codex/src/migration/provider.ts
2345
+ function buildCodexMigrationProvider(params = {}) {
2346
+ return {
2347
+ id: "codex",
2548
2348
  label: "Codex",
2549
- description: "Import Codex memory and skills while keeping Codex native plugins and hooks explicit.",
2550
- supportedItemKinds: ["memory"],
2349
+ description: "Inventory and promote Codex CLI skills while keeping Codex native plugins and hooks explicit.",
2551
2350
  async detect(ctx) {
2552
- const source = await discoverCodexSource({
2553
- input: ctx.source,
2554
- memoryOnly: isMemoryOnlyMigration(ctx)
2555
- });
2556
- const found = isMemoryOnlyMigration(ctx) ? source.memoryFiles.length > 0 : hasCodexSource(source);
2351
+ const source = await discoverCodexSource({ input: ctx.source });
2352
+ const found = hasCodexSource(source);
2557
2353
  return {
2558
2354
  found,
2559
2355
  source: source.root,
@@ -2563,9 +2359,7 @@ function buildCodexMigrationProvider(params = {}) {
2563
2359
  };
2564
2360
  },
2565
2361
  plan: buildCodexMigrationPlan,
2566
- deferredApply: { retrySafe: true },
2567
2362
  prepareApply(ctx) {
2568
- if (isMemoryOnlyMigration(ctx)) return;
2569
2363
  return prepareTargetCodexAppServer(ctx);
2570
2364
  },
2571
2365
  async apply(ctx, plan) {
@@ -2627,7 +2421,7 @@ const CodexThreadsParamsSchema = Type.Union([
2627
2421
  ArchiveParamsSchema,
2628
2422
  UnarchiveParamsSchema
2629
2423
  ]);
2630
- function asRecord$1(value) {
2424
+ function asRecord(value) {
2631
2425
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2632
2426
  }
2633
2427
  function readBoolean(value, fallback = false) {
@@ -2646,9 +2440,13 @@ function resolveToolSession(context, runtime) {
2646
2440
  });
2647
2441
  const sessionId = context.sessionId?.trim() || entry?.sessionId?.trim();
2648
2442
  if (!sessionId) return;
2443
+ const storePath = runtime.agent.session.resolveStorePath(void 0, { agentId: context.agentId });
2649
2444
  return {
2650
2445
  sessionId,
2651
- modelSelectionLocked: isModelSelectionLocked(entry)
2446
+ sessionFile: runtime.agent.session.resolveSessionFilePath(sessionId, entry, {
2447
+ agentId: context.agentId,
2448
+ sessionsDir: path.dirname(storePath)
2449
+ })
2652
2450
  };
2653
2451
  }
2654
2452
  function readThreadId(params) {
@@ -2661,106 +2459,42 @@ function readThreadStatusType(value) {
2661
2459
  if (!isJsonObject(value) || !isJsonObject(value.thread) || !isJsonObject(value.thread.status)) return;
2662
2460
  return typeof value.thread.status.type === "string" ? value.thread.status.type : void 0;
2663
2461
  }
2664
- function assertThreadMayBeArchived(value, expectedThreadId) {
2665
- if (!isJsonObject(value) || !isJsonObject(value.thread)) throw new Error("Codex app-server returned an invalid thread/read response");
2666
- if (value.thread.id !== expectedThreadId) throw new Error("Codex app-server returned a different thread than requested");
2667
- const status = readThreadStatusType(value);
2668
- if (status === "active") throw new Error("cannot archive an active Codex thread; wait for its turn to finish");
2669
- if (status !== "idle" && status !== "notLoaded") throw new Error("cannot verify that the Codex thread is idle; refusing to archive");
2670
- }
2671
- function assertThreadMayBeForked(value, expectedThreadId) {
2672
- if (!isJsonObject(value) || !isJsonObject(value.thread)) throw new Error("Codex app-server returned an invalid thread/read response");
2673
- if (value.thread.id !== expectedThreadId) throw new Error("Codex app-server returned a different thread than requested");
2674
- const status = readThreadStatusType(value);
2675
- if (status !== "idle" && status !== "notLoaded") throw new Error("cannot fork a Codex thread unless it is idle or not loaded");
2676
- }
2677
- function redactNativeThreadTranscriptFields(value) {
2678
- if (!isJsonObject(value)) return value;
2679
- const redacted = { ...value };
2680
- delete redacted.preview;
2681
- delete redacted.turns;
2682
- return redacted;
2683
- }
2684
- function redactNativeThreadResponse(value) {
2685
- if (!isJsonObject(value)) return value;
2686
- const redacted = { ...value };
2687
- if (Array.isArray(redacted.data)) redacted.data = redacted.data.map(redactNativeThreadTranscriptFields);
2688
- if (isJsonObject(redacted.thread)) redacted.thread = redactNativeThreadTranscriptFields(redacted.thread);
2689
- return redacted;
2690
- }
2691
- /** Builds the native Codex thread tool only for owner runs with native-home access. */
2462
+ /** Builds the native Codex thread tool only for owner runs in shared-user-home mode. */
2692
2463
  function createCodexThreadsTool(options) {
2693
2464
  if (options.context.senderIsOwner !== true) return null;
2694
- const configured = readCodexPluginConfig(options.getPluginConfig());
2695
- if (configured.appServer?.homeScope !== "user" && configured.supervision?.enabled !== true) return null;
2465
+ if (readCodexPluginConfig(options.getPluginConfig()).appServer?.homeScope !== "user") return null;
2696
2466
  const request = options.request ?? codexControlRequest;
2697
- const runtimeConfig = () => options.context.getRuntimeConfig?.() ?? options.context.runtimeConfig ?? options.context.config;
2698
- const baseRequestOptions = () => ({
2467
+ const requestOptions = () => ({
2699
2468
  agentDir: options.context.agentDir,
2700
- config: runtimeConfig(),
2469
+ config: options.context.getRuntimeConfig?.() ?? options.context.runtimeConfig ?? options.context.config,
2701
2470
  sessionId: options.context.sessionId,
2702
2471
  sessionKey: options.context.sessionKey
2703
2472
  });
2704
2473
  const currentSession = () => resolveToolSession(options.context, options.runtime);
2705
- const currentIdentity = (sessionId) => sessionBindingIdentity({
2706
- sessionId,
2707
- sessionKey: options.context.sessionKey,
2708
- agentId: options.context.agentId,
2709
- config: runtimeConfig()
2710
- });
2711
- const currentBinding = async (session) => session ? await options.bindingStore.read(currentIdentity(session.sessionId)) : void 0;
2712
- const requestOptions = async (pluginConfig) => {
2713
- const plugin = readCodexPluginConfig(pluginConfig);
2714
- const session = currentSession();
2715
- const binding = await currentBinding(session);
2716
- if (binding?.connectionScope === "supervision") {
2717
- const connection = resolveCodexBindingAppServerConnection({
2718
- binding,
2719
- pluginConfig
2720
- });
2721
- return {
2722
- ...baseRequestOptions(),
2723
- startOptions: connection.appServer.start,
2724
- authProfileId: connection.clientAuthProfileId
2725
- };
2726
- }
2727
- if (plugin.appServer?.homeScope === "user") {
2728
- const connection = resolveCodexBindingAppServerConnection({
2729
- binding,
2730
- pluginConfig
2731
- });
2732
- return {
2733
- ...baseRequestOptions(),
2734
- startOptions: connection.appServer.start,
2735
- authProfileId: null
2736
- };
2737
- }
2738
- if (plugin.supervision?.enabled !== true) throw new Error("Codex native thread access is disabled for this run.");
2739
- return {
2740
- ...baseRequestOptions(),
2741
- startOptions: resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig }).start,
2742
- authProfileId: null
2743
- };
2474
+ const currentIdentity = (sessionId) => {
2475
+ return sessionBindingIdentity({
2476
+ sessionId,
2477
+ sessionKey: options.context.sessionKey,
2478
+ agentId: options.context.agentId,
2479
+ config: requestOptions().config
2480
+ });
2744
2481
  };
2482
+ const currentBinding = async (session) => session ? await options.bindingStore.read(currentIdentity(session.sessionId)) : void 0;
2745
2483
  return {
2746
2484
  name: "codex_threads",
2747
2485
  label: "Codex Threads",
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.",
2486
+ description: "List, read, fork, rename, archive, or restore native Codex threads. Fork to continue a thread safely across Codex clients; do not resume the same thread from two clients.",
2749
2487
  parameters: CodexThreadsParamsSchema,
2750
2488
  async execute(_toolCallId, rawParams) {
2751
- const params = asRecord$1(rawParams);
2489
+ const params = asRecord(rawParams);
2752
2490
  const action = readStringParam(params, "action", {
2753
2491
  required: true,
2754
2492
  label: "action"
2755
2493
  });
2756
2494
  const pluginConfig = options.getPluginConfig();
2757
- const plugin = readCodexPluginConfig(pluginConfig);
2758
- const supervision = plugin.supervision;
2759
- const mayReadRawTranscripts = supervision?.enabled !== true || supervision.allowRawTranscripts === true;
2760
2495
  if (action === "list") {
2761
2496
  const cursor = readStringParam(params, "cursor");
2762
2497
  const searchTerm = readStringParam(params, "search");
2763
- if (searchTerm && !mayReadRawTranscripts) throw new Error("Codex native thread search is disabled while raw transcript access is disabled.");
2764
2498
  const response = await request(pluginConfig, CODEX_CONTROL_METHODS.listThreads, {
2765
2499
  archived: readBoolean(params.archived),
2766
2500
  limit: readLimit(params.limit) ?? 20,
@@ -2770,20 +2504,17 @@ function createCodexThreadsTool(options) {
2770
2504
  sourceKinds: [...CODEX_INTERACTIVE_THREAD_SOURCE_KINDS],
2771
2505
  ...cursor ? { cursor } : {},
2772
2506
  ...searchTerm ? { searchTerm } : {}
2773
- }, await requestOptions(pluginConfig));
2774
- return jsonResult(mayReadRawTranscripts ? response : redactNativeThreadResponse(response));
2507
+ }, requestOptions());
2508
+ return jsonResult(response);
2775
2509
  }
2776
2510
  const threadId = readThreadId(params);
2777
2511
  if (action === "read") {
2778
- const includeTurns = readBoolean(params.include_turns);
2779
- if (includeTurns && !mayReadRawTranscripts) throw new Error("Codex raw transcript reads are disabled for this codex plugin supervision config.");
2780
2512
  const response = await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
2781
2513
  threadId,
2782
- includeTurns
2783
- }, await requestOptions(pluginConfig));
2784
- return jsonResult(mayReadRawTranscripts ? response : redactNativeThreadResponse(response));
2514
+ includeTurns: readBoolean(params.include_turns)
2515
+ }, requestOptions());
2516
+ return jsonResult(response);
2785
2517
  }
2786
- 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.");
2787
2518
  if (action === "rename") {
2788
2519
  const name = readStringParam(params, "name", {
2789
2520
  required: true,
@@ -2792,7 +2523,7 @@ function createCodexThreadsTool(options) {
2792
2523
  await request(pluginConfig, CODEX_CONTROL_METHODS.renameThread, {
2793
2524
  threadId,
2794
2525
  name
2795
- }, await requestOptions(pluginConfig));
2526
+ }, requestOptions());
2796
2527
  return jsonResult({
2797
2528
  action,
2798
2529
  threadId,
@@ -2800,42 +2531,23 @@ function createCodexThreadsTool(options) {
2800
2531
  });
2801
2532
  }
2802
2533
  if (action === "unarchive") {
2803
- const response = await request(pluginConfig, CODEX_CONTROL_METHODS.unarchiveThread, { threadId }, await requestOptions(pluginConfig));
2804
- return jsonResult(mayReadRawTranscripts ? response : redactNativeThreadResponse(response));
2534
+ const response = await request(pluginConfig, CODEX_CONTROL_METHODS.unarchiveThread, { threadId }, requestOptions());
2535
+ return jsonResult(response);
2805
2536
  }
2806
2537
  const session = currentSession();
2807
2538
  const binding = await currentBinding(session);
2808
2539
  if (action === "archive") {
2809
2540
  if (params.confirm !== true) throw new Error("confirm=true is required to archive a native Codex thread");
2810
- if (!session) throw new Error("cannot safely archive a native Codex thread without a session identity");
2811
- const identity = currentIdentity(session.sessionId);
2812
- await options.bindingStore.withThreadArchiveFence(async () => {
2813
- const archivedBinding = await currentBinding(session);
2814
- if (archivedBinding?.threadId === threadId) {
2815
- if (session.modelSelectionLocked) throw new ModelSelectionLockedError();
2816
- assertCodexBindingMayBeReplaced(archivedBinding, "archiving its bound native thread");
2817
- }
2818
- assertThreadMayBeArchived(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
2541
+ if (binding?.threadId === threadId) {
2542
+ if (readThreadStatusType(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
2819
2543
  threadId,
2820
2544
  includeTurns: false
2821
- }, await requestOptions(pluginConfig)), threadId);
2822
- if (await options.bindingStore.hasOtherThreadOwner(threadId, identity)) throw new Error("cannot archive a native Codex thread owned by another OpenClaw session");
2823
- await assertCodexArchiveDescendantsUnowned({
2824
- bindingStore: options.bindingStore,
2825
- threadId,
2826
- listPage: async (listParams) => await request(pluginConfig, CODEX_CONTROL_METHODS.listThreads, listParams, await requestOptions(pluginConfig)),
2827
- assertDescendantIdle: async (descendantThreadId) => {
2828
- assertThreadMayBeArchived(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
2829
- threadId: descendantThreadId,
2830
- includeTurns: false
2831
- }, await requestOptions(pluginConfig)), descendantThreadId);
2832
- }
2833
- });
2834
- await request(pluginConfig, CODEX_CONTROL_METHODS.archiveThread, { threadId }, await requestOptions(pluginConfig));
2835
- if (archivedBinding?.threadId === threadId) await options.bindingStore.mutate(identity, {
2836
- kind: "clear",
2837
- threadId
2838
- });
2545
+ }, requestOptions())) === "active") throw new Error("cannot archive the Codex thread active in this OpenClaw session");
2546
+ }
2547
+ await request(pluginConfig, CODEX_CONTROL_METHODS.archiveThread, { threadId }, requestOptions());
2548
+ if (session && binding?.threadId === threadId) await options.bindingStore.mutate(currentIdentity(session.sessionId), {
2549
+ kind: "clear",
2550
+ threadId
2839
2551
  });
2840
2552
  return jsonResult({
2841
2553
  action,
@@ -2845,21 +2557,16 @@ function createCodexThreadsTool(options) {
2845
2557
  if (action !== "fork") throw new Error(`unsupported codex_threads action: ${action}`);
2846
2558
  const attach = readBoolean(params.attach, true);
2847
2559
  if (attach && !session) throw new Error("cannot attach a Codex fork without an active OpenClaw session");
2848
- if (attach && session?.modelSelectionLocked) throw new ModelSelectionLockedError();
2849
- const usesSupervisionConnection = binding?.connectionScope === "supervision" || plugin.appServer?.homeScope !== "user" && supervision?.enabled === true;
2850
- if (attach && usesSupervisionConnection) throw new Error("Supervised Codex forks must stay detached; set attach=false.");
2851
- if (attach) {
2852
- assertCodexBindingMayBeReplaced(binding, "attaching a different native fork");
2853
- assertThreadMayBeForked(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
2560
+ if (attach && binding?.threadId === threadId) {
2561
+ if (readThreadStatusType(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
2854
2562
  threadId,
2855
2563
  includeTurns: false
2856
- }, await requestOptions(pluginConfig)), threadId);
2564
+ }, requestOptions())) === "active") throw new Error("cannot replace the Codex thread active in this OpenClaw turn");
2857
2565
  }
2858
2566
  const response = await request(pluginConfig, CODEX_CONTROL_METHODS.forkThread, {
2859
2567
  threadId,
2860
- threadSource: "user",
2861
- excludeTurns: true
2862
- }, await requestOptions(pluginConfig));
2568
+ threadSource: "user"
2569
+ }, requestOptions());
2863
2570
  if (!isJsonObject(response) || !isJsonObject(response.thread)) throw new Error("Codex app-server returned an invalid thread/fork response");
2864
2571
  const forkThreadId = typeof response.thread.id === "string" && response.thread.id.trim() ? response.thread.id : void 0;
2865
2572
  if (!forkThreadId) throw new Error("Codex app-server thread/fork response did not include a thread id");
@@ -2875,802 +2582,18 @@ function createCodexThreadsTool(options) {
2875
2582
  }
2876
2583
  })) throw new Error("Codex session binding changed before the fork could be attached");
2877
2584
  }
2878
- const result = {
2585
+ return jsonResult({
2879
2586
  action,
2880
2587
  sourceThreadId: threadId,
2881
2588
  thread: response.thread,
2882
2589
  attached: attach
2883
- };
2884
- return jsonResult(mayReadRawTranscripts ? result : redactNativeThreadResponse(result));
2885
- }
2886
- };
2887
- }
2888
- //#endregion
2889
- //#region extensions/codex/src/supervision-tools.ts
2890
- /**
2891
- * Compatibility tools for the retired Codex Supervisor plugin.
2892
- *
2893
- * Read operations and active-turn controls use the Codex plugin's canonical
2894
- * shared app-server client. Idle threads are never resumed or started here:
2895
- * continuation belongs to the Codex harness, which installs approval and tool
2896
- * handlers before it starts or resumes the harness-owned Codex thread.
2897
- */
2898
- /** Legacy endpoint env retained for the shipped Supervisor tool contract. */
2899
- const LEGACY_CODEX_SUPERVISOR_ENDPOINTS_ENV = "OPENCLAW_CODEX_SUPERVISOR_ENDPOINTS";
2900
- /** Legacy standalone-MCP transcript gate. Agent tools use canonical config. */
2901
- const LEGACY_CODEX_SUPERVISOR_RAW_TRANSCRIPTS_ENV = "OPENCLAW_CODEX_SUPERVISOR_ALLOW_RAW_TRANSCRIPTS";
2902
- /** Legacy standalone-MCP write gate. Agent tools use canonical config. */
2903
- const LEGACY_CODEX_SUPERVISOR_WRITE_CONTROLS_ENV = "OPENCLAW_CODEX_SUPERVISOR_ALLOW_WRITE_CONTROLS";
2904
- const CODEX_SUPERVISION_COMPAT_TOOL_NAMES = [
2905
- "codex_endpoint_probe",
2906
- "codex_sessions_list",
2907
- "codex_session_read",
2908
- "codex_session_send",
2909
- "codex_session_interrupt"
2910
- ];
2911
- const EmptyParamsSchema = Type.Object({}, { additionalProperties: false });
2912
- const SessionsListParamsSchema = Type.Object({
2913
- include_stored: Type.Optional(Type.Boolean()),
2914
- max_stored_sessions: Type.Optional(Type.Integer({
2915
- minimum: 1,
2916
- maximum: 1e3
2917
- }))
2918
- }, { additionalProperties: false });
2919
- const SessionReadParamsSchema = Type.Object({
2920
- endpoint_id: Type.Optional(Type.String()),
2921
- thread_id: Type.String(),
2922
- include_turns: Type.Optional(Type.Boolean())
2923
- }, { additionalProperties: false });
2924
- const SessionSendParamsSchema = Type.Object({
2925
- endpoint_id: Type.Optional(Type.String()),
2926
- thread_id: Type.String(),
2927
- text: Type.String(),
2928
- mode: Type.Optional(Type.Union([
2929
- Type.Literal("auto"),
2930
- Type.Literal("start"),
2931
- Type.Literal("steer")
2932
- ]))
2933
- }, { additionalProperties: false });
2934
- const SessionInterruptParamsSchema = Type.Object({
2935
- endpoint_id: Type.Optional(Type.String()),
2936
- thread_id: Type.String(),
2937
- turn_id: Type.Optional(Type.String())
2938
- }, { additionalProperties: false });
2939
- const ALL_CODEX_THREAD_SOURCE_KINDS = [
2940
- "cli",
2941
- "vscode",
2942
- "exec",
2943
- "appServer",
2944
- "subAgent",
2945
- "subAgentReview",
2946
- "subAgentCompact",
2947
- "subAgentThreadSpawn",
2948
- "subAgentOther",
2949
- "unknown"
2950
- ];
2951
- const DEFAULT_MAX_STORED_SESSIONS = 200;
2952
- const PAGE_LIMIT = 100;
2953
- const MAX_COMPAT_PAGINATION_PAGES = 100;
2954
- const MAX_COMPAT_CURSOR_LENGTH = 4096;
2955
- const MAX_COMPAT_THREAD_ID_LENGTH = 4096;
2956
- var CodexSupervisionPolicyError = class extends Error {};
2957
- function asRecord(value) {
2958
- return isRecord(value) ? value : {};
2959
- }
2960
- function asRecordArray(value) {
2961
- return Array.isArray(value) ? value.filter(isRecord) : [];
2962
- }
2963
- function readCompatNextCursor(value, method) {
2964
- if (value === null || value === void 0) return;
2965
- if (typeof value !== "string" || value.trim().length === 0 || value.length > MAX_COMPAT_CURSOR_LENGTH) throw new Error(`Codex ${method} returned an invalid nextCursor`);
2966
- return value;
2967
- }
2968
- function readCompatThreadId(value, method, index) {
2969
- if (typeof value !== "string" || value.trim().length === 0 || value.length > MAX_COMPAT_THREAD_ID_LENGTH) throw new Error(`Codex ${method} returned an invalid thread id at data[${index}]`);
2970
- return value;
2971
- }
2972
- function readLoadedThreadIds(data) {
2973
- if (data.length > PAGE_LIMIT) throw new Error(`Codex thread/loaded/list returned more than ${PAGE_LIMIT} entries`);
2974
- return data.map((entry, index) => readCompatThreadId(entry, "thread/loaded/list", index));
2975
- }
2976
- function readStoredThreads(data, maxEntries) {
2977
- if (data.length > maxEntries) throw new Error(`Codex thread/list returned more than ${maxEntries} entries`);
2978
- return data.map((entry, index) => {
2979
- if (!isRecord(entry)) throw new Error(`Codex thread/list returned an invalid entry at data[${index}]`);
2980
- readCompatThreadId(entry.id, "thread/list", index);
2981
- return entry;
2982
- });
2983
- }
2984
- function readBooleanParam(params, key) {
2985
- return params[key] === true;
2986
- }
2987
- function readIntegerParam(params, key) {
2988
- const value = params[key];
2989
- if (value === void 0) return;
2990
- if (typeof value !== "number" || !Number.isInteger(value)) throw new Error(`${key} must be an integer`);
2991
- if (value < 1 || value > 1e3) throw new Error(`${key} must be between 1 and 1000`);
2992
- return value;
2993
- }
2994
- function readModeParam(params) {
2995
- const mode = readStringParam(params, "mode");
2996
- if (!mode) return;
2997
- if (mode === "auto" || mode === "start" || mode === "steer") return mode;
2998
- throw new Error("mode must be auto, start, or steer");
2999
- }
3000
- function normalizeEndpointId(value, index) {
3001
- const trimmed = value.trim();
3002
- return trimmed ? trimmed.replace(/[^a-zA-Z0-9_.:-]/g, "-") : `endpoint-${index + 1}`;
3003
- }
3004
- function normalizeConfiguredEndpoint(endpoint, index) {
3005
- return {
3006
- id: normalizeEndpointId(endpoint.id ?? endpoint.label ?? "", index),
3007
- ...endpoint.label?.trim() ? { label: endpoint.label.trim() } : {},
3008
- configured: endpoint
3009
- };
3010
- }
3011
- function parseEndpointRecord(value) {
3012
- if (!isRecord(value)) return;
3013
- const transport = typeof value.transport === "string" ? value.transport : void 0;
3014
- const common = {
3015
- ...typeof value.id === "string" ? { id: value.id } : {},
3016
- ...typeof value.label === "string" ? { label: value.label } : {}
3017
- };
3018
- if (transport === "websocket" && typeof value.url === "string") return {
3019
- ...common,
3020
- transport,
3021
- url: value.url,
3022
- ...typeof value.authTokenEnv === "string" ? { authTokenEnv: value.authTokenEnv } : {}
3023
- };
3024
- if (transport === "stdio-proxy" || transport === void 0) {
3025
- const args = Array.isArray(value.args) ? value.args.filter((entry) => typeof entry === "string") : void 0;
3026
- return {
3027
- ...common,
3028
- transport: "stdio-proxy",
3029
- ...typeof value.command === "string" ? { command: value.command } : {},
3030
- ...args && args.length > 0 ? { args } : {},
3031
- ...typeof value.cwd === "string" ? { cwd: value.cwd } : {}
3032
- };
3033
- }
3034
- }
3035
- function endpointFromToken(token, index) {
3036
- const trimmed = token.trim();
3037
- if (!trimmed) return;
3038
- if (trimmed.startsWith("ws://") || trimmed.startsWith("wss://") || trimmed.startsWith("unix://")) return {
3039
- id: normalizeEndpointId("", index),
3040
- transport: "websocket",
3041
- url: trimmed
3042
- };
3043
- if (trimmed === "local" || trimmed === "proxy" || trimmed === "stdio") return {
3044
- id: "local",
3045
- label: "local Codex app-server",
3046
- transport: "stdio-proxy"
3047
- };
3048
- const separatorIndex = trimmed.indexOf("=");
3049
- const id = separatorIndex >= 0 ? trimmed.slice(0, separatorIndex) : trimmed;
3050
- const url = separatorIndex >= 0 ? trimmed.slice(separatorIndex + 1) : void 0;
3051
- if (url?.startsWith("ws://") || url?.startsWith("wss://") || url?.startsWith("unix://")) return {
3052
- id: normalizeEndpointId(id, index),
3053
- transport: "websocket",
3054
- url
3055
- };
3056
- }
3057
- function requireUniqueEndpointIds(endpoints) {
3058
- const seen = /* @__PURE__ */ new Set();
3059
- for (const endpoint of endpoints) {
3060
- if (seen.has(endpoint.id)) throw new Error(`duplicate Codex supervisor endpoint id: ${endpoint.id}`);
3061
- seen.add(endpoint.id);
3062
- }
3063
- return endpoints;
3064
- }
3065
- function readLegacyEnvEndpoints(env) {
3066
- const raw = env[LEGACY_CODEX_SUPERVISOR_ENDPOINTS_ENV]?.trim();
3067
- if (!raw) return;
3068
- if (raw.startsWith("[")) {
3069
- const parsed = JSON.parse(raw);
3070
- if (!Array.isArray(parsed)) throw new Error(`${LEGACY_CODEX_SUPERVISOR_ENDPOINTS_ENV} must be a JSON array`);
3071
- return parsed.map((entry) => parseEndpointRecord(entry)).filter((entry) => Boolean(entry));
3072
- }
3073
- return raw.split(",").map(endpointFromToken).filter((entry) => Boolean(entry));
3074
- }
3075
- function resolveEndpoints(pluginConfig, env, runtimeConfig) {
3076
- const configured = readCodexPluginConfig(pluginConfig).supervision?.endpoints;
3077
- const endpoints = configured?.length ? configured : readLegacyEnvEndpoints(env);
3078
- return (endpoints ? requireUniqueEndpointIds(endpoints.map(normalizeConfiguredEndpoint)) : [{
3079
- id: "local",
3080
- label: "local Codex app-server"
3081
- }]).map((endpoint) => {
3082
- const resolved = {
3083
- id: endpoint.id,
3084
- connectionKey: supervisionEndpointConnectionKey({
3085
- endpoint,
3086
- pluginConfig,
3087
- env,
3088
- runtimeConfig
3089
- })
3090
- };
3091
- if (endpoint.label !== void 0) resolved.label = endpoint.label;
3092
- if (endpoint.configured !== void 0) resolved.configured = endpoint.configured;
3093
- return resolved;
3094
- });
3095
- }
3096
- function resolveEndpointStartOptions(params) {
3097
- const base = resolveCodexSupervisionAppServerRuntimeOptions({
3098
- pluginConfig: params.pluginConfig,
3099
- env: params.env
3100
- }).start;
3101
- const configured = params.endpoint.configured;
3102
- if (!configured) return base;
3103
- if (!("url" in configured)) return {
3104
- transport: "stdio",
3105
- homeScope: "user",
3106
- command: configured.command?.trim() || "codex",
3107
- commandSource: "config",
3108
- args: configured.args?.length ? [...configured.args] : [
3109
- "app-server",
3110
- "--listen",
3111
- "stdio://"
3112
- ],
3113
- ...configured.cwd !== void 0 ? { cwd: configured.cwd } : {},
3114
- headers: {}
3115
- };
3116
- const tokenEnv = configured.authTokenEnv?.trim();
3117
- const authToken = tokenEnv ? params.env[tokenEnv]?.trim() : void 0;
3118
- const startOptions = {
3119
- transport: configured.url.startsWith("unix://") ? "unix" : "websocket",
3120
- ...configured.url.startsWith("unix://") ? { homeScope: "user" } : {},
3121
- command: base.command,
3122
- ...base.commandSource ? { commandSource: base.commandSource } : {},
3123
- ...base.managedFallbackCommandPaths ? { managedFallbackCommandPaths: [...base.managedFallbackCommandPaths] } : {},
3124
- args: [...base.args],
3125
- url: configured.url,
3126
- ...authToken ? { authToken } : {},
3127
- headers: {}
3128
- };
3129
- if (params.validateSecurity !== false) assertCodexAppServerConnectionSecurity(startOptions);
3130
- return startOptions;
3131
- }
3132
- function supervisionEndpointConnectionKey(params) {
3133
- const startOptions = resolveEndpointStartOptions({
3134
- ...params,
3135
- validateSecurity: false
3136
- });
3137
- const usesNativeAuth = params.endpoint.configured !== void 0 || startOptions.homeScope === "user";
3138
- const agentDir = usesNativeAuth ? void 0 : resolveDefaultAgentDir(params.runtimeConfig ?? {});
3139
- const authProfileId = usesNativeAuth ? void 0 : resolveCodexAppServerAuthProfileIdForAgent({
3140
- agentDir,
3141
- config: params.runtimeConfig
3142
- });
3143
- const fallbackApiKeyCacheKey = authProfileId ? void 0 : resolveCodexAppServerFallbackApiKeyCacheKey({ startOptions });
3144
- return JSON.stringify({
3145
- homeScope: startOptions.homeScope ?? null,
3146
- startOptions: codexAppServerStartOptionsKey(startOptions, {
3147
- authProfileId,
3148
- agentDir,
3149
- fallbackApiKeyCacheKey
3150
- })
3151
- });
3152
- }
3153
- function createCanonicalEndpointRequest(options) {
3154
- return async (endpoint, method, requestParams) => {
3155
- const pluginConfig = options.getPluginConfig();
3156
- const env = options.env ?? process.env;
3157
- const runtime = resolveCodexSupervisionAppServerRuntimeOptions({
3158
- pluginConfig,
3159
- env
3160
- });
3161
- const config = options.getRuntimeConfig?.();
3162
- const startOptions = resolveEndpointStartOptions({
3163
- endpoint,
3164
- pluginConfig,
3165
- env
3166
- });
3167
- return await requestCodexAppServerJson({
3168
- method,
3169
- requestParams,
3170
- timeoutMs: runtime.requestTimeoutMs,
3171
- startOptions,
3172
- ...endpoint.configured || startOptions.homeScope === "user" ? { authProfileId: null } : {},
3173
- ...config ? { config } : {}
3174
- });
3175
- };
3176
- }
3177
- function statusType(thread) {
3178
- const status = isRecord(thread.status) ? thread.status.type : thread.status;
3179
- return typeof status === "string" ? status : "unknown";
3180
- }
3181
- function sourceLabel(value) {
3182
- if (typeof value === "string") return value;
3183
- if (!isRecord(value)) return;
3184
- if (typeof value.custom === "string") return `custom:${value.custom}`;
3185
- return Object.keys(value).toSorted()[0];
3186
- }
3187
- function toSession(endpointId, thread, humanAttached) {
3188
- if (typeof thread.id !== "string") return;
3189
- const source = sourceLabel(thread.source);
3190
- return {
3191
- endpointId,
3192
- threadId: thread.id,
3193
- status: statusType(thread),
3194
- ...typeof thread.sessionId === "string" ? { sessionId: thread.sessionId } : {},
3195
- ...typeof thread.cwd === "string" ? { cwd: thread.cwd } : {},
3196
- ...typeof thread.preview === "string" ? { preview: thread.preview } : {},
3197
- ...typeof thread.name === "string" || thread.name === null ? { name: thread.name } : {},
3198
- ...source ? { source } : {},
3199
- ...typeof thread.updatedAt === "number" ? { updatedAt: thread.updatedAt } : {},
3200
- ...humanAttached !== void 0 ? { humanAttached } : {}
3201
- };
3202
- }
3203
- function threadFromRead(value) {
3204
- return isRecord(value) && isRecord(value.thread) ? value.thread : void 0;
3205
- }
3206
- function isLoadedThreadReadMiss(error) {
3207
- const message = error instanceof Error ? error.message : String(error);
3208
- return message.includes("thread not found") || message.includes("thread not loaded");
3209
- }
3210
- async function readThread(params) {
3211
- try {
3212
- const thread = threadFromRead(await params.request(params.endpoint, "thread/read", {
3213
- threadId: params.threadId,
3214
- includeTurns: params.includeTurns
3215
- }));
3216
- if (!thread) throw new Error("Codex thread/read returned an invalid response");
3217
- return thread;
3218
- } catch (error) {
3219
- if (!params.includeTurns || !String(error).includes("not materialized yet")) throw error;
3220
- const thread = threadFromRead(await params.request(params.endpoint, "thread/read", {
3221
- threadId: params.threadId,
3222
- includeTurns: false
3223
- }));
3224
- if (!thread) throw new Error("Codex thread/read returned an invalid response", { cause: error });
3225
- return thread;
3226
- }
3227
- }
3228
- async function listLoadedSessions(request, endpoint) {
3229
- const sessions = [];
3230
- const seenCursors = /* @__PURE__ */ new Set();
3231
- let cursor;
3232
- for (let pageIndex = 0; pageIndex < MAX_COMPAT_PAGINATION_PAGES; pageIndex += 1) {
3233
- const listed = await request(endpoint, "thread/loaded/list", {
3234
- limit: PAGE_LIMIT,
3235
- ...cursor ? { cursor } : {}
3236
- });
3237
- if (!isRecord(listed) || !Array.isArray(listed.data)) throw new Error("Codex thread/loaded/list returned an invalid response");
3238
- const threadIds = readLoadedThreadIds(listed.data);
3239
- for (const threadId of threadIds) {
3240
- if (sessions.some((entry) => entry.threadId === threadId)) continue;
3241
- try {
3242
- const thread = await readThread({
3243
- request,
3244
- endpoint,
3245
- threadId,
3246
- includeTurns: false
3247
- });
3248
- const session = toSession(endpoint.id, thread, true);
3249
- if (session) sessions.push(session);
3250
- } catch (error) {
3251
- if (!isLoadedThreadReadMiss(error)) throw error;
3252
- }
3253
- }
3254
- const nextCursor = readCompatNextCursor(listed.nextCursor, "thread/loaded/list");
3255
- if (nextCursor && seenCursors.has(nextCursor)) throw new Error(`Codex thread/loaded/list returned repeated cursor ${nextCursor}`);
3256
- if (nextCursor) seenCursors.add(nextCursor);
3257
- cursor = nextCursor;
3258
- if (!cursor) break;
3259
- }
3260
- if (cursor) throw new Error(`Codex thread/loaded/list exceeded ${MAX_COMPAT_PAGINATION_PAGES} pages with a continuation cursor`);
3261
- return sessions;
3262
- }
3263
- async function listStoredSessions(params) {
3264
- const sessions = [];
3265
- const seenCursors = /* @__PURE__ */ new Set();
3266
- let cursor;
3267
- for (let pageIndex = 0; pageIndex < MAX_COMPAT_PAGINATION_PAGES; pageIndex += 1) {
3268
- const remaining = params.limit - sessions.length;
3269
- if (remaining <= 0) break;
3270
- const pageLimit = Math.min(PAGE_LIMIT, remaining);
3271
- const listed = await params.request(params.endpoint, "thread/list", {
3272
- archived: false,
3273
- limit: pageLimit,
3274
- sourceKinds: [...ALL_CODEX_THREAD_SOURCE_KINDS],
3275
- modelProviders: [],
3276
- sortKey: "recency_at",
3277
- sortDirection: "desc",
3278
- useStateDbOnly: true,
3279
- ...cursor ? { cursor } : {}
3280
- });
3281
- if (!isRecord(listed) || !Array.isArray(listed.data)) throw new Error("Codex thread/list returned an invalid response");
3282
- for (const thread of readStoredThreads(listed.data, pageLimit)) {
3283
- if (sessions.length >= params.limit) break;
3284
- const session = toSession(params.endpoint.id, thread);
3285
- if (session && !sessions.some((entry) => entry.threadId === session.threadId)) sessions.push(session);
3286
- }
3287
- const nextCursor = readCompatNextCursor(listed.nextCursor, "thread/list");
3288
- if (nextCursor && sessions.length < params.limit && seenCursors.has(nextCursor)) throw new Error(`Codex thread/list returned repeated cursor ${nextCursor}`);
3289
- if (nextCursor) seenCursors.add(nextCursor);
3290
- cursor = nextCursor;
3291
- if (!cursor || sessions.length >= params.limit) break;
3292
- }
3293
- if (cursor && sessions.length < params.limit) throw new Error(`Codex thread/list exceeded ${MAX_COMPAT_PAGINATION_PAGES} pages with a continuation cursor`);
3294
- return sessions;
3295
- }
3296
- async function listSessionSnapshot(params) {
3297
- const sessions = [];
3298
- const errors = [];
3299
- for (const endpoint of params.endpoints) try {
3300
- const loaded = await listLoadedSessions(params.request, endpoint);
3301
- sessions.push(...loaded);
3302
- if (params.includeStored) {
3303
- const stored = await listStoredSessions({
3304
- request: params.request,
3305
- endpoint,
3306
- limit: params.maxStoredSessions ?? DEFAULT_MAX_STORED_SESSIONS
3307
2590
  });
3308
- for (const session of stored) if (!sessions.some((entry) => entry.endpointId === endpoint.id && entry.threadId === session.threadId)) sessions.push(session);
3309
2591
  }
3310
- } catch (error) {
3311
- if (error instanceof CodexSupervisionPolicyError) throw error;
3312
- errors.push({
3313
- endpointId: endpoint.id,
3314
- ok: false,
3315
- detail: error instanceof Error ? error.message : String(error)
3316
- });
3317
- }
3318
- return {
3319
- sessions,
3320
- errors
3321
2592
  };
3322
2593
  }
3323
- async function resolveEndpointForThread(params) {
3324
- if (params.endpointId) {
3325
- const endpoint = params.endpoints.find((entry) => entry.id === params.endpointId);
3326
- if (!endpoint) throw new Error(`Unknown Codex supervisor endpoint: ${params.endpointId}`);
3327
- return endpoint;
3328
- }
3329
- const matches = [];
3330
- for (const endpoint of params.endpoints) try {
3331
- if ((await readThread({
3332
- request: params.request,
3333
- endpoint,
3334
- threadId: params.threadId,
3335
- includeTurns: false
3336
- })).id === params.threadId) matches.push(endpoint);
3337
- } catch (error) {
3338
- if (error instanceof CodexSupervisionPolicyError) throw error;
3339
- if (!isLoadedThreadReadMiss(error)) continue;
3340
- }
3341
- if (matches.length === 1) return expectDefined(matches[0], "single matching Codex supervision endpoint");
3342
- if (matches.length > 1) throw new Error(`Codex thread id is ambiguous across endpoints: ${params.threadId}`);
3343
- throw new Error(`Codex thread not found: ${params.threadId}`);
3344
- }
3345
- function findInProgressTurnId(thread) {
3346
- const turns = asRecordArray(thread.turns);
3347
- for (const turn of turns.toReversed()) if (turn.status === "inProgress" && typeof turn.id === "string") return turn.id;
3348
- }
3349
- async function resolveInProgressTurnId(params) {
3350
- const inline = findInProgressTurnId(params.thread);
3351
- if (inline) return inline;
3352
- try {
3353
- const response = await params.request(params.endpoint, "thread/turns/list", {
3354
- threadId: params.threadId,
3355
- limit: 10,
3356
- sortDirection: "desc",
3357
- itemsView: "summary"
3358
- });
3359
- return isRecord(response) ? findInProgressTurnId({ turns: response.data }) : void 0;
3360
- } catch (error) {
3361
- if (error instanceof CodexSupervisionPolicyError) throw error;
3362
- return;
3363
- }
3364
- }
3365
- function redactString(value) {
3366
- return value.replace(/\b(?:sk|glpat|xox[baprs])-[-_a-zA-Z0-9]{12,}\b/g, "[redacted]").replace(/\b(?:ghp|gho|ghu|ghs)_[-_a-zA-Z0-9]{12,}\b/g, "[redacted]").replace(/\bBearer\s+[-._~+/a-zA-Z0-9]+=*/g, "Bearer [redacted]");
3367
- }
3368
- /** Redacts secret-bearing fields before legacy tool results leave the plugin. */
3369
- function redactCodexSupervisionValue(value, key = "") {
3370
- if (typeof value === "string") return /authorization|password|secret|token|api[-_]?key/i.test(key) ? "[redacted]" : redactString(value);
3371
- if (Array.isArray(value)) return value.map((entry) => redactCodexSupervisionValue(entry));
3372
- if (!isRecord(value)) return value;
3373
- return Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [entryKey, redactCodexSupervisionValue(entryValue, entryKey)]));
3374
- }
3375
- function redactEndpointUrl(value) {
3376
- if (value.startsWith("unix://")) return "unix://";
3377
- try {
3378
- const url = new URL(value);
3379
- url.username = "";
3380
- url.password = "";
3381
- if (url.search) url.search = "?[redacted]";
3382
- return url.toString();
3383
- } catch {
3384
- return "[redacted]";
3385
- }
3386
- }
3387
- function endpointResult(endpoint, pluginConfig, env) {
3388
- const configured = endpoint.configured;
3389
- if (configured && (configured.transport === "stdio-proxy" || configured.transport === void 0)) return {
3390
- id: endpoint.id,
3391
- transport: "stdio-proxy",
3392
- ...endpoint.label ? { label: endpoint.label } : {}
3393
- };
3394
- if (configured?.transport === "websocket") return {
3395
- id: endpoint.id,
3396
- transport: "websocket",
3397
- ...endpoint.label ? { label: endpoint.label } : {},
3398
- url: redactEndpointUrl(configured.url)
3399
- };
3400
- const start = resolveCodexSupervisionAppServerRuntimeOptions({
3401
- pluginConfig,
3402
- env
3403
- }).start;
3404
- return {
3405
- id: endpoint.id,
3406
- transport: start.transport === "stdio" ? "stdio-proxy" : "websocket",
3407
- ...endpoint.label ? { label: endpoint.label } : {},
3408
- ...start.transport === "stdio" ? {} : { url: redactEndpointUrl(start.transport === "unix" ? start.url ?? "unix://" : start.url ?? "") }
3409
- };
3410
- }
3411
- function sanitizeSessionListResult(result, includeTranscriptDerivedFields) {
3412
- return {
3413
- sessions: result.sessions.map((session) => {
3414
- const sanitized = redactCodexSupervisionValue(session);
3415
- if (!includeTranscriptDerivedFields) {
3416
- delete sanitized.preview;
3417
- delete sanitized.name;
3418
- }
3419
- return sanitized;
3420
- }),
3421
- errors: includeTranscriptDerivedFields ? redactCodexSupervisionValue(result.errors) : result.errors.map(({ endpointId, ok }) => ({
3422
- endpointId,
3423
- ok
3424
- }))
3425
- };
3426
- }
3427
- function requireSupervisionEnabled(pluginConfig) {
3428
- if (readCodexPluginConfig(pluginConfig).supervision?.enabled !== true) throw new CodexSupervisionPolicyError("Codex supervision is disabled in the codex plugin config.");
3429
- }
3430
- function requireOwnerAccess(options) {
3431
- if (!options.senderIsOwner) throw new CodexSupervisionPolicyError("Codex supervision compatibility tools require an owner-authorized sender.");
3432
- }
3433
- function resolveToolPolicy(options, pluginConfig) {
3434
- const config = readCodexPluginConfig(pluginConfig).supervision;
3435
- const env = options.env ?? process.env;
3436
- return {
3437
- allowRawTranscripts: config?.allowRawTranscripts === true || options.useLegacyMcpPolicyEnv === true && env[LEGACY_CODEX_SUPERVISOR_RAW_TRANSCRIPTS_ENV] === "1",
3438
- allowWriteControls: config?.allowWriteControls === true || options.useLegacyMcpPolicyEnv === true && env[LEGACY_CODEX_SUPERVISOR_WRITE_CONTROLS_ENV] === "1"
3439
- };
3440
- }
3441
- function requireRawTranscriptAccess(options, pluginConfig) {
3442
- if (!resolveToolPolicy(options, pluginConfig).allowRawTranscripts) throw new CodexSupervisionPolicyError("Codex session reads are disabled for this codex plugin supervision config.");
3443
- }
3444
- function requireWriteAccess(options, pluginConfig) {
3445
- if (!resolveToolPolicy(options, pluginConfig).allowWriteControls) throw new CodexSupervisionPolicyError("Codex write controls are disabled for this codex plugin supervision config.");
3446
- }
3447
- function requireLiveToolPolicy(options, policy) {
3448
- requireOwnerAccess(options);
3449
- const pluginConfig = options.getPluginConfig();
3450
- requireSupervisionEnabled(pluginConfig);
3451
- if (policy === "raw-transcripts") requireRawTranscriptAccess(options, pluginConfig);
3452
- else if (policy === "write-controls") requireWriteAccess(options, pluginConfig);
3453
- return {
3454
- pluginConfig,
3455
- endpoints: resolveEndpoints(pluginConfig, options.env ?? process.env, options.getRuntimeConfig?.())
3456
- };
3457
- }
3458
- function requireCurrentEndpoint(options, policy, endpoint) {
3459
- const { endpoints } = requireLiveToolPolicy(options, policy);
3460
- const currentEndpoint = endpoints.find((candidate) => candidate.id === endpoint.id);
3461
- if (!currentEndpoint || currentEndpoint.connectionKey !== endpoint.connectionKey) throw new CodexSupervisionPolicyError(`Codex supervision endpoint ${endpoint.id} was removed or changed during the request.`);
3462
- return currentEndpoint;
3463
- }
3464
- function requireCurrentEndpointSet(options, expected) {
3465
- const current = requireLiveToolPolicy(options, "enabled");
3466
- if (!(current.endpoints.length === expected.length && expected.every((endpoint) => current.endpoints.some((candidate) => candidate.id === endpoint.id && candidate.connectionKey === endpoint.connectionKey)))) throw new CodexSupervisionPolicyError("Codex supervision endpoint configuration changed during the request.");
3467
- return { pluginConfig: current.pluginConfig };
3468
- }
3469
- function createPolicyGuardedRequest(options, request, policy) {
3470
- return async (endpoint, method, params) => {
3471
- return await request(requireCurrentEndpoint(options, policy, endpoint), method, params);
3472
- };
3473
- }
3474
- function idleContinuationError(threadId) {
3475
- return /* @__PURE__ */ new Error(`Codex thread ${threadId} is idle. Continue it from Codex Sessions so OpenClaw can install the Codex harness approval and tool handlers before resume.`);
3476
- }
3477
- /** Builds the five shipped Codex Supervisor compatibility tools. */
3478
- function createCodexSupervisionTools(options) {
3479
- const baseRequest = options.request ?? createCanonicalEndpointRequest(options);
3480
- const request = createPolicyGuardedRequest(options, baseRequest, "enabled");
3481
- const rawTranscriptRequest = createPolicyGuardedRequest(options, baseRequest, "raw-transcripts");
3482
- const writeRequest = createPolicyGuardedRequest(options, baseRequest, "write-controls");
3483
- const current = () => {
3484
- return requireLiveToolPolicy(options, "enabled");
3485
- };
3486
- return [
3487
- {
3488
- name: "codex_endpoint_probe",
3489
- label: "Codex Endpoint Probe",
3490
- description: "Check configured Codex app-server endpoints.",
3491
- parameters: EmptyParamsSchema,
3492
- execute: async () => {
3493
- const { pluginConfig, endpoints } = current();
3494
- const health = [];
3495
- for (const endpoint of endpoints) try {
3496
- await request(endpoint, "thread/loaded/list", { limit: 1 });
3497
- health.push({
3498
- endpointId: endpoint.id,
3499
- ok: true
3500
- });
3501
- } catch (error) {
3502
- if (error instanceof CodexSupervisionPolicyError) throw error;
3503
- health.push({
3504
- endpointId: endpoint.id,
3505
- ok: false
3506
- });
3507
- }
3508
- requireCurrentEndpointSet(options, endpoints);
3509
- return jsonResult({
3510
- summary: `codex endpoints: ${health.filter((entry) => entry.ok).length}/${health.length} ok`,
3511
- endpoints: endpoints.map((endpoint) => endpointResult(endpoint, pluginConfig, options.env ?? process.env)),
3512
- health
3513
- });
3514
- }
3515
- },
3516
- {
3517
- name: "codex_sessions_list",
3518
- label: "Codex Sessions List",
3519
- description: "List Codex sessions visible to the OpenClaw supervisor.",
3520
- parameters: SessionsListParamsSchema,
3521
- execute: async (_toolCallId, rawParams) => {
3522
- const params = asRecord(rawParams);
3523
- const { endpoints } = current();
3524
- const result = await listSessionSnapshot({
3525
- endpoints,
3526
- request,
3527
- includeStored: readBooleanParam(params, "include_stored"),
3528
- maxStoredSessions: readIntegerParam(params, "max_stored_sessions")
3529
- });
3530
- const { pluginConfig } = requireCurrentEndpointSet(options, endpoints);
3531
- return jsonResult({
3532
- summary: `codex sessions: ${result.sessions.length}`,
3533
- ...sanitizeSessionListResult(result, resolveToolPolicy(options, pluginConfig).allowRawTranscripts)
3534
- });
3535
- }
3536
- },
3537
- {
3538
- name: "codex_session_read",
3539
- label: "Codex Session Read",
3540
- description: "Read one Codex session transcript from app-server.",
3541
- parameters: SessionReadParamsSchema,
3542
- execute: async (_toolCallId, rawParams) => {
3543
- const { endpoints, pluginConfig } = current();
3544
- requireRawTranscriptAccess(options, pluginConfig);
3545
- const params = asRecord(rawParams);
3546
- const threadId = readStringParam(params, "thread_id", { required: true });
3547
- const endpoint = await resolveEndpointForThread({
3548
- endpoints,
3549
- request: rawTranscriptRequest,
3550
- endpointId: readStringParam(params, "endpoint_id"),
3551
- threadId
3552
- });
3553
- const thread = await readThread({
3554
- request: rawTranscriptRequest,
3555
- endpoint,
3556
- threadId,
3557
- includeTurns: readBooleanParam(params, "include_turns")
3558
- });
3559
- requireCurrentEndpoint(options, "raw-transcripts", endpoint);
3560
- return jsonResult({
3561
- summary: `codex session: ${threadId}`,
3562
- response: redactCodexSupervisionValue({ thread })
3563
- });
3564
- }
3565
- },
3566
- {
3567
- name: "codex_session_send",
3568
- label: "Codex Session Send",
3569
- description: "Steer an active Codex turn. Idle sessions must be continued through Codex Sessions.",
3570
- parameters: SessionSendParamsSchema,
3571
- execute: async (_toolCallId, rawParams) => {
3572
- const { endpoints, pluginConfig } = current();
3573
- requireWriteAccess(options, pluginConfig);
3574
- const params = asRecord(rawParams);
3575
- const threadId = readStringParam(params, "thread_id", { required: true });
3576
- const text = readStringParam(params, "text", {
3577
- required: true,
3578
- allowEmpty: false
3579
- });
3580
- if ((readModeParam(params) ?? "auto") === "start") throw idleContinuationError(threadId);
3581
- const endpoint = await resolveEndpointForThread({
3582
- endpoints,
3583
- request: writeRequest,
3584
- endpointId: readStringParam(params, "endpoint_id"),
3585
- threadId
3586
- });
3587
- const thread = await readThread({
3588
- request: writeRequest,
3589
- endpoint,
3590
- threadId,
3591
- includeTurns: true
3592
- });
3593
- requireCurrentEndpoint(options, "write-controls", endpoint);
3594
- if (statusType(thread) !== "active") throw idleContinuationError(threadId);
3595
- const turnId = await resolveInProgressTurnId({
3596
- request: writeRequest,
3597
- endpoint,
3598
- thread,
3599
- threadId
3600
- });
3601
- if (!turnId) throw new Error(`Codex thread ${threadId} is active but no in-progress turn is readable`);
3602
- await writeRequest(endpoint, "turn/steer", {
3603
- threadId,
3604
- expectedTurnId: turnId,
3605
- input: [{
3606
- type: "text",
3607
- text,
3608
- text_elements: []
3609
- }]
3610
- });
3611
- const result = {
3612
- endpointId: endpoint.id,
3613
- threadId,
3614
- mode: "steer",
3615
- turnId
3616
- };
3617
- return jsonResult({
3618
- summary: `codex steer: ${turnId}`,
3619
- result
3620
- });
3621
- }
3622
- },
3623
- {
3624
- name: "codex_session_interrupt",
3625
- label: "Codex Session Interrupt",
3626
- description: "Interrupt an active Codex turn.",
3627
- parameters: SessionInterruptParamsSchema,
3628
- execute: async (_toolCallId, rawParams) => {
3629
- const { endpoints, pluginConfig } = current();
3630
- requireWriteAccess(options, pluginConfig);
3631
- const params = asRecord(rawParams);
3632
- const threadId = readStringParam(params, "thread_id", { required: true });
3633
- const endpoint = await resolveEndpointForThread({
3634
- endpoints,
3635
- request: writeRequest,
3636
- endpointId: readStringParam(params, "endpoint_id"),
3637
- threadId
3638
- });
3639
- const thread = await readThread({
3640
- request: writeRequest,
3641
- endpoint,
3642
- threadId,
3643
- includeTurns: true
3644
- });
3645
- requireCurrentEndpoint(options, "write-controls", endpoint);
3646
- if (statusType(thread) !== "active") throw new Error(`Codex thread ${threadId} has no active turn to interrupt`);
3647
- const turnId = readStringParam(params, "turn_id") ?? await resolveInProgressTurnId({
3648
- request: writeRequest,
3649
- endpoint,
3650
- thread,
3651
- threadId
3652
- });
3653
- if (!turnId) throw new Error(`Codex thread ${threadId} has no readable in-progress turn`);
3654
- await writeRequest(endpoint, "turn/interrupt", {
3655
- threadId,
3656
- turnId
3657
- });
3658
- const result = {
3659
- endpointId: endpoint.id,
3660
- threadId,
3661
- turnId
3662
- };
3663
- return jsonResult({
3664
- summary: `codex interrupted: ${turnId}`,
3665
- result
3666
- });
3667
- }
3668
- }
3669
- ];
3670
- }
3671
2594
  //#endregion
3672
2595
  //#region extensions/codex/src/web-search-provider.ts
3673
- const loadCodexWebSearchRuntime = createLazyRuntimeModule(() => import("./web-search-provider.runtime-CaQMYDbp.js"));
2596
+ const loadCodexWebSearchRuntime = createLazyRuntimeModule(() => import("./.setup/web-search-provider.runtime-Ce2zOHxW.mjs"));
3674
2597
  const CodexWebSearchSchema = {
3675
2598
  type: "object",
3676
2599
  properties: { query: {
@@ -3712,96 +2635,21 @@ const ENDED_SESSION_REASONS = /* @__PURE__ */ new Set([
3712
2635
  var codex_default = definePluginEntry({
3713
2636
  id: "codex",
3714
2637
  name: "Codex",
3715
- description: "Codex app-server harness and native session supervision.",
2638
+ description: "Codex app-server harness and Codex-managed GPT model catalog.",
3716
2639
  register(api) {
3717
2640
  const resolveCurrentConfig = () => api.runtime.config?.current ? api.runtime.config.current() : void 0;
3718
- const resolvePluginConfig = (resolveConfig) => {
3719
- const liveConfig = resolveConfig();
3720
- if (!liveConfig) return api.pluginConfig;
3721
- const livePluginConfig = resolveLivePluginConfigObject(() => liveConfig, "codex", api.pluginConfig);
3722
- if (!resolveEffectiveEnableState({
3723
- id: "codex",
3724
- origin: "bundled",
3725
- config: normalizePluginsConfig(liveConfig.plugins),
3726
- rootConfig: liveConfig,
3727
- enabledByDefault: livePluginConfig !== void 0
3728
- }).enabled) return;
3729
- return livePluginConfig;
3730
- };
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
- }
3760
- let bindingStateStore;
3761
- const openBindingStateStore = () => bindingStateStore ??= api.runtime.state.openSyncKeyedStore({
2641
+ const resolveCurrentPluginConfig = () => resolveLivePluginConfigObject(resolveCurrentConfig, "codex", api.pluginConfig) ?? api.pluginConfig;
2642
+ const bindingStore = createLazyCodexAppServerBindingStore(api.runtime.state.openSyncKeyedStore({
3762
2643
  namespace: CODEX_APP_SERVER_BINDING_NAMESPACE,
3763
2644
  maxEntries: CODEX_APP_SERVER_BINDING_MAX_ENTRIES,
3764
2645
  overflowPolicy: "reject-new"
3765
- });
3766
- const bindingStore = createLazyCodexAppServerBindingStore({
3767
- entries: () => openBindingStateStore().entries(),
3768
- lookup: (key) => openBindingStateStore().lookup(key),
3769
- get update() {
3770
- const store = openBindingStateStore();
3771
- return store.update?.bind(store);
3772
- }
3773
- });
3774
- registerCodexCliMetadata(api);
3775
- const sessionCatalogControl = createCodexSessionCatalogControl({
3776
- getPluginConfig: resolveCurrentPluginConfig,
3777
- getRuntimeConfig: resolveCurrentConfig
3778
- });
3779
- if (readCodexPluginConfig(resolveCurrentPluginConfig()).sessionCatalog?.enabled !== false) {
3780
- codexSessionCatalogRuntime.register({
3781
- api,
3782
- bindingStore,
3783
- control: sessionCatalogControl,
3784
- getRuntimeConfig: resolveCurrentConfig
3785
- });
3786
- for (const command of createCodexSessionCatalogNodeHostCommands(sessionCatalogControl)) api.registerNodeHostCommand(command);
3787
- }
3788
- for (const policy of createCodexSessionCatalogNodeInvokePolicies()) api.registerNodeInvokePolicy(policy);
3789
- if (readCodexPluginConfig(resolveCurrentPluginConfig()).supervision?.enabled === true) api.registerTool((context) => {
3790
- if (context.senderIsOwner !== true) return [];
3791
- const resolveToolRuntimeConfig = () => context.getRuntimeConfig?.() ?? context.runtimeConfig ?? context.config ?? resolveCurrentConfig();
3792
- return createCodexSupervisionTools({
3793
- getPluginConfig: () => resolvePluginConfig(resolveToolRuntimeConfig),
3794
- getRuntimeConfig: resolveToolRuntimeConfig,
3795
- senderIsOwner: context.senderIsOwner
3796
- });
3797
- }, { names: [...CODEX_SUPERVISION_COMPAT_TOOL_NAMES] });
2646
+ }));
3798
2647
  api.registerAgentHarness(createCodexAppServerAgentHarness({
3799
2648
  bindingStore,
3800
- sessionCatalogControl,
3801
2649
  resolveConfig: resolveCurrentConfig,
3802
- resolvePluginConfig: resolveCurrentPluginConfig,
3803
- runtime: api.runtime
2650
+ resolvePluginConfig: resolveCurrentPluginConfig
3804
2651
  }));
2652
+ api.registerProvider(buildCodexProvider({ pluginConfig: api.pluginConfig }));
3805
2653
  api.registerMediaUnderstandingProvider(buildCodexMediaUnderstandingProvider({ pluginConfig: api.pluginConfig }));
3806
2654
  api.registerWebSearchProvider(createCodexWebSearchProvider({ resolvePluginConfig: resolveCurrentPluginConfig }));
3807
2655
  api.registerMigrationProvider(buildCodexMigrationProvider({ runtime: api.runtime }));
@@ -3872,7 +2720,7 @@ var codex_default = definePluginEntry({
3872
2720
  }
3873
2721
  }
3874
2722
  }));
3875
- api.on("inbound_claim", (event, ctx) => codexConversationBindingRuntime.handleInboundClaim(event, ctx, {
2723
+ api.on("inbound_claim", (event, ctx) => handleCodexConversationInboundClaim(event, ctx, {
3876
2724
  bindingStore,
3877
2725
  pluginConfig: resolveCurrentPluginConfig(),
3878
2726
  config: resolveCurrentConfig(),
@@ -3881,14 +2729,14 @@ var codex_default = definePluginEntry({
3881
2729
  ...params
3882
2730
  })
3883
2731
  }));
3884
- api.onConversationBindingResolved?.((event) => codexConversationBindingRuntime.handleBindingResolved(event, { bindingStore }));
2732
+ api.onConversationBindingResolved?.((event) => handleCodexConversationBindingResolved(event, { bindingStore }));
3885
2733
  api.on("after_compaction", async (event, ctx) => {
3886
2734
  const previousSessionId = event.previousSessionId?.trim();
3887
2735
  const sessionId = ctx.sessionId?.trim();
3888
2736
  if (!previousSessionId || !sessionId || previousSessionId === sessionId) return;
3889
2737
  const config = resolveCurrentConfig();
3890
2738
  const sessionKey = ctx.sessionKey?.trim();
3891
- const { sessionBindingIdentity } = await import("./session-binding-DlVQ-Pxs.js").then((n) => n.u);
2739
+ const { sessionBindingIdentity } = await import("./.setup/session-binding-DuihlMJc.mjs").then((n) => n.c);
3892
2740
  const identity = sessionBindingIdentity({
3893
2741
  sessionId,
3894
2742
  ...sessionKey ? { sessionKey } : {},
@@ -3901,12 +2749,8 @@ var codex_default = definePluginEntry({
3901
2749
  api.on("session_end", async (event, ctx) => {
3902
2750
  if (!event.reason || !ENDED_SESSION_REASONS.has(event.reason)) return;
3903
2751
  const sessionKey = event.sessionKey ?? ctx.sessionKey;
3904
- const endedSessionKey = sessionKey?.trim();
3905
- const nextSessionKey = event.nextSessionKey?.trim();
3906
- if (endedSessionKey && nextSessionKey && nextSessionKey !== endedSessionKey) return;
3907
- if (event.nextSessionId?.trim() === event.sessionId.trim()) return;
3908
2752
  const config = resolveCurrentConfig();
3909
- const { sessionBindingIdentity } = await import("./session-binding-DlVQ-Pxs.js").then((n) => n.u);
2753
+ const { sessionBindingIdentity } = await import("./.setup/session-binding-DuihlMJc.mjs").then((n) => n.c);
3910
2754
  await bindingStore.retireSessionGeneration(sessionBindingIdentity({
3911
2755
  sessionId: event.sessionId,
3912
2756
  ...sessionKey ? { sessionKey } : {},