@openclaw/codex 2026.5.7-beta.1 → 2026.5.9-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.
package/dist/harness.js CHANGED
@@ -18,21 +18,21 @@ function createCodexAppServerAgentHarness(options) {
18
18
  };
19
19
  },
20
20
  runAttempt: async (params) => {
21
- const { runCodexAppServerAttempt } = await import("./run-attempt-BaT_FcTv.js");
21
+ const { runCodexAppServerAttempt } = await import("./run-attempt-DCCThMaw.js");
22
22
  return runCodexAppServerAttempt(params, { pluginConfig: options?.pluginConfig });
23
23
  },
24
24
  compact: async (params) => {
25
- const { maybeCompactCodexAppServerSession } = await import("./compact-DcR5aTxd.js");
25
+ const { maybeCompactCodexAppServerSession } = await import("./compact-BaeaEEoQ.js");
26
26
  return maybeCompactCodexAppServerSession(params, { pluginConfig: options?.pluginConfig });
27
27
  },
28
28
  reset: async (params) => {
29
29
  if (params.sessionFile) {
30
- const { clearCodexAppServerBinding } = await import("./session-binding-DuJYTJQy.js").then((n) => n.a);
30
+ const { clearCodexAppServerBinding } = await import("./session-binding-C_HDlZx8.js").then((n) => n.a);
31
31
  await clearCodexAppServerBinding(params.sessionFile);
32
32
  }
33
33
  },
34
34
  dispose: async () => {
35
- const { clearSharedCodexAppServerClientAndWait } = await import("./shared-client-Dfk3Enm-.js").then((n) => n.r);
35
+ const { clearSharedCodexAppServerClientAndWait } = await import("./shared-client-pLCOzMaZ.js").then((n) => n.r);
36
36
  await clearSharedCodexAppServerClientAndWait();
37
37
  }
38
38
  };
package/dist/index.js CHANGED
@@ -1,16 +1,20 @@
1
1
  import { createCodexAppServerAgentHarness } from "./harness.js";
2
+ import { t as CODEX_PLUGINS_MARKETPLACE_NAME } from "./config-BZiM7rhH.js";
2
3
  import { buildCodexMediaUnderstandingProvider } from "./media-understanding-provider.js";
3
4
  import { buildCodexProvider } from "./provider.js";
