@openclaw/codex 2026.7.2-beta.7 → 2026.7.34

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 (64) hide show
  1. package/README.md +3 -27
  2. package/dist/{command-formatters-C336eRVS.js → .setup/app-server-policy-C_qTMEMy.mjs} +51 -9
  3. package/dist/{attempt-notifications-AP-a-Q5k.js → .setup/attempt-notifications-F8krwWL4.mjs} +6 -3
  4. package/dist/{command-handlers-DAHUSry9.js → .setup/command-handlers-DSRFfor4.mjs} +1156 -1319
  5. package/dist/.setup/command-rpc-BvYsfQ7G.mjs +786 -0
  6. package/dist/{compact-D_uvHurS.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-D2ZAgZVz.js → .setup/media-understanding-provider-EbJCyDqe.mjs} +203 -109
  11. package/dist/{models-BBMKU4jA.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/{transcript-mirror-DzsdNCf5.js → .setup/protocol-validators-dZQ-UTOa.mjs} +112 -922
  16. package/dist/{rate-limits-CU-gVCCS.js → .setup/provider-DGxDs6g8.mjs} +269 -13
  17. package/dist/{dynamic-tools-D2gYkSBO.js → .setup/provider-capabilities-DS2N_zc1.mjs} +5632 -3953
  18. package/dist/.setup/request-DFR0JyoV.mjs +36 -0
  19. package/dist/.setup/run-attempt-C7xEKFVB.mjs +8180 -0
  20. package/dist/{sandbox-guard-NuhpXmPB.js → .setup/sandbox-guard-DA2TQfZW.mjs} +1 -14
  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-_ulBk1AL.js → .setup/side-question-Cv6JXWCS.mjs} +84 -159
  24. package/dist/.setup/thread-lifecycle-u6CZdOOH.mjs +2593 -0
  25. package/dist/{web-search-provider.runtime-CCFhg2z5.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 -89
  28. package/dist/index.js +436 -1615
  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 -9
  37. package/dist/app-server-policy-BSaO2JIT.js +0 -49
  38. package/dist/auth-binding-B4FfbcCg.js +0 -62
  39. package/dist/capabilities-BFDTVYr4.js +0 -36
  40. package/dist/cli-metadata.js +0 -20
  41. package/dist/computer-use-CYEc8U3w.js +0 -693
  42. package/dist/conversation-control-xsIeXbo4.js +0 -287
  43. package/dist/effective-mcp-catalog-Bbd98Inw.js +0 -114
  44. package/dist/event-projector-assistant-message-DTgnPPVF.js +0 -101
  45. package/dist/event-projector-usage-Xp4AlR2k.js +0 -86
  46. package/dist/incognito-session-ET_ufDH-.js +0 -2
  47. package/dist/notification-correlation-CU6_AnOb.js +0 -67
  48. package/dist/plugin-app-cache-key-DztPafOA.js +0 -408
  49. package/dist/request-qKh331yu.js +0 -157
  50. package/dist/run-attempt-CY9WlDLc.js +0 -12413
  51. package/dist/runtime-artifact-CmtG_t1L.js +0 -554
  52. package/dist/session-binding-BXpo9ymA.js +0 -2214
  53. package/dist/session-catalog-DyOgR4WH.js +0 -2668
  54. package/dist/session-cli-2PZaFLRh.js +0 -175
  55. package/dist/session-upstream-marker-B6U7tLw5.js +0 -82
  56. package/dist/settled-turn-finalizer-CXY2Z9dT.js +0 -316
  57. package/dist/shared-client-DTbvH-XV.js +0 -3069
  58. package/dist/thread-lifecycle-DF53ogiA.js +0 -3987
  59. package/dist/transport-stdio-DXgGZ7Ib.js +0 -88
  60. package/dist/upstream-session-fork-Dy4Xvuoj.js +0 -320
  61. package/dist/usage-CQ6GhD5-.js +0 -25
  62. /package/dist/{rolldown-runtime-8H4AJuhK.js → .setup/rolldown-runtime-8H4AJuhK.mjs} +0 -0
  63. /package/dist/{session-binding-meta-B7aEMU7g.js → .setup/session-binding-meta-B7aEMU7g.mjs} +0 -0
  64. /package/dist/{web-search-provider.shared-BrZmlqyR.js → .setup/web-search-provider.shared-BrZmlqyR.mjs} +0 -0
package/dist/index.js CHANGED
@@ -1,84 +1,59 @@
1
- import { registerCodexCliMetadata } from "./cli-metadata.js";
2
1
  import { createCodexAppServerAgentHarness } from "./harness.js";
3
- import { I as resolveCodexAppServerAuthProfileIdForAgent, L as resolveCodexAppServerFallbackApiKeyCacheKey, P as resolveCodexAppServerAuthAccountCacheKey, V as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS, W as isJsonObject, a as getLeasedSharedCodexAppServerClient, c as releaseCodexAppServerClientLease, h as withLeasedCodexAppServerClientStartSelectionRetry, l as releaseLeasedSharedCodexAppServerClient, r as clearSharedCodexAppServerClientIfCurrentAndWait } from "./shared-client-DTbvH-XV.js";
4
- import { A as resolveOpenClawExecPolicyForCodexAppServer, C as canUseCodexModelBackedApprovalsReviewerForModel, M as CODEX_PLUGINS_MARKETPLACE_NAME, O as readCodexPluginConfig, b as assertCodexAppServerConnectionSecurity, d as sessionBindingIdentity, g as resolveCodexAppServerRuntimeOptions, m as codexSandboxPolicyForTurn, n as assertCodexBindingMayBeReplaced, o as isCodexAppServerNativeAuthProfile, p as codexAppServerStartOptionsKey, s as normalizeCodexAppServerBindingModelProvider, y as resolveCodexSupervisionAppServerRuntimeOptions } from "./session-binding-BXpo9ymA.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 { J as isOpenAiCuratedMarketplace, Y as pluginReadParams, _t as unsubscribeCodexThreadBestEffort, b as resolveCodexAppServerRequestModelSelection, dt as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, l as resumeCodexAppServerThread, q as ensureCodexPluginActivation, v as CODEX_NATIVE_PERSONALITY_NONE } from "./thread-lifecycle-DF53ogiA.js";
7
- import { _ as assertCodexThreadStartResponse } from "./transcript-mirror-DzsdNCf5.js";
8
- import { i as defaultCodexAppInventoryCache, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-DztPafOA.js";
9
- import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
10
- import { buildCodexMediaUnderstandingProvider } from "./media-understanding-provider.js";
11
- import { n as describeControlFailure, t as CODEX_CONTROL_METHODS } from "./capabilities-BFDTVYr4.js";
12
- import { r as formatCodexDisplayText } from "./command-formatters-C336eRVS.js";
13
- import { t as resolveCodexAppServerForModelProvider } from "./app-server-policy-BSaO2JIT.js";
14
- import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "./sandbox-guard-NuhpXmPB.js";
15
- import { d as CODEX_NATIVE_EXECUTION_AUTH_ERROR, f as canMutateCodexHost, u as trackCodexConversationActiveTurn } from "./conversation-control-xsIeXbo4.js";
16
- import { a as readCodexConversationBindingDataRecord, i as readCodexConversationBindingData, o as resolveCodexDefaultWorkspaceDir, r as createCodexConversationBindingData } from "./conversation-binding-data-BHRpYbX3.js";
17
- import { a as resolveCodexBindingAppServerConnection, n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./notification-correlation-CU6_AnOb.js";
18
- 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-DyOgR4WH.js";
19
- import { i as withCodexAppServerJsonClient, r as requestCodexAppServerJson } from "./request-qKh331yu.js";
20
- 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";
21
19
  import { mutateConfigFile } from "openclaw/plugin-sdk/config-mutation";
22
- import { normalizePluginsConfig, resolveEffectiveEnableState, resolveLivePluginConfigObject, resolvePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
20
+ import { resolveLivePluginConfigObject, resolvePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
23
21
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
24
22
  import fs from "node:fs/promises";
25
23
  import path from "node:path";
26
24
  import { parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
27
- import { formatErrorMessage, resolveSandboxContext } from "openclaw/plugin-sdk/agent-harness-runtime";
28
- import { loadAuthProfileStoreWithoutExternalProfiles, resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentDir, resolveDefaultAgentId, resolveSessionAgentIds as resolveSessionAgentIds$1 } from "openclaw/plugin-sdk/agent-runtime";
29
25
  import os from "node:os";
30
- 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";
31
27
  import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
32
28
  import { asBoolean, asOptionalRecord, isRecord, normalizeOptionalString, normalizeSingleOrTrimmedStringList, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
33
- 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";
34
32
  import { fileURLToPath } from "node:url";
35
- import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
36
- 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";
37
35
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
38
- import { ModelSelectionLockedError, isModelSelectionLocked } from "openclaw/plugin-sdk/model-session-runtime";
39
- import { MIGRATION_REASON_TARGET_EXISTS, applyMigrationManualItem, createMigrationItem, createMigrationManualItem, hasMigrationConfigPatchConflict, markMigrationItemConflict, markMigrationItemError, markMigrationItemSkipped, mergeMigrationConfigValue, readMigrationConfigPath, resolveMigrationConfigRuntime, summarizeMigrationItems, writeMigrationConfigPath } from "openclaw/plugin-sdk/migration";
40
- 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";
41
38
  import { sleep } from "openclaw/plugin-sdk/runtime-env";
42
39
  import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
43
- import { jsonResult, readStringParam } from "openclaw/plugin-sdk/core";
44
40
  import { Type } from "typebox";
45
41
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
46
42
  //#region extensions/codex/src/app-server/session-binding-store.ts
47
43
  /** Defers schema compilation and auth loading until the first binding operation. */
48
44
  function createLazyCodexAppServerBindingStore(state) {
49
45
  let resolved;
50
- const store = () => resolved ??= import("./session-binding-BXpo9ymA.js").then((n) => n.f).then(({ createCodexAppServerBindingStore }) => createCodexAppServerBindingStore(state));
46
+ const store = () => resolved ??= import("./.setup/session-binding-DuihlMJc.mjs").then((n) => n.c).then(({ createCodexAppServerBindingStore }) => createCodexAppServerBindingStore(state));
51
47
  return {
52
48
  read: async (identity) => (await store()).read(identity),
53
- hasOtherThreadOwner: async (threadId, currentIdentity) => (await store()).hasOtherThreadOwner(threadId, currentIdentity),
54
49
  mutate: async (identity, mutation) => (await store()).mutate(identity, mutation),
55
50
  prepareSessionGenerationReclaim: async (identity) => (await store()).prepareSessionGenerationReclaim(identity),
56
51
  adoptSessionGeneration: async (identity, previousSessionId) => (await store()).adoptSessionGeneration(identity, previousSessionId),
57
- resetSessionGeneration: async (identity) => (await store()).resetSessionGeneration(identity),
58
52
  retireSessionGeneration: async (identity) => (await store()).retireSessionGeneration(identity),
59
- withThreadArchiveFence: async (run) => (await store()).withThreadArchiveFence(run),
60
53
  withLease: async (identity, run) => (await store()).withLease(identity, run)
61
54
  };
62
55
  }
63
56
  //#endregion
64
- //#region extensions/codex/src/command-dispatch.ts
65
- /** Dispatches a `/codex` command to the lazily loaded handler. */
66
- async function handleCodexCommand(ctx, options) {
67
- const { loadSubcommandHandler, resolvePluginConfig, ...subcommandOptions } = options;
68
- try {
69
- return await (loadSubcommandHandler ? await loadSubcommandHandler() : await loadDefaultCodexSubcommandHandler())(ctx, {
70
- ...subcommandOptions,
71
- pluginConfig: resolvePluginConfig?.() ?? subcommandOptions.pluginConfig
72
- });
73
- } catch (error) {
74
- return { text: `Codex command failed: ${formatCodexDisplayText(describeControlFailure(error))}` };
75
- }
76
- }
77
- async function loadDefaultCodexSubcommandHandler() {
78
- const { handleCodexSubcommand } = await import("./command-handlers-DAHUSry9.js");
79
- return handleCodexSubcommand;
80
- }
81
- //#endregion
82
57
  //#region extensions/codex/src/commands.ts
83
58
  /** Creates the reserved `/codex` command definition exposed by the plugin. */
84
59
  function createCodexCommand(options) {
@@ -98,6 +73,22 @@ function createCodexCommand(options) {
98
73
  handler: (ctx) => handleCodexCommand(ctx, options)
99
74
  };
100
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
+ }
101
92
  //#endregion
102
93
  //#region extensions/codex/src/conversation-turn-collector.ts
103
94
  const MAX_PENDING_NOTIFICATIONS_PER_TURN = 100;
@@ -293,7 +284,7 @@ function readLocalMediaPath(value) {
293
284
  }
294
285
  //#endregion
295
286
  //#region extensions/codex/src/conversation-binding.ts
296
- const DEFAULT_BOUND_TURN_TIMEOUT_MS = 20 * 6e4;
287
+ const DEFAULT_BOUND_TURN_TIMEOUT_MS = 12e5;
297
288
  const DEFAULT_AGENT_ID = "main";
298
289
  const VALID_AGENT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
299
290
  const INVALID_AGENT_ID_CHARS_PATTERN = /[^a-z0-9_-]+/g;
@@ -331,72 +322,6 @@ function getGlobalState() {
331
322
  globalState[CODEX_CONVERSATION_GLOBAL_STATE] ??= { queue: new KeyedAsyncQueue() };
332
323
  return globalState[CODEX_CONVERSATION_GLOBAL_STATE];
333
324
  }
334
- async function startCodexConversationThread(params) {
335
- const workspaceDir = params.workspaceDir?.trim() || resolveCodexDefaultWorkspaceDir(params.pluginConfig);
336
- const agentDir = params.agentDir?.trim();
337
- const agentLookup = buildAgentLookup({
338
- agentDir,
339
- config: params.config
340
- });
341
- const identity = sessionBindingIdentity({
342
- agentId: params.agentId,
343
- sessionId: params.sessionFile,
344
- sessionKey: params.sessionKey,
345
- config: params.config
346
- });
347
- const existingBinding = await params.bindingStore.read(identity);
348
- assertCodexBindingMayBeReplaced(existingBinding, "starting a conversation-bound Codex thread");
349
- const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
350
- authProfileId: params.authProfileId ?? existingBinding?.authProfileId,
351
- ...agentLookup
352
- });
353
- if (params.threadId?.trim()) await attachExistingThread({
354
- pluginConfig: params.pluginConfig,
355
- bindingStore: params.bindingStore,
356
- identity,
357
- threadId: params.threadId.trim(),
358
- workspaceDir,
359
- ...agentDir ? { agentDir } : {},
360
- model: params.model,
361
- modelProvider: params.modelProvider,
362
- authProfileId,
363
- approvalPolicy: params.approvalPolicy,
364
- sandbox: params.sandbox,
365
- serviceTier: params.serviceTier,
366
- config: params.config,
367
- sessionKey: params.sessionKey,
368
- agentId: params.agentId
369
- });
370
- else await createThread({
371
- pluginConfig: params.pluginConfig,
372
- bindingStore: params.bindingStore,
373
- identity,
374
- workspaceDir,
375
- ...agentDir ? { agentDir } : {},
376
- model: params.model,
377
- modelProvider: params.modelProvider,
378
- authProfileId,
379
- approvalPolicy: params.approvalPolicy,
380
- sandbox: params.sandbox,
381
- serviceTier: params.serviceTier,
382
- config: params.config,
383
- sessionKey: params.sessionKey,
384
- agentId: params.agentId
385
- });
386
- const storedBinding = await params.bindingStore.read(identity);
387
- if (!storedBinding) throw new Error("Codex session binding disappeared while starting its conversation thread.");
388
- return createCodexConversationBindingData({
389
- source: {
390
- agentId: identity.agentId,
391
- sessionId: identity.sessionId,
392
- threadId: storedBinding.threadId,
393
- ...identity.sessionKey ? { sessionKey: identity.sessionKey } : {}
394
- },
395
- workspaceDir,
396
- ...agentDir ? { agentDir } : {},
397
- agentId: params.agentId
398
- });
399
- }
400
325
  async function handleCodexConversationInboundClaim(event, ctx, options) {
401
326
  const data = readCodexConversationBindingData(ctx.pluginBinding);
402
327
  if (!data) return;
@@ -474,7 +399,6 @@ async function handleCodexConversationBindingResolved(event, options) {
474
399
  if (!data || data.kind !== "codex-app-server-session") return;
475
400
  const identity = conversationBindingIdentity(data);
476
401
  const binding = await options.bindingStore.read(identity);
477
- assertCodexBindingMayBeReplaced(binding, "clearing a denied conversation binding");
478
402
  if (!data.start?.id || binding?.conversationStartId === data.start.id) await options.bindingStore.mutate(identity, { kind: "clear" });
479
403
  }
480
404
  async function resolveThreadBindingRuntime(params) {
@@ -528,22 +452,19 @@ async function resolveThreadBindingRuntime(params) {
528
452
  agentDir: params.agentDir
529
453
  })
530
454
  });
531
- const clientOptions = {
455
+ const client = await getLeasedSharedCodexAppServerClient({
532
456
  startOptions: runtime.start,
533
457
  timeoutMs: runtime.requestTimeoutMs,
534
458
  authProfileId: params.authProfileId,
535
459
  ...agentLookup
536
- };
537
- const client = await getLeasedSharedCodexAppServerClient(clientOptions);
460
+ });
538
461
  return {
539
462
  execPolicy,
540
463
  runtime: modelScopedRuntime,
541
464
  agentLookup,
542
465
  model: modelSelection?.model,
543
466
  modelProvider: modelSelection?.modelProvider ?? modelProvider,
544
- client,
545
- clientLease: { client },
546
- clientOptions
467
+ client
547
468
  };
548
469
  }
549
470
  function buildThreadRequestRuntimeOptions(params, resolved) {
@@ -562,32 +483,22 @@ function codexConversationSandboxOrPermissions(runtime, sandbox) {
562
483
  return { sandbox };
563
484
  }
564
485
  async function requestNewConversationBindingThread(params, resolved) {
565
- return await withLeasedCodexAppServerClientStartSelectionRetry({
566
- lease: resolved.clientLease,
567
- options: resolved.clientOptions,
568
- run: async (client, requestOptions) => await client.request("thread/start", {
569
- cwd: params.workspaceDir,
570
- ...resolved.model ? { model: resolved.model } : {},
571
- ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
572
- personality: CODEX_NATIVE_PERSONALITY_NONE,
573
- ...buildThreadRequestRuntimeOptions(params, resolved),
574
- developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
575
- experimentalRawEvents: true,
576
- ...isIncognitoSessionKey(params.sessionKey) ? { ephemeral: true } : {}
577
- }, requestOptions),
578
- onClientChange: (client) => {
579
- resolved.client = client;
580
- }
581
- });
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 });
582
495
  }
583
496
  async function writeThreadBindingFromResponse(params, resolved, response) {
584
- assertCodexBindingMayBeReplaced(await params.bindingStore.read(params.identity), "storing a conversation-bound Codex thread");
585
497
  const runtimeApprovalPolicy = typeof resolved.runtime.approvalPolicy === "string" ? resolved.runtime.approvalPolicy : void 0;
586
498
  if (!await params.bindingStore.mutate(params.identity, {
587
499
  kind: "set",
588
500
  binding: {
589
501
  threadId: response.thread.id,
590
- clientId: resolved.client.getInstanceId(),
591
502
  cwd: response.thread.cwd ?? params.workspaceDir,
592
503
  authProfileId: params.authProfileId,
593
504
  model: response.model ?? resolved.model ?? params.model,
@@ -605,34 +516,25 @@ async function writeThreadBindingFromResponse(params, resolved, response) {
605
516
  })) throw new Error("Codex conversation binding changed while storing its thread.");
606
517
  }
607
518
  async function attachExistingThread(params) {
608
- assertCodexBindingMayBeReplaced(await params.bindingStore.read(params.identity), "attaching a conversation-bound Codex thread");
609
519
  const resolved = await resolveThreadBindingRuntime(params);
610
520
  try {
611
- await writeThreadBindingFromResponse(params, resolved, resolved.runtime.networkProxy ? await requestNewConversationBindingThread(params, resolved) : await withLeasedCodexAppServerClientStartSelectionRetry({
612
- lease: resolved.clientLease,
613
- options: resolved.clientOptions,
614
- run: async (client, requestOptions) => await client.request(CODEX_CONTROL_METHODS.resumeThread, {
615
- threadId: params.threadId,
616
- ...resolved.model ? { model: resolved.model } : {},
617
- ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
618
- personality: CODEX_NATIVE_PERSONALITY_NONE,
619
- ...buildThreadRequestRuntimeOptions(params, resolved)
620
- }, requestOptions),
621
- onClientChange: (client) => {
622
- resolved.client = client;
623
- }
624
- }));
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 }));
625
528
  } finally {
626
- releaseCodexAppServerClientLease(resolved.clientLease);
529
+ releaseLeasedSharedCodexAppServerClient(resolved.client);
627
530
  }
628
531
  }
629
532
  async function createThread(params) {
630
- assertCodexBindingMayBeReplaced(await params.bindingStore.read(params.identity), "creating a conversation-bound Codex thread");
631
533
  const resolved = await resolveThreadBindingRuntime(params);
632
534
  try {
633
535
  await writeThreadBindingFromResponse(params, resolved, await requestNewConversationBindingThread(params, resolved));
634
536
  } finally {
635
- releaseCodexAppServerClientLease(resolved.clientLease);
537
+ releaseLeasedSharedCodexAppServerClient(resolved.client);
636
538
  }
637
539
  }
638
540
  async function runBoundTurn(params) {
@@ -643,7 +545,6 @@ async function runBoundTurn(params) {
643
545
  const identity = conversationBindingIdentity(params.data);
644
546
  const binding = await params.bindingStore.read(identity);
645
547
  if (!binding?.threadId) throw new Error("bound Codex conversation has no thread binding");
646
- assertCodexBindingMayBeReplaced(binding, "running a conversation-bound Codex thread");
647
548
  let threadId = binding.threadId;
648
549
  const workspaceDir = binding.cwd || params.data.workspaceDir;
649
550
  const reviewerModelProvider = resolveModelBackedReviewerPolicyProvider({
@@ -696,44 +597,33 @@ async function runBoundTurn(params) {
696
597
  authProfileId: binding.authProfileId,
697
598
  ...agentLookup
698
599
  }) : void 0;
699
- const clientOptions = {
600
+ const client = await getLeasedSharedCodexAppServerClient({
700
601
  startOptions: runtime.start,
701
602
  timeoutMs: runtime.requestTimeoutMs,
702
603
  authProfileId: binding.authProfileId,
703
604
  ...agentLookup
704
- };
705
- let client = await getLeasedSharedCodexAppServerClient(clientOptions);
706
- const clientLease = { client };
605
+ });
707
606
  let notificationCleanup = () => void 0;
708
607
  let requestCleanup = () => void 0;
709
608
  try {
710
609
  if (networkProxyBindingChanged) {
711
- const response = assertCodexThreadStartResponse(await withLeasedCodexAppServerClientStartSelectionRetry({
712
- lease: clientLease,
713
- options: clientOptions,
714
- run: async (requestClient, requestOptions) => await requestClient.request("thread/start", {
715
- cwd: workspaceDir,
716
- ...modelSelection?.model ? { model: modelSelection.model } : {},
717
- ...modelSelection?.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
718
- personality: CODEX_NATIVE_PERSONALITY_NONE,
719
- approvalPolicy,
720
- approvalsReviewer: modelScopedRuntime.approvalsReviewer,
721
- ...modelScopedRuntime.networkProxy ? { config: modelScopedRuntime.networkProxy.configPatch } : { sandbox },
722
- ...serviceTier ? { serviceTier } : {},
723
- developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
724
- experimentalRawEvents: true,
725
- ...isIncognitoSessionKey(params.sessionKey) ? { ephemeral: true } : {}
726
- }, requestOptions),
727
- onClientChange: (nextClient) => {
728
- client = nextClient;
729
- }
730
- }));
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 }));
731
622
  threadId = response.thread.id;
732
623
  if (!await params.bindingStore.mutate(identity, {
733
624
  kind: "set",
734
625
  binding: {
735
626
  threadId,
736
- clientId: client.getInstanceId(),
737
627
  cwd: response.thread.cwd ?? workspaceDir,
738
628
  authProfileId: binding.authProfileId,
739
629
  model: response.model ?? modelSelection?.model ?? binding.model,
@@ -753,46 +643,6 @@ async function runBoundTurn(params) {
753
643
  }
754
644
  })) throw new Error("Codex conversation binding changed while rotating its thread.");
755
645
  useStickyNetworkProfile = modelScopedRuntime.networkProxy !== void 0;
756
- } else if (binding.clientId !== client.getInstanceId()) {
757
- const response = await withLeasedCodexAppServerClientStartSelectionRetry({
758
- lease: clientLease,
759
- options: clientOptions,
760
- run: async (requestClient, requestOptions) => await resumeCodexAppServerThread({
761
- client: requestClient,
762
- abandonClient: async () => {
763
- await requestClient.closeAndWait();
764
- },
765
- request: {
766
- threadId,
767
- ...modelSelection?.model ? { model: modelSelection.model } : {},
768
- ...modelSelection?.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
769
- personality: CODEX_NATIVE_PERSONALITY_NONE,
770
- approvalPolicy,
771
- approvalsReviewer: modelScopedRuntime.approvalsReviewer,
772
- ...codexConversationSandboxOrPermissions(modelScopedRuntime, sandbox),
773
- ...serviceTier ? { serviceTier } : {}
774
- },
775
- ...requestOptions
776
- }),
777
- onClientChange: (nextClient) => {
778
- client = nextClient;
779
- }
780
- });
781
- threadId = response.thread.id;
782
- if (!await params.bindingStore.mutate(identity, {
783
- kind: "patch",
784
- threadId: binding.threadId,
785
- patch: {
786
- clientId: client.getInstanceId(),
787
- cwd: response.thread.cwd ?? binding.cwd,
788
- model: response.model ?? modelSelection?.model ?? binding.model,
789
- modelProvider: normalizeCodexAppServerBindingModelProvider({
790
- authProfileId: binding.authProfileId,
791
- modelProvider: response.modelProvider ?? modelSelection?.modelProvider ?? binding.modelProvider,
792
- ...agentLookup
793
- })
794
- }
795
- })) throw new Error("Codex conversation binding changed while resuming on a new client.");
796
646
  }
797
647
  const collector = createCodexConversationTurnCollector(threadId);
798
648
  notificationCleanup = client.addNotificationHandler((notification) => collector.handleNotification(notification));
@@ -833,29 +683,15 @@ async function runBoundTurn(params) {
833
683
  }, { timeoutMs: runtime.requestTimeoutMs })).turn.id;
834
684
  const activeCleanup = trackCodexConversationActiveTurn({
835
685
  identity,
836
- client,
837
686
  threadId,
838
687
  turnId
839
688
  });
840
689
  collector.setTurnId(turnId);
841
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." } };
842
- } catch (error) {
843
- if (isIncognitoSessionKey(params.sessionKey)) {
844
- const bindingReleased = await params.bindingStore.mutate(identity, {
845
- kind: "clear",
846
- threadId
847
- });
848
- const timedOut = error instanceof Error && error.message === "codex app-server bound turn timed out";
849
- if (bindingReleased && !timedOut) await unsubscribeCodexThreadBestEffort(client, {
850
- threadId,
851
- timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
852
- });
853
- }
854
- throw error;
855
691
  } finally {
856
692
  notificationCleanup();
857
693
  requestCleanup();
858
- releaseCodexAppServerClientLease(clientLease);
694
+ releaseLeasedSharedCodexAppServerClient(client);
859
695
  }
