@openclaw/codex 2026.5.24-beta.1 → 2026.5.25-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/dist/{client-1sSy4p3z.js → client-SrtuTTVa.js} +10 -3
  2. package/dist/{client-factory-Bk6i4FnW.js → client-factory-A7DQkSLq.js} +1 -1
  3. package/dist/{command-handlers-CcTABuem.js → command-handlers-CLtPT37L.js} +10 -23
  4. package/dist/compact-acCua7zr.js +155 -0
  5. package/dist/{computer-use-Hdq1WgTA.js → computer-use-CVfE2Vfb.js} +2 -2
  6. package/dist/{config-DDMrwfJl.js → config-D6TUuyo2.js} +6 -3
  7. package/dist/{node-cli-sessions-9CAqnIaA.js → conversation-binding-B-GevkEb.js} +426 -434
  8. package/dist/{dynamic-tools-Bq717oJR.js → dynamic-tools-BXAE1U1-.js} +20 -11
  9. package/dist/harness.js +5 -5
  10. package/dist/index.js +451 -17
  11. package/dist/media-understanding-provider.js +7 -5
  12. package/dist/{models-DtGLkqMP.js → models-CC2IusQs.js} +3 -3
  13. package/dist/{notification-correlation-qKY_sgga.js → notification-correlation-D7tJW3FW.js} +26 -11
  14. package/dist/provider.js +3 -3
  15. package/dist/{request-D93E78SA.js → request-BO0z3vA8.js} +3 -3
  16. package/dist/{run-attempt-CT1N__qp.js → run-attempt-BjoI3hUJ.js} +279 -230
  17. package/dist/{session-binding-Bw_mfIW2.js → session-binding-CwNOIDP2.js} +1 -1
  18. package/dist/{shared-client-CFCUGEVs.js → shared-client-CZP1DjaN.js} +3 -4
  19. package/dist/{side-question-CYFMTA1O.js → side-question-Cb4COOQ9.js} +16 -11
  20. package/dist/test-api.js +3 -3
  21. package/dist/{thread-lifecycle-DGoaguJh.js → thread-lifecycle-CXMd9UgJ.js} +10 -12
  22. package/dist/{vision-tools-DOnxzH2y.js → vision-tools-CETeQgze.js} +16 -4
  23. package/npm-shrinkwrap.json +225 -238
  24. package/package.json +7 -7
  25. package/dist/compact-DnTgL6UT.js +0 -475
  26. package/dist/protocol-oeJQu4rs.js +0 -9