4
- import { f as describeControlFailure, r as formatCodexDisplayText } from "./command-formatters-PiJcdUbu.js";
5
- import { n as handleCodexConversationInboundClaim, t as handleCodexConversationBindingResolved } from "./conversation-binding-CtHkMJfG.js";
5
+ import { i as formatCodexDisplayText, p as describeControlFailure, t as requestCodexAppServerJson } from "./request-CaW8DCAa.js";
6
+ import { n as handleCodexConversationInboundClaim, t as handleCodexConversationBindingResolved } from "./conversation-binding-BjanzgDh.js";
7
+ import { i as defaultCodexAppInventoryCache, t as ensureCodexPluginActivation } from "./plugin-activation-D0uXssSN.js";
6
8
  import { resolveLivePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
7
9
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
10
+ import os from "node:os";
8
11
  import fs from "node:fs/promises";
9
12
  import path from "node:path";
10
13
  import { resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
11
- import { MIGRATION_REASON_TARGET_EXISTS, createMigrationItem, createMigrationManualItem, summarizeMigrationItems } from "openclaw/plugin-sdk/migration";
12
- import { archiveMigrationItem, copyMigrationFileItem, writeMigrationReport } from "openclaw/plugin-sdk/migration-runtime";
13
- import os from "node:os";
14
+ import { pathExists } from "openclaw/plugin-sdk/security-runtime";
15
+ import { MIGRATION_REASON_TARGET_EXISTS, applyMigrationManualItem, createMigrationItem, createMigrationManualItem, hasMigrationConfigPatchConflict, markMigrationItemConflict, markMigrationItemError, markMigrationItemSkipped, readMigrationConfigPath, summarizeMigrationItems, writeMigrationConfigPath } from "openclaw/plugin-sdk/migration";
16
+ import { archiveMigrationItem, copyMigrationFileItem, withCachedMigrationConfigRuntime, writeMigrationReport } from "openclaw/plugin-sdk/migration-runtime";
17
+ import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
14
18
  //#region extensions/codex/src/commands.ts
15
19
  function createCodexCommand(options) {
16
20
  return {
@@ -24,7 +28,7 @@ function createCodexCommand(options) {
24
28
  };
25
29
  }
26
30
  async function handleCodexCommand(ctx, options = {}) {
27
- const { handleCodexSubcommand } = await import("./command-handlers-DiH-D13x.js");
31
+ const { handleCodexSubcommand } = await import("./command-handlers-De-V2-4K.js");
28
32
  try {
29
33
  return await handleCodexSubcommand(ctx, options);
30
34
  } catch (error) {
@@ -34,12 +38,7 @@ async function handleCodexCommand(ctx, options = {}) {
34
38
  //#endregion
35
39
  //#region extensions/codex/src/migration/helpers.ts
36
40
  async function exists(filePath) {
37
- try {
38
- await fs.access(filePath);
39
- return true;
40
- } catch {
41
- return false;
42
- }
41
+ return await pathExists(filePath);
43
42
  }
44
43
  async function isDirectory(filePath) {
45
44
  if (!filePath) return false;
@@ -62,12 +61,8 @@ function sanitizeName(value) {
62
61
  }
63
62
  async function readJsonObject(filePath) {
64
63
  if (!filePath) return {};
65
- try {
66
- const parsed = JSON.parse(await fs.readFile(filePath, "utf8"));
67
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
68
- } catch {
69
- return {};
70
- }
64
+ const { value: parsed } = await readJsonFileWithFallback(filePath, {});
65
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
71
66
  }
72
67
  //#endregion
73
68
  //#region extensions/codex/src/migration/source.ts
@@ -119,7 +114,10 @@ async function discoverPluginDirs(codexHome) {
119
114
  discovered.set(dir, {
120
115
  name,
121
116
  source: dir,
122
- manifestPath
117
+ manifestPath,
118
+ sourceKind: "cache",
119
+ migratable: false,
120
+ message: "Cached Codex plugin bundle found. Review manually unless the plugin is also installed in the source Codex app-server inventory."
123
121
  });
124
122
  return;
125
123
  }
@@ -131,6 +129,62 @@ async function discoverPluginDirs(codexHome) {
131
129
  await visit(root, 0);
132
130
  return [...discovered.values()].toSorted((a, b) => a.source.localeCompare(b.source));
133
131
  }
132
+ async function discoverInstalledCuratedPlugins(codexHome) {
133
+ try {
134
+ const marketplace = (await requestCodexAppServerJson({
135
+ method: "plugin/list",
136
+ requestParams: { cwds: [] },
137
+ timeoutMs: 6e4,
138
+ startOptions: {
139
+ transport: "stdio",
140
+ command: "codex",
141
+ commandSource: "config",
142
+ args: [
143
+ "app-server",
144
+ "--listen",
145
+ "stdio://"
146
+ ],
147
+ headers: {},
148
+ env: {
149
+ CODEX_HOME: codexHome,
150
+ HOME: path.dirname(codexHome)
151
+ }
152
+ }
153
+ })).marketplaces.find((entry) => entry.name === CODEX_PLUGINS_MARKETPLACE_NAME);
154
+ if (!marketplace) return {
155
+ plugins: [],
156
+ error: `Codex marketplace ${CODEX_PLUGINS_MARKETPLACE_NAME} was not found in source plugin inventory.`
157
+ };
158
+ return { plugins: marketplace.plugins.filter((plugin) => plugin.installed).map((plugin) => {
159
+ const pluginName = pluginNameFromSummary(plugin);
160
+ if (!pluginName) return;
161
+ return {
162
+ name: plugin.name,
163
+ pluginName,
164
+ marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
165
+ source: `${CODEX_PLUGINS_MARKETPLACE_NAME}/${pluginName}`,
166
+ sourceKind: "app-server",
167
+ migratable: true,
168
+ installed: plugin.installed,
169
+ enabled: plugin.enabled
170
+ };
171
+ }).filter((plugin) => plugin !== void 0).toSorted((a, b) => (a.pluginName ?? a.name).localeCompare(b.pluginName ?? b.name)) };
172
+ } catch (error) {
173
+ return {
174
+ plugins: [],
175
+ error: error instanceof Error ? error.message : String(error)
176
+ };
177
+ }
178
+ }
179
+ function pluginNameFromSummary(summary) {
180
+ const candidates = [summary.id, summary.name];
181
+ for (const candidate of candidates) {
182
+ const trimmed = candidate.trim();
183
+ if (!trimmed) continue;
184
+ const normalized = ((trimmed.endsWith(`@openai-curated`) ? trimmed.slice(0, -`@${CODEX_PLUGINS_MARKETPLACE_NAME}`.length) : trimmed).split("/").at(-1)?.trim())?.toLowerCase().replaceAll(/\s+/gu, "-");
185
+ if (normalized) return normalized;
186
+ }
187
+ }
134
188
  async function discoverCodexSource(input) {
135
189
  const codexHome = resolveHomePath(input?.trim() || defaultCodexHome());
136
190
  const codexSkillsDir = path.join(codexHome, "skills");
@@ -146,7 +200,13 @@ async function discoverCodexSource(input) {
146
200
  root: agentsSkillsDir,
147
201
  sourceLabel: "personal AgentSkill"
148
202
  });
149
- const plugins = await discoverPluginDirs(codexHome);
203
+ const sourcePluginDiscovery = await discoverInstalledCuratedPlugins(codexHome);
204
+ const sourcePluginNames = new Set(sourcePluginDiscovery.plugins.flatMap((plugin) => plugin.pluginName ? [plugin.pluginName] : []));
205
+ const cachedPlugins = (await discoverPluginDirs(codexHome)).filter((plugin) => {
206
+ const normalizedName = sanitizePluginName(plugin.name);
207
+ return !sourcePluginNames.has(normalizedName);
208
+ });
209
+ const plugins = [...sourcePluginDiscovery.plugins, ...cachedPlugins].toSorted((a, b) => a.source.localeCompare(b.source));
150
210
  const archivePaths = [];
151
211
  if (await exists(configPath)) archivePaths.push({
152
212
  id: "archive:config.toml",
@@ -173,12 +233,16 @@ async function discoverCodexSource(input) {
173
233
  ...await exists(hooksPath) ? { hooksPath } : {},
174
234
  skills,
175
235
  plugins,
236
+ ...sourcePluginDiscovery.error ? { pluginDiscoveryError: sourcePluginDiscovery.error } : {},
176
237
  archivePaths
177
238
  };
178
239
  }
179
240
  function hasCodexSource(source) {
180
241
  return source.confidence !== "low";
181
242
  }
243
+ function sanitizePluginName(value) {
244
+ return value.trim().toLowerCase().replaceAll(/\s+/gu, "-");
245
+ }
182
246
  //#endregion
183
247
  //#region extensions/codex/src/migration/targets.ts
184
248
  function resolveCodexMigrationTargets(ctx) {
@@ -193,6 +257,26 @@ function resolveCodexMigrationTargets(ctx) {
193
257
  }
194
258
  //#endregion
195
259
  //#region extensions/codex/src/migration/plan.ts
260
+ const CODEX_PLUGIN_CONFIG_ITEM_ID = "config:codex-plugins";
261
+ const CODEX_PLUGIN_CONFIG_PATH = [
262
+ "plugins",
263
+ "entries",
264
+ "codex"
265
+ ];
266
+ const CODEX_PLUGIN_ENABLED_PATH = [
267
+ "plugins",
268
+ "entries",
269
+ "codex",
270
+ "enabled"
271
+ ];
272
+ const CODEX_PLUGIN_NATIVE_CONFIG_PATH = [
273
+ "plugins",
274
+ "entries",
275
+ "codex",
276
+ "config",
277
+ "codexPlugins"
278
+ ];
279
+ const MIGRATION_REASON_PLUGIN_EXISTS = "plugin exists";
196
280
  function uniqueSkillName(skill, counts) {
197
281
  const base = sanitizeName(skill.name) || "codex-skill";
198
282
  if ((counts.get(base) ?? 0) <= 1) return base;
@@ -239,6 +323,137 @@ async function buildSkillItems(params) {
239
323
  }
240
324
  return items;
241
325
  }
326
+ function uniquePluginConfigKey(plugin, counts, usedCounts) {
327
+ const base = sanitizeName(plugin.pluginName ?? plugin.name) || "codex-plugin";
328
+ if ((counts.get(base) ?? 0) <= 1) return base;
329
+ const next = (usedCounts.get(base) ?? 0) + 1;
330
+ usedCounts.set(base, next);
331
+ return sanitizeName(`${base}-${next}`) || base;
332
+ }
333
+ function readExistingCodexPluginEntries(config) {
334
+ const entries = readMigrationConfigPath(config, [...CODEX_PLUGIN_NATIVE_CONFIG_PATH, "plugins"]);
335
+ return isRecord(entries) ? entries : {};
336
+ }
337
+ function hasExistingCodexPluginEntry(existingEntries, configKey, pluginName) {
338
+ if (existingEntries[configKey] !== void 0) return true;
339
+ return Object.values(existingEntries).some((entry) => {
340
+ if (!isRecord(entry)) return false;
341
+ return entry.pluginName === pluginName;
342
+ });
343
+ }
344
+ function buildPluginItems(ctx, plugins) {
345
+ const baseCounts = /* @__PURE__ */ new Map();
346
+ for (const plugin of plugins.filter((entry) => entry.migratable)) {
347
+ const base = sanitizeName(plugin.pluginName ?? plugin.name) || "codex-plugin";
348
+ baseCounts.set(base, (baseCounts.get(base) ?? 0) + 1);
349
+ }
350
+ const existingPluginEntries = readExistingCodexPluginEntries(ctx.config);
351
+ const usedCounts = /* @__PURE__ */ new Map();
352
+ let manualIndex = 0;
353
+ const items = [];
354
+ for (const plugin of plugins) {
355
+ if (plugin.migratable && plugin.marketplaceName === "openai-curated" && plugin.pluginName) {
356
+ const configKey = uniquePluginConfigKey(plugin, baseCounts, usedCounts);
357
+ const conflict = !ctx.overwrite && hasExistingCodexPluginEntry(existingPluginEntries, configKey, plugin.pluginName);
358
+ items.push(createMigrationItem({
359
+ id: `plugin:${configKey}`,
360
+ kind: "plugin",
361
+ action: "install",
362
+ status: conflict ? "conflict" : "planned",
363
+ reason: conflict ? MIGRATION_REASON_PLUGIN_EXISTS : void 0,
364
+ source: plugin.source,
365
+ target: `plugins.entries.codex.config.codexPlugins.plugins.${configKey}`,
366
+ message: `Install Codex plugin "${plugin.pluginName}" in the OpenClaw-managed Codex app-server runtime.`,
367
+ details: {
368
+ configKey,
369
+ marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
370
+ pluginName: plugin.pluginName,
371
+ sourceInstalled: plugin.installed === true,
372
+ sourceEnabled: plugin.enabled === true
373
+ }
374
+ }));
375
+ continue;
376
+ }
377
+ manualIndex += 1;
378
+ items.push(createMigrationManualItem({
379
+ id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${manualIndex}`,
380
+ source: plugin.source,
381
+ message: plugin.message ?? `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
382
+ recommendation: "Review the plugin bundle first, then install trusted compatible plugins with openclaw plugins install <path>."
383
+ }));
384
+ }
385
+ return items;
386
+ }
387
+ function readCodexPluginMigrationConfigEntry(item, enabled) {
388
+ const configKey = item.details?.configKey;
389
+ const marketplaceName = item.details?.marketplaceName;
390
+ const pluginName = item.details?.pluginName;
391
+ if (item.kind !== "plugin" || item.action !== "install" || typeof configKey !== "string" || marketplaceName !== "openai-curated" || typeof pluginName !== "string") return;
392
+ return {
393
+ configKey,
394
+ pluginName,
395
+ enabled
396
+ };
397
+ }
398
+ function readExistingAllowDestructiveActions(config) {
399
+ const value = readMigrationConfigPath(config, [...CODEX_PLUGIN_NATIVE_CONFIG_PATH, "allow_destructive_actions"]);
400
+ return typeof value === "boolean" ? value : void 0;
401
+ }
402
+ function isRecord(value) {
403
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
404
+ }
405
+ function buildCodexPluginsConfigValue(entries, params = {}) {
406
+ const plugins = Object.fromEntries(entries.toSorted((a, b) => a.configKey.localeCompare(b.configKey)).map((entry) => [entry.configKey, {
407
+ enabled: entry.enabled,
408
+ marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
409
+ pluginName: entry.pluginName
410
+ }]));
411
+ return {
412
+ enabled: true,
413
+ config: { codexPlugins: {
414
+ enabled: true,
415
+ allow_destructive_actions: params.config === void 0 ? false : readExistingAllowDestructiveActions(params.config) ?? false,
416
+ plugins
417
+ } }
418
+ };
419
+ }
420
+ function hasCodexPluginConfigConflict(config, value) {
421
+ const enabled = readMigrationConfigPath(config, CODEX_PLUGIN_ENABLED_PATH);
422
+ if (enabled !== void 0 && enabled !== true) return true;
423
+ const nativeConfig = value.config?.codexPlugins;
424
+ if (!isRecord(nativeConfig)) return hasMigrationConfigPatchConflict(config, CODEX_PLUGIN_NATIVE_CONFIG_PATH, nativeConfig);
425
+ const existingNativeConfig = readMigrationConfigPath(config, CODEX_PLUGIN_NATIVE_CONFIG_PATH);
426
+ if (existingNativeConfig === void 0) return false;
427
+ if (!isRecord(existingNativeConfig)) return true;
428
+ if (existingNativeConfig.enabled !== void 0 && existingNativeConfig.enabled !== true) return true;
429
+ const allowDestructiveActions = nativeConfig.allow_destructive_actions;
430
+ if (existingNativeConfig.allow_destructive_actions !== void 0 && existingNativeConfig.allow_destructive_actions !== allowDestructiveActions) return true;
431
+ const plugins = nativeConfig.plugins;
432
+ if (!isRecord(plugins)) return false;
433
+ return Object.entries(plugins).some(([configKey, plugin]) => {
434
+ if (!isRecord(plugin)) return existingNativeConfig[configKey] !== void 0;
435
+ return hasExistingCodexPluginEntry(readExistingCodexPluginEntries(config), configKey, typeof plugin.pluginName === "string" ? plugin.pluginName : configKey);
436
+ });
437
+ }
438
+ function buildPluginConfigItem(ctx, pluginItems) {
439
+ const entries = pluginItems.filter((item) => item.status === "planned").map((item) => readCodexPluginMigrationConfigEntry(item, true)).filter((entry) => entry !== void 0);
440
+ if (entries.length === 0) return;
441
+ const value = buildCodexPluginsConfigValue(entries, { config: ctx.config });
442
+ const conflict = !ctx.overwrite && hasCodexPluginConfigConflict(ctx.config, value);
443
+ return createMigrationItem({
444
+ id: CODEX_PLUGIN_CONFIG_ITEM_ID,
445
+ kind: "config",
446
+ action: "merge",
447
+ target: "plugins.entries.codex.config.codexPlugins",
448
+ status: conflict ? "conflict" : "planned",
449
+ reason: conflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
450
+ message: "Enable OpenClaw's Codex plugin integration and record migrated source-installed curated plugins.",
451
+ details: {
452
+ path: [...CODEX_PLUGIN_CONFIG_PATH],
453
+ value
454
+ }
455
+ });
456
+ }
242
457
  async function buildCodexMigrationPlan(ctx) {
243
458
  const source = await discoverCodexSource(ctx.source);
244
459
  if (!hasCodexSource(source)) throw new Error(`Codex state was not found at ${source.root}. Pass --from <path> if it lives elsewhere.`);
@@ -249,12 +464,10 @@ async function buildCodexMigrationPlan(ctx) {
249
464
  workspaceDir: targets.workspaceDir,
250
465
  overwrite: ctx.overwrite
251
466
  }));
252
- for (const [index, plugin] of source.plugins.entries()) items.push(createMigrationManualItem({
253
- id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${index + 1}`,
254
- source: plugin.source,
255
- message: `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
256
- recommendation: "Review the plugin bundle first, then install trusted compatible plugins with openclaw plugins install <path>."
257
- }));
467
+ const pluginItems = buildPluginItems(ctx, source.plugins);
468
+ items.push(...pluginItems);
469
+ const pluginConfigItem = buildPluginConfigItem(ctx, pluginItems);
470
+ if (pluginConfigItem) items.push(pluginConfigItem);
258
471
  for (const archivePath of source.archivePaths) items.push(createMigrationItem({
259
472
  id: archivePath.id,
260
473
  kind: "archive",
@@ -264,8 +477,9 @@ async function buildCodexMigrationPlan(ctx) {
264
477
  details: { archiveRelativePath: archivePath.relativePath }
265
478
  }));
266
479
  const warnings = [
267
- ...items.some((item) => item.status === "conflict") ? ["Conflicts were found. Re-run with --overwrite to replace conflicting skill targets after item-level backups."] : [],
268
- ...source.plugins.length > 0 ? ["Codex native plugins are reported for manual review only. OpenClaw does not auto-activate plugin bundles, hooks, MCP servers, or apps from another Codex home."] : [],
480
+ ...items.some((item) => item.status === "conflict") ? ["Conflicts were found. Re-run with --overwrite to replace conflicting migration targets after item-level backups."] : [],
481
+ ...source.plugins.length > 0 ? ["Codex source-installed openai-curated plugins are planned for native activation; cached plugin bundles remain manual-review only."] : [],
482
+ ...source.pluginDiscoveryError ? [`Codex app-server plugin inventory discovery failed: ${source.pluginDiscoveryError}. Cached plugin bundles, if any, are advisory only.`] : [],
269
483
  ...source.archivePaths.length > 0 ? ["Codex config and hook files are archive-only. They are preserved in the migration report, not loaded into OpenClaw automatically."] : []
270
484
  ];
271
485
  return {
@@ -275,7 +489,7 @@ async function buildCodexMigrationPlan(ctx) {
275
489
  summary: summarizeMigrationItems(items),
276
490
  items,
277
491
  warnings,
278
- nextSteps: ["Run openclaw doctor after applying the migration.", "Review skipped Codex plugin/config/hook items before installing or recreating them in OpenClaw."],
492
+ nextSteps: ["Run openclaw doctor after applying the migration.", "Review skipped or auth-required Codex plugin/config/hook items before exposing them in OpenClaw sessions."],
279
493
  metadata: {
280
494
  agentDir: targets.agentDir,
281
495
  codexHome: source.codexHome,
@@ -286,16 +500,33 @@ async function buildCodexMigrationPlan(ctx) {
286
500
  }
287
501
  //#endregion
288
502
  //#region extensions/codex/src/migration/apply.ts
503
+ const CODEX_PLUGIN_AUTH_REQUIRED_REASON = "auth_required";
504
+ const CODEX_PLUGIN_NOT_SELECTED_REASON = "not selected for migration";
505
+ var CodexPluginConfigConflictError = class extends Error {
506
+ constructor(reason) {
507
+ super(reason);
508
+ this.reason = reason;
509
+ this.name = "CodexPluginConfigConflictError";
510
+ }
511
+ };
289
512
  async function applyCodexMigrationPlan(params) {
290
513
  const plan = params.plan ?? await buildCodexMigrationPlan(params.ctx);
291
514
  const reportDir = params.ctx.reportDir ?? path.join(params.ctx.stateDir, "migration", "codex");
292
515
  const items = [];
516
+ const runtime = withCachedMigrationConfigRuntime(params.ctx.runtime ?? params.runtime, params.ctx.config);
517
+ const applyCtx = {
518
+ ...params.ctx,
519
+ runtime
520
+ };
293
521
  for (const item of plan.items) {
294
522
  if (item.status !== "planned") {
295
523
  items.push(item);
296
524
  continue;
297
525
  }
298
- if (item.action === "archive") items.push(await archiveMigrationItem(item, reportDir));
526
+ if (item.id === "config:codex-plugins") items.push(await applyCodexPluginConfigItem(applyCtx, item, items));
527
+ else if (item.kind === "plugin" && item.action === "install") items.push(await applyCodexPluginInstallItem(applyCtx, item));
528
+ else if (item.kind === "manual") items.push(applyMigrationManualItem(item));
529
+ else if (item.action === "archive") items.push(await archiveMigrationItem(item, reportDir));
299
530
  else items.push(await copyMigrationFileItem(item, reportDir, { overwrite: params.ctx.overwrite }));
300
531
  }
301
532
  const result = {
@@ -308,9 +539,150 @@ async function applyCodexMigrationPlan(params) {
308
539
  await writeMigrationReport(result, { title: "Codex Migration Report" });
309
540
  return result;
310
541
  }
542
+ async function applyCodexPluginInstallItem(ctx, item) {
543
+ const policy = readCodexPluginPolicy(item);
544
+ if (!policy) return {
545
+ ...markMigrationItemError(item, "invalid Codex plugin migration item"),
546
+ details: {
547
+ ...item.details,
548
+ code: "invalid_plugin_item"
549
+ }
550
+ };
551
+ try {
552
+ const result = await ensureCodexPluginActivation({
553
+ identity: policy,
554
+ installEvenIfActive: true,
555
+ request: async (method, requestParams) => await requestCodexAppServerJson({
556
+ method,
557
+ requestParams,
558
+ timeoutMs: 6e4,
559
+ config: ctx.config
560
+ })
561
+ });
562
+ defaultCodexAppInventoryCache.clear();
563
+ const baseDetails = {
564
+ ...item.details,
565
+ code: result.reason,
566
+ activationReason: result.reason,
567
+ ...codexPluginActivationReportState(result),
568
+ installAttempted: result.installAttempted,
569
+ diagnostics: result.diagnostics.map((diagnostic) => diagnostic.message)
570
+ };
571
+ if (result.ok) return {
572
+ ...item,
573
+ status: "migrated",
574
+ ...result.reason === "already_active" ? { reason: "already active" } : {},
575
+ details: baseDetails
576
+ };
577
+ if (result.reason === CODEX_PLUGIN_AUTH_REQUIRED_REASON) return {
578
+ ...item,
579
+ status: "skipped",
580
+ reason: CODEX_PLUGIN_AUTH_REQUIRED_REASON,
581
+ details: {
582
+ ...baseDetails,
583
+ appsNeedingAuth: sanitizeAppsNeedingAuth(result.installResponse?.appsNeedingAuth ?? [])
584
+ }
585
+ };
586
+ return {
587
+ ...item,
588
+ status: "error",
589
+ reason: result.reason,
590
+ details: baseDetails
591
+ };
592
+ } catch (error) {
593
+ return {
594
+ ...item,
595
+ status: "error",
596
+ reason: error instanceof Error ? error.message : String(error),
597
+ details: {
598
+ ...item.details,
599
+ code: "plugin_install_failed"
600
+ }
601
+ };
602
+ }
603
+ }
604
+ async function applyCodexPluginConfigItem(ctx, item, appliedItems) {
605
+ const entries = appliedItems.map(readAppliedPluginConfigEntry).filter((entry) => entry !== void 0);
606
+ if (entries.length === 0) return markMigrationItemSkipped(item, "no selected Codex plugins");
607
+ const configApi = ctx.runtime?.config;
608
+ if (!configApi?.current || !configApi.mutateConfigFile) return markMigrationItemError(item, "config runtime unavailable");
609
+ const currentConfig = configApi.current();
610
+ const value = buildCodexPluginsConfigValue(entries, { config: currentConfig });
611
+ if (!ctx.overwrite && hasCodexPluginConfigConflict(currentConfig, value)) return markMigrationItemConflict(item, MIGRATION_REASON_TARGET_EXISTS);
612
+ try {
613
+ await configApi.mutateConfigFile({
614
+ base: "runtime",
615
+ afterWrite: { mode: "auto" },
616
+ mutate(draft) {
617
+ if (!ctx.overwrite && hasCodexPluginConfigConflict(draft, value)) throw new CodexPluginConfigConflictError(MIGRATION_REASON_TARGET_EXISTS);
618
+ writeMigrationConfigPath(draft, CODEX_PLUGIN_CONFIG_PATH, value);
619
+ }
620
+ });
621
+ return {
622
+ ...item,
623
+ status: "migrated",
624
+ details: {
625
+ ...item.details,
626
+ path: [...CODEX_PLUGIN_CONFIG_PATH],
627
+ value
628
+ }
629
+ };
630
+ } catch (error) {
631
+ if (error instanceof CodexPluginConfigConflictError) return markMigrationItemConflict(item, error.reason);
632
+ return markMigrationItemError(item, error instanceof Error ? error.message : String(error));
633
+ }
634
+ }
635
+ function readAppliedPluginConfigEntry(item) {
636
+ if (item.status === "migrated") return readCodexPluginMigrationConfigEntry(item, true);
637
+ if (item.status === "skipped" && item.reason !== CODEX_PLUGIN_NOT_SELECTED_REASON && item.reason === CODEX_PLUGIN_AUTH_REQUIRED_REASON) return readCodexPluginMigrationConfigEntry(item, false);
638
+ }
639
+ function readCodexPluginPolicy(item) {
640
+ const configKey = item.details?.configKey;
641
+ const marketplaceName = item.details?.marketplaceName;
642
+ const pluginName = item.details?.pluginName;
643
+ if (typeof configKey !== "string" || marketplaceName !== "openai-curated" || typeof pluginName !== "string") return;
644
+ return {
645
+ configKey,
646
+ marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
647
+ pluginName,
648
+ enabled: true,
649
+ allowDestructiveActions: false
650
+ };
651
+ }
652
+ function codexPluginActivationReportState(result) {
653
+ switch (result.reason) {
654
+ case "already_active":
655
+ case "installed": return {
656
+ installed: true,
657
+ enabled: true
658
+ };
659
+ case "auth_required": return {
660
+ installed: true,
661
+ enabled: false
662
+ };
663
+ case "disabled":
664
+ case "marketplace_missing":
665
+ case "plugin_missing": return {
666
+ installed: false,
667
+ enabled: false
668
+ };
669
+ case "refresh_failed": return {
670
+ installed: true,
671
+ enabled: false
672
+ };
673
+ }
674
+ return result.reason;
675
+ }
676
+ function sanitizeAppsNeedingAuth(apps) {
677
+ return apps.map((app) => ({
678
+ id: app.id,
679
+ name: app.name,
680
+ needsAuth: app.needsAuth
681
+ }));
682
+ }
311
683
  //#endregion
312
684
  //#region extensions/codex/src/migration/provider.ts
313
- function buildCodexMigrationProvider() {
685
+ function buildCodexMigrationProvider(params = {}) {
314
686
  return {
315
687
  id: "codex",
316
688
  label: "Codex",
@@ -330,7 +702,8 @@ function buildCodexMigrationProvider() {
330
702
  async apply(ctx, plan) {
331
703
  return await applyCodexMigrationPlan({
332
704
  ctx,
333
- plan
705
+ plan,
706
+ runtime: params.runtime
334
707
  });
335
708
  }
336
709
  };
@@ -346,7 +719,7 @@ var codex_default = definePluginEntry({
346
719
  api.registerAgentHarness(createCodexAppServerAgentHarness({ pluginConfig: api.pluginConfig }));
347
720
  api.registerProvider(buildCodexProvider({ pluginConfig: api.pluginConfig }));
348
721
  api.registerMediaUnderstandingProvider(buildCodexMediaUnderstandingProvider({ pluginConfig: api.pluginConfig }));
349
- api.registerMigrationProvider(buildCodexMigrationProvider());
722
+ api.registerMigrationProvider(buildCodexMigrationProvider({ runtime: api.runtime }));
350
723
  api.registerCommand(createCodexCommand({ pluginConfig: api.pluginConfig }));
351
724
  api.on("inbound_claim", (event, ctx) => handleCodexConversationInboundClaim(event, ctx, { pluginConfig: resolveCurrentPluginConfig() }));
352
725
  api.onConversationBindingResolved?.(handleCodexConversationBindingResolved);
@@ -1,7 +1,7 @@
1
1
  import { CODEX_PROVIDER_ID, FALLBACK_CODEX_MODELS } from "./provider-catalog.js";
2
- import { i as resolveCodexAppServerRuntimeOptions } from "./config-ByrA30No.js";
3
- import { a as readCodexErrorNotification, c as readCodexTurnCompletedNotification, n as assertCodexThreadStartResponse, r as assertCodexTurnStartResponse } from "./protocol-validators-Dky2yV4W.js";
4
- import { i as readModelListResult } from "./models-CkowdYbm.js";
2
+ import { s as resolveCodexAppServerRuntimeOptions } from "./config-BZiM7rhH.js";
3
+ import { a as readCodexErrorNotification, c as readCodexTurnCompletedNotification, n as assertCodexThreadStartResponse, r as assertCodexTurnStartResponse } from "./protocol-validators-CbqWfY5M.js";
4
+ import { i as readModelListResult } from "./models-nfvRggjT.js";
5
5
  import { t as isJsonObject } from "./protocol-C9UWI98H.js";
6
6
  //#region extensions/codex/media-understanding-provider.ts
7
7
  const DEFAULT_CODEX_IMAGE_MODEL = FALLBACK_CODEX_MODELS.find((model) => model.inputModalities.includes("image"))?.id ?? FALLBACK_CODEX_MODELS[0]?.id;
@@ -37,7 +37,7 @@ async function describeCodexImages(req, options) {
37
37
  const appServer = resolveCodexAppServerRuntimeOptions({ pluginConfig: options.pluginConfig });
38
38
  const timeoutMs = Math.max(100, req.timeoutMs);
39
39
  const ownsClient = !options.clientFactory;
40
- const client = options.clientFactory ? await options.clientFactory(appServer.start, req.profile) : await import("./shared-client-Dfk3Enm-.js").then((n) => n.r).then(({ createIsolatedCodexAppServerClient }) => createIsolatedCodexAppServerClient({
40
+ const client = options.clientFactory ? await options.clientFactory(appServer.start, req.profile) : await import("./shared-client-pLCOzMaZ.js").then((n) => n.r).then(({ createIsolatedCodexAppServerClient }) => createIsolatedCodexAppServerClient({
41
41
  startOptions: appServer.start,
42
42
  timeoutMs,
43
43
  authProfileId: req.profile
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-DUslC3ob.js";
2
- import { o as readCodexModelListResponse } from "./protocol-validators-Dky2yV4W.js";
2
+ import { o as readCodexModelListResponse } from "./protocol-validators-CbqWfY5M.js";
3
3
  //#region extensions/codex/src/app-server/models.ts
4
4
  var models_exports = /* @__PURE__ */ __exportAll({
5
5
  listAllCodexAppServerModels: () => listAllCodexAppServerModels,
@@ -39,7 +39,7 @@ async function listAllCodexAppServerModels(options = {}) {
39
39
  async function withCodexAppServerModelClient(options, run) {
40
40
  const timeoutMs = options.timeoutMs ?? 2500;
41
41
  const useSharedClient = options.sharedClient !== false;
42
- const { createIsolatedCodexAppServerClient, getSharedCodexAppServerClient } = await import("./shared-client-Dfk3Enm-.js").then((n) => n.r);
42
+ const { createIsolatedCodexAppServerClient, getSharedCodexAppServerClient } = await import("./shared-client-pLCOzMaZ.js").then((n) => n.r);
43
43
  const client = useSharedClient ? await getSharedCodexAppServerClient({
44
44
  startOptions: options.startOptions,
45
45
  timeoutMs,