860
696
  }
861
697
  function assertNativeConversationApprovalPolicySupported(params) {
@@ -884,8 +720,6 @@ async function prepareConversationBinding(params, options = {}) {
884
720
  config: params.config
885
721
  }) : void 0;
886
722
  const sourceBinding = sourceIdentity ? await params.bindingStore.read(sourceIdentity) : void 0;
887
- assertCodexBindingMayBeReplaced(current, "initializing a conversation-bound Codex thread");
888
- assertCodexBindingMayBeReplaced(sourceBinding, "transferring a session into a conversation-bound Codex thread");
889
723
  const inherited = current ?? sourceBinding;
890
724
  const execPolicy = resolveConversationExecPolicy({
891
725
  config: params.config,
@@ -938,7 +772,7 @@ async function prepareConversationBinding(params, options = {}) {
938
772
  });
939
773
  }
940
774
  function resolveConversationExecPolicy(params) {
941
- const agentId = params.agentId ?? (params.config ? resolveSessionAgentIds$1({
775
+ const agentId = params.agentId ?? (params.config ? resolveSessionAgentIds({
942
776
  sessionKey: params.sessionKey,
943
777
  config: params.config
944
778
  }).sessionAgentId : void 0);
@@ -961,8 +795,9 @@ function readSessionExecOverrides(params) {
961
795
  agentId: params.agentId,
962
796
  sessionKey
963
797
  })) return;
798
+ const storePath = resolveStorePath(params.config.session?.store, { agentId: params.agentId });
964
799
  const entry = getSessionEntry({
965
- storePath: resolveStorePath(params.config.session?.store, { agentId: params.agentId }),
800
+ storePath,
966
801
  sessionKey,
967
802
  readConsistency: "latest"
968
803
  });
@@ -1048,44 +883,6 @@ function conversationBindingIdentity(data) {
1048
883
  bindingId: data.bindingId
1049
884
  };
1050
885
  }
1051
- const codexConversationBindingRuntime = {
1052
- startThread: startCodexConversationThread,
1053
- handleInboundClaim: handleCodexConversationInboundClaim,
1054
- handleBindingResolved: handleCodexConversationBindingResolved
1055
- };
1056
- //#endregion
1057
- //#region extensions/codex/src/migration/apply-report.ts
1058
- function codexPluginActivationReportState(result) {
1059
- switch (result.reason) {
1060
- case "already_active":
1061
- case "installed": return {
1062
- installed: true,
1063
- enabled: true
1064
- };
1065
- case "auth_required": return {
1066
- installed: true,
1067
- enabled: false
1068
- };
1069
- case "disabled":
1070
- case "marketplace_missing":
1071
- case "plugin_missing": return {
1072
- installed: false,
1073
- enabled: false
1074
- };
1075
- case "refresh_failed": return {
1076
- installed: true,
1077
- enabled: false
1078
- };
1079
- }
1080
- return result.reason;
1081
- }
1082
- function sanitizeAppsNeedingAuth(apps) {
1083
- return apps.map((app) => ({
1084
- id: app.id,
1085
- name: app.name,
1086
- needsAuth: true
1087
- }));
1088
- }
1089
886
  //#endregion
1090
887
  //#region extensions/codex/src/migration/helpers.ts
1091
888
  async function exists(filePath) {
@@ -1139,6 +936,11 @@ async function readModelRefs(source) {
1139
936
  refs.add(OPENAI_CODEX_DEFAULT_MODEL);
1140
937
  return [...refs].toSorted();
1141
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
+ }
1142
944
  async function buildCodexOAuthCredential(source) {
1143
945
  const credential = readCodexCliCredentialsCached({
1144
946
  codexHome: source.codexHome,
@@ -1173,11 +975,13 @@ async function buildCodexOAuthCredential(source) {
1173
975
  kind: "oauth",
1174
976
  provider: OPENAI_PROVIDER_ID,
1175
977
  profileId: profile.profileId,
1176
- result
978
+ result,
979
+ modelConfigs: readProviderAuthModelConfigs(result)
1177
980
  } : null;
1178
981
  }
1179
982
  async function buildCodexApiKeyCredential(source) {
1180
- 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);
1181
985
  if (!key) return null;
1182
986
  return {
1183
987
  kind: "api_key",
@@ -1231,7 +1035,7 @@ function hasAuthProfileConfigConflict(config, profile, overwrite) {
1231
1035
  function hasCurrentAuthProfileConfigConflict(ctx, profile) {
1232
1036
  let config = ctx.config;
1233
1037
  try {
1234
- config = resolveMigrationConfigRuntime(ctx)?.current?.() ?? config;
1038
+ config = ctx.runtime?.config?.current?.() ?? config;
1235
1039
  } catch {}
1236
1040
  return hasAuthProfileConfigConflict(config, profile, Boolean(ctx.overwrite));
1237
1041
  }
@@ -1252,8 +1056,30 @@ function applyDefaultModelIfMissing(cfg) {
1252
1056
  }
1253
1057
  };
1254
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
+ }
1255
1081
  function applyOAuthConfigToConfig(cfg, credential, profileId) {
1256
- let next = mergeMigrationConfigValue(cfg, credential.result.configPatch);
1082
+ let next = applyOAuthModelConfigsToConfig(cfg, credential);
1257
1083
  const profile = credential.result.profiles[0];
1258
1084
  if (profile) next = applyAuthProfileConfig(next, {
1259
1085
  profileId,
@@ -1277,25 +1103,30 @@ function applyApiKeyConfigToConfig(cfg, credential, profileId) {
1277
1103
  function shouldReturnAuthConfigPatch(ctx) {
1278
1104
  return ctx.providerOptions?.configPatchMode === CODEX_CONFIG_PATCH_MODE_RETURN$1;
1279
1105
  }
1280
- function authProfileConfigForCredential(credential, profileId) {
1281
- if (credential.kind === "api_key") return {
1282
- profileId,
1283
- provider: credential.provider,
1284
- mode: "api_key",
1285
- displayName: CODEX_IMPORT_DISPLAY_NAME
1286
- };
1106
+ function oauthAuthProfileConfig(credential, profileId) {
1287
1107
  const profile = credential.result.profiles[0];
1288
1108
  if (!profile || profile.credential.type !== "oauth") return null;
1289
1109
  return {
1290
1110
  profileId,
1291
1111
  provider: profile.credential.provider,
1292
1112
  mode: "oauth",
1293
- ...profile.credential.email ? { email: profile.credential.email } : {},
1294
- ...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
1295
1123
  };
1296
1124
  }
1125
+ function authProfileConfigForCredential(credential, profileId) {
1126
+ return credential.kind === "oauth" ? oauthAuthProfileConfig(credential, profileId) : apiKeyAuthProfileConfig(credential, profileId);
1127
+ }
1297
1128
  async function applyCodexAuthProfileConfig(ctx, profile, applyConfig) {
1298
- const configApi = resolveMigrationConfigRuntime(ctx);
1129
+ const configApi = ctx.runtime?.config;
1299
1130
  if (!configApi?.current || !configApi.mutateConfigFile) return "unavailable";
1300
1131
  try {
1301
1132
  await configApi.mutateConfigFile({
@@ -1312,13 +1143,13 @@ async function applyCodexAuthProfileConfig(ctx, profile, applyConfig) {
1312
1143
  return error instanceof CodexAuthConfigConflict ? "conflict" : "unavailable";
1313
1144
  }
1314
1145
  }
1315
- async function applyCodexAuthConfig(ctx, credential, profileId) {
1316
- const profile = authProfileConfigForCredential(credential, profileId);
1146
+ async function applyOAuthConfig(ctx, credential, profileId) {
1147
+ const profile = oauthAuthProfileConfig(credential, profileId);
1317
1148
  if (!profile) return "unavailable";
1318
- return applyCodexAuthProfileConfig(ctx, profile, (config) => applyCredentialConfig(config, credential, profileId));
1149
+ return applyCodexAuthProfileConfig(ctx, profile, (config) => applyOAuthConfigToConfig(config, credential, profileId));
1319
1150
  }
1320
- function applyCredentialConfig(config, credential, profileId) {
1321
- 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));
1322
1153
  }
1323
1154
  async function buildCodexAuthItems(params) {
1324
1155
  const credentials = await readCodexAuthCredentials(params.source);
@@ -1351,27 +1182,26 @@ async function buildCodexAuthItems(params) {
1351
1182
  });
1352
1183
  });
1353
1184
  }
1354
- async function applyCodexAuthItems(params) {
1185
+ async function applyCodexAuthItem(params) {
1355
1186
  const { ctx, item, source, targets } = params;
1356
- if (item.status !== "planned") return [item];
1187
+ if (item.status !== "planned") return item;
1357
1188
  const profileId = typeof item.details?.profileId === "string" ? item.details.profileId : "";
1358
1189
  const provider = typeof item.details?.provider === "string" ? item.details.provider : "";
1359
1190
  const sourceProfileId = typeof item.details?.sourceProfileId === "string" ? item.details.sourceProfileId : void 0;
1360
- if (!profileId || !provider) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
1191
+ if (!profileId || !provider) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
1361
1192
  const credential = (await readCodexAuthCredentials(source)).find((candidate) => candidate.provider === provider);
1362
- if (!credential) return [markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT)];
1363
- 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);
1364
1195
  const oauthProfile = credential.kind === "oauth" ? credential.result.profiles[0] : void 0;
1365
1196
  const oauthCredential = oauthProfile?.credential.type === "oauth" ? oauthProfile.credential : void 0;
1366
- 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);
1367
1198
  const configProfile = authProfileConfigForCredential(credential, profileId);
1368
- if (!configProfile) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
1369
- 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);
1370
1201
  let conflicted = false;
1371
1202
  let wrote = false;
1372
1203
  const store = await updateAuthProfileStoreWithLock({
1373
1204
  agentDir: targets.agentDir,
1374
- stateDir: ctx.stateDir,
1375
1205
  updater: (freshStore) => {
1376
1206
  const existing = freshStore.profiles[profileId];
1377
1207
  if (!ctx.overwrite && existing) {
@@ -1390,11 +1220,11 @@ async function applyCodexAuthItems(params) {
1390
1220
  return true;
1391
1221
  }
1392
1222
  });
1393
- if (conflicted) return [markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS)];
1394
- if (!store?.profiles[profileId]) return [markMigrationItemError(item, CODEX_REASON_AUTH_PROFILE_WRITE_FAILED)];
1395
- const configResult = shouldReturnAuthConfigPatch(ctx) ? "unavailable" : await applyCodexAuthConfig(ctx, credential, profileId);
1396
- if (configResult === "conflict") return [markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS)];
1397
- 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 {
1398
1228
  ...item,
1399
1229
  status: "migrated",
1400
1230
  details: {
@@ -1404,10 +1234,18 @@ async function applyCodexAuthItems(params) {
1404
1234
  ...shouldReturnAuthConfigPatch(ctx) ? { configPatchReturned: true } : {}
1405
1235
  }
1406
1236
  };
1407
- return [migratedItem, ...shouldReturnAuthConfigPatch(ctx) ? buildCodexAuthConfigPatchItems(ctx, migratedItem, credential, profileId) : []];
1408
1237
  }
1409
- function buildCodexAuthConfigPatchItems(ctx, item, credential, profileId) {
1410
- 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);
1411
1249
  const items = [];
1412
1250
  if (next.auth) items.push(createMigrationItem({
1413
1251
  id: `${item.id}:config:auth`,
@@ -1436,10 +1274,16 @@ function buildCodexAuthConfigPatchItems(ctx, item, credential, profileId) {
1436
1274
  return items;
1437
1275
  }
1438
1276
  //#endregion
1439
- //#region extensions/codex/src/migration/source-files.ts
1277
+ //#region extensions/codex/src/migration/source.ts
1440
1278
  const SKILL_FILENAME = "SKILL.md";
1441
1279
  const MAX_SCAN_DEPTH = 6;
1442
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
+ }
1443
1287
  async function safeReadDir(dir) {
1444
1288
  return await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
1445
1289
  }
@@ -1458,7 +1302,10 @@ async function discoverSkillDirs(params) {
1458
1302
  });
1459
1303
  return;
1460
1304
  }
1461
- 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
+ }
1462
1309
  }
1463
1310
  await visit(params.root, 0);
1464
1311
  return discovered;
@@ -1472,86 +1319,43 @@ async function discoverPluginDirs(codexHome) {
1472
1319
  const manifestPath = path.join(dir, ".codex-plugin", "plugin.json");
1473
1320
  if (await exists(manifestPath)) {
1474
1321
  const manifest = await readJsonObject(manifestPath);
1475
- const manifestName = typeof manifest.name === "string" ? manifest.name.trim() : "";
1322
+ const name = (typeof manifest.name === "string" ? manifest.name.trim() : "") || path.basename(dir);
1476
1323
  discovered.set(dir, {
1477
- name: manifestName || path.basename(dir),
1324
+ name,
1478
1325
  source: dir,
1326
+ manifestPath,
1327
+ sourceKind: "cache",
1479
1328
  migratable: false,
1480
1329
  message: "Cached Codex plugin bundle found. Review manually unless the plugin is also installed in the source Codex app-server inventory"
1481
1330
  });
1482
1331
  return;
1483
1332
  }
1484
- 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
+ }
1485
1337
  }
1486
1338
  await visit(root, 0);
1487
1339
  return [...discovered.values()].toSorted((a, b) => a.source.localeCompare(b.source));
1488
1340
  }
1489
- async function discoverCodexMemoryFile(candidate) {
1490
- try {
1491
- const stat = await fs.lstat(candidate.path);
1492
- if (stat.isSymbolicLink()) throw new Error(`Codex memory source must not be a symbolic link: ${candidate.path}`);
1493
- if (!stat.isFile()) throw new Error(`Codex memory source must be a regular file: ${candidate.path}`);
1494
- return candidate;
1495
- } catch (error) {
1496
- if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return;
1497
- throw error;
1498
- }
1499
- }
1500
- async function discoverCodexMemorySources(codexHome) {
1501
- const memoriesDir = path.join(codexHome, "memories");
1502
- return (await Promise.all([{
1503
- id: "memory:codex:MEMORY.md",
1504
- label: "Codex consolidated memory",
1505
- name: "MEMORY.md"
1506
- }, {
1507
- id: "memory:codex:memory_summary.md",
1508
- label: "Codex memory summary",
1509
- name: "memory_summary.md"
1510
- }].map(async (candidate) => await discoverCodexMemoryFile({
1511
- id: candidate.id,
1512
- label: candidate.label,
1513
- path: path.join(memoriesDir, candidate.name)
1514
- })))).filter((entry) => entry !== void 0);
1515
- }
1516
- //#endregion
1517
- //#region extensions/codex/src/migration/source.ts
1518
- function defaultCodexHome() {
1519
- const configuredHome = process.env.CODEX_HOME;
1520
- return resolveHomePath(configuredHome !== void 0 && configuredHome.length > 0 ? configuredHome : "~/.codex");
1521
- }
1522
- function personalAgentsSkillsDir() {
1523
- return path.join(resolveUserHomeDir(), ".agents", "skills");
1524
- }
1525
1341
  async function discoverInstalledCuratedPlugins(codexHome, options = {}) {
1526
- const startOptions = sourceCodexAppServerStartOptions(codexHome);
1342
+ const requestOptions = { startOptions: sourceCodexAppServerStartOptions(codexHome) };
1527
1343
  try {
1528
- return await withCodexAppServerJsonClient({
1529
- timeoutMs: 6e4,
1530
- startOptions,
1531
- authProfileId: null,
1532
- isolated: true
1533
- }, async (request) => {
1534
- const requestOptions = {
1535
- startOptions,
1536
- request
1537
- };
1538
- const response = await request({
1539
- method: "plugin/installed",
1540
- requestParams: { cwds: [] }
1541
- });
1542
- const curatedSyncRoot = path.join(codexHome, ".tmp", "plugins");
1543
- const curatedMarketplaceErrors = response.marketplaceLoadErrors.filter((error) => isPathInside(curatedSyncRoot, error.marketplacePath));
1544
- if (curatedMarketplaceErrors.length > 0) return {
1545
- plugins: [],
1546
- error: curatedMarketplaceErrors.map((error) => error.message).join("; ")
1547
- };
1548
- const installed = discoverInstalledCuratedPluginSources(response);
1549
- return { plugins: (options.evaluatePluginMigrationEligibility === true ? await withPluginMigrationEligibility({
1550
- plugins: installed,
1551
- requestOptions,
1552
- verifyPluginApps: options.verifyPluginApps === true
1553
- }) : installed.map(({ plugin }) => plugin)).toSorted((left, right) => (left.pluginName ?? left.name).localeCompare(right.pluginName ?? right.name)) };
1554
- });
1344
+ const marketplace = (await requestSourceCodexAppServerJson(requestOptions, {
1345
+ method: "plugin/list",
1346
+ requestParams: { cwds: [] }
1347
+ })).marketplaces.find((entry) => entry.name === CODEX_PLUGINS_MARKETPLACE_NAME);
1348
+ if (!marketplace) return {
1349
+ plugins: [],
1350
+ error: `Codex marketplace ${CODEX_PLUGINS_MARKETPLACE_NAME} was not found in source plugin inventory.`
1351
+ };
1352
+ const plugins = marketplace.plugins.filter((plugin) => plugin.installed).map((plugin) => buildInstalledPluginSource(plugin)).filter((plugin) => plugin !== void 0);
1353
+ return { plugins: (options.evaluatePluginMigrationEligibility === true ? await withPluginMigrationEligibility({
1354
+ plugins,
1355
+ marketplace: marketplaceRef(marketplace),
1356
+ requestOptions,
1357
+ verifyPluginApps: options.verifyPluginApps === true
1358
+ }) : plugins).toSorted((a, b) => (a.pluginName ?? a.name).localeCompare(b.pluginName ?? b.name)) };
1555
1359
  } catch (error) {
1556
1360
  return {
1557
1361
  plugins: [],
@@ -1564,7 +1368,6 @@ function sourceCodexAppServerStartOptions(codexHome) {
1564
1368
  transport: "stdio",
1565
1369
  command: "codex",
1566
1370
  commandSource: "managed",
1567
- managedCommandOrder: "desktop-first",
1568
1371
  args: [
1569
1372
  "app-server",
1570
1373
  "--listen",
@@ -1577,6 +1380,16 @@ function sourceCodexAppServerStartOptions(codexHome) {
1577
1380
  }
1578
1381
  };
1579
1382
  }
1383
+ async function requestSourceCodexAppServerJson(options, params) {
1384
+ return await requestCodexAppServerJson({
1385
+ method: params.method,
1386
+ requestParams: params.requestParams,
1387
+ timeoutMs: 6e4,
1388
+ startOptions: options.startOptions,
1389
+ authProfileId: null,
1390
+ isolated: true
1391
+ });
1392
+ }
1580
1393
  function buildInstalledPluginSource(plugin) {
1581
1394
  const pluginName = pluginNameFromSummary(plugin);
1582
1395
  if (!pluginName) return;
@@ -1585,32 +1398,12 @@ 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
1591
1405
  };
1592
1406
  }
1593
- function discoverInstalledCuratedPluginSources(response) {
1594
- const installedByName = /* @__PURE__ */ new Map();
1595
- for (const marketplace of response.marketplaces) {
1596
- if (!isOpenAiCuratedMarketplace(marketplace)) continue;
1597
- const remote = !marketplace.path;
1598
- for (const summary of marketplace.plugins) {
1599
- if (!summary.installed) continue;
1600
- const plugin = buildInstalledPluginSource(summary);
1601
- if (!plugin?.pluginName) continue;
1602
- const existing = installedByName.get(plugin.pluginName);
1603
- if (existing && (!remote || existing.remote)) continue;
1604
- installedByName.set(plugin.pluginName, {
1605
- plugin,
1606
- marketplace: marketplaceRef(marketplace),
1607
- ...remote ? { readPluginName: summary.remotePluginId?.trim() || void 0 } : { readPluginName: plugin.pluginName },
1608
- remote
1609
- });
1610
- }
1611
- }
1612
- return Array.from(installedByName.values());
1613
- }
1614
1407
  function marketplaceRef(marketplace) {
1615
1408
  return {
1616
1409
  name: CODEX_PLUGINS_MARKETPLACE_NAME,
@@ -1621,7 +1414,7 @@ function marketplaceRef(marketplace) {
1621
1414
  async function withPluginMigrationEligibility(params) {
1622
1415
  const pending = [];
1623
1416
  const evaluated = [];
1624
- for (const { plugin, marketplace, readPluginName } of params.plugins) {
1417
+ for (const plugin of params.plugins) {
1625
1418
  if (plugin.enabled !== true) {
1626
1419
  evaluated.push({
1627
1420
  ...plugin,
@@ -1631,7 +1424,7 @@ async function withPluginMigrationEligibility(params) {
1631
1424
  });
1632
1425
  continue;
1633
1426
  }
1634
- const detail = await readPluginDetail(params.requestOptions, marketplace, plugin, readPluginName);
1427
+ const detail = await readPluginDetail(params.requestOptions, params.marketplace, plugin);
1635
1428
  if (!detail.ok) {
1636
1429
  evaluated.push({
1637
1430
  ...plugin,
@@ -1659,27 +1452,25 @@ async function withPluginMigrationEligibility(params) {
1659
1452
  }
1660
1453
  if (pending.length === 0) return evaluated;
1661
1454
  let sourceAccount;
1662
- let sourceAccountError;
1663
1455
  try {
1664
1456
  sourceAccount = await readSourceCodexAccount(params.requestOptions);
1665
- if (sourceAccount === "missing") sourceAccountError = "Codex app-server did not report an authenticated source account.";
1666
1457
  } catch (error) {
1667
- sourceAccountError = error instanceof Error ? error.message : String(error);
1668
- }
1669
- if (sourceAccountError && !params.verifyPluginApps) {
1670
- for (const { plugin, apps } of pending) evaluated.push({
1671
- ...plugin,
1672
- migratable: false,
1673
- migrationBlock: {
1674
- code: "codex_account_unavailable",
1675
- apps,
1676
- error: sourceAccountError
1677
- },
1678
- message: `Codex plugin "${plugin.pluginName ?? plugin.name}" owns apps, but the source Codex app-server account could not be read: ${sourceAccountError}`
1679
- });
1680
- return evaluated;
1458
+ if (!params.verifyPluginApps) {
1459
+ const message = error instanceof Error ? error.message : String(error);
1460
+ for (const { plugin, apps } of pending) evaluated.push({
1461
+ ...plugin,
1462
+ migratable: false,
1463
+ migrationBlock: {
1464
+ code: "codex_account_unavailable",
1465
+ apps,
1466
+ error: message
1467
+ },
1468
+ message: `Codex plugin "${plugin.pluginName ?? plugin.name}" owns apps, but the source Codex app-server account could not be read: ${message}`
1469
+ });
1470
+ return evaluated;
1471
+ }
1681
1472
  }
1682
- if (sourceAccount === "non_chatgpt") {
1473
+ if (sourceAccount && sourceAccount !== "chatgpt") {
1683
1474
  for (const { plugin, apps } of pending) evaluated.push({
1684
1475
  ...plugin,
1685
1476
  migratable: false,
@@ -1714,9 +1505,8 @@ async function withPluginMigrationEligibility(params) {
1714
1505
  });
1715
1506
  if (!snapshot) return evaluated;
1716
1507
  const appInfoById = new Map(snapshot.apps.map((app) => [app.id, app]));
1717
- const installedAppsById = new Map(snapshot.installedApps.map((app) => [app.id, app]));
1718
1508
  for (const { plugin, apps: declaredApps } of pending) {
1719
- const apps = declaredApps.map((app) => sourcePluginAppFactWithInventory(app, appInfoById.get(app.id), installedAppsById.get(app.id))).toSorted((left, right) => left.id.localeCompare(right.id));
1509
+ const apps = declaredApps.map((app) => sourcePluginAppFactWithInventory(app, appInfoById.get(app.id))).toSorted((left, right) => left.id.localeCompare(right.id));
1720
1510
  const blockCode = migrationBlockCodeForApps(apps);
1721
1511
  if (!blockCode) {
1722
1512
  evaluated.push({
@@ -1739,29 +1529,20 @@ async function withPluginMigrationEligibility(params) {
1739
1529
  return evaluated;
1740
1530
  }
1741
1531
  async function readSourceCodexAccount(options) {
1742
- const response = await options.request({
1532
+ const response = await requestSourceCodexAppServerJson(options, {
1743
1533
  method: "account/read",
1744
1534
  requestParams: { refreshToken: false }
1745
1535
  });
1746
1536
  if (!response.account || typeof response.account !== "object" || Array.isArray(response.account)) return "missing";
1747
- switch (response.account.type) {
1748
- case "chatgpt": return "chatgpt";
1749
- case "apiKey":
1750
- case "amazonBedrock": return "non_chatgpt";
1751
- default: return "missing";
1752
- }
1537
+ return response.account.type === "chatgpt" ? "chatgpt" : "non_chatgpt";
1753
1538
  }
1754
- async function readPluginDetail(options, marketplace, plugin, readPluginName) {
1755
- if (!readPluginName) return {
1756
- ok: false,
1757
- error: `Codex remote plugin "${plugin.pluginName ?? plugin.name}" has no readable remote plugin id.`
1758
- };
1539
+ async function readPluginDetail(options, marketplace, plugin) {
1759
1540
  try {
1760
1541
  return {
1761
1542
  ok: true,
1762
- detail: (await options.request({
1543
+ detail: (await requestSourceCodexAppServerJson(options, {
1763
1544
  method: "plugin/read",
1764
- requestParams: pluginReadParams(marketplace, readPluginName)
1545
+ requestParams: pluginReadParams(marketplace, plugin.pluginName ?? plugin.name)
1765
1546
  })).plugin
1766
1547
  };
1767
1548
  } catch (error) {
@@ -1773,7 +1554,7 @@ async function readPluginDetail(options, marketplace, plugin, readPluginName) {
1773
1554
  }
1774
1555
  async function refreshSourceAppInventory(options) {
1775
1556
  const key = buildCodexPluginAppCacheKey({ appServer: { start: options.startOptions } });
1776
- const request = async (method, requestParams) => await options.request({
1557
+ const request = async (method, requestParams) => await requestSourceCodexAppServerJson(options, {
1777
1558
  method,
1778
1559
  requestParams
1779
1560
  });
@@ -1786,29 +1567,16 @@ async function refreshSourceAppInventory(options) {
1786
1567
  function sourcePluginAppFact(app) {
1787
1568
  return {
1788
1569
  id: app.id,
1789
- name: app.name
1570
+ name: app.name,
1571
+ needsAuth: app.needsAuth
1790
1572
  };
1791
1573
  }
1792
- function sourcePluginAppFactWithInventory(app, info, installedApp) {
1793
- if (!installedApp) return app;
1794
- if (!info) return installedApp.enabled ? {
1795
- ...app,
1796
- isAccessible: false,
1797
- isEnabled: true
1798
- } : {
1799
- ...app,
1800
- isEnabled: false
1801
- };
1802
- if (!installedApp.enabled) return {
1803
- ...app,
1804
- isAccessible: info.isAccessible,
1805
- isEnabled: false
1806
- };
1574
+ function sourcePluginAppFactWithInventory(app, info) {
1575
+ if (!info) return app;
1807
1576
  return {
1808
1577
  ...app,
1809
- isAccessible: info.isAccessible && installedApp.callable,
1810
- isEnabled: info.isEnabled,
1811
- ...!installedApp.callable ? { isCallable: false } : {}
1578
+ isAccessible: info.isAccessible,
1579
+ isEnabled: info.isEnabled
1812
1580
  };
1813
1581
  }
1814
1582
  function migrationBlockCodeForApps(apps) {
@@ -1817,7 +1585,7 @@ function migrationBlockCodeForApps(apps) {
1817
1585
  if (apps.some((app) => app.isAccessible === void 0 || app.isEnabled === void 0)) return "app_missing";
1818
1586
  }
1819
1587
  function appInventoryBlockMessage(plugin, apps, code) {
1820
- const status = code === "app_inaccessible" ? apps.some((app) => app.isCallable === false) ? "not callable" : "inaccessible" : code === "app_disabled" ? "disabled" : "missing";
1588
+ const status = code === "app_inaccessible" ? "inaccessible" : code === "app_disabled" ? "disabled" : "missing";
1821
1589
  const blocking = apps.find((app) => code === "app_inaccessible" ? app.isAccessible === false : code === "app_disabled" ? app.isEnabled === false : app.isAccessible === void 0 || app.isEnabled === void 0) ?? apps[0];
1822
1590
  const appLabel = blocking ? ` app "${blocking.name}"` : " an owned app";
1823
1591
  return `Codex plugin "${plugin.pluginName ?? plugin.name}" owns${appLabel} but the source app inventory reports it is ${status}; authenticate or enable the app in Codex before migrating it to OpenClaw.`;
@@ -1829,20 +1597,16 @@ function codexSubscriptionRequiredMessage(plugin) {
1829
1597
  return `Codex plugin "${plugin.pluginName ?? plugin.name}" owns apps, but ${codexPluginMigrationSubscriptionWarning()}`;
1830
1598
  }
1831
1599
  function pluginNameFromSummary(summary) {
1832
- const candidates = [summary.name, summary.id];
1600
+ const candidates = [summary.id, summary.name];
1833
1601
  for (const candidate of candidates) {
1834
1602
  const trimmed = candidate.trim();
1835
1603
  if (!trimmed) continue;
1836
- const marketplaceSuffix = [
1837
- `@${CODEX_PLUGINS_MARKETPLACE_NAME}-remote`,
1838
- `@openai-api-curated`,
1839
- `@${CODEX_PLUGINS_MARKETPLACE_NAME}`
1840
- ].find((suffix) => trimmed.endsWith(suffix));
1841
- const normalized = ((marketplaceSuffix ? trimmed.slice(0, -marketplaceSuffix.length) : trimmed).split("/").at(-1)?.trim())?.toLowerCase().replaceAll(/\s+/gu, "-");
1604
+ const normalized = ((trimmed.endsWith(`@openai-curated`) ? trimmed.slice(0, -`@${CODEX_PLUGINS_MARKETPLACE_NAME}`.length) : trimmed).split("/").at(-1)?.trim())?.toLowerCase().replaceAll(/\s+/gu, "-");
1842
1605
  if (normalized) return normalized;
1843
1606
  }
1844
1607
  }
1845
- async function discoverCodexSource(options = {}) {
1608
+ async function discoverCodexSource(inputOrOptions) {
1609
+ const options = typeof inputOrOptions === "string" || inputOrOptions === void 0 ? { input: inputOrOptions } : inputOrOptions;
1846
1610
  const codexHome = resolveHomePath(options.input?.trim() || defaultCodexHome());
1847
1611
  const codexSkillsDir = path.join(codexHome, "skills");
1848
1612
  const agentsSkillsDir = personalAgentsSkillsDir();
@@ -1850,39 +1614,38 @@ async function discoverCodexSource(options = {}) {
1850
1614
  const authPath = path.join(codexHome, "auth.json");
1851
1615
  const modelsCachePath = path.join(codexHome, "models_cache.json");
1852
1616
  const hooksPath = path.join(codexHome, "hooks", "hooks.json");
1853
- const memoryFiles = await discoverCodexMemorySources(codexHome);
1854
- const codexSkills = options.memoryOnly ? [] : await discoverSkillDirs({
1617
+ const codexSkills = await discoverSkillDirs({
1855
1618
  root: codexSkillsDir,
1856
1619
  sourceLabel: "Codex skill",
1857
1620
  excludeSystem: true
1858
1621
  });
1859
- const personalAgentSkills = options.memoryOnly ? [] : await discoverSkillDirs({
1622
+ const personalAgentSkills = await discoverSkillDirs({
1860
1623
  root: agentsSkillsDir,
1861
1624
  sourceLabel: "personal AgentSkill"
1862
1625
  });
1863
- const sourcePluginDiscovery = options.memoryOnly ? { plugins: [] } : await discoverInstalledCuratedPlugins(codexHome, options);
1626
+ const sourcePluginDiscovery = await discoverInstalledCuratedPlugins(codexHome, options);
1864
1627
  const sourcePluginNames = new Set(sourcePluginDiscovery.plugins.flatMap((plugin) => plugin.pluginName ? [plugin.pluginName] : []));
1865
- const cachedPlugins = (options.memoryOnly ? [] : await discoverPluginDirs(codexHome)).filter((plugin) => {
1628
+ const cachedPlugins = (await discoverPluginDirs(codexHome)).filter((plugin) => {
1866
1629
  const normalizedName = sanitizePluginName(plugin.name);
1867
1630
  return !sourcePluginNames.has(normalizedName);
1868
1631
  });
1869
1632
  const plugins = [...sourcePluginDiscovery.plugins, ...cachedPlugins].toSorted((a, b) => a.source.localeCompare(b.source));
1870
1633
  const archivePaths = [];
1871
- if (!options.memoryOnly && await exists(configPath)) archivePaths.push({
1634
+ if (await exists(configPath)) archivePaths.push({
1872
1635
  id: "archive:config.toml",
1873
1636
  path: configPath,
1874
1637
  relativePath: "config.toml",
1875
1638
  message: "Codex config is archived for manual review; it is not activated automatically"
1876
1639
  });
1877
- if (!options.memoryOnly && await exists(hooksPath)) archivePaths.push({
1640
+ if (await exists(hooksPath)) archivePaths.push({
1878
1641
  id: "archive:hooks/hooks.json",
1879
1642
  path: hooksPath,
1880
1643
  relativePath: "hooks/hooks.json",
1881
1644
  message: "Codex native hooks are archived for manual review because they can execute commands"
1882
1645
  });
1883
1646
  const skills = [...codexSkills, ...personalAgentSkills].toSorted((a, b) => a.source.localeCompare(b.source));
1884
- const hasAuth = !options.memoryOnly && await exists(authPath);
1885
- 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);
1886
1649
  const medium = personalAgentSkills.length > 0;
1887
1650
  return {
1888
1651
  root: codexHome,
@@ -1890,9 +1653,10 @@ async function discoverCodexSource(options = {}) {
1890
1653
  codexHome,
1891
1654
  ...await isDirectory(codexSkillsDir) ? { codexSkillsDir } : {},
1892
1655
  ...await isDirectory(agentsSkillsDir) ? { personalAgentsSkillsDir: agentsSkillsDir } : {},
1656
+ ...await exists(configPath) ? { configPath } : {},
1893
1657
  ...hasAuth ? { authPath } : {},
1894
1658
  ...await exists(modelsCachePath) ? { modelsCachePath } : {},
1895
- memoryFiles,
1659
+ ...await exists(hooksPath) ? { hooksPath } : {},
1896
1660
  skills,
1897
1661
  plugins,
1898
1662
  ...sourcePluginDiscovery.error ? { pluginDiscoveryError: sourcePluginDiscovery.error } : {},
@@ -1909,7 +1673,7 @@ function sanitizePluginName(value) {
1909
1673
  //#region extensions/codex/src/migration/targets.ts
1910
1674
  function resolveCodexMigrationTargets(ctx) {
1911
1675
  const cfg = ctx.config;
1912
- const agentId = ctx.targetAgentId ?? resolveDefaultAgentId(cfg);
1676
+ const agentId = resolveDefaultAgentId(cfg);
1913
1677
  const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
1914
1678
  const configuredAgentDir = resolveAgentConfig(cfg, agentId)?.agentDir?.trim();
1915
1679
  return {
@@ -1940,52 +1704,6 @@ const CODEX_PLUGIN_NATIVE_CONFIG_PATH = [
1940
1704
  ];
1941
1705
  const MIGRATION_REASON_PLUGIN_EXISTS = "plugin exists";
1942
1706
  const CODEX_PLUGIN_SOURCE_APP_VERIFICATION_UNVERIFIED = "not_run";
1943
- const MIGRATION_REASON_TARGET_NOT_REGULAR = "target is not a regular file";
1944
- async function lstatIfExists(filePath) {
1945
- try {
1946
- return await fs.lstat(filePath);
1947
- } catch (error) {
1948
- const code = extractErrorCode(error);
1949
- if (code === "ENOENT" || code === "ENOTDIR") return;
1950
- throw error;
1951
- }
1952
- }
1953
- async function buildCodexMemoryItems(params) {
1954
- const items = [];
1955
- for (const memory of params.memoryFiles) {
1956
- const target = path.join(params.workspaceDir, "memory", "imports", "codex", path.basename(memory.path));
1957
- const targetStat = await lstatIfExists(target);
1958
- const targetNotRegular = targetStat !== void 0 && !targetStat.isFile();
1959
- if (!targetNotRegular) {
1960
- const [source, workspace, destination] = await Promise.all([
1961
- fs.realpath(path.dirname(memory.path)),
1962
- canonicalPathFromExistingAncestor(params.workspaceDir),
1963
- canonicalPathFromExistingAncestor(target)
1964
- ]);
1965
- if (!isPathInside(workspace, destination)) throw new Error("Codex memory import destination must stay in the selected workspace.");
1966
- if (isPathInside(source, destination) || isPathInside(destination, source)) throw new Error("Codex memory source and OpenClaw import destination must be separate paths.");
1967
- }
1968
- const targetConflict = targetStat !== void 0 && !params.overwrite;
1969
- items.push(createMigrationItem({
1970
- id: memory.id,
1971
- kind: "memory",
1972
- action: "copy",
1973
- source: memory.path,
1974
- target,
1975
- status: targetNotRegular || targetConflict ? "conflict" : "planned",
1976
- reason: targetNotRegular ? MIGRATION_REASON_TARGET_NOT_REGULAR : targetConflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
1977
- message: "Copy consolidated Codex memory into the OpenClaw memory index.",
1978
- details: {
1979
- sourceType: "codex-memory",
1980
- sourceLabel: memory.label,
1981
- collectionId: "codex",
1982
- collectionLabel: "Codex",
1983
- relativePath: path.basename(memory.path)
1984
- }
1985
- }));
1986
- }
1987
- return items;
1988
- }
1989
1707
  function uniqueSkillName(skill, counts) {
1990
1708
  const base = sanitizeName(skill.name) || "codex-skill";
1991
1709
  if ((counts.get(base) ?? 0) <= 1) return base;
@@ -1995,44 +1713,50 @@ function uniqueSkillName(skill, counts) {
1995
1713
  base
1996
1714
  ].filter(Boolean).join("-")) || base;
1997
1715
  }
1998
- async function buildCodexSkillItems(params) {
1999
- const counts = /* @__PURE__ */ new Map();
1716
+ async function buildSkillItems(params) {
1717
+ const baseCounts = /* @__PURE__ */ new Map();
2000
1718
  for (const skill of params.skills) {
2001
1719
  const base = sanitizeName(skill.name) || "codex-skill";
2002
- counts.set(base, (counts.get(base) ?? 0) + 1);
1720
+ baseCounts.set(base, (baseCounts.get(base) ?? 0) + 1);
2003
1721
  }
1722
+ const resolvedCounts = /* @__PURE__ */ new Map();
2004
1723
  const planned = params.skills.map((skill) => {
2005
- const name = uniqueSkillName(skill, counts);
1724
+ const name = uniqueSkillName(skill, baseCounts);
1725
+ resolvedCounts.set(name, (resolvedCounts.get(name) ?? 0) + 1);
2006
1726
  return {
2007
1727
  skill,
2008
1728
  name,
2009
1729
  target: path.join(params.workspaceDir, "skills", name)
2010
1730
  };
2011
1731
  });
2012
- const resolvedCounts = planned.reduce((resolved, item) => {
2013
- resolved.set(item.name, (resolved.get(item.name) ?? 0) + 1);
2014
- return resolved;
2015
- }, /* @__PURE__ */ new Map());
2016
- return await Promise.all(planned.map(async (item) => {
2017
- 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;
2018
1735
  const targetExists = await exists(item.target);
2019
- const conflict = collision || targetExists && !params.overwrite;
2020
- return createMigrationItem({
1736
+ items.push(createMigrationItem({
2021
1737
  id: `skill:${item.name}`,
2022
1738
  kind: "skill",
2023
1739
  action: "copy",
2024
1740
  source: item.skill.source,
2025
1741
  target: item.target,
2026
- status: conflict ? "conflict" : "planned",
2027
- 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,
2028
1744
  message: `Copy ${item.skill.sourceLabel} into this OpenClaw agent workspace.`,
2029
1745
  details: {
2030
1746
  skillName: item.name,
2031
1747
  sourceLabel: item.skill.sourceLabel
2032
1748
  }
2033
- });
2034
- }));
2035
- }
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;
1759
+ }
2036
1760
  function readExistingCodexPluginEntries(config) {
2037
1761
  const entries = readMigrationConfigPath(config, [...CODEX_PLUGIN_NATIVE_CONFIG_PATH, "plugins"]);
2038
1762
  return isRecord(entries) ? entries : {};
@@ -2062,12 +1786,18 @@ function readExistingPluginAllowDestructiveActions(existing, pluginName) {
2062
1786
  return normalized === "auto" || normalized === "ask" ? normalized : void 0;
2063
1787
  }
2064
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
+ }
2065
1794
  const existingPluginEntries = readExistingCodexPluginEntries(ctx.config);
1795
+ const usedCounts = /* @__PURE__ */ new Map();
2066
1796
  let manualIndex = 0;
2067
1797
  const items = [];
2068
1798
  for (const plugin of plugins) {
2069
1799
  if (plugin.migratable && plugin.marketplaceName === "openai-curated" && plugin.pluginName) {
2070
- const configKey = plugin.pluginName;
1800
+ const configKey = uniquePluginConfigKey(plugin, baseCounts, usedCounts);
2071
1801
  const plannedEntry = {
2072
1802
  enabled: true,
2073
1803
  marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
@@ -2084,7 +1814,6 @@ function buildPluginItems(ctx, plugins) {
2084
1814
  action: "install",
2085
1815
  status: conflict ? "conflict" : "planned",
2086
1816
  reason: conflict ? MIGRATION_REASON_PLUGIN_EXISTS : void 0,
2087
- applyPhase: "after-promotion",
2088
1817
  source: plugin.source,
2089
1818
  target: `plugins.entries.codex.config.codexPlugins.plugins.${configKey}`,
2090
1819
  message: `Install Codex plugin "${plugin.pluginName}" in the OpenClaw-managed Codex app-server runtime.`,
@@ -2102,6 +1831,12 @@ function buildPluginItems(ctx, plugins) {
2102
1831
  }
2103
1832
  manualIndex += 1;
2104
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
+ };
2105
1840
  items.push(createMigrationItem({
2106
1841
  id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${manualIndex}`,
2107
1842
  kind: "manual",
@@ -2110,12 +1845,7 @@ function buildPluginItems(ctx, plugins) {
2110
1845
  status: "skipped",
2111
1846
  reason: plugin.migrationBlock.code,
2112
1847
  message: plugin.message ?? `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
2113
- details: {
2114
- pluginName: plugin.pluginName,
2115
- marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
2116
- ...plugin.migrationBlock.apps ? { apps: plugin.migrationBlock.apps } : {},
2117
- ...plugin.migrationBlock.error ? { error: plugin.migrationBlock.error } : {}
2118
- }
1848
+ details: { ...details }
2119
1849
  }));
2120
1850
  continue;
2121
1851
  }
@@ -2123,7 +1853,7 @@ function buildPluginItems(ctx, plugins) {
2123
1853
  id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${manualIndex}`,
2124
1854
  source: plugin.source,
2125
1855
  message: plugin.message ?? `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
2126
- 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>."
2127
1857
  }));
2128
1858
  }
2129
1859
  return items;
@@ -2153,6 +1883,7 @@ function normalizeExistingAllowDestructiveActions(value) {
2153
1883
  return asBoolean(value);
2154
1884
  }
2155
1885
  function readExistingPluginPolicyRepairs(config) {
1886
+ if (config === void 0) return {};
2156
1887
  return Object.fromEntries(Object.entries(readExistingCodexPluginEntries(config)).flatMap(([configKey, entry]) => {
2157
1888
  const pluginEntry = isRecord(entry) ? entry : void 0;
2158
1889
  if (pluginEntry?.allow_destructive_actions !== "on-request") return [];
@@ -2162,9 +1893,9 @@ function readExistingPluginPolicyRepairs(config) {
2162
1893
  }]];
2163
1894
  }));
2164
1895
  }
2165
- function buildCodexPluginsConfigValue(entries, config) {
1896
+ function buildCodexPluginsConfigValue(entries, params = {}) {
2166
1897
  const plugins = {
2167
- ...readExistingPluginPolicyRepairs(config),
1898
+ ...readExistingPluginPolicyRepairs(params.config),
2168
1899
  ...Object.fromEntries(entries.toSorted((a, b) => a.configKey.localeCompare(b.configKey)).map((entry) => [entry.configKey, {
2169
1900
  enabled: entry.enabled,
2170
1901
  marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
@@ -2176,7 +1907,7 @@ function buildCodexPluginsConfigValue(entries, config) {
2176
1907
  enabled: true,
2177
1908
  config: { codexPlugins: {
2178
1909
  enabled: true,
2179
- allow_destructive_actions: readExistingAllowDestructiveActions(config) ?? true,
1910
+ allow_destructive_actions: params.config === void 0 ? true : readExistingAllowDestructiveActions(params.config) ?? true,
2180
1911
  plugins
2181
1912
  } }
2182
1913
  };
@@ -2203,7 +1934,7 @@ function hasCodexPluginConfigConflict(config, value) {
2203
1934
  function buildPluginConfigItem(ctx, pluginItems) {
2204
1935
  const entries = pluginItems.filter((item) => item.status === "planned").map((item) => readCodexPluginMigrationConfigEntry(item, true)).filter((entry) => entry !== void 0);
2205
1936
  if (entries.length === 0) return;
2206
- const value = buildCodexPluginsConfigValue(entries, ctx.config);
1937
+ const value = buildCodexPluginsConfigValue(entries, { config: ctx.config });
2207
1938
  const conflict = !ctx.overwrite && hasCodexPluginConfigConflict(ctx.config, value);
2208
1939
  return createMigrationItem({
2209
1940
  id: CODEX_PLUGIN_CONFIG_ITEM_ID,
@@ -2212,7 +1943,6 @@ function buildPluginConfigItem(ctx, pluginItems) {
2212
1943
  target: "plugins.entries.codex.config.codexPlugins",
2213
1944
  status: conflict ? "conflict" : "planned",
2214
1945
  reason: conflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
2215
- applyPhase: "after-promotion",
2216
1946
  message: "Enable OpenClaw's Codex plugin integration and record migrated source-installed curated plugins.",
2217
1947
  details: {
2218
1948
  path: [...CODEX_PLUGIN_CONFIG_PATH],
@@ -2222,44 +1952,35 @@ function buildPluginConfigItem(ctx, pluginItems) {
2222
1952
  }
2223
1953
  async function buildCodexMigrationPlan(ctx) {
2224
1954
  const targets = resolveCodexMigrationTargets(ctx);
2225
- const memoryOnly = ctx.itemKinds !== void 0 && ctx.itemKinds.length > 0 && ctx.itemKinds.every((kind) => kind === "memory");
2226
1955
  const source = await discoverCodexSource({
2227
1956
  input: ctx.source,
2228
- memoryOnly,
2229
- evaluatePluginMigrationEligibility: !memoryOnly,
1957
+ evaluatePluginMigrationEligibility: true,
2230
1958
  verifyPluginApps: shouldVerifyPluginApps(ctx)
2231
1959
  });
2232
1960
  if (!hasCodexSource(source)) throw new Error(`Codex state was not found at ${source.root}. Pass --from <path> if it lives elsewhere.`);
2233
1961
  const items = [];
2234
- items.push(...await buildCodexMemoryItems({
2235
- memoryFiles: source.memoryFiles,
1962
+ items.push(...await buildCodexAuthItems({
1963
+ ctx,
1964
+ source,
1965
+ targets
1966
+ }));
1967
+ items.push(...await buildSkillItems({
1968
+ skills: source.skills,
2236
1969
  workspaceDir: targets.workspaceDir,
2237
1970
  overwrite: ctx.overwrite
2238
1971
  }));
2239
- if (!memoryOnly) {
2240
- items.push(...await buildCodexAuthItems({
2241
- ctx,
2242
- source,
2243
- targets
2244
- }));
2245
- items.push(...await buildCodexSkillItems({
2246
- skills: source.skills,
2247
- workspaceDir: targets.workspaceDir,
2248
- overwrite: ctx.overwrite
2249
- }));
2250
- const pluginItems = buildPluginItems(ctx, source.plugins);
2251
- items.push(...pluginItems);
2252
- const pluginConfigItem = buildPluginConfigItem(ctx, pluginItems);
2253
- if (pluginConfigItem) items.push(pluginConfigItem);
2254
- for (const archivePath of source.archivePaths) items.push(createMigrationItem({
2255
- id: archivePath.id,
2256
- kind: "archive",
2257
- action: "archive",
2258
- source: archivePath.path,
2259
- message: archivePath.message ?? "Archived in the migration report for manual review; not imported into live config.",
2260
- details: { archiveRelativePath: archivePath.relativePath }
2261
- }));
2262
- }
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
+ }));
2263
1984
  const warnings = [
2264
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."] : [],
2265
1986
  ...items.some((item) => item.status === "conflict") ? ["Conflicts were found. Re-run with --overwrite to replace conflicting migration targets after item-level backups."] : [],
@@ -2273,7 +1994,7 @@ async function buildCodexMigrationPlan(ctx) {
2273
1994
  summary: summarizeMigrationItems(items),
2274
1995
  items,
2275
1996
  warnings,
2276
- 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."],
2277
1998
  metadata: {
2278
1999
  agentDir: targets.agentDir,
2279
2000
  codexHome: source.codexHome,
@@ -2329,9 +2050,14 @@ async function applyCodexMigrationPlan(params) {
2329
2050
  const targets = resolveCodexMigrationTargets(params.ctx);
2330
2051
  const codexHome = typeof plan.metadata?.codexHome === "string" && plan.metadata.codexHome.trim() ? plan.metadata.codexHome : plan.source;
2331
2052
  const authSource = {
2053
+ root: plan.source,
2054
+ confidence: "high",
2332
2055
  codexHome,
2333
2056
  authPath: path.join(codexHome, "auth.json"),
2334
- modelsCachePath: path.join(codexHome, "models_cache.json")
2057
+ modelsCachePath: path.join(codexHome, "models_cache.json"),
2058
+ skills: [],
2059
+ plugins: [],
2060
+ archivePaths: []
2335
2061
  };
2336
2062
  const runtime = withCachedMigrationConfigRuntime(params.ctx.runtime ?? params.runtime, params.ctx.config);
2337
2063
  const applyCtx = {
@@ -2344,19 +2070,22 @@ async function applyCodexMigrationPlan(params) {
2344
2070
  continue;
2345
2071
  }
2346
2072
  if (item.id === "config:codex-plugins") items.push(await applyCodexPluginConfigItem(applyCtx, item, items));
2347
- else if (item.kind === "auth") items.push(...await applyCodexAuthItems({
2348
- ctx: applyCtx,
2349
- item,
2350
- source: authSource,
2351
- targets
2352
- }));
2353
- 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));
2354
2087
  else if (item.kind === "manual") items.push(applyMigrationManualItem(item));
2355
2088
  else if (item.action === "archive") items.push(await archiveMigrationItem(item, reportDir));
2356
- else if (item.kind === "memory") items.push(await copyMemoryMigrationFileItem(item, reportDir, {
2357
- workspaceDir: targets.workspaceDir,
2358
- overwrite: params.ctx.overwrite
2359
- }));
2360
2089
  else items.push(await copyMigrationFileItem(item, reportDir, { overwrite: params.ctx.overwrite }));
2361
2090
  }
2362
2091
  const result = {
@@ -2488,7 +2217,8 @@ async function requestTargetCodexAppServerJson(params) {
2488
2217
  });
2489
2218
  if (hasOpenAiCuratedMarketplace(lastResponse)) return lastResponse;
2490
2219
  if (Date.now() >= discoveryDeadline) return lastResponse;
2491
- 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);
2492
2222
  } while (Date.now() < discoveryDeadline);
2493
2223
  return lastResponse;
2494
2224
  }
@@ -2527,21 +2257,13 @@ async function buildTargetCodexPluginAppCacheKey(ctx) {
2527
2257
  });
2528
2258
  }
2529
2259
  async function applyCodexPluginConfigItem(ctx, item, appliedItems) {
2530
- if (appliedItems.filter((candidate) => candidate.kind === "plugin" && candidate.action === "install" && readCodexPluginPolicy(candidate) !== void 0 && !isCodexPluginConfigTerminal(candidate)).length > 0) return {
2531
- ...item,
2532
- status: "warning",
2533
- reason: "selected Codex plugin activation is incomplete"
2534
- };
2535
2260
  const entries = appliedItems.map(readAppliedPluginConfigEntry).filter((entry) => entry !== void 0);
2536
- if (entries.length === 0) return {
2537
- ...markMigrationItemSkipped(item, "no selected Codex plugins"),
2538
- deferredCompletion: true
2539
- };
2261
+ if (entries.length === 0) return markMigrationItemSkipped(item, "no selected Codex plugins");
2540
2262
  const returnPatch = shouldReturnCodexPluginConfigPatch(ctx);
2541
- const configApi = resolveMigrationConfigRuntime(ctx);
2263
+ const configApi = ctx.runtime?.config;
2542
2264
  const currentConfig = returnPatch ? ctx.config : configApi?.current?.();
2543
2265
  if (!currentConfig) return markMigrationItemError(item, "config runtime unavailable");
2544
- const value = buildCodexPluginsConfigValue(entries, currentConfig);
2266
+ const value = buildCodexPluginsConfigValue(entries, { config: currentConfig });
2545
2267
  if (!ctx.overwrite && hasCodexPluginConfigConflict(currentConfig, value)) return markMigrationItemConflict(item, MIGRATION_REASON_TARGET_EXISTS);
2546
2268
  const migratedItem = {
2547
2269
  ...item,
@@ -2569,42 +2291,65 @@ async function applyCodexPluginConfigItem(ctx, item, appliedItems) {
2569
2291
  return markMigrationItemError(item, error instanceof Error ? error.message : String(error));
2570
2292
  }
2571
2293
  }
2572
- function isCodexPluginConfigTerminal(item) {
2573
- return item.status === "migrated" || item.status === "skipped" && (item.deferredCompletion === true || item.reason === CODEX_PLUGIN_NOT_SELECTED_REASON || item.reason === CODEX_PLUGIN_AUTH_REQUIRED_REASON);
2574
- }
2575
2294
  function readAppliedPluginConfigEntry(item) {
2576
- if (item.status === "migrated" || item.deferredCompletion === true) return readCodexPluginMigrationConfigEntry(item, true);
2577
- 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);
2578
2297
  }
2579
2298
  function readCodexPluginPolicy(item) {
2580
- const entry = readCodexPluginMigrationConfigEntry(item, true);
2581
- 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;
2582
2303
  return {
2583
- configKey: entry.configKey,
2304
+ configKey,
2584
2305
  marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
2585
- pluginName: entry.pluginName,
2306
+ pluginName,
2586
2307
  enabled: true,
2587
2308
  allowDestructiveActions: true,
2588
2309
  destructiveApprovalMode: "allow"
2589
2310
  };
2590
2311
  }
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
+ }
2591
2343
  //#endregion
2592
2344
  //#region extensions/codex/src/migration/provider.ts
2593
- function isMemoryOnlyMigration(ctx) {
2594
- return Boolean(ctx.itemKinds && ctx.itemKinds.length > 0 && ctx.itemKinds.every((kind) => kind === "memory"));
2595
- }
2596
2345
  function buildCodexMigrationProvider(params = {}) {
2597
2346
  return {
2598
2347
  id: "codex",
2599
2348
  label: "Codex",
2600
- description: "Import Codex memory and skills while keeping Codex native plugins and hooks explicit.",
2601
- supportedItemKinds: ["memory"],
2349
+ description: "Inventory and promote Codex CLI skills while keeping Codex native plugins and hooks explicit.",
2602
2350
  async detect(ctx) {
2603
- const source = await discoverCodexSource({
2604
- input: ctx.source,
2605
- memoryOnly: isMemoryOnlyMigration(ctx)
2606
- });
2607
- const found = isMemoryOnlyMigration(ctx) ? source.memoryFiles.length > 0 : hasCodexSource(source);
2351
+ const source = await discoverCodexSource({ input: ctx.source });
2352
+ const found = hasCodexSource(source);
2608
2353
  return {
2609
2354
  found,
2610
2355
  source: source.root,
@@ -2614,9 +2359,7 @@ function buildCodexMigrationProvider(params = {}) {
2614
2359
  };
2615
2360
  },
2616
2361
  plan: buildCodexMigrationPlan,
2617
- deferredApply: { retrySafe: true },
2618
2362
  prepareApply(ctx) {
2619
- if (isMemoryOnlyMigration(ctx)) return;
2620
2363
  return prepareTargetCodexAppServer(ctx);
2621
2364
  },
2622
2365
  async apply(ctx, plan) {
@@ -2678,7 +2421,7 @@ const CodexThreadsParamsSchema = Type.Union([
2678
2421
  ArchiveParamsSchema,
2679
2422
  UnarchiveParamsSchema
2680
2423
  ]);
2681
- function asRecord$1(value) {
2424
+ function asRecord(value) {
2682
2425
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2683
2426
  }
2684
2427
  function readBoolean(value, fallback = false) {
@@ -2697,9 +2440,13 @@ function resolveToolSession(context, runtime) {
2697
2440
  });
2698
2441
  const sessionId = context.sessionId?.trim() || entry?.sessionId?.trim();
2699
2442
  if (!sessionId) return;
2443
+ const storePath = runtime.agent.session.resolveStorePath(void 0, { agentId: context.agentId });
2700
2444
  return {
2701
2445
  sessionId,
2702
- modelSelectionLocked: isModelSelectionLocked(entry)
2446
+ sessionFile: runtime.agent.session.resolveSessionFilePath(sessionId, entry, {
2447
+ agentId: context.agentId,
2448
+ sessionsDir: path.dirname(storePath)
2449
+ })
2703
2450
  };
2704
2451
  }
2705
2452
  function readThreadId(params) {
@@ -2712,106 +2459,42 @@ function readThreadStatusType(value) {
2712
2459
  if (!isJsonObject(value) || !isJsonObject(value.thread) || !isJsonObject(value.thread.status)) return;
2713
2460
  return typeof value.thread.status.type === "string" ? value.thread.status.type : void 0;
2714
2461
  }
2715
- function assertThreadMayBeArchived(value, expectedThreadId) {
2716
- if (!isJsonObject(value) || !isJsonObject(value.thread)) throw new Error("Codex app-server returned an invalid thread/read response");
2717
- if (value.thread.id !== expectedThreadId) throw new Error("Codex app-server returned a different thread than requested");
2718
- const status = readThreadStatusType(value);
2719
- if (status === "active") throw new Error("cannot archive an active Codex thread; wait for its turn to finish");
2720
- if (status !== "idle" && status !== "notLoaded") throw new Error("cannot verify that the Codex thread is idle; refusing to archive");
2721
- }
2722
- function assertThreadMayBeForked(value, expectedThreadId) {
2723
- if (!isJsonObject(value) || !isJsonObject(value.thread)) throw new Error("Codex app-server returned an invalid thread/read response");
2724
- if (value.thread.id !== expectedThreadId) throw new Error("Codex app-server returned a different thread than requested");
2725
- const status = readThreadStatusType(value);
2726
- if (status !== "idle" && status !== "notLoaded") throw new Error("cannot fork a Codex thread unless it is idle or not loaded");
2727
- }
2728
- function redactNativeThreadTranscriptFields(value) {
2729
- if (!isJsonObject(value)) return value;
2730
- const redacted = { ...value };
2731
- delete redacted.preview;
2732
- delete redacted.turns;
2733
- return redacted;
2734
- }
2735
- function redactNativeThreadResponse(value) {
2736
- if (!isJsonObject(value)) return value;
2737
- const redacted = { ...value };
2738
- if (Array.isArray(redacted.data)) redacted.data = redacted.data.map(redactNativeThreadTranscriptFields);
2739
- if (isJsonObject(redacted.thread)) redacted.thread = redactNativeThreadTranscriptFields(redacted.thread);
2740
- return redacted;
2741
- }
2742
- /** 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. */
2743
2463
  function createCodexThreadsTool(options) {
2744
2464
  if (options.context.senderIsOwner !== true) return null;
2745
- const configured = readCodexPluginConfig(options.getPluginConfig());
2746
- if (configured.appServer?.homeScope !== "user" && configured.supervision?.enabled !== true) return null;
2465
+ if (readCodexPluginConfig(options.getPluginConfig()).appServer?.homeScope !== "user") return null;
2747
2466
  const request = options.request ?? codexControlRequest;
2748
- const runtimeConfig = () => options.context.getRuntimeConfig?.() ?? options.context.runtimeConfig ?? options.context.config;
2749
- const baseRequestOptions = () => ({
2467
+ const requestOptions = () => ({
2750
2468
  agentDir: options.context.agentDir,
2751
- config: runtimeConfig(),
2469
+ config: options.context.getRuntimeConfig?.() ?? options.context.runtimeConfig ?? options.context.config,
2752
2470
  sessionId: options.context.sessionId,
2753
2471
  sessionKey: options.context.sessionKey
2754
2472
  });
2755
2473
  const currentSession = () => resolveToolSession(options.context, options.runtime);
2756
- const currentIdentity = (sessionId) => sessionBindingIdentity({
2757
- sessionId,
2758
- sessionKey: options.context.sessionKey,
2759
- agentId: options.context.agentId,
2760
- config: runtimeConfig()
2761
- });
2762
- const currentBinding = async (session) => session ? await options.bindingStore.read(currentIdentity(session.sessionId)) : void 0;
2763
- const requestOptions = async (pluginConfig) => {
2764
- const plugin = readCodexPluginConfig(pluginConfig);
2765
- const session = currentSession();
2766
- const binding = await currentBinding(session);
2767
- if (binding?.connectionScope === "supervision") {
2768
- const connection = resolveCodexBindingAppServerConnection({
2769
- binding,
2770
- pluginConfig
2771
- });
2772
- return {
2773
- ...baseRequestOptions(),
2774
- startOptions: connection.appServer.start,
2775
- authProfileId: connection.clientAuthProfileId
2776
- };
2777
- }
2778
- if (plugin.appServer?.homeScope === "user") {
2779
- const connection = resolveCodexBindingAppServerConnection({
2780
- binding,
2781
- pluginConfig
2782
- });
2783
- return {
2784
- ...baseRequestOptions(),
2785
- startOptions: connection.appServer.start,
2786
- authProfileId: null
2787
- };
2788
- }
2789
- if (plugin.supervision?.enabled !== true) throw new Error("Codex native thread access is disabled for this run.");
2790
- return {
2791
- ...baseRequestOptions(),
2792
- startOptions: resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig }).start,
2793
- authProfileId: null
2794
- };
2474
+ const currentIdentity = (sessionId) => {
2475
+ return sessionBindingIdentity({
2476
+ sessionId,
2477
+ sessionKey: options.context.sessionKey,
2478
+ agentId: options.context.agentId,
2479
+ config: requestOptions().config
2480
+ });
2795
2481
  };
2482
+ const currentBinding = async (session) => session ? await options.bindingStore.read(currentIdentity(session.sessionId)) : void 0;
2796
2483
  return {
2797
2484
  name: "codex_threads",
2798
2485
  label: "Codex Threads",
2799
- 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.",
2800
2487
  parameters: CodexThreadsParamsSchema,
2801
2488
  async execute(_toolCallId, rawParams) {
2802
- const params = asRecord$1(rawParams);
2489
+ const params = asRecord(rawParams);
2803
2490
  const action = readStringParam(params, "action", {
2804
2491
  required: true,
2805
2492
  label: "action"
2806
2493
  });
2807
2494
  const pluginConfig = options.getPluginConfig();
2808
- const plugin = readCodexPluginConfig(pluginConfig);
2809
- const supervision = plugin.supervision;
2810
- const mayReadRawTranscripts = supervision?.enabled !== true || supervision.allowRawTranscripts === true;
2811
2495
  if (action === "list") {
2812
2496
  const cursor = readStringParam(params, "cursor");
2813
2497
  const searchTerm = readStringParam(params, "search");
2814
- if (searchTerm && !mayReadRawTranscripts) throw new Error("Codex native thread search is disabled while raw transcript access is disabled.");
2815
2498
  const response = await request(pluginConfig, CODEX_CONTROL_METHODS.listThreads, {
2816
2499
  archived: readBoolean(params.archived),
2817
2500
  limit: readLimit(params.limit) ?? 20,
@@ -2821,20 +2504,17 @@ function createCodexThreadsTool(options) {
2821
2504
  sourceKinds: [...CODEX_INTERACTIVE_THREAD_SOURCE_KINDS],
2822
2505
  ...cursor ? { cursor } : {},
2823
2506
  ...searchTerm ? { searchTerm } : {}
2824
- }, await requestOptions(pluginConfig));
2825
- return jsonResult(mayReadRawTranscripts ? response : redactNativeThreadResponse(response));
2507
+ }, requestOptions());
2508
+ return jsonResult(response);
2826
2509
  }
2827
2510
  const threadId = readThreadId(params);
2828
2511
  if (action === "read") {
2829
- const includeTurns = readBoolean(params.include_turns);
2830
- if (includeTurns && !mayReadRawTranscripts) throw new Error("Codex raw transcript reads are disabled for this codex plugin supervision config.");
2831
2512
  const response = await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
2832
2513
  threadId,
2833
- includeTurns
2834
- }, await requestOptions(pluginConfig));
2835
- return jsonResult(mayReadRawTranscripts ? response : redactNativeThreadResponse(response));
2514
+ includeTurns: readBoolean(params.include_turns)
2515
+ }, requestOptions());
2516
+ return jsonResult(response);
2836
2517
  }
2837
- 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.");
2838
2518
  if (action === "rename") {
2839
2519
  const name = readStringParam(params, "name", {
2840
2520
  required: true,
@@ -2843,7 +2523,7 @@ function createCodexThreadsTool(options) {
2843
2523
  await request(pluginConfig, CODEX_CONTROL_METHODS.renameThread, {
2844
2524
  threadId,
2845
2525
  name
2846
- }, await requestOptions(pluginConfig));
2526
+ }, requestOptions());
2847
2527
  return jsonResult({
2848
2528
  action,
2849
2529
  threadId,
@@ -2851,42 +2531,23 @@ function createCodexThreadsTool(options) {
2851
2531
  });
2852
2532
  }
2853
2533
  if (action === "unarchive") {
2854
- const response = await request(pluginConfig, CODEX_CONTROL_METHODS.unarchiveThread, { threadId }, await requestOptions(pluginConfig));
2855
- return jsonResult(mayReadRawTranscripts ? response : redactNativeThreadResponse(response));
2534
+ const response = await request(pluginConfig, CODEX_CONTROL_METHODS.unarchiveThread, { threadId }, requestOptions());
2535
+ return jsonResult(response);
2856
2536
  }
2857
2537
  const session = currentSession();
2858
2538
  const binding = await currentBinding(session);
2859
2539
  if (action === "archive") {
2860
2540
  if (params.confirm !== true) throw new Error("confirm=true is required to archive a native Codex thread");
2861
- if (!session) throw new Error("cannot safely archive a native Codex thread without a session identity");
2862
- const identity = currentIdentity(session.sessionId);
2863
- await options.bindingStore.withThreadArchiveFence(async () => {
2864
- const archivedBinding = await currentBinding(session);
2865
- if (archivedBinding?.threadId === threadId) {
2866
- if (session.modelSelectionLocked) throw new ModelSelectionLockedError();
2867
- assertCodexBindingMayBeReplaced(archivedBinding, "archiving its bound native thread");
2868
- }
2869
- assertThreadMayBeArchived(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
2541
+ if (binding?.threadId === threadId) {
2542
+ if (readThreadStatusType(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
2870
2543
  threadId,
2871
2544
  includeTurns: false
2872
- }, await requestOptions(pluginConfig)), threadId);
2873
- if (await options.bindingStore.hasOtherThreadOwner(threadId, identity)) throw new Error("cannot archive a native Codex thread owned by another OpenClaw session");
2874
- await assertCodexArchiveDescendantsUnowned({
2875
- bindingStore: options.bindingStore,
2876
- threadId,
2877
- listPage: async (listParams) => await request(pluginConfig, CODEX_CONTROL_METHODS.listThreads, listParams, await requestOptions(pluginConfig)),
2878
- assertDescendantIdle: async (descendantThreadId) => {
2879
- assertThreadMayBeArchived(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
2880
- threadId: descendantThreadId,
2881
- includeTurns: false
2882
- }, await requestOptions(pluginConfig)), descendantThreadId);
2883
- }
2884
- });
2885
- await request(pluginConfig, CODEX_CONTROL_METHODS.archiveThread, { threadId }, await requestOptions(pluginConfig));
2886
- if (archivedBinding?.threadId === threadId) await options.bindingStore.mutate(identity, {
2887
- kind: "clear",
2888
- threadId
2889
- });
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
2890
2551
  });
2891
2552
  return jsonResult({
2892
2553
  action,
@@ -2896,21 +2557,16 @@ function createCodexThreadsTool(options) {
2896
2557
  if (action !== "fork") throw new Error(`unsupported codex_threads action: ${action}`);
2897
2558
  const attach = readBoolean(params.attach, true);
2898
2559
  if (attach && !session) throw new Error("cannot attach a Codex fork without an active OpenClaw session");
2899
- if (attach && session?.modelSelectionLocked) throw new ModelSelectionLockedError();
2900
- const usesSupervisionConnection = binding?.connectionScope === "supervision" || plugin.appServer?.homeScope !== "user" && supervision?.enabled === true;
2901
- if (attach && usesSupervisionConnection) throw new Error("Supervised Codex forks must stay detached; set attach=false.");
2902
- if (attach) {
2903
- assertCodexBindingMayBeReplaced(binding, "attaching a different native fork");
2904
- assertThreadMayBeForked(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
2560
+ if (attach && binding?.threadId === threadId) {
2561
+ if (readThreadStatusType(await request(pluginConfig, CODEX_CONTROL_METHODS.readThread, {
2905
2562
  threadId,
2906
2563
  includeTurns: false
2907
- }, await requestOptions(pluginConfig)), threadId);
2564
+ }, requestOptions())) === "active") throw new Error("cannot replace the Codex thread active in this OpenClaw turn");
2908
2565
  }
2909
2566
  const response = await request(pluginConfig, CODEX_CONTROL_METHODS.forkThread, {
2910
2567
  threadId,
2911
- threadSource: "user",
2912
- excludeTurns: true
2913
- }, await requestOptions(pluginConfig));
2568
+ threadSource: "user"
2569
+ }, requestOptions());
2914
2570
  if (!isJsonObject(response) || !isJsonObject(response.thread)) throw new Error("Codex app-server returned an invalid thread/fork response");
2915
2571
  const forkThreadId = typeof response.thread.id === "string" && response.thread.id.trim() ? response.thread.id : void 0;
2916
2572
  if (!forkThreadId) throw new Error("Codex app-server thread/fork response did not include a thread id");
@@ -2926,802 +2582,18 @@ function createCodexThreadsTool(options) {
2926
2582
  }
2927
2583
  })) throw new Error("Codex session binding changed before the fork could be attached");
2928
2584
  }
2929
- const result = {
2585
+ return jsonResult({
2930
2586
  action,
2931
2587
  sourceThreadId: threadId,
2932
2588
  thread: response.thread,
2933
2589
  attached: attach
2934
- };
2935
- return jsonResult(mayReadRawTranscripts ? result : redactNativeThreadResponse(result));
2936
- }
2937
- };
2938
- }
2939
- //#endregion
2940
- //#region extensions/codex/src/supervision-tools.ts
2941
- /**
2942
- * Compatibility tools for the retired Codex Supervisor plugin.
2943
- *
2944
- * Read operations and active-turn controls use the Codex plugin's canonical
2945
- * shared app-server client. Idle threads are never resumed or started here:
2946
- * continuation belongs to the Codex harness, which installs approval and tool
2947
- * handlers before it starts or resumes the harness-owned Codex thread.
2948
- */
2949
- /** Legacy endpoint env retained for the shipped Supervisor tool contract. */
2950
- const LEGACY_CODEX_SUPERVISOR_ENDPOINTS_ENV = "OPENCLAW_CODEX_SUPERVISOR_ENDPOINTS";
2951
- /** Legacy standalone-MCP transcript gate. Agent tools use canonical config. */
2952
- const LEGACY_CODEX_SUPERVISOR_RAW_TRANSCRIPTS_ENV = "OPENCLAW_CODEX_SUPERVISOR_ALLOW_RAW_TRANSCRIPTS";
2953
- /** Legacy standalone-MCP write gate. Agent tools use canonical config. */
2954
- const LEGACY_CODEX_SUPERVISOR_WRITE_CONTROLS_ENV = "OPENCLAW_CODEX_SUPERVISOR_ALLOW_WRITE_CONTROLS";
2955
- const CODEX_SUPERVISION_COMPAT_TOOL_NAMES = [
2956
- "codex_endpoint_probe",
2957
- "codex_sessions_list",
2958
- "codex_session_read",
2959
- "codex_session_send",
2960
- "codex_session_interrupt"
2961
- ];
2962
- const EmptyParamsSchema = Type.Object({}, { additionalProperties: false });
2963
- const SessionsListParamsSchema = Type.Object({
2964
- include_stored: Type.Optional(Type.Boolean()),
2965
- max_stored_sessions: Type.Optional(Type.Integer({
2966
- minimum: 1,
2967
- maximum: 1e3
2968
- }))
2969
- }, { additionalProperties: false });
2970
- const SessionReadParamsSchema = Type.Object({
2971
- endpoint_id: Type.Optional(Type.String()),
2972
- thread_id: Type.String(),
2973
- include_turns: Type.Optional(Type.Boolean())
2974
- }, { additionalProperties: false });
2975
- const SessionSendParamsSchema = Type.Object({
2976
- endpoint_id: Type.Optional(Type.String()),
2977
- thread_id: Type.String(),
2978
- text: Type.String(),
2979
- mode: Type.Optional(Type.Union([
2980
- Type.Literal("auto"),
2981
- Type.Literal("start"),
2982
- Type.Literal("steer")
2983
- ]))
2984
- }, { additionalProperties: false });
2985
- const SessionInterruptParamsSchema = Type.Object({
2986
- endpoint_id: Type.Optional(Type.String()),
2987
- thread_id: Type.String(),
2988
- turn_id: Type.Optional(Type.String())
2989
- }, { additionalProperties: false });
2990
- const ALL_CODEX_THREAD_SOURCE_KINDS = [
2991
- "cli",
2992
- "vscode",
2993
- "exec",
2994
- "appServer",
2995
- "subAgent",
2996
- "subAgentReview",
2997
- "subAgentCompact",
2998
- "subAgentThreadSpawn",
2999
- "subAgentOther",
3000
- "unknown"
3001
- ];
3002
- const DEFAULT_MAX_STORED_SESSIONS = 200;
3003
- const PAGE_LIMIT = 100;
3004
- const MAX_COMPAT_PAGINATION_PAGES = 100;
3005
- const MAX_COMPAT_CURSOR_LENGTH = 4096;
3006
- const MAX_COMPAT_THREAD_ID_LENGTH = 4096;
3007
- var CodexSupervisionPolicyError = class extends Error {};
3008
- function asRecord(value) {
3009
- return isRecord(value) ? value : {};
3010
- }
3011
- function asRecordArray(value) {
3012
- return Array.isArray(value) ? value.filter(isRecord) : [];
3013
- }
3014
- function readCompatNextCursor(value, method) {
3015
- if (value === null || value === void 0) return;
3016
- if (typeof value !== "string" || value.trim().length === 0 || value.length > MAX_COMPAT_CURSOR_LENGTH) throw new Error(`Codex ${method} returned an invalid nextCursor`);
3017
- return value;
3018
- }
3019
- function readCompatThreadId(value, method, index) {
3020
- 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}]`);
3021
- return value;
3022
- }
3023
- function readLoadedThreadIds(data) {
3024
- if (data.length > PAGE_LIMIT) throw new Error(`Codex thread/loaded/list returned more than ${PAGE_LIMIT} entries`);
3025
- return data.map((entry, index) => readCompatThreadId(entry, "thread/loaded/list", index));
3026
- }
3027
- function readStoredThreads(data, maxEntries) {
3028
- if (data.length > maxEntries) throw new Error(`Codex thread/list returned more than ${maxEntries} entries`);
3029
- return data.map((entry, index) => {
3030
- if (!isRecord(entry)) throw new Error(`Codex thread/list returned an invalid entry at data[${index}]`);
3031
- readCompatThreadId(entry.id, "thread/list", index);
3032
- return entry;
3033
- });
3034
- }
3035
- function readBooleanParam(params, key) {
3036
- return params[key] === true;
3037
- }
3038
- function readIntegerParam(params, key) {
3039
- const value = params[key];
3040
- if (value === void 0) return;
3041
- if (typeof value !== "number" || !Number.isInteger(value)) throw new Error(`${key} must be an integer`);
3042
- if (value < 1 || value > 1e3) throw new Error(`${key} must be between 1 and 1000`);
3043
- return value;
3044
- }
3045
- function readModeParam(params) {
3046
- const mode = readStringParam(params, "mode");
3047
- if (!mode) return;
3048
- if (mode === "auto" || mode === "start" || mode === "steer") return mode;
3049
- throw new Error("mode must be auto, start, or steer");
3050
- }
3051
- function normalizeEndpointId(value, index) {
3052
- const trimmed = value.trim();
3053
- return trimmed ? trimmed.replace(/[^a-zA-Z0-9_.:-]/g, "-") : `endpoint-${index + 1}`;
3054
- }
3055
- function normalizeConfiguredEndpoint(endpoint, index) {
3056
- return {
3057
- id: normalizeEndpointId(endpoint.id ?? endpoint.label ?? "", index),
3058
- ...endpoint.label?.trim() ? { label: endpoint.label.trim() } : {},
3059
- configured: endpoint
3060
- };
3061
- }
3062
- function parseEndpointRecord(value) {
3063
- if (!isRecord(value)) return;
3064
- const transport = typeof value.transport === "string" ? value.transport : void 0;
3065
- const common = {
3066
- ...typeof value.id === "string" ? { id: value.id } : {},
3067
- ...typeof value.label === "string" ? { label: value.label } : {}
3068
- };
3069
- if (transport === "websocket" && typeof value.url === "string") return {
3070
- ...common,
3071
- transport,
3072
- url: value.url,
3073
- ...typeof value.authTokenEnv === "string" ? { authTokenEnv: value.authTokenEnv } : {}
3074
- };
3075
- if (transport === "stdio-proxy" || transport === void 0) {
3076
- const args = Array.isArray(value.args) ? value.args.filter((entry) => typeof entry === "string") : void 0;
3077
- return {
3078
- ...common,
3079
- transport: "stdio-proxy",
3080
- ...typeof value.command === "string" ? { command: value.command } : {},
3081
- ...args && args.length > 0 ? { args } : {},
3082
- ...typeof value.cwd === "string" ? { cwd: value.cwd } : {}
3083
- };
3084
- }
3085
- }
3086
- function endpointFromToken(token, index) {
3087
- const trimmed = token.trim();
3088
- if (!trimmed) return;
3089
- if (trimmed.startsWith("ws://") || trimmed.startsWith("wss://") || trimmed.startsWith("unix://")) return {
3090
- id: normalizeEndpointId("", index),
3091
- transport: "websocket",
3092
- url: trimmed
3093
- };
3094
- if (trimmed === "local" || trimmed === "proxy" || trimmed === "stdio") return {
3095
- id: "local",
3096
- label: "local Codex app-server",
3097
- transport: "stdio-proxy"
3098
- };
3099
- const separatorIndex = trimmed.indexOf("=");
3100
- const id = separatorIndex >= 0 ? trimmed.slice(0, separatorIndex) : trimmed;
3101
- const url = separatorIndex >= 0 ? trimmed.slice(separatorIndex + 1) : void 0;
3102
- if (url?.startsWith("ws://") || url?.startsWith("wss://") || url?.startsWith("unix://")) return {
3103
- id: normalizeEndpointId(id, index),
3104
- transport: "websocket",
3105
- url
3106
- };
3107
- }
3108
- function requireUniqueEndpointIds(endpoints) {
3109
- const seen = /* @__PURE__ */ new Set();
3110
- for (const endpoint of endpoints) {
3111
- if (seen.has(endpoint.id)) throw new Error(`duplicate Codex supervisor endpoint id: ${endpoint.id}`);
3112
- seen.add(endpoint.id);
3113
- }
3114
- return endpoints;
3115
- }
3116
- function readLegacyEnvEndpoints(env) {
3117
- const raw = env[LEGACY_CODEX_SUPERVISOR_ENDPOINTS_ENV]?.trim();
3118
- if (!raw) return;
3119
- if (raw.startsWith("[")) {
3120
- const parsed = JSON.parse(raw);
3121
- if (!Array.isArray(parsed)) throw new Error(`${LEGACY_CODEX_SUPERVISOR_ENDPOINTS_ENV} must be a JSON array`);
3122
- return parsed.map((entry) => parseEndpointRecord(entry)).filter((entry) => Boolean(entry));
3123
- }
3124
- return raw.split(",").map(endpointFromToken).filter((entry) => Boolean(entry));
3125
- }
3126
- function resolveEndpoints(pluginConfig, env, runtimeConfig) {
3127
- const configured = readCodexPluginConfig(pluginConfig).supervision?.endpoints;
3128
- const endpoints = configured?.length ? configured : readLegacyEnvEndpoints(env);
3129
- return (endpoints ? requireUniqueEndpointIds(endpoints.map(normalizeConfiguredEndpoint)) : [{
3130
- id: "local",
3131
- label: "local Codex app-server"
3132
- }]).map((endpoint) => {
3133
- const resolved = {
3134
- id: endpoint.id,
3135
- connectionKey: supervisionEndpointConnectionKey({
3136
- endpoint,
3137
- pluginConfig,
3138
- env,
3139
- runtimeConfig
3140
- })
3141
- };
3142
- if (endpoint.label !== void 0) resolved.label = endpoint.label;
3143
- if (endpoint.configured !== void 0) resolved.configured = endpoint.configured;
3144
- return resolved;
3145
- });
3146
- }
3147
- function resolveEndpointStartOptions(params) {
3148
- const base = resolveCodexSupervisionAppServerRuntimeOptions({
3149
- pluginConfig: params.pluginConfig,
3150
- env: params.env
3151
- }).start;
3152
- const configured = params.endpoint.configured;
3153
- if (!configured) return base;
3154
- if (!("url" in configured)) return {
3155
- transport: "stdio",
3156
- homeScope: "user",
3157
- command: configured.command?.trim() || "codex",
3158
- commandSource: "config",
3159
- args: configured.args?.length ? [...configured.args] : [
3160
- "app-server",
3161
- "--listen",
3162
- "stdio://"
3163
- ],
3164
- ...configured.cwd !== void 0 ? { cwd: configured.cwd } : {},
3165
- headers: {}
3166
- };
3167
- const tokenEnv = configured.authTokenEnv?.trim();
3168
- const authToken = tokenEnv ? params.env[tokenEnv]?.trim() : void 0;
3169
- const startOptions = {
3170
- transport: configured.url.startsWith("unix://") ? "unix" : "websocket",
3171
- ...configured.url.startsWith("unix://") ? { homeScope: "user" } : {},
3172
- command: base.command,
3173
- ...base.commandSource ? { commandSource: base.commandSource } : {},
3174
- ...base.managedFallbackCommandPaths ? { managedFallbackCommandPaths: [...base.managedFallbackCommandPaths] } : {},
3175
- args: [...base.args],
3176
- url: configured.url,
3177
- ...authToken ? { authToken } : {},
3178
- headers: {}
3179
- };
3180
- if (params.validateSecurity !== false) assertCodexAppServerConnectionSecurity(startOptions);
3181
- return startOptions;
3182
- }
3183
- function supervisionEndpointConnectionKey(params) {
3184
- const startOptions = resolveEndpointStartOptions({
3185
- ...params,
3186
- validateSecurity: false
3187
- });
3188
- const usesNativeAuth = params.endpoint.configured !== void 0 || startOptions.homeScope === "user";
3189
- const agentDir = usesNativeAuth ? void 0 : resolveDefaultAgentDir(params.runtimeConfig ?? {});
3190
- const authProfileId = usesNativeAuth ? void 0 : resolveCodexAppServerAuthProfileIdForAgent({
3191
- agentDir,
3192
- config: params.runtimeConfig
3193
- });
3194
- const fallbackApiKeyCacheKey = authProfileId ? void 0 : resolveCodexAppServerFallbackApiKeyCacheKey({ startOptions });
3195
- return JSON.stringify({
3196
- homeScope: startOptions.homeScope ?? null,
3197
- startOptions: codexAppServerStartOptionsKey(startOptions, {
3198
- authProfileId,
3199
- agentDir,
3200
- fallbackApiKeyCacheKey
3201
- })
3202
- });
3203
- }
3204
- function createCanonicalEndpointRequest(options) {
3205
- return async (endpoint, method, requestParams) => {
3206
- const pluginConfig = options.getPluginConfig();
3207
- const env = options.env ?? process.env;
3208
- const runtime = resolveCodexSupervisionAppServerRuntimeOptions({
3209
- pluginConfig,
3210
- env
3211
- });
3212
- const config = options.getRuntimeConfig?.();
3213
- const startOptions = resolveEndpointStartOptions({
3214
- endpoint,
3215
- pluginConfig,
3216
- env
3217
- });
3218
- return await requestCodexAppServerJson({
3219
- method,
3220
- requestParams,
3221
- timeoutMs: runtime.requestTimeoutMs,
3222
- startOptions,
3223
- ...endpoint.configured || startOptions.homeScope === "user" ? { authProfileId: null } : {},
3224
- ...config ? { config } : {}
3225
- });
3226
- };
3227
- }
3228
- function statusType(thread) {
3229
- const status = isRecord(thread.status) ? thread.status.type : thread.status;
3230
- return typeof status === "string" ? status : "unknown";
3231
- }
3232
- function sourceLabel(value) {
3233
- if (typeof value === "string") return value;
3234
- if (!isRecord(value)) return;
3235
- if (typeof value.custom === "string") return `custom:${value.custom}`;
3236
- return Object.keys(value).toSorted()[0];
3237
- }
3238
- function toSession(endpointId, thread, humanAttached) {
3239
- if (typeof thread.id !== "string") return;
3240
- const source = sourceLabel(thread.source);
3241
- return {
3242
- endpointId,
3243
- threadId: thread.id,
3244
- status: statusType(thread),
3245
- ...typeof thread.sessionId === "string" ? { sessionId: thread.sessionId } : {},
3246
- ...typeof thread.cwd === "string" ? { cwd: thread.cwd } : {},
3247
- ...typeof thread.preview === "string" ? { preview: thread.preview } : {},
3248
- ...typeof thread.name === "string" || thread.name === null ? { name: thread.name } : {},
3249
- ...source ? { source } : {},
3250
- ...typeof thread.updatedAt === "number" ? { updatedAt: thread.updatedAt } : {},
3251
- ...humanAttached !== void 0 ? { humanAttached } : {}
3252
- };
3253
- }
3254
- function threadFromRead(value) {
3255
- return isRecord(value) && isRecord(value.thread) ? value.thread : void 0;
3256
- }
3257
- function isLoadedThreadReadMiss(error) {
3258
- const message = error instanceof Error ? error.message : String(error);
3259
- return message.includes("thread not found") || message.includes("thread not loaded");
3260
- }
3261
- async function readThread(params) {
3262
- try {
3263
- const thread = threadFromRead(await params.request(params.endpoint, "thread/read", {
3264
- threadId: params.threadId,
3265
- includeTurns: params.includeTurns
3266
- }));
3267
- if (!thread) throw new Error("Codex thread/read returned an invalid response");
3268
- return thread;
3269
- } catch (error) {
3270
- if (!params.includeTurns || !String(error).includes("not materialized yet")) throw error;
3271
- const thread = threadFromRead(await params.request(params.endpoint, "thread/read", {
3272
- threadId: params.threadId,
3273
- includeTurns: false
3274
- }));
3275
- if (!thread) throw new Error("Codex thread/read returned an invalid response", { cause: error });
3276
- return thread;
3277
- }
3278
- }
3279
- async function listLoadedSessions(request, endpoint) {
3280
- const sessions = [];
3281
- const seenCursors = /* @__PURE__ */ new Set();
3282
- let cursor;
3283
- for (let pageIndex = 0; pageIndex < MAX_COMPAT_PAGINATION_PAGES; pageIndex += 1) {
3284
- const listed = await request(endpoint, "thread/loaded/list", {
3285
- limit: PAGE_LIMIT,
3286
- ...cursor ? { cursor } : {}
3287
- });
3288
- if (!isRecord(listed) || !Array.isArray(listed.data)) throw new Error("Codex thread/loaded/list returned an invalid response");
3289
- const threadIds = readLoadedThreadIds(listed.data);
3290
- for (const threadId of threadIds) {
3291
- if (sessions.some((entry) => entry.threadId === threadId)) continue;
3292
- try {
3293
- const thread = await readThread({
3294
- request,
3295
- endpoint,
3296
- threadId,
3297
- includeTurns: false
3298
- });
3299
- const session = toSession(endpoint.id, thread, true);
3300
- if (session) sessions.push(session);
3301
- } catch (error) {
3302
- if (!isLoadedThreadReadMiss(error)) throw error;
3303
- }
3304
- }
3305
- const nextCursor = readCompatNextCursor(listed.nextCursor, "thread/loaded/list");
3306
- if (nextCursor && seenCursors.has(nextCursor)) throw new Error(`Codex thread/loaded/list returned repeated cursor ${nextCursor}`);
3307
- if (nextCursor) seenCursors.add(nextCursor);
3308
- cursor = nextCursor;
3309
- if (!cursor) break;
3310
- }
3311
- if (cursor) throw new Error(`Codex thread/loaded/list exceeded ${MAX_COMPAT_PAGINATION_PAGES} pages with a continuation cursor`);
3312
- return sessions;
3313
- }
3314
- async function listStoredSessions(params) {
3315
- const sessions = [];
3316
- const seenCursors = /* @__PURE__ */ new Set();
3317
- let cursor;
3318
- for (let pageIndex = 0; pageIndex < MAX_COMPAT_PAGINATION_PAGES; pageIndex += 1) {
3319
- const remaining = params.limit - sessions.length;
3320
- if (remaining <= 0) break;
3321
- const pageLimit = Math.min(PAGE_LIMIT, remaining);
3322
- const listed = await params.request(params.endpoint, "thread/list", {
3323
- archived: false,
3324
- limit: pageLimit,
3325
- sourceKinds: [...ALL_CODEX_THREAD_SOURCE_KINDS],
3326
- modelProviders: [],
3327
- sortKey: "recency_at",
3328
- sortDirection: "desc",
3329
- useStateDbOnly: true,
3330
- ...cursor ? { cursor } : {}
3331
- });
3332
- if (!isRecord(listed) || !Array.isArray(listed.data)) throw new Error("Codex thread/list returned an invalid response");
3333
- for (const thread of readStoredThreads(listed.data, pageLimit)) {
3334
- if (sessions.length >= params.limit) break;
3335
- const session = toSession(params.endpoint.id, thread);
3336
- if (session && !sessions.some((entry) => entry.threadId === session.threadId)) sessions.push(session);
3337
- }
3338
- const nextCursor = readCompatNextCursor(listed.nextCursor, "thread/list");
3339
- if (nextCursor && sessions.length < params.limit && seenCursors.has(nextCursor)) throw new Error(`Codex thread/list returned repeated cursor ${nextCursor}`);
3340
- if (nextCursor) seenCursors.add(nextCursor);
3341
- cursor = nextCursor;
3342
- if (!cursor || sessions.length >= params.limit) break;
3343
- }
3344
- if (cursor && sessions.length < params.limit) throw new Error(`Codex thread/list exceeded ${MAX_COMPAT_PAGINATION_PAGES} pages with a continuation cursor`);
3345
- return sessions;
3346
- }
3347
- async function listSessionSnapshot(params) {
3348
- const sessions = [];
3349
- const errors = [];
3350
- for (const endpoint of params.endpoints) try {
3351
- const loaded = await listLoadedSessions(params.request, endpoint);
3352
- sessions.push(...loaded);
3353
- if (params.includeStored) {
3354
- const stored = await listStoredSessions({
3355
- request: params.request,
3356
- endpoint,
3357
- limit: params.maxStoredSessions ?? DEFAULT_MAX_STORED_SESSIONS
3358
2590
  });
3359
- for (const session of stored) if (!sessions.some((entry) => entry.endpointId === endpoint.id && entry.threadId === session.threadId)) sessions.push(session);
3360
2591
  }
3361
- } catch (error) {
3362
- if (error instanceof CodexSupervisionPolicyError) throw error;
3363
- errors.push({
3364
- endpointId: endpoint.id,
3365
- ok: false,
3366
- detail: error instanceof Error ? error.message : String(error)
3367
- });
3368
- }
3369
- return {
3370
- sessions,
3371
- errors
3372
- };
3373
- }
3374
- async function resolveEndpointForThread(params) {
3375
- if (params.endpointId) {
3376
- const endpoint = params.endpoints.find((entry) => entry.id === params.endpointId);
3377
- if (!endpoint) throw new Error(`Unknown Codex supervisor endpoint: ${params.endpointId}`);
3378
- return endpoint;
3379
- }
3380
- const matches = [];
3381
- for (const endpoint of params.endpoints) try {
3382
- if ((await readThread({
3383
- request: params.request,
3384
- endpoint,
3385
- threadId: params.threadId,
3386
- includeTurns: false
3387
- })).id === params.threadId) matches.push(endpoint);
3388
- } catch (error) {
3389
- if (error instanceof CodexSupervisionPolicyError) throw error;
3390
- if (!isLoadedThreadReadMiss(error)) continue;
3391
- }
3392
- if (matches.length === 1) return expectDefined(matches[0], "single matching Codex supervision endpoint");
3393
- if (matches.length > 1) throw new Error(`Codex thread id is ambiguous across endpoints: ${params.threadId}`);
3394
- throw new Error(`Codex thread not found: ${params.threadId}`);
3395
- }
3396
- function findInProgressTurnId(thread) {
3397
- const turns = asRecordArray(thread.turns);
3398
- for (const turn of turns.toReversed()) if (turn.status === "inProgress" && typeof turn.id === "string") return turn.id;
3399
- }
3400
- async function resolveInProgressTurnId(params) {
3401
- const inline = findInProgressTurnId(params.thread);
3402
- if (inline) return inline;
3403
- try {
3404
- const response = await params.request(params.endpoint, "thread/turns/list", {
3405
- threadId: params.threadId,
3406
- limit: 10,
3407
- sortDirection: "desc",
3408
- itemsView: "summary"
3409
- });
3410
- return isRecord(response) ? findInProgressTurnId({ turns: response.data }) : void 0;
3411
- } catch (error) {
3412
- if (error instanceof CodexSupervisionPolicyError) throw error;
3413
- return;
3414
- }
3415
- }
3416
- function redactString(value) {
3417
- 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]");
3418
- }
3419
- /** Redacts secret-bearing fields before legacy tool results leave the plugin. */
3420
- function redactCodexSupervisionValue(value, key = "") {
3421
- if (typeof value === "string") return /authorization|password|secret|token|api[-_]?key/i.test(key) ? "[redacted]" : redactString(value);
3422
- if (Array.isArray(value)) return value.map((entry) => redactCodexSupervisionValue(entry));
3423
- if (!isRecord(value)) return value;
3424
- return Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [entryKey, redactCodexSupervisionValue(entryValue, entryKey)]));
3425
- }
3426
- function redactEndpointUrl(value) {
3427
- if (value.startsWith("unix://")) return "unix://";
3428
- try {
3429
- const url = new URL(value);
3430
- url.username = "";
3431
- url.password = "";
3432
- if (url.search) url.search = "?[redacted]";
3433
- return url.toString();
3434
- } catch {
3435
- return "[redacted]";
3436
- }
3437
- }
3438
- function endpointResult(endpoint, pluginConfig, env) {
3439
- const configured = endpoint.configured;
3440
- if (configured && (configured.transport === "stdio-proxy" || configured.transport === void 0)) return {
3441
- id: endpoint.id,
3442
- transport: "stdio-proxy",
3443
- ...endpoint.label ? { label: endpoint.label } : {}
3444
- };
3445
- if (configured?.transport === "websocket") return {
3446
- id: endpoint.id,
3447
- transport: "websocket",
3448
- ...endpoint.label ? { label: endpoint.label } : {},
3449
- url: redactEndpointUrl(configured.url)
3450
- };
3451
- const start = resolveCodexSupervisionAppServerRuntimeOptions({
3452
- pluginConfig,
3453
- env
3454
- }).start;
3455
- return {
3456
- id: endpoint.id,
3457
- transport: start.transport === "stdio" ? "stdio-proxy" : "websocket",
3458
- ...endpoint.label ? { label: endpoint.label } : {},
3459
- ...start.transport === "stdio" ? {} : { url: redactEndpointUrl(start.transport === "unix" ? start.url ?? "unix://" : start.url ?? "") }
3460
- };
3461
- }
3462
- function sanitizeSessionListResult(result, includeTranscriptDerivedFields) {
3463
- return {
3464
- sessions: result.sessions.map((session) => {
3465
- const sanitized = redactCodexSupervisionValue(session);
3466
- if (!includeTranscriptDerivedFields) {
3467
- delete sanitized.preview;
3468
- delete sanitized.name;
3469
- }
3470
- return sanitized;
3471
- }),
3472
- errors: includeTranscriptDerivedFields ? redactCodexSupervisionValue(result.errors) : result.errors.map(({ endpointId, ok }) => ({
3473
- endpointId,
3474
- ok
3475
- }))
3476
- };
3477
- }
3478
- function requireSupervisionEnabled(pluginConfig) {
3479
- if (readCodexPluginConfig(pluginConfig).supervision?.enabled !== true) throw new CodexSupervisionPolicyError("Codex supervision is disabled in the codex plugin config.");
3480
- }
3481
- function requireOwnerAccess(options) {
3482
- if (!options.senderIsOwner) throw new CodexSupervisionPolicyError("Codex supervision compatibility tools require an owner-authorized sender.");
3483
- }
3484
- function resolveToolPolicy(options, pluginConfig) {
3485
- const config = readCodexPluginConfig(pluginConfig).supervision;
3486
- const env = options.env ?? process.env;
3487
- return {
3488
- allowRawTranscripts: config?.allowRawTranscripts === true || options.useLegacyMcpPolicyEnv === true && env[LEGACY_CODEX_SUPERVISOR_RAW_TRANSCRIPTS_ENV] === "1",
3489
- allowWriteControls: config?.allowWriteControls === true || options.useLegacyMcpPolicyEnv === true && env[LEGACY_CODEX_SUPERVISOR_WRITE_CONTROLS_ENV] === "1"
3490
- };
3491
- }
3492
- function requireRawTranscriptAccess(options, pluginConfig) {
3493
- if (!resolveToolPolicy(options, pluginConfig).allowRawTranscripts) throw new CodexSupervisionPolicyError("Codex session reads are disabled for this codex plugin supervision config.");
3494
- }
3495
- function requireWriteAccess(options, pluginConfig) {
3496
- if (!resolveToolPolicy(options, pluginConfig).allowWriteControls) throw new CodexSupervisionPolicyError("Codex write controls are disabled for this codex plugin supervision config.");
3497
- }
3498
- function requireLiveToolPolicy(options, policy) {
3499
- requireOwnerAccess(options);
3500
- const pluginConfig = options.getPluginConfig();
3501
- requireSupervisionEnabled(pluginConfig);
3502
- if (policy === "raw-transcripts") requireRawTranscriptAccess(options, pluginConfig);
3503
- else if (policy === "write-controls") requireWriteAccess(options, pluginConfig);
3504
- return {
3505
- pluginConfig,
3506
- endpoints: resolveEndpoints(pluginConfig, options.env ?? process.env, options.getRuntimeConfig?.())
3507
- };
3508
- }
3509
- function requireCurrentEndpoint(options, policy, endpoint) {
3510
- const { endpoints } = requireLiveToolPolicy(options, policy);
3511
- const currentEndpoint = endpoints.find((candidate) => candidate.id === endpoint.id);
3512
- if (!currentEndpoint || currentEndpoint.connectionKey !== endpoint.connectionKey) throw new CodexSupervisionPolicyError(`Codex supervision endpoint ${endpoint.id} was removed or changed during the request.`);
3513
- return currentEndpoint;
3514
- }
3515
- function requireCurrentEndpointSet(options, expected) {
3516
- const current = requireLiveToolPolicy(options, "enabled");
3517
- 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.");
3518
- return { pluginConfig: current.pluginConfig };
3519
- }
3520
- function createPolicyGuardedRequest(options, request, policy) {
3521
- return async (endpoint, method, params) => {
3522
- return await request(requireCurrentEndpoint(options, policy, endpoint), method, params);
3523
- };
3524
- }
3525
- function idleContinuationError(threadId) {
3526
- 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.`);
3527
- }
3528
- /** Builds the five shipped Codex Supervisor compatibility tools. */
3529
- function createCodexSupervisionTools(options) {
3530
- const baseRequest = options.request ?? createCanonicalEndpointRequest(options);
3531
- const request = createPolicyGuardedRequest(options, baseRequest, "enabled");
3532
- const rawTranscriptRequest = createPolicyGuardedRequest(options, baseRequest, "raw-transcripts");
3533
- const writeRequest = createPolicyGuardedRequest(options, baseRequest, "write-controls");
3534
- const current = () => {
3535
- return requireLiveToolPolicy(options, "enabled");
3536
2592
  };
3537
- return [
3538
- {
3539
- name: "codex_endpoint_probe",
3540
- label: "Codex Endpoint Probe",
3541
- description: "Check configured Codex app-server endpoints.",
3542
- parameters: EmptyParamsSchema,
3543
- execute: async () => {
3544
- const { pluginConfig, endpoints } = current();
3545
- const health = [];
3546
- for (const endpoint of endpoints) try {
3547
- await request(endpoint, "thread/loaded/list", { limit: 1 });
3548
- health.push({
3549
- endpointId: endpoint.id,
3550
- ok: true
3551
- });
3552
- } catch (error) {
3553
- if (error instanceof CodexSupervisionPolicyError) throw error;
3554
- health.push({
3555
- endpointId: endpoint.id,
3556
- ok: false
3557
- });
3558
- }
3559
- requireCurrentEndpointSet(options, endpoints);
3560
- return jsonResult({
3561
- summary: `codex endpoints: ${health.filter((entry) => entry.ok).length}/${health.length} ok`,
3562
- endpoints: endpoints.map((endpoint) => endpointResult(endpoint, pluginConfig, options.env ?? process.env)),
3563
- health
3564
- });
3565
- }
3566
- },
3567
- {
3568
- name: "codex_sessions_list",
3569
- label: "Codex Sessions List",
3570
- description: "List Codex sessions visible to the OpenClaw supervisor.",
3571
- parameters: SessionsListParamsSchema,
3572
- execute: async (_toolCallId, rawParams) => {
3573
- const params = asRecord(rawParams);
3574
- const { endpoints } = current();
3575
- const result = await listSessionSnapshot({
3576
- endpoints,
3577
- request,
3578
- includeStored: readBooleanParam(params, "include_stored"),
3579
- maxStoredSessions: readIntegerParam(params, "max_stored_sessions")
3580
- });
3581
- const { pluginConfig } = requireCurrentEndpointSet(options, endpoints);
3582
- return jsonResult({
3583
- summary: `codex sessions: ${result.sessions.length}`,
3584
- ...sanitizeSessionListResult(result, resolveToolPolicy(options, pluginConfig).allowRawTranscripts)
3585
- });
3586
- }
3587
- },
3588
- {
3589
- name: "codex_session_read",
3590
- label: "Codex Session Read",
3591
- description: "Read one Codex session transcript from app-server.",
3592
- parameters: SessionReadParamsSchema,
3593
- execute: async (_toolCallId, rawParams) => {
3594
- const { endpoints, pluginConfig } = current();
3595
- requireRawTranscriptAccess(options, pluginConfig);
3596
- const params = asRecord(rawParams);
3597
- const threadId = readStringParam(params, "thread_id", { required: true });
3598
- const endpoint = await resolveEndpointForThread({
3599
- endpoints,
3600
- request: rawTranscriptRequest,
3601
- endpointId: readStringParam(params, "endpoint_id"),
3602
- threadId
3603
- });
3604
- const thread = await readThread({
3605
- request: rawTranscriptRequest,
3606
- endpoint,
3607
- threadId,
3608
- includeTurns: readBooleanParam(params, "include_turns")
3609
- });
3610
- requireCurrentEndpoint(options, "raw-transcripts", endpoint);
3611
- return jsonResult({
3612
- summary: `codex session: ${threadId}`,
3613
- response: redactCodexSupervisionValue({ thread })
3614
- });
3615
- }
3616
- },
3617
- {
3618
- name: "codex_session_send",
3619
- label: "Codex Session Send",
3620
- description: "Steer an active Codex turn. Idle sessions must be continued through Codex Sessions.",
3621
- parameters: SessionSendParamsSchema,
3622
- execute: async (_toolCallId, rawParams) => {
3623
- const { endpoints, pluginConfig } = current();
3624
- requireWriteAccess(options, pluginConfig);
3625
- const params = asRecord(rawParams);
3626
- const threadId = readStringParam(params, "thread_id", { required: true });
3627
- const text = readStringParam(params, "text", {
3628
- required: true,
3629
- allowEmpty: false
3630
- });
3631
- if ((readModeParam(params) ?? "auto") === "start") throw idleContinuationError(threadId);
3632
- const endpoint = await resolveEndpointForThread({
3633
- endpoints,
3634
- request: writeRequest,
3635
- endpointId: readStringParam(params, "endpoint_id"),
3636
- threadId
3637
- });
3638
- const thread = await readThread({
3639
- request: writeRequest,
3640
- endpoint,
3641
- threadId,
3642
- includeTurns: true
3643
- });
3644
- requireCurrentEndpoint(options, "write-controls", endpoint);
3645
- if (statusType(thread) !== "active") throw idleContinuationError(threadId);
3646
- const turnId = await resolveInProgressTurnId({
3647
- request: writeRequest,
3648
- endpoint,
3649
- thread,
3650
- threadId
3651
- });
3652
- if (!turnId) throw new Error(`Codex thread ${threadId} is active but no in-progress turn is readable`);
3653
- await writeRequest(endpoint, "turn/steer", {
3654
- threadId,
3655
- expectedTurnId: turnId,
3656
- input: [{
3657
- type: "text",
3658
- text,
3659
- text_elements: []
3660
- }]
3661
- });
3662
- const result = {
3663
- endpointId: endpoint.id,
3664
- threadId,
3665
- mode: "steer",
3666
- turnId
3667
- };
3668
- return jsonResult({
3669
- summary: `codex steer: ${turnId}`,
3670
- result
3671
- });
3672
- }
3673
- },
3674
- {
3675
- name: "codex_session_interrupt",
3676
- label: "Codex Session Interrupt",
3677
- description: "Interrupt an active Codex turn.",
3678
- parameters: SessionInterruptParamsSchema,
3679
- execute: async (_toolCallId, rawParams) => {
3680
- const { endpoints, pluginConfig } = current();
3681
- requireWriteAccess(options, pluginConfig);
3682
- const params = asRecord(rawParams);
3683
- const threadId = readStringParam(params, "thread_id", { required: true });
3684
- const endpoint = await resolveEndpointForThread({
3685
- endpoints,
3686
- request: writeRequest,
3687
- endpointId: readStringParam(params, "endpoint_id"),
3688
- threadId
3689
- });
3690
- const thread = await readThread({
3691
- request: writeRequest,
3692
- endpoint,
3693
- threadId,
3694
- includeTurns: true
3695
- });
3696
- requireCurrentEndpoint(options, "write-controls", endpoint);
3697
- if (statusType(thread) !== "active") throw new Error(`Codex thread ${threadId} has no active turn to interrupt`);
3698
- const turnId = readStringParam(params, "turn_id") ?? await resolveInProgressTurnId({
3699
- request: writeRequest,
3700
- endpoint,
3701
- thread,
3702
- threadId
3703
- });
3704
- if (!turnId) throw new Error(`Codex thread ${threadId} has no readable in-progress turn`);
3705
- await writeRequest(endpoint, "turn/interrupt", {
3706
- threadId,
3707
- turnId
3708
- });
3709
- const result = {
3710
- endpointId: endpoint.id,
3711
- threadId,
3712
- turnId
3713
- };
3714
- return jsonResult({
3715
- summary: `codex interrupted: ${turnId}`,
3716
- result
3717
- });
3718
- }
3719
- }
3720
- ];
3721
2593
  }
3722
2594
  //#endregion
3723
2595
  //#region extensions/codex/src/web-search-provider.ts
3724
- const loadCodexWebSearchRuntime = createLazyRuntimeModule(() => import("./web-search-provider.runtime-CCFhg2z5.js"));
2596
+ const loadCodexWebSearchRuntime = createLazyRuntimeModule(() => import("./.setup/web-search-provider.runtime-Ce2zOHxW.mjs"));
3725
2597
  const CodexWebSearchSchema = {
3726
2598
  type: "object",
3727
2599
  properties: { query: {
@@ -3763,68 +2635,21 @@ const ENDED_SESSION_REASONS = /* @__PURE__ */ new Set([
3763
2635
  var codex_default = definePluginEntry({
3764
2636
  id: "codex",
3765
2637
  name: "Codex",
3766
- description: "Codex app-server harness and native session supervision.",
2638
+ description: "Codex app-server harness and Codex-managed GPT model catalog.",
3767
2639
  register(api) {
3768
2640
  const resolveCurrentConfig = () => api.runtime.config?.current ? api.runtime.config.current() : void 0;
3769
- const resolvePluginConfig = (resolveConfig) => {
3770
- const liveConfig = resolveConfig();
3771
- if (!liveConfig) return api.pluginConfig;
3772
- const livePluginConfig = resolveLivePluginConfigObject(() => liveConfig, "codex", api.pluginConfig);
3773
- if (!resolveEffectiveEnableState({
3774
- id: "codex",
3775
- origin: "bundled",
3776
- config: normalizePluginsConfig(liveConfig.plugins),
3777
- rootConfig: liveConfig,
3778
- enabledByDefault: livePluginConfig !== void 0
3779
- }).enabled) return;
3780
- return livePluginConfig;
3781
- };
3782
- const resolveCurrentPluginConfig = () => resolvePluginConfig(resolveCurrentConfig);
3783
- let bindingStateStore;
3784
- 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({
3785
2643
  namespace: CODEX_APP_SERVER_BINDING_NAMESPACE,
3786
2644
  maxEntries: CODEX_APP_SERVER_BINDING_MAX_ENTRIES,
3787
2645
  overflowPolicy: "reject-new"
3788
- });
3789
- const bindingStore = createLazyCodexAppServerBindingStore({
3790
- entries: () => openBindingStateStore().entries(),
3791
- lookup: (key) => openBindingStateStore().lookup(key),
3792
- get update() {
3793
- const store = openBindingStateStore();
3794
- return store.update?.bind(store);
3795
- }
3796
- });
3797
- registerCodexCliMetadata(api);
3798
- const sessionCatalogControl = createCodexSessionCatalogControl({
3799
- getPluginConfig: resolveCurrentPluginConfig,
3800
- getRuntimeConfig: resolveCurrentConfig
3801
- });
3802
- if (readCodexPluginConfig(resolveCurrentPluginConfig()).sessionCatalog?.enabled !== false) {
3803
- codexSessionCatalogRuntime.register({
3804
- api,
3805
- bindingStore,
3806
- control: sessionCatalogControl,
3807
- getRuntimeConfig: resolveCurrentConfig
3808
- });
3809
- for (const command of createCodexSessionCatalogNodeHostCommands(sessionCatalogControl)) api.registerNodeHostCommand(command);
3810
- }
3811
- for (const policy of createCodexSessionCatalogNodeInvokePolicies()) api.registerNodeInvokePolicy(policy);
3812
- if (readCodexPluginConfig(resolveCurrentPluginConfig()).supervision?.enabled === true) api.registerTool((context) => {
3813
- if (context.senderIsOwner !== true) return [];
3814
- const resolveToolRuntimeConfig = () => context.getRuntimeConfig?.() ?? context.runtimeConfig ?? context.config ?? resolveCurrentConfig();
3815
- return createCodexSupervisionTools({
3816
- getPluginConfig: () => resolvePluginConfig(resolveToolRuntimeConfig),
3817
- getRuntimeConfig: resolveToolRuntimeConfig,
3818
- senderIsOwner: context.senderIsOwner
3819
- });
3820
- }, { names: [...CODEX_SUPERVISION_COMPAT_TOOL_NAMES] });
2646
+ }));
3821
2647
  api.registerAgentHarness(createCodexAppServerAgentHarness({
3822
2648
  bindingStore,
3823
- sessionCatalogControl,
3824
2649
  resolveConfig: resolveCurrentConfig,
3825
- resolvePluginConfig: resolveCurrentPluginConfig,
3826
- runtime: api.runtime
2650
+ resolvePluginConfig: resolveCurrentPluginConfig
3827
2651
  }));
2652
+ api.registerProvider(buildCodexProvider({ pluginConfig: api.pluginConfig }));
3828
2653
  api.registerMediaUnderstandingProvider(buildCodexMediaUnderstandingProvider({ pluginConfig: api.pluginConfig }));
3829
2654
  api.registerWebSearchProvider(createCodexWebSearchProvider({ resolvePluginConfig: resolveCurrentPluginConfig }));
3830
2655
  api.registerMigrationProvider(buildCodexMigrationProvider({ runtime: api.runtime }));
@@ -3895,7 +2720,7 @@ var codex_default = definePluginEntry({
3895
2720
  }
3896
2721
  }
3897
2722
  }));
3898
- api.on("inbound_claim", (event, ctx) => codexConversationBindingRuntime.handleInboundClaim(event, ctx, {
2723
+ api.on("inbound_claim", (event, ctx) => handleCodexConversationInboundClaim(event, ctx, {
3899
2724
  bindingStore,
3900
2725
  pluginConfig: resolveCurrentPluginConfig(),
3901
2726
  config: resolveCurrentConfig(),
@@ -3904,14 +2729,14 @@ var codex_default = definePluginEntry({
3904
2729
  ...params
3905
2730
  })
3906
2731
  }));
3907
- api.onConversationBindingResolved?.((event) => codexConversationBindingRuntime.handleBindingResolved(event, { bindingStore }));
2732
+ api.onConversationBindingResolved?.((event) => handleCodexConversationBindingResolved(event, { bindingStore }));
3908
2733
  api.on("after_compaction", async (event, ctx) => {
3909
2734
  const previousSessionId = event.previousSessionId?.trim();
3910
2735
  const sessionId = ctx.sessionId?.trim();
3911
2736
  if (!previousSessionId || !sessionId || previousSessionId === sessionId) return;
3912
2737
  const config = resolveCurrentConfig();
3913
2738
  const sessionKey = ctx.sessionKey?.trim();
3914
- const { sessionBindingIdentity } = await import("./session-binding-BXpo9ymA.js").then((n) => n.f);
2739
+ const { sessionBindingIdentity } = await import("./.setup/session-binding-DuihlMJc.mjs").then((n) => n.c);
3915
2740
  const identity = sessionBindingIdentity({
3916
2741
  sessionId,
3917
2742
  ...sessionKey ? { sessionKey } : {},
@@ -3924,12 +2749,8 @@ var codex_default = definePluginEntry({
3924
2749
  api.on("session_end", async (event, ctx) => {
3925
2750
  if (!event.reason || !ENDED_SESSION_REASONS.has(event.reason)) return;
3926
2751
  const sessionKey = event.sessionKey ?? ctx.sessionKey;
3927
- const endedSessionKey = sessionKey?.trim();
3928
- const nextSessionKey = event.nextSessionKey?.trim();
3929
- if (endedSessionKey && nextSessionKey && nextSessionKey !== endedSessionKey) return;
3930
- if (event.nextSessionId?.trim() === event.sessionId.trim()) return;
3931
2752
  const config = resolveCurrentConfig();
3932
- const { sessionBindingIdentity } = await import("./session-binding-BXpo9ymA.js").then((n) => n.f);
2753
+ const { sessionBindingIdentity } = await import("./.setup/session-binding-DuihlMJc.mjs").then((n) => n.c);
3933
2754
  await bindingStore.retireSessionGeneration(sessionBindingIdentity({
3934
2755
  sessionId: event.sessionId,
3935
2756
  ...sessionKey ? { sessionKey } : {},