@@ -1,4 +1,5 @@
1
- import { S as invalidInlineImageText, w as sanitizeInlineImageDataUrl } from "./thread-lifecycle-DGoaguJh.js";
1
+ import { C as invalidInlineImageText, T as sanitizeInlineImageDataUrl } from "./thread-lifecycle-CXMd9UgJ.js";
2
+ import { asOptionalRecord, isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
2
3
  import { HEARTBEAT_RESPONSE_TOOL_NAME, createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, extractToolResultMediaArtifact, filterToolResultMediaUrls, isMessagingTool, isMessagingToolSendAction, isToolWrappedWithBeforeToolCallHook, normalizeHeartbeatToolResponse, runAgentHarnessAfterToolCallHook, setBeforeToolCallDiagnosticsEnabled, wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
3
4
  import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
4
5
  //#region extensions/codex/src/app-server/dynamic-tool-profile.ts
@@ -154,6 +155,7 @@ function createCodexDynamicToolBridge(params) {
154
155
  success: !resultIsError
155
156
  }, terminalType);
156
157
  withDynamicToolTermination(response, rawResult.terminate === true || result.terminate === true || isToolResultYield(rawResult) || isToolResultYield(result));
158
+ withDynamicToolAsyncStarted(response, isAsyncStartedToolResult(rawResult) || isAsyncStartedToolResult(result));
157
159
  return withSideEffectEvidence(response, terminalType !== "blocked");
158
160
  } catch (error) {
159
161
  collectToolTelemetry({
@@ -217,14 +219,14 @@ function resolveCodexDynamicToolResultMaxChars(ctx) {
217
219
  }) ?? DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS;
218
220
  }
219
221
  function resolveAgentContextLimitValue(params) {
220
- const agents = readRecord(params.config?.agents);
221
- const defaultValue = readPositiveInteger(readRecord(readRecord(agents?.defaults)?.contextLimits)?.[params.key]);
222
+ const agents = asOptionalRecord(params.config?.agents);
223
+ const defaultValue = readPositiveInteger(asOptionalRecord(asOptionalRecord(agents?.defaults)?.contextLimits)?.[params.key]);
222
224
  if (!params.agentId) return defaultValue;
223
225
  const list = agents?.list;
224
226
  if (!Array.isArray(list)) return defaultValue;
225
227
  const normalizedAgentId = normalizeAgentId(params.agentId);
226
- return readPositiveInteger(readRecord(readRecord(list.find((entry) => {
227
- const entryId = readRecord(entry)?.id;
228
+ return readPositiveInteger(asOptionalRecord(asOptionalRecord(list.find((entry) => {
229
+ const entryId = asOptionalRecord(entry)?.id;
228
230
  return typeof entryId === "string" && normalizeAgentId(entryId) === normalizedAgentId;
229
231
  }))?.contextLimits)?.[params.key]) ?? defaultValue;
230
232
  }
@@ -306,12 +308,6 @@ function extractInternalSourceReplyPayload(details) {
306
308
  };
307
309
  return text || mediaUrls.length > 0 || payload.presentation || payload.interactive ? payload : void 0;
308
310
  }
309
- function isRecord(value) {
310
- return value !== null && typeof value === "object" && !Array.isArray(value);
311
- }
312
- function readRecord(value) {
313
- return isRecord(value) ? value : void 0;
314
- }
315
311
  function readPositiveInteger(value) {
316
312
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return;
317
313
  return Math.floor(value);
@@ -330,6 +326,10 @@ function isToolResultYield(result) {
330
326
  if (!isRecord(details) || typeof details.status !== "string") return false;
331
327
  return details.status.trim().toLowerCase() === "yielded";
332
328
  }
329
+ function isAsyncStartedToolResult(result) {
330
+ const details = result.details;
331
+ return isRecord(details) && details.async === true && details.status === "started";
332
+ }
333
333
  function inferToolResultDiagnosticTerminalType(result, isError) {
334
334
  const details = result.details;
335
335
  if (isRecord(details) && typeof details.status === "string") {
@@ -363,6 +363,15 @@ function withDynamicToolTermination(response, terminate) {
363
363
  });
364
364
  return response;
365
365
  }
366
+ function withDynamicToolAsyncStarted(response, asyncStarted) {
367
+ if (!asyncStarted) return response;
368
+ Object.defineProperty(response, "asyncStarted", {
369
+ configurable: true,
370
+ enumerable: false,
371
+ value: true
372
+ });
373
+ return response;
374
+ }
366
375
  function normalizeToolResultMaxChars(maxChars) {
367
376
  return typeof maxChars === "number" && Number.isFinite(maxChars) && maxChars > 0 ? Math.floor(maxChars) : DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS;
368
377
  }
package/dist/harness.js CHANGED
@@ -28,31 +28,31 @@ function createCodexAppServerAgentHarness(options) {
28
28
  };
29
29
  },
30
30
  runAttempt: async (params) => {
31
- const { runCodexAppServerAttempt } = await import("./run-attempt-CT1N__qp.js");
31
+ const { runCodexAppServerAttempt } = await import("./run-attempt-BjoI3hUJ.js");
32
32
  return runCodexAppServerAttempt(params, {
33
33
  pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig,
34
34
  nativeHookRelay: { enabled: true }
35
35
  });
36
36
  },
37
37
  runSideQuestion: async (params) => {
38
- const { runCodexAppServerSideQuestion } = await import("./side-question-CYFMTA1O.js");
38
+ const { runCodexAppServerSideQuestion } = await import("./side-question-Cb4COOQ9.js");
39
39
  return runCodexAppServerSideQuestion(params, {
40
40
  pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig,
41
41
  nativeHookRelay: { enabled: true }
42
42
  });
43
43
  },
44
44
  compact: async (params) => {
45
- const { maybeCompactCodexAppServerSession } = await import("./compact-DnTgL6UT.js");
45
+ const { maybeCompactCodexAppServerSession } = await import("./compact-acCua7zr.js");
46
46
  return maybeCompactCodexAppServerSession(params, { pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig });
47
47
  },
48
48
  reset: async (params) => {
49
49
  if (params.sessionFile) {
50
- const { clearCodexAppServerBinding } = await import("./session-binding-Bw_mfIW2.js").then((n) => n.a);
50
+ const { clearCodexAppServerBinding } = await import("./session-binding-CwNOIDP2.js").then((n) => n.a);
51
51
  await clearCodexAppServerBinding(params.sessionFile);
52
52
  }
53
53
  },
54
54
  dispose: async () => {
55
- const { clearSharedCodexAppServerClientAndWait } = await import("./shared-client-CFCUGEVs.js").then((n) => n.a);
55
+ const { clearSharedCodexAppServerClientAndWait } = await import("./shared-client-CZP1DjaN.js").then((n) => n.a);
56
56
  await clearSharedCodexAppServerClientAndWait();
57
57
  }
58
58
  };
package/dist/index.js CHANGED
@@ -1,22 +1,24 @@
1
1
  import { createCodexAppServerAgentHarness } from "./harness.js";
2
- import { c as readCodexPluginConfig, l as resolveCodexAppServerRuntimeOptions, t as CODEX_PLUGINS_MARKETPLACE_NAME } from "./config-DDMrwfJl.js";
2
+ import { c as readCodexPluginConfig, l as resolveCodexAppServerRuntimeOptions, t as CODEX_PLUGINS_MARKETPLACE_NAME } from "./config-D6TUuyo2.js";
3
3
  import { buildCodexProvider } from "./provider.js";
4
- import { v as ensureCodexPluginActivation, x as defaultCodexAppInventoryCache, y as pluginReadParams } from "./thread-lifecycle-DGoaguJh.js";
4
+ import { S as defaultCodexAppInventoryCache, b as pluginReadParams, y as ensureCodexPluginActivation } from "./thread-lifecycle-CXMd9UgJ.js";
5
5
  import { buildCodexMediaUnderstandingProvider } from "./media-understanding-provider.js";
6
- import { i as describeControlFailure, n as buildCodexPluginAppCacheKey, t as requestCodexAppServerJson } from "./request-D93E78SA.js";
7
- import { s as formatCodexDisplayText } from "./notification-correlation-qKY_sgga.js";
8
- import { c as resolveCodexAppServerAuthAccountCacheKey, d as resolveCodexAppServerEnvApiKeyCacheKey, i as getSharedCodexAppServerClient, n as clearSharedCodexAppServerClientIfCurrentAndWait, u as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-CFCUGEVs.js";
9
- import { a as resolveCodexCliSessionForBindingOnNode, c as handleCodexConversationInboundClaim, i as listCodexCliSessionsOnNode, n as createCodexCliSessionNodeInvokePolicies, o as resumeCodexCliSessionOnNode, s as handleCodexConversationBindingResolved, t as createCodexCliSessionNodeHostCommands } from "./node-cli-sessions-9CAqnIaA.js";
6
+ import { i as describeControlFailure, n as buildCodexPluginAppCacheKey, t as requestCodexAppServerJson } from "./request-BO0z3vA8.js";
7
+ import { s as formatCodexDisplayText } from "./notification-correlation-D7tJW3FW.js";
8
+ import { c as resolveCodexAppServerAuthAccountCacheKey, d as resolveCodexAppServerEnvApiKeyCacheKey, i as getSharedCodexAppServerClient, n as clearSharedCodexAppServerClientIfCurrentAndWait, u as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-CZP1DjaN.js";
9
+ import { a as createCodexCliSessionNodeInvokePolicies, c as resolveCodexCliSessionForBindingOnNode, i as createCodexCliSessionNodeHostCommands, l as resumeCodexCliSessionOnNode, n as handleCodexConversationInboundClaim, s as listCodexCliSessionsOnNode, t as handleCodexConversationBindingResolved } from "./conversation-binding-B-GevkEb.js";
10
10
  import { mutateConfigFile } from "openclaw/plugin-sdk/config-mutation";
11
11
  import { resolveLivePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
12
12
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
13
13
  import os from "node:os";
14
+ import { asBoolean, isRecord, normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
14
15
  import fs from "node:fs/promises";
15
16
  import path from "node:path";
16
- import { resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
17
+ import { loadAuthProfileStoreWithoutExternalProfiles, resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
17
18
  import { pathExists } from "openclaw/plugin-sdk/security-runtime";
18
19
  import { MIGRATION_REASON_TARGET_EXISTS, applyMigrationManualItem, createMigrationItem, createMigrationManualItem, hasMigrationConfigPatchConflict, markMigrationItemConflict, markMigrationItemError, markMigrationItemSkipped, readMigrationConfigPath, summarizeMigrationItems, writeMigrationConfigPath } from "openclaw/plugin-sdk/migration";
19
20
  import { archiveMigrationItem, copyMigrationFileItem, withCachedMigrationConfigRuntime, writeMigrationReport } from "openclaw/plugin-sdk/migration-runtime";
21
+ import { applyAuthProfileConfig, buildApiKeyCredential, buildOauthProviderAuthResult, readCodexCliCredentialsCached, updateAuthProfileStoreWithLock } from "openclaw/plugin-sdk/provider-auth";
20
22
  import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
21
23
  //#region extensions/codex/src/commands.ts
22
24
  function createCodexCommand(options) {
@@ -45,7 +47,7 @@ async function handleCodexCommand(ctx, options = {}) {
45
47
  }
46
48
  }
47
49
  async function loadDefaultCodexSubcommandHandler() {
48
- const { handleCodexSubcommand } = await import("./command-handlers-CcTABuem.js");
50
+ const { handleCodexSubcommand } = await import("./command-handlers-CLtPT37L.js");
49
51
  return handleCodexSubcommand;
50
52
  }
51
53
  //#endregion
@@ -78,6 +80,406 @@ async function readJsonObject(filePath) {
78
80
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
79
81
  }
80
82
  //#endregion
83
+ //#region extensions/codex/src/migration/auth.ts
84
+ const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
85
+ const OPENAI_PROVIDER_ID = "openai";
86
+ const OPENAI_CODEX_DEFAULT_MODEL = "openai/gpt-5.5";
87
+ const CODEX_IMPORT_DISPLAY_NAME = "Codex import";
88
+ const CODEX_REASON_AUTH_NOT_SELECTED = "auth credential migration not selected";
89
+ const CODEX_REASON_AUTH_PROFILE_EXISTS = "auth profile exists";
90
+ const CODEX_REASON_AUTH_PROFILE_WRITE_FAILED = "failed to write auth profile";
91
+ const CODEX_REASON_AUTH_NO_LONGER_PRESENT = "auth credential no longer present";
92
+ const CODEX_REASON_MISSING_AUTH_METADATA = "missing auth metadata";
93
+ const CODEX_CONFIG_PATCH_MODE_RETURN$1 = "return";
94
+ var CodexAuthConfigConflict = class extends Error {};
95
+ function decodeJwtPayload(token) {
96
+ const payload = token.split(".")[1];
97
+ if (!payload) return;
98
+ try {
99
+ const parsed = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
100
+ return isRecord(parsed) ? parsed : void 0;
101
+ } catch {
102
+ return;
103
+ }
104
+ }
105
+ function resolveCodexIdentity(access, accountId) {
106
+ const payload = decodeJwtPayload(access);
107
+ const auth = isRecord(payload?.["https://api.openai.com/auth"]) ? payload["https://api.openai.com/auth"] : {};
108
+ const email = normalizeOptionalString((isRecord(payload?.["https://api.openai.com/profile"]) ? payload["https://api.openai.com/profile"] : {}).email);
109
+ const resolvedAccountId = accountId ?? normalizeOptionalString(auth.chatgpt_account_id);
110
+ const chatgptPlanType = normalizeOptionalString(auth.chatgpt_plan_type);
111
+ if (email) return {
112
+ ...resolvedAccountId ? { accountId: resolvedAccountId } : {},
113
+ ...chatgptPlanType ? { chatgptPlanType } : {},
114
+ email,
115
+ profileName: email
116
+ };
117
+ const stableSubject = normalizeOptionalString(auth.chatgpt_account_user_id) ?? normalizeOptionalString(auth.chatgpt_user_id) ?? normalizeOptionalString(auth.user_id) ?? normalizeOptionalString(payload?.sub) ?? resolvedAccountId;
118
+ return {
119
+ ...resolvedAccountId ? { accountId: resolvedAccountId } : {},
120
+ ...chatgptPlanType ? { chatgptPlanType } : {},
121
+ ...stableSubject ? { profileName: `id-${Buffer.from(stableSubject).toString("base64url")}` } : {}
122
+ };
123
+ }
124
+ function credentialExtra(identity) {
125
+ const extra = {
126
+ ...identity.accountId ? { accountId: identity.accountId } : {},
127
+ ...identity.chatgptPlanType ? { chatgptPlanType: identity.chatgptPlanType } : {},
128
+ ...identity.idToken ? { idToken: identity.idToken } : {}
129
+ };
130
+ return Object.keys(extra).length > 0 ? extra : void 0;
131
+ }
132
+ function importProfileName(identity, fallback) {
133
+ if (identity.accountId) return `account-${identity.accountId.replaceAll(/[^A-Za-z0-9._-]+/gu, "-")}`;
134
+ if (identity.profileName?.startsWith("id-")) return identity.profileName;
135
+ return fallback;
136
+ }
137
+ async function readModelRefs(source) {
138
+ const cache = await readJsonObject(source.modelsCachePath);
139
+ const models = Array.isArray(cache.models) ? cache.models : [];
140
+ const refs = /* @__PURE__ */ new Set();
141
+ for (const model of models) {
142
+ const slug = typeof model === "string" ? model.trim() : isRecord(model) ? normalizeOptionalString(model.slug) ?? normalizeOptionalString(model.id) ?? normalizeOptionalString(model.name) : void 0;
143
+ if (!slug) continue;
144
+ refs.add(`${OPENAI_PROVIDER_ID}/${slug}`);
145
+ }
146
+ refs.add(OPENAI_CODEX_DEFAULT_MODEL);
147
+ return [...refs].toSorted();
148
+ }
149
+ function readProviderAuthModelConfigs(result) {
150
+ const models = result.configPatch?.agents?.defaults?.models;
151
+ if (isRecord(models)) return { ...models };
152
+ return { [normalizeOptionalString(result.defaultModel) ?? OPENAI_CODEX_DEFAULT_MODEL]: {} };
153
+ }
154
+ async function buildCodexOAuthCredential(source) {
155
+ const credential = readCodexCliCredentialsCached({
156
+ codexHome: source.codexHome,
157
+ allowKeychainPrompt: false,
158
+ ttlMs: 0
159
+ });
160
+ if (!credential) return null;
161
+ const identity = resolveCodexIdentity(credential.access, credential.accountId);
162
+ const modelRefs = await readModelRefs(source);
163
+ const configPatch = { agents: { defaults: { models: Object.fromEntries(modelRefs.map((modelRef) => [modelRef, {}])) } } };
164
+ const result = buildOauthProviderAuthResult({
165
+ providerId: OPENAI_CODEX_PROVIDER_ID,
166
+ defaultModel: OPENAI_CODEX_DEFAULT_MODEL,
167
+ access: credential.access,
168
+ refresh: credential.refresh,
169
+ expires: credential.expires,
170
+ email: identity.email,
171
+ profileName: importProfileName(identity, "codex-import"),
172
+ displayName: CODEX_IMPORT_DISPLAY_NAME,
173
+ credentialExtra: credentialExtra({
174
+ accountId: identity.accountId,
175
+ chatgptPlanType: identity.chatgptPlanType,
176
+ idToken: credential.idToken
177
+ }),
178
+ configPatch
179
+ });
180
+ const profile = result.profiles[0];
181
+ return profile ? {
182
+ kind: "oauth",
183
+ provider: OPENAI_CODEX_PROVIDER_ID,
184
+ profileId: profile.profileId,
185
+ result,
186
+ modelConfigs: readProviderAuthModelConfigs(result)
187
+ } : null;
188
+ }
189
+ async function buildCodexApiKeyCredential(source) {
190
+ const key = normalizeOptionalString((await readJsonObject(source.authPath)).OPENAI_API_KEY);
191
+ if (!key) return null;
192
+ return {
193
+ kind: "api_key",
194
+ provider: OPENAI_PROVIDER_ID,
195
+ profileId: "openai:codex-import",
196
+ key
197
+ };
198
+ }
199
+ async function readCodexAuthCredentials(source) {
200
+ return [await buildCodexOAuthCredential(source), await buildCodexApiKeyCredential(source)].filter((entry) => entry !== null);
201
+ }
202
+ function findMatchingOAuthProfile(store, credential) {
203
+ for (const [profileId, existing] of Object.entries(store.profiles)) {
204
+ if (existing.type !== "oauth" || existing.provider !== credential.provider) continue;
205
+ if (credential.accountId && existing.accountId === credential.accountId) return profileId;
206
+ if ((!credential.accountId || !existing.accountId) && credential.email && existing.email === credential.email) return profileId;
207
+ }
208
+ }
209
+ function findMatchingApiKeyProfile(store, provider, key) {
210
+ for (const [profileId, existing] of Object.entries(store.profiles)) if (existing.type === "api_key" && existing.provider === provider && existing.key === key) return profileId;
211
+ }
212
+ function itemProfileTarget(credential, store) {
213
+ if (credential.kind === "oauth") {
214
+ const profile = credential.result.profiles[0];
215
+ const matched = profile?.credential.type === "oauth" ? findMatchingOAuthProfile(store, profile.credential) : void 0;
216
+ return {
217
+ profileId: matched ?? credential.profileId,
218
+ matchedExisting: Boolean(matched)
219
+ };
220
+ }
221
+ const matched = findMatchingApiKeyProfile(store, credential.provider, credential.key);
222
+ return {
223
+ profileId: matched ?? credential.profileId,
224
+ matchedExisting: Boolean(matched)
225
+ };
226
+ }
227
+ function replaceConfigDraft(draft, next) {
228
+ for (const key of Object.keys(draft)) delete draft[key];
229
+ Object.assign(draft, next);
230
+ }
231
+ function existingAuthProfileConfigIsCompatible(existing, profile) {
232
+ if (existing.provider !== profile.provider || existing.mode !== profile.mode) return false;
233
+ if (existing.email && profile.email && existing.email !== profile.email) return false;
234
+ return true;
235
+ }
236
+ function hasAuthProfileConfigConflict(config, profile, overwrite) {
237
+ if (overwrite) return false;
238
+ const existing = config.auth?.profiles?.[profile.profileId];
239
+ return Boolean(existing && !existingAuthProfileConfigIsCompatible(existing, profile));
240
+ }
241
+ function hasCurrentAuthProfileConfigConflict(ctx, profile) {
242
+ let config = ctx.config;
243
+ try {
244
+ config = ctx.runtime?.config?.current?.() ?? config;
245
+ } catch {}
246
+ return hasAuthProfileConfigConflict(config, profile, Boolean(ctx.overwrite));
247
+ }
248
+ function applyDefaultModelIfMissing(cfg) {
249
+ const currentModel = cfg.agents?.defaults?.model;
250
+ if (typeof currentModel === "string" ? currentModel : isRecord(currentModel) ? normalizeOptionalString(currentModel.primary) : void 0) return cfg;
251
+ return {
252
+ ...cfg,
253
+ agents: {
254
+ ...cfg.agents,
255
+ defaults: {
256
+ ...cfg.agents?.defaults,
257
+ model: {
258
+ ...isRecord(currentModel) ? currentModel : {},
259
+ primary: OPENAI_CODEX_DEFAULT_MODEL
260
+ }
261
+ }
262
+ }
263
+ };
264
+ }
265
+ function mergeModelConfigEntry(existing, patch) {
266
+ if (existing && isRecord(existing) && isRecord(patch)) return {
267
+ ...existing,
268
+ ...patch
269
+ };
270
+ return existing ?? patch;
271
+ }
272
+ function applyOAuthModelConfigsToConfig(cfg, credential) {
273
+ const existingModels = cfg.agents?.defaults?.models ?? {};
274
+ const models = credential.result.replaceDefaultModels ? { ...credential.modelConfigs } : { ...existingModels };
275
+ if (!credential.result.replaceDefaultModels) for (const [modelRef, modelConfig] of Object.entries(credential.modelConfigs)) models[modelRef] = mergeModelConfigEntry(models[modelRef], modelConfig);
276
+ return {
277
+ ...cfg,
278
+ agents: {
279
+ ...cfg.agents,
280
+ defaults: {
281
+ ...cfg.agents?.defaults,
282
+ models
283
+ }
284
+ }
285
+ };
286
+ }
287
+ function applyOAuthConfigToConfig(cfg, credential, profileId) {
288
+ let next = applyOAuthModelConfigsToConfig(cfg, credential);
289
+ const profile = credential.result.profiles[0];
290
+ if (profile) next = applyAuthProfileConfig(next, {
291
+ profileId,
292
+ provider: profile.credential.provider,
293
+ mode: "oauth",
294
+ ..."email" in profile.credential && profile.credential.email ? { email: profile.credential.email } : {},
295
+ ..."displayName" in profile.credential && profile.credential.displayName ? { displayName: profile.credential.displayName } : {},
296
+ preferProfileFirst: false
297
+ });
298
+ return applyDefaultModelIfMissing(next);
299
+ }
300
+ function applyApiKeyConfigToConfig(cfg, credential, profileId) {
301
+ return applyAuthProfileConfig(cfg, {
302
+ profileId,
303
+ provider: credential.provider,
304
+ mode: "api_key",
305
+ displayName: CODEX_IMPORT_DISPLAY_NAME,
306
+ preferProfileFirst: false
307
+ });
308
+ }
309
+ function shouldReturnAuthConfigPatch(ctx) {
310
+ return ctx.providerOptions?.configPatchMode === CODEX_CONFIG_PATCH_MODE_RETURN$1;
311
+ }
312
+ function oauthAuthProfileConfig(credential, profileId) {
313
+ const profile = credential.result.profiles[0];
314
+ if (!profile || profile.credential.type !== "oauth") return null;
315
+ return {
316
+ profileId,
317
+ provider: profile.credential.provider,
318
+ mode: "oauth",
319
+ ..."email" in profile.credential && profile.credential.email ? { email: profile.credential.email } : {},
320
+ ..."displayName" in profile.credential && profile.credential.displayName ? { displayName: profile.credential.displayName } : {}
321
+ };
322
+ }
323
+ function apiKeyAuthProfileConfig(credential, profileId) {
324
+ return {
325
+ profileId,
326
+ provider: credential.provider,
327
+ mode: "api_key",
328
+ displayName: CODEX_IMPORT_DISPLAY_NAME
329
+ };
330
+ }
331
+ function authProfileConfigForCredential(credential, profileId) {
332
+ return credential.kind === "oauth" ? oauthAuthProfileConfig(credential, profileId) : apiKeyAuthProfileConfig(credential, profileId);
333
+ }
334
+ async function applyCodexAuthProfileConfig(ctx, profile, applyConfig) {
335
+ const configApi = ctx.runtime?.config;
336
+ if (!configApi?.current || !configApi.mutateConfigFile) return "unavailable";
337
+ try {
338
+ await configApi.mutateConfigFile({
339
+ base: "runtime",
340
+ afterWrite: { mode: "auto" },
341
+ mutate(draft) {
342
+ const current = draft;
343
+ if (hasAuthProfileConfigConflict(current, profile, Boolean(ctx.overwrite))) throw new CodexAuthConfigConflict();
344
+ replaceConfigDraft(draft, applyConfig(current));
345
+ }
346
+ });
347
+ return "configured";
348
+ } catch (error) {
349
+ return error instanceof CodexAuthConfigConflict ? "conflict" : "unavailable";
350
+ }
351
+ }
352
+ async function applyOAuthConfig(ctx, credential, profileId) {
353
+ const profile = oauthAuthProfileConfig(credential, profileId);
354
+ if (!profile) return "unavailable";
355
+ return applyCodexAuthProfileConfig(ctx, profile, (config) => applyOAuthConfigToConfig(config, credential, profileId));
356
+ }
357
+ async function applyApiKeyConfig(ctx, credential, profileId) {
358
+ return applyCodexAuthProfileConfig(ctx, apiKeyAuthProfileConfig(credential, profileId), (config) => applyApiKeyConfigToConfig(config, credential, profileId));
359
+ }
360
+ async function buildCodexAuthItems(params) {
361
+ const credentials = await readCodexAuthCredentials(params.source);
362
+ if (credentials.length === 0) return [];
363
+ const store = loadAuthProfileStoreWithoutExternalProfiles(params.targets.agentDir);
364
+ const skipped = !params.ctx.includeSecrets;
365
+ return credentials.map((credential) => {
366
+ const { profileId, matchedExisting } = itemProfileTarget(credential, store);
367
+ const targetExists = Boolean(store.profiles[profileId]);
368
+ const configProfile = authProfileConfigForCredential(credential, profileId);
369
+ const configConflict = configProfile ? hasAuthProfileConfigConflict(params.ctx.config, configProfile, Boolean(params.ctx.overwrite)) : false;
370
+ const conflict = (targetExists && !matchedExisting && !params.ctx.overwrite || configConflict) && !skipped;
371
+ return createMigrationItem({
372
+ id: `auth:${credential.provider}`,
373
+ kind: "auth",
374
+ action: skipped ? "skip" : "create",
375
+ source: params.source.authPath,
376
+ target: `${params.targets.agentDir}/auth-profiles.json#${profileId}`,
377
+ status: skipped ? "skipped" : conflict ? "conflict" : "planned",
378
+ sensitive: true,
379
+ reason: skipped ? CODEX_REASON_AUTH_NOT_SELECTED : conflict ? CODEX_REASON_AUTH_PROFILE_EXISTS : void 0,
380
+ message: credential.kind === "oauth" ? "Import Codex OAuth credentials and configure OpenAI Codex models." : "Import Codex OpenAI API key.",
381
+ details: {
382
+ provider: credential.provider,
383
+ profileId,
384
+ sourceProfileId: credential.profileId,
385
+ sourceKind: "codex-auth-json",
386
+ credentialKind: credential.kind
387
+ }
388
+ });
389
+ });
390
+ }
391
+ async function applyCodexAuthItem(params) {
392
+ const { ctx, item, source, targets } = params;
393
+ if (item.status !== "planned") return item;
394
+ const profileId = typeof item.details?.profileId === "string" ? item.details.profileId : "";
395
+ const provider = typeof item.details?.provider === "string" ? item.details.provider : "";
396
+ const sourceProfileId = typeof item.details?.sourceProfileId === "string" ? item.details.sourceProfileId : void 0;
397
+ if (!profileId || !provider) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
398
+ const credential = (await readCodexAuthCredentials(source)).find((candidate) => candidate.provider === provider);
399
+ if (!credential) return markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT);
400
+ if (credential.kind === "oauth" && sourceProfileId && credential.profileId !== sourceProfileId) return markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT);
401
+ const oauthProfile = credential.kind === "oauth" ? credential.result.profiles[0] : void 0;
402
+ const oauthCredential = oauthProfile?.credential.type === "oauth" ? oauthProfile.credential : void 0;
403
+ if (credential.kind === "oauth" && !oauthCredential) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
404
+ const configProfile = authProfileConfigForCredential(credential, profileId);
405
+ if (!configProfile) return markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA);
406
+ if (hasCurrentAuthProfileConfigConflict(ctx, configProfile)) return markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS);
407
+ let conflicted = false;
408
+ let wrote = false;
409
+ const store = await updateAuthProfileStoreWithLock({
410
+ agentDir: targets.agentDir,
411
+ updater: (freshStore) => {
412
+ const existing = freshStore.profiles[profileId];
413
+ if (!ctx.overwrite && existing) {
414
+ if ((credential.kind === "oauth" ? findMatchingOAuthProfile(freshStore, oauthCredential) : findMatchingApiKeyProfile(freshStore, credential.provider, credential.key)) === profileId) return false;
415
+ conflicted = true;
416
+ return false;
417
+ }
418
+ freshStore.profiles[profileId] = credential.kind === "oauth" ? {
419
+ ...oauthCredential,
420
+ displayName: CODEX_IMPORT_DISPLAY_NAME
421
+ } : {
422
+ ...buildApiKeyCredential(credential.provider, credential.key),
423
+ displayName: CODEX_IMPORT_DISPLAY_NAME
424
+ };
425
+ wrote = true;
426
+ return true;
427
+ }
428
+ });
429
+ if (conflicted) return markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS);
430
+ if (!store?.profiles[profileId]) return markMigrationItemError(item, CODEX_REASON_AUTH_PROFILE_WRITE_FAILED);
431
+ const configResult = shouldReturnAuthConfigPatch(ctx) ? "unavailable" : credential.kind === "oauth" ? await applyOAuthConfig(ctx, credential, profileId) : await applyApiKeyConfig(ctx, credential, profileId);
432
+ if (configResult === "conflict") return markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS);
433
+ return {
434
+ ...item,
435
+ status: "migrated",
436
+ details: {
437
+ ...item.details,
438
+ wroteAuthProfile: wrote,
439
+ configUpdated: configResult === "configured",
440
+ ...shouldReturnAuthConfigPatch(ctx) ? { configPatchReturned: true } : {}
441
+ }
442
+ };
443
+ }
444
+ async function buildCodexAuthConfigPatchItems(params) {
445
+ const { ctx, item, source } = params;
446
+ if (item.status !== "migrated" || !shouldReturnAuthConfigPatch(ctx)) return [];
447
+ const profileId = typeof item.details?.profileId === "string" ? item.details.profileId : "";
448
+ const provider = typeof item.details?.provider === "string" ? item.details.provider : "";
449
+ const sourceProfileId = typeof item.details?.sourceProfileId === "string" ? item.details.sourceProfileId : void 0;
450
+ if (!profileId || !provider) return [];
451
+ const credential = (await readCodexAuthCredentials(source)).find((candidate) => candidate.provider === provider);
452
+ if (!credential) return [];
453
+ if (credential.kind === "oauth" && sourceProfileId && credential.profileId !== sourceProfileId) return [];
454
+ const next = credential.kind === "oauth" ? applyOAuthConfigToConfig(ctx.config, credential, profileId) : applyApiKeyConfigToConfig(ctx.config, credential, profileId);
455
+ const items = [];
456
+ if (next.auth) items.push(createMigrationItem({
457
+ id: `${item.id}:config:auth`,
458
+ kind: "config",
459
+ action: "merge",
460
+ status: "migrated",
461
+ target: "auth",
462
+ message: "Configure imported Codex auth profile.",
463
+ details: {
464
+ path: ["auth"],
465
+ value: next.auth
466
+ }
467
+ }));
468
+ if (next.agents?.defaults) items.push(createMigrationItem({
469
+ id: `${item.id}:config:agents-defaults`,
470
+ kind: "config",
471
+ action: "merge",
472
+ status: "migrated",
473
+ target: "agents.defaults",
474
+ message: "Configure imported Codex models.",
475
+ details: {
476
+ path: ["agents", "defaults"],
477
+ value: next.agents.defaults
478
+ }
479
+ }));
480
+ return items;
481
+ }
482
+ //#endregion
81
483
  //#region extensions/codex/src/migration/source.ts
82
484
  const SKILL_FILENAME = "SKILL.md";
83
485
  const MAX_SCAN_DEPTH = 6;
@@ -415,6 +817,8 @@ async function discoverCodexSource(inputOrOptions) {
415
817
  const codexSkillsDir = path.join(codexHome, "skills");
416
818
  const agentsSkillsDir = personalAgentsSkillsDir();
417
819
  const configPath = path.join(codexHome, "config.toml");
820
+ const authPath = path.join(codexHome, "auth.json");
821
+ const modelsCachePath = path.join(codexHome, "models_cache.json");
418
822
  const hooksPath = path.join(codexHome, "hooks", "hooks.json");
419
823
  const codexSkills = await discoverSkillDirs({
420
824
  root: codexSkillsDir,
@@ -446,7 +850,8 @@ async function discoverCodexSource(inputOrOptions) {
446
850
  message: "Codex native hooks are archived for manual review because they can execute commands"
447
851
  });
448
852
  const skills = [...codexSkills, ...personalAgentSkills].toSorted((a, b) => a.source.localeCompare(b.source));
449
- const high = Boolean(codexSkills.length || plugins.length || archivePaths.length);
853
+ const hasAuth = await exists(authPath);
854
+ const high = Boolean(codexSkills.length || plugins.length || archivePaths.length || hasAuth);
450
855
  const medium = personalAgentSkills.length > 0;
451
856
  return {
452
857
  root: codexHome,
@@ -455,6 +860,8 @@ async function discoverCodexSource(inputOrOptions) {
455
860
  ...await isDirectory(codexSkillsDir) ? { codexSkillsDir } : {},
456
861
  ...await isDirectory(agentsSkillsDir) ? { personalAgentsSkillsDir: agentsSkillsDir } : {},
457
862
  ...await exists(configPath) ? { configPath } : {},
863
+ ...hasAuth ? { authPath } : {},
864
+ ...await exists(modelsCachePath) ? { modelsCachePath } : {},
458
865
  ...await exists(hooksPath) ? { hooksPath } : {},
459
866
  skills,
460
867
  plugins,
@@ -645,11 +1052,7 @@ function readCodexPluginMigrationConfigEntry(item, enabled) {
645
1052
  };
646
1053
  }
647
1054
  function readExistingAllowDestructiveActions(config) {
648
- const value = readMigrationConfigPath(config, [...CODEX_PLUGIN_NATIVE_CONFIG_PATH, "allow_destructive_actions"]);
649
- return typeof value === "boolean" ? value : void 0;
650
- }
651
- function isRecord(value) {
652
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
1055
+ return asBoolean(readMigrationConfigPath(config, [...CODEX_PLUGIN_NATIVE_CONFIG_PATH, "allow_destructive_actions"]));
653
1056
  }
654
1057
  function buildCodexPluginsConfigValue(entries, params = {}) {
655
1058
  const plugins = Object.fromEntries(entries.toSorted((a, b) => a.configKey.localeCompare(b.configKey)).map((entry) => [entry.configKey, {
@@ -712,6 +1115,11 @@ async function buildCodexMigrationPlan(ctx) {
712
1115
  });
713
1116
  if (!hasCodexSource(source)) throw new Error(`Codex state was not found at ${source.root}. Pass --from <path> if it lives elsewhere.`);
714
1117
  const items = [];
1118
+ items.push(...await buildCodexAuthItems({
1119
+ ctx,
1120
+ source,
1121
+ targets
1122
+ }));
715
1123
  items.push(...await buildSkillItems({
716
1124
  skills: source.skills,
717
1125
  workspaceDir: targets.workspaceDir,
@@ -730,6 +1138,7 @@ async function buildCodexMigrationPlan(ctx) {
730
1138
  details: { archiveRelativePath: archivePath.relativePath }
731
1139
  }));
732
1140
  const warnings = [
1141
+ ...!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."] : [],
733
1142
  ...items.some((item) => item.status === "conflict") ? ["Conflicts were found. Re-run with --overwrite to replace conflicting migration targets after item-level backups."] : [],
734
1143
  ...source.pluginDiscoveryError ? [`Codex app-server plugin inventory discovery failed: ${source.pluginDiscoveryError}. Cached plugin bundles, if any, are advisory only.`] : [],
735
1144
  ...source.plugins.some((plugin) => plugin.migrationBlock?.code === "codex_subscription_required") ? [codexPluginMigrationSubscriptionWarning()] : []
@@ -793,6 +1202,18 @@ async function applyCodexMigrationPlan(params) {
793
1202
  const plan = params.plan ?? await buildCodexMigrationPlan(params.ctx);
794
1203
  const reportDir = params.ctx.reportDir ?? path.join(params.ctx.stateDir, "migration", "codex");
795
1204
  const items = [];
1205
+ const targets = resolveCodexMigrationTargets(params.ctx);
1206
+ const codexHome = typeof plan.metadata?.codexHome === "string" && plan.metadata.codexHome.trim() ? plan.metadata.codexHome : plan.source;
1207
+ const authSource = {
1208
+ root: plan.source,
1209
+ confidence: "high",
1210
+ codexHome,
1211
+ authPath: path.join(codexHome, "auth.json"),
1212
+ modelsCachePath: path.join(codexHome, "models_cache.json"),
1213
+ skills: [],
1214
+ plugins: [],
1215
+ archivePaths: []
1216
+ };
796
1217
  const runtime = withCachedMigrationConfigRuntime(params.ctx.runtime ?? params.runtime, params.ctx.config);
797
1218
  const applyCtx = {
798
1219
  ...params.ctx,
@@ -804,7 +1225,20 @@ async function applyCodexMigrationPlan(params) {
804
1225
  continue;
805
1226
  }
806
1227
  if (item.id === "config:codex-plugins") items.push(await applyCodexPluginConfigItem(applyCtx, item, items));
807
- else if (item.kind === "plugin" && item.action === "install") items.push(await applyCodexPluginInstallItem(applyCtx, item));
1228
+ else if (item.kind === "auth") {
1229
+ const authItem = await applyCodexAuthItem({
1230
+ ctx: applyCtx,
1231
+ item,
1232
+ source: authSource,
1233
+ targets
1234
+ });
1235
+ items.push(authItem);
1236
+ items.push(...await buildCodexAuthConfigPatchItems({
1237
+ ctx: applyCtx,
1238
+ item: authItem,
1239
+ source: authSource
1240
+ }));
1241
+ } else if (item.kind === "plugin" && item.action === "install") items.push(await applyCodexPluginInstallItem(applyCtx, item));
808
1242
  else if (item.kind === "manual") items.push(applyMigrationManualItem(item));
809
1243
  else if (item.action === "archive") items.push(await archiveMigrationItem(item, reportDir));
810
1244
  else items.push(await copyMigrationFileItem(item, reportDir, { overwrite: params.ctx.overwrite }));
@@ -817,8 +1251,8 @@ async function applyCodexMigrationPlan(params) {
817
1251
  reportDir
818
1252
  };
819
1253
  if (items.some(isCodexPluginLoadWarningItem)) {
820
- result.warnings = [...new Set([...result.warnings ?? [], CODEX_PLUGIN_LOAD_WARNING])];
821
- result.nextSteps = [...new Set([CODEX_PLUGIN_LOAD_WARNING, ...result.nextSteps ?? []])];
1254
+ result.warnings = uniqueStrings([...result.warnings ?? [], CODEX_PLUGIN_LOAD_WARNING]);
1255
+ result.nextSteps = uniqueStrings([CODEX_PLUGIN_LOAD_WARNING, ...result.nextSteps ?? []]);
822
1256
  }
823
1257
  await writeMigrationReport(result, { title: "Codex Migration Report" });
824
1258
  return result;