@hasna/skills 0.10.23 → 0.10.25

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/sdk/index.js CHANGED
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
32373
32373
  // package.json
32374
32374
  var package_default = {
32375
32375
  name: "@hasna/skills",
32376
- version: "0.10.23",
32376
+ version: "0.10.25",
32377
32377
  description: "Skills library for AI coding agents",
32378
32378
  type: "module",
32379
32379
  bin: {
@@ -38175,6 +38175,10 @@ async function connectCodexHookRpc(options) {
38175
38175
  }
38176
38176
 
38177
38177
  // src/lib/codex-native-skill-catalog.ts
38178
+ function codexPluginSourceIsConfigControlled(plugin) {
38179
+ const remoteRealms = ["openai-curated-remote", "created-by-me-remote", "workspace-directory", "workspace-shared-with-me", "workspace-shared-with-me-private", "workspace-shared-with-me-unlisted"];
38180
+ return Boolean(plugin.sourceType && ["local", "git", "npm"].includes(plugin.sourceType) && plugin.sourceSha256 && /^[a-f0-9]{64}$/.test(plugin.sourceSha256) && !plugin.remotePluginId && !remoteRealms.includes(plugin.id.slice(plugin.id.lastIndexOf("@") + 1)));
38181
+ }
38178
38182
  var MAX_SKILLS = 4096;
38179
38183
  var MAX_NAME_BYTES = 1024;
38180
38184
  var MAX_PATH_BYTES = 16384;
@@ -38186,6 +38190,15 @@ var object2 = (value) => value !== null && typeof value === "object" && !Array.i
38186
38190
  var scalar = (value, max) => typeof value === "string" && value.length > 0 && value.trim() === value && Buffer.byteLength(value, "utf8") <= max && !/[\u0000-\u001f\u007f]/u.test(value);
38187
38191
  var remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value);
38188
38192
  var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute5(value);
38193
+ function projectPluginSource(source) {
38194
+ if (!object2(source))
38195
+ return {};
38196
+ if (source.type === "remote")
38197
+ return { sourceType: "remote" };
38198
+ const optionalText = (value) => value === undefined || value === null || scalar(value, MAX_PATH_BYTES);
38199
+ const valid = source.type === "local" ? Object.keys(source).every((key) => ["type", "path"].includes(key)) && absolutePath2(source.path) : source.type === "git" ? Object.keys(source).every((key) => ["type", "url", "path", "refName", "sha"].includes(key)) && scalar(source.url, MAX_PATH_BYTES) && [source.path, source.refName, source.sha].every(optionalText) : source.type === "npm" ? Object.keys(source).every((key) => ["type", "package", "version", "registry"].includes(key)) && scalar(source.package, MAX_NAME_BYTES) && [source.version, source.registry].every(optionalText) : false;
38200
+ return valid ? { sourceType: source.type, sourceSha256: hashNativeJsonControls(JSON.stringify(source)), ...source.type === "local" ? { sourcePath: source.path } : {} } : {};
38201
+ }
38189
38202
  function projectCodexNativeSkillCatalog(response, cwd) {
38190
38203
  if (!absolutePath2(cwd) || !object2(response) || !Array.isArray(response.data) || response.data.length !== 1)
38191
38204
  refuse2();
@@ -38215,7 +38228,10 @@ function projectCodexInstalledPlugins(response) {
38215
38228
  plugins.push({
38216
38229
  ...value,
38217
38230
  marketplace: marketplace.name,
38218
- sourceType: object2(value.source) && value.source.type === "remote" ? "remote" : undefined
38231
+ sourceType: undefined,
38232
+ sourceSha256: undefined,
38233
+ sourcePath: undefined,
38234
+ ...projectPluginSource(value.source)
38219
38235
  });
38220
38236
  }
38221
38237
  }
@@ -38226,7 +38242,7 @@ function projectCodexInstalledPluginEntries(value) {
38226
38242
  refuse2();
38227
38243
  const ids = new Set;
38228
38244
  return value.map((item) => {
38229
- if (!object2(item) || !scalar(item.id, MAX_PLUGIN_ID_BYTES) || !scalar(item.name, MAX_NAME_BYTES) || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean" || item.localVersion !== null && !scalar(item.localVersion, MAX_NAME_BYTES) || item.remotePluginId !== undefined && item.remotePluginId !== null && !remotePluginIdentifier(item.remotePluginId) || item.sourceType !== undefined && item.sourceType !== "remote")
38245
+ if (!object2(item) || !scalar(item.id, MAX_PLUGIN_ID_BYTES) || !scalar(item.name, MAX_NAME_BYTES) || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean" || item.localVersion !== null && !scalar(item.localVersion, MAX_NAME_BYTES) || item.remotePluginId !== undefined && item.remotePluginId !== null && !remotePluginIdentifier(item.remotePluginId) || item.sourceType !== undefined && !["remote", "local", "git", "npm"].includes(item.sourceType) || (["local", "git", "npm"].includes(item.sourceType) ? typeof item.sourceSha256 !== "string" || !/^[a-f0-9]{64}$/.test(item.sourceSha256) : item.sourceSha256 !== undefined) || (item.sourceType === "local" ? !absolutePath2(item.sourcePath) || normalize(item.sourcePath) !== item.sourcePath || item.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: item.sourcePath })) : item.sourcePath !== undefined))
38230
38246
  refuse2();
38231
38247
  const at = item.id.lastIndexOf("@");
38232
38248
  if (at < 1 || item.id.slice(0, at) !== item.name || at === item.id.length - 1 || ids.has(item.id))
@@ -38238,7 +38254,9 @@ function projectCodexInstalledPluginEntries(value) {
38238
38254
  installed: item.installed,
38239
38255
  enabled: item.enabled,
38240
38256
  localVersion: item.localVersion ?? null,
38241
- ...item.sourceType === "remote" ? { sourceType: "remote" } : {},
38257
+ ...item.sourceType === undefined ? {} : { sourceType: item.sourceType },
38258
+ ...item.sourceSha256 === undefined ? {} : { sourceSha256: item.sourceSha256 },
38259
+ ...item.sourcePath === undefined ? {} : { sourcePath: item.sourcePath },
38242
38260
  ...item.remotePluginId == null ? {} : { remotePluginId: item.remotePluginId }
38243
38261
  };
38244
38262
  });
@@ -38359,27 +38377,62 @@ function mcpControls(path, read) {
38359
38377
  const object3 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
38360
38378
  const textField = (v, empty = false) => typeof v === "string" && (empty || Boolean(v.trim())) && Buffer.byteLength(v) <= 16384 && !/[\x00-\x1f\x7f]/.test(v);
38361
38379
  const envName = (v) => typeof v === "string" && /^[A-Za-z_][A-Za-z0-9_]{0,255}$/.test(v);
38380
+ const optional = (v, valid) => v === undefined || v === null || valid(v);
38381
+ const stringList = (v) => Array.isArray(v) && v.length <= 256 && v.every((item) => textField(item));
38382
+ const approval = (v) => ["auto", "prompt", "writes", "approve"].includes(v);
38383
+ const duration = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0;
38384
+ const positiveInteger = (v) => Number.isSafeInteger(v) && v > 0;
38385
+ const fields = [
38386
+ "command",
38387
+ "args",
38388
+ "cwd",
38389
+ "env",
38390
+ "env_vars",
38391
+ "enabled",
38392
+ "required",
38393
+ "environment_id",
38394
+ "startup_readiness",
38395
+ "supports_parallel_tool_calls",
38396
+ "tool_input_schema_max_bytes",
38397
+ "startup_timeout_sec",
38398
+ "startup_timeout_ms",
38399
+ "tool_timeout_sec",
38400
+ "default_tools_approval_mode",
38401
+ "enabled_tools",
38402
+ "disabled_tools",
38403
+ "omit_tools_from",
38404
+ "tools",
38405
+ "name"
38406
+ ];
38362
38407
  if (!object3(value) || Object.keys(value).length !== 1 || !object3(value.mcpServers))
38363
38408
  refuse3();
38364
38409
  const servers = Object.entries(value.mcpServers);
38365
38410
  if (!servers.length || servers.length > 256)
38366
38411
  refuse3();
38367
38412
  for (const [name, server] of servers) {
38368
- if (!identifier3(name) || !object3(server) || !Object.keys(server).every((key) => ["command", "args", "cwd", "env_vars"].includes(key)) || !textField(server.command) || server.args !== undefined && (!Array.isArray(server.args) || server.args.length > 256 || !server.args.every((arg) => textField(arg, true))) || server.cwd !== undefined && !textField(server.cwd))
38413
+ if (!identifier3(name) || !object3(server) || !Object.keys(server).every((key) => fields.includes(key)) || !textField(server.command) || !optional(server.args, (v) => Array.isArray(v) && v.length <= 256 && v.every((arg) => textField(arg, true))) || !optional(server.cwd, textField) || !optional(server.env, (v) => object3(v) && Object.keys(v).length <= 256 && Object.entries(v).every(([key, value2]) => envName(key) && textField(value2, true))) || !["enabled", "required", "supports_parallel_tool_calls"].every((key) => optional(server[key], (v) => typeof v === "boolean")) || !optional(server.environment_id, (v) => v === "local") || !optional(server.startup_readiness, (v) => ["connection", "catalog"].includes(v)) || !optional(server.tool_input_schema_max_bytes, positiveInteger) || !["startup_timeout_sec", "tool_timeout_sec"].every((key) => optional(server[key], duration)) || !optional(server.startup_timeout_ms, (v) => Number.isSafeInteger(v) && v >= 0) || !optional(server.default_tools_approval_mode, approval) || !["enabled_tools", "disabled_tools"].every((key) => optional(server[key], stringList)) || !optional(server.omit_tools_from, (v) => Array.isArray(v) && v.length <= 256 && v.every((item) => ["code_mode", "deferred", "direct"].includes(item))) || !optional(server.name, textField))
38369
38414
  refuse3();
38370
- if (server.env_vars !== undefined) {
38415
+ if (server.env_vars !== undefined && server.env_vars !== null) {
38371
38416
  if (!Array.isArray(server.env_vars) || server.env_vars.length > 256)
38372
38417
  refuse3();
38373
38418
  const names = server.env_vars.map((entry) => {
38374
38419
  if (envName(entry))
38375
38420
  return entry;
38376
- if (!object3(entry) || !Object.keys(entry).every((key) => ["name", "source"].includes(key)) || !envName(entry.name) || entry.source !== undefined && entry.source !== "local")
38421
+ if (!object3(entry) || !Object.keys(entry).every((key) => ["name", "source"].includes(key)) || !envName(entry.name) || !optional(entry.source, (v) => v === "local"))
38377
38422
  refuse3();
38378
38423
  return entry.name;
38379
38424
  });
38380
38425
  if (new Set(names).size !== names.length)
38381
38426
  refuse3();
38382
38427
  }
38428
+ if (server.tools !== undefined && server.tools !== null) {
38429
+ if (!object3(server.tools) || Object.keys(server.tools).length > 256)
38430
+ refuse3();
38431
+ for (const [tool, config] of Object.entries(server.tools)) {
38432
+ if (!textField(tool) || !object3(config) || !Object.keys(config).every((key) => ["approval_mode", "output_token_limit"].includes(key)) || !optional(config.approval_mode, approval) || !optional(config.output_token_limit, positiveInteger))
38433
+ refuse3();
38434
+ }
38435
+ }
38383
38436
  }
38384
38437
  return sha2562;
38385
38438
  }
@@ -38416,7 +38469,7 @@ function rootControls(root, read) {
38416
38469
  const appSha256 = present ? appControls(app, read) : undefined;
38417
38470
  return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
38418
38471
  }
38419
- function identity(document, cache, read) {
38472
+ function documentIdentity(document, cache, read) {
38420
38473
  if (!document.startsWith(cache + sep2) || !document.endsWith(sep2 + "SKILL.md"))
38421
38474
  refuse3();
38422
38475
  const text = read(document);
@@ -38444,11 +38497,33 @@ function identity(document, cache, read) {
38444
38497
  const path = join10(root, ".codex-plugin", "plugin.json");
38445
38498
  if (!lstatSync6(path, { throwIfNoEntry: false }))
38446
38499
  continue;
38447
- const controls = rootControls(root, read);
38448
- return { name: `${controls.namespace}:${front.name}`, pluginParent: dirname5(root), ...controls };
38500
+ const manifestFile = lstatSync6(path);
38501
+ if (!lstatSync6(join10(root, ".codex-plugin")).isDirectory() || !manifestFile.isFile() || manifestFile.size > 1024 * 1024)
38502
+ refuse3();
38503
+ let manifest;
38504
+ try {
38505
+ const text2 = read(path);
38506
+ hashNativeJsonControls(text2);
38507
+ manifest = JSON.parse(text2);
38508
+ } catch {
38509
+ refuse3();
38510
+ }
38511
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier3(manifest.name))
38512
+ refuse3();
38513
+ const declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
38514
+ if (declaredVersion === null || (declaredVersion ? declaredVersion !== basename3(root) : basename3(root) !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
38515
+ refuse3();
38516
+ return { name: `${manifest.name}:${front.name}`, namespace: manifest.name, pluginParent: dirname5(root), root };
38449
38517
  }
38450
38518
  refuse3();
38451
38519
  }
38520
+ function identity(document, cache, read) {
38521
+ const parsed = documentIdentity(document, cache, read);
38522
+ const controls = rootControls(parsed.root, read);
38523
+ if (controls.namespace !== parsed.namespace)
38524
+ refuse3();
38525
+ return { name: parsed.name, pluginParent: parsed.pluginParent, ...controls };
38526
+ }
38452
38527
  function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read) {
38453
38528
  if (!Array.isArray(controls) || controls.length > 4096)
38454
38529
  return false;
@@ -38486,11 +38561,13 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read) {
38486
38561
  return false;
38487
38562
  }
38488
38563
  }
38489
- function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read, rules) {
38564
+ function reviewCodexPluginControls(catalog, documents, cache, cwd, read, rules, pluginSettings) {
38490
38565
  if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd)
38491
38566
  refuse3();
38492
38567
  const skills = projectCodexNativeSkillCatalog({ data: [{ cwd, errors: [], skills: catalog.skills }] }, cwd);
38493
- const allowed = new Set(documents), result = [];
38568
+ const allowed = new Set(documents), result = [], inertDocuments = new Set;
38569
+ const inactivePlugins = new Map;
38570
+ const sourceInputs = new Map;
38494
38571
  let installedPlugins;
38495
38572
  try {
38496
38573
  installedPlugins = projectCodexInstalledPluginEntries(catalog.plugins ?? []);
@@ -38501,25 +38578,48 @@ function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read, ru
38501
38578
  for (const document of candidates) {
38502
38579
  if (!document.startsWith(cache + sep2))
38503
38580
  continue;
38504
- const parsed = identity(document, cache, read);
38505
- if (parsed.name === "skills-cli" || parsed.name.endsWith(":skills-cli"))
38581
+ const located = documentIdentity(document, cache, read);
38582
+ if (located.name === "skills-cli" || located.name.endsWith(":skills-cli"))
38506
38583
  refuse3();
38507
- const cacheParts = relative3(cache, parsed.pluginParent).split(sep2);
38584
+ const cacheParts = relative3(cache, located.pluginParent).split(sep2);
38508
38585
  if (cacheParts.length !== 2 || cacheParts.some((part) => !identifier3(part)))
38509
38586
  refuse3();
38510
38587
  const [marketplace, pluginName] = cacheParts;
38511
- if (pluginName !== parsed.namespace)
38588
+ if (pluginName !== located.namespace)
38512
38589
  refuse3();
38513
38590
  const expectedPluginId = `${pluginName}@${marketplace}`;
38514
38591
  const listed = skills.filter((skill) => skill.path === document);
38515
38592
  if (listed.length > 1)
38516
38593
  refuse3();
38594
+ const installed = installedPlugins.filter((plugin) => plugin.id === expectedPluginId && plugin.name === pluginName);
38595
+ if (catalog.version === "codex-cli 0.160.0" && installed.length === 1 && installed[0].installed && installed[0].localVersion === basename3(located.root) && installed[0].sourceType === "local" && codexPluginSourceIsConfigControlled(installed[0])) {
38596
+ const plugin = installed[0], input = { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceRoot: plugin.sourcePath, sourceSha256: plugin.sourceSha256 };
38597
+ if (input.sourceRoot !== located.root && !input.sourceRoot.startsWith(cache + sep2) && (lstatSync6(input.sourceRoot, { throwIfNoEntry: false }) || documents.some((path) => path.startsWith(input.sourceRoot + sep2)))) {
38598
+ if (skills.some((skill) => skill.path === input.sourceRoot || skill.path.startsWith(input.sourceRoot + sep2)) || !sourceInputUnchanged(input, cache, read))
38599
+ refuse3();
38600
+ sourceInputs.set(expectedPluginId, input);
38601
+ }
38602
+ }
38603
+ const settings = pluginSettings && typeof pluginSettings === "object" && !Array.isArray(pluginSettings) ? pluginSettings : undefined;
38604
+ if (catalog.version === "codex-cli 0.160.0" && !listed.length && !skills.some((skill) => skill.pluginId === expectedPluginId) && installed.length === 1 && installed[0].installed && !installed[0].enabled && codexPluginSourceIsConfigControlled(installed[0]) && settings && Object.hasOwn(settings, expectedPluginId) && settings[expectedPluginId]?.enabled === false) {
38605
+ if (!Array.isArray(rules))
38606
+ refuse3();
38607
+ const exactPathDeny = rules.some((rule) => rule?.path === document && rule?.enabled === false);
38608
+ if (!lstatSync6(document).isFile() || realpathSync5(document) !== document || realpathSync5(located.root) !== located.root || realpathSync5(located.pluginParent) !== located.pluginParent || realpathSync5(join10(located.root, ".codex-plugin/plugin.json")) !== join10(located.root, ".codex-plugin/plugin.json") || !exactPathDeny || rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === located.name && rule?.enabled !== false) || !isCodexNativeSkillDisabled({ name: located.name, path: document, pluginId: expectedPluginId, enabled: true }, rules))
38609
+ refuse3();
38610
+ inertDocuments.add(document);
38611
+ const plugin = installed[0];
38612
+ inactivePlugins.set(expectedPluginId, { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceType: plugin.sourceType, sourceSha256: plugin.sourceSha256 });
38613
+ continue;
38614
+ }
38615
+ const parsed = { name: located.name, pluginParent: located.pluginParent, ...rootControls(located.root, read) };
38616
+ if (parsed.namespace !== located.namespace)
38617
+ refuse3();
38517
38618
  let remotePluginId;
38518
38619
  if (listed.length === 1) {
38519
38620
  if (listed[0].name !== parsed.name || listed[0].pluginId !== expectedPluginId)
38520
38621
  refuse3();
38521
38622
  } else {
38522
- const installed = installedPlugins.filter((plugin2) => plugin2.id === expectedPluginId && plugin2.name === pluginName);
38523
38623
  if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled)
38524
38624
  refuse3();
38525
38625
  const plugin = installed[0];
@@ -38537,9 +38637,48 @@ function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read, ru
38537
38637
  }
38538
38638
  result.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
38539
38639
  }
38540
- if (!result.length && documents.some((document) => document.startsWith(cache + sep2)) || new Set(result.map((item) => item.name)).size !== result.length)
38640
+ if (!result.length && documents.some((document) => document.startsWith(cache + sep2) && !inertDocuments.has(document)) || new Set(result.map((item) => item.name)).size !== result.length)
38541
38641
  refuse3();
38542
- return result;
38642
+ return { skills: result, inactivePlugins: [...inactivePlugins.values()], sourceInputs: [...sourceInputs.values()] };
38643
+ }
38644
+ function sourceInputUnchanged(input, cache, read) {
38645
+ try {
38646
+ const parts = relative3(cache, input.pluginParent).split(sep2);
38647
+ if (parts.length !== 2 || parts.some((part) => !identifier3(part)) || parts[1] !== input.namespace || input.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ id: input.pluginId, sourceType: "local", sourceSha256: input.sourceSha256 }) || input.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: input.sourceRoot })) || resolve4(input.sourceRoot) !== input.sourceRoot || !lstatSync6(input.sourceRoot).isDirectory() || realpathSync5(input.sourceRoot) !== input.sourceRoot || input.sourceRoot === cache || input.sourceRoot.startsWith(cache + sep2) || cache.startsWith(input.sourceRoot + sep2))
38648
+ return false;
38649
+ const path = join10(input.sourceRoot, ".codex-plugin/plugin.json"), stat = lstatSync6(path);
38650
+ if (!stat.isFile() || stat.size > 1024 * 1024 || realpathSync5(path) !== path)
38651
+ return false;
38652
+ const text = read(path);
38653
+ hashNativeJsonControls(text);
38654
+ const manifest = JSON.parse(text);
38655
+ return Boolean(manifest && typeof manifest === "object" && !Array.isArray(manifest) && manifest.name === input.namespace);
38656
+ } catch {
38657
+ return false;
38658
+ }
38659
+ }
38660
+ function reviewedCodexPluginSourceRoots(cache, inputs, read) {
38661
+ if (!Array.isArray(inputs) || inputs.length > 4096 || new Set(inputs.map((input) => input.pluginId)).size !== inputs.length || new Set(inputs.map((input) => input.sourceRoot)).size !== inputs.length || inputs.some((input) => !sourceInputUnchanged(input, cache, read)))
38662
+ refuse3();
38663
+ const roots = inputs.map((input) => input.sourceRoot);
38664
+ if (roots.some((root) => roots.some((other) => root !== other && root.startsWith(other + sep2))))
38665
+ refuse3();
38666
+ return roots;
38667
+ }
38668
+ function isReviewedCodexPluginInactive(document, cache, controls, pluginSettings, rules, read) {
38669
+ if (!Array.isArray(controls) || controls.length > 4096 || !pluginSettings || typeof pluginSettings !== "object" || Array.isArray(pluginSettings) || !Array.isArray(rules))
38670
+ return false;
38671
+ try {
38672
+ const parsed = documentIdentity(document, cache, read), matches = controls.filter((control2) => control2.pluginParent === parsed.pluginParent && control2.namespace === parsed.namespace);
38673
+ if (matches.length !== 1 || !lstatSync6(document).isFile() || realpathSync5(document) !== document || realpathSync5(parsed.root) !== parsed.root || realpathSync5(parsed.pluginParent) !== parsed.pluginParent || realpathSync5(join10(parsed.root, ".codex-plugin/plugin.json")) !== join10(parsed.root, ".codex-plugin/plugin.json"))
38674
+ return false;
38675
+ const control = matches[0], parts = relative3(cache, parsed.pluginParent).split(sep2);
38676
+ if (parts.length !== 2 || parts.some((part) => !identifier3(part)) || parts[1] !== parsed.namespace || control.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ ...control, id: control.pluginId }) || !Object.hasOwn(pluginSettings, control.pluginId) || pluginSettings[control.pluginId]?.enabled !== false)
38677
+ return false;
38678
+ return !rules.some((rule) => (rule?.path === document || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule?.enabled !== false);
38679
+ } catch {
38680
+ return false;
38681
+ }
38543
38682
  }
38544
38683
  function isReviewedCodexPluginSkillDisabled(document, cache, controls, rules, read) {
38545
38684
  if (!document.startsWith(cache + sep2) || !Array.isArray(controls) || controls.length > 4096)
@@ -38915,6 +39054,18 @@ function assertAgentPolicyCollections(policy) {
38915
39054
  if (control.appSha256 !== undefined)
38916
39055
  requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
38917
39056
  }
39057
+ if (bridge.codexInactivePlugins !== undefined) {
39058
+ const controls = array(bridge.codexInactivePlugins, 4096), ids = new Set;
39059
+ requireBound(controls.length === 0 || object3(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === "codex-cli 0.160.0" && typeof bridge.codexPluginSkillReview.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(bridge.codexPluginSkillReview.catalogSha256));
39060
+ for (const control of controls) {
39061
+ requireBound(object3(control) && Object.keys(control).every((key) => ["pluginId", "namespace", "pluginParent", "sourceType", "sourceSha256"].includes(key)));
39062
+ text(control.pluginId, 1024);
39063
+ text(control.namespace, 64);
39064
+ text(control.pluginParent);
39065
+ requireBound(!ids.has(control.pluginId) && isAbsolute6(control.pluginParent) && resolve5(control.pluginParent) === control.pluginParent && ["local", "git", "npm"].includes(control.sourceType) && typeof control.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(control.sourceSha256));
39066
+ ids.add(control.pluginId);
39067
+ }
39068
+ }
38918
39069
  if (bridge.disabledBuiltins !== undefined)
38919
39070
  for (const builtin of array(bridge.disabledBuiltins, AGENT_POLICY_LIMITS.builtinNames)) {
38920
39071
  requireBound(object3(builtin));
@@ -38927,6 +39078,25 @@ function assertAgentPolicyCollections(policy) {
38927
39078
  text(agent, 128);
38928
39079
  requireBound(object3(value));
38929
39080
  text(value.agent, 128);
39081
+ if (value.codexInstallationInputs !== undefined) {
39082
+ const proof = record2(value.codexInstallationInputs, 4), inputs = array(proof.plugins, 4096), ids = new Set;
39083
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && proof.version === "codex-cli 0.160.0" && typeof proof.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(proof.catalogSha256) && inputs.length > 0 && object3(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === proof.version && bridge.codexPluginSkillReview.catalogSha256 === proof.catalogSha256);
39084
+ for (const input of inputs) {
39085
+ requireBound(object3(input) && Object.keys(input).every((key) => ["pluginId", "namespace", "pluginParent", "sourceRoot", "sourceSha256"].includes(key)));
39086
+ text(input.pluginId, 1024);
39087
+ text(input.namespace, 64);
39088
+ text(input.pluginParent);
39089
+ text(input.sourceRoot);
39090
+ requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) => isAbsolute6(path) && resolve5(path) === path) && typeof input.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(input.sourceSha256));
39091
+ ids.add(input.pluginId);
39092
+ }
39093
+ if (proof.directories !== undefined)
39094
+ for (const directory of array(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
39095
+ requireBound(object3(directory));
39096
+ text(directory.path);
39097
+ requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
39098
+ }
39099
+ }
38930
39100
  for (const root of array(value.roots, AGENT_POLICY_LIMITS.discoveryRoots))
38931
39101
  text(root);
38932
39102
  for (const source of array(value.sources, AGENT_POLICY_LIMITS.discoverySources)) {
@@ -39369,7 +39539,7 @@ function safe(path) {
39369
39539
  function same(before, after) {
39370
39540
  return Boolean(after && after.isDirectory() && before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs);
39371
39541
  }
39372
- function capture(path, budget) {
39542
+ function capture(path, budget, excludedRoots) {
39373
39543
  safe(path);
39374
39544
  const root = lstatSync10(path, { throwIfNoEntry: false });
39375
39545
  if (!root)
@@ -39398,7 +39568,7 @@ function capture(path, budget) {
39398
39568
  budget.bytes += Buffer.byteLength(row);
39399
39569
  need4(budget.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
39400
39570
  hash2.update(row);
39401
- if (stat.isDirectory())
39571
+ if (stat.isDirectory() && !excludedRoots.includes(child))
39402
39572
  walk2(child, childRelative, stat, depth + 1);
39403
39573
  }
39404
39574
  need4(same(before, lstatSync10(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
@@ -39407,16 +39577,18 @@ function capture(path, budget) {
39407
39577
  safe(path);
39408
39578
  return { path, sha256: hash2.digest("hex") };
39409
39579
  }
39410
- function captureDiscoveryDirectories(paths) {
39580
+ function captureDiscoveryDirectories(paths, excludedRoots = []) {
39411
39581
  need4(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
39412
39582
  const budget = { entries: 0, bytes: 0 };
39413
- return paths.map((path) => capture(path, budget));
39583
+ for (const root of excludedRoots)
39584
+ safe(root);
39585
+ return paths.map((path) => capture(path, budget, excludedRoots));
39414
39586
  }
39415
- function verifyDiscoveryDirectories(directories) {
39587
+ function verifyDiscoveryDirectories(directories, excludedRoots = []) {
39416
39588
  need4(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
39417
39589
  for (const directory of directories)
39418
39590
  need4(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
39419
- const current = captureDiscoveryDirectories(directories.map((directory) => directory.path));
39591
+ const current = captureDiscoveryDirectories(directories.map((directory) => directory.path), excludedRoots);
39420
39592
  for (let i = 0;i < directories.length; i++)
39421
39593
  need4(current[i].sha256 === directories[i].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`);
39422
39594
  }
@@ -41503,8 +41675,16 @@ function verifyAgentDiscovery(binding, codexRecovery) {
41503
41675
  throw new Error("Invalid native discovery binding");
41504
41676
  if (binding.agent === "hermes" && !binding.directories?.length)
41505
41677
  throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
41506
- if (binding.directories !== undefined)
41507
- verifyDiscoveryDirectories(binding.directories);
41678
+ const installationRoots = codexInstallationRoots(binding);
41679
+ const installationInput = (path) => installationRoots.some((root2) => path === root2 || path.startsWith(root2 + sep4));
41680
+ if (binding.directories !== undefined) {
41681
+ const projected2 = binding.codexInstallationInputs?.directories ?? [];
41682
+ const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root2) => root2.startsWith(directory.path + sep4)));
41683
+ if (projected2.length !== ancestors2.length || projected2.some((directory) => !ancestors2.some((original) => original.path === directory.path)))
41684
+ throw new Error("Missing installation input directory projection");
41685
+ verifyDiscoveryDirectories(binding.directories.filter((directory) => !installationInput(directory.path) && !ancestors2.includes(directory)));
41686
+ verifyDiscoveryDirectories(projected2, installationRoots);
41687
+ }
41508
41688
  const budget = discoveryByteBudget();
41509
41689
  for (const source of binding.sources) {
41510
41690
  assertMarketplaceBinding(binding, source);
@@ -41514,6 +41694,10 @@ function verifyAgentDiscovery(binding, codexRecovery) {
41514
41694
  throw new Error("Invalid native discovery projection");
41515
41695
  if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
41516
41696
  throw new Error("Invalid native discovery digest");
41697
+ if (installationInput(source.path)) {
41698
+ safe4(source.path);
41699
+ continue;
41700
+ }
41517
41701
  const current = projected(source, undefined, budget);
41518
41702
  if (current !== source.sha256 && !verifiesCodexHookDiscoverySource(binding, source, current, codexRecovery))
41519
41703
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
@@ -41521,6 +41705,27 @@ function verifyAgentDiscovery(binding, codexRecovery) {
41521
41705
  for (const root2 of binding.roots)
41522
41706
  safe4(root2);
41523
41707
  }
41708
+ function codexInstallationRoots(binding) {
41709
+ const proof = binding.codexInstallationInputs;
41710
+ if (proof === undefined)
41711
+ return [];
41712
+ if (binding.agent !== "codex" || binding.method !== "reviewed" || proof.version !== "codex-cli 0.160.0" || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
41713
+ throw new Error("Invalid native installation input proof");
41714
+ const cache = dirname14(dirname14(proof.plugins[0].pluginParent));
41715
+ const config = join21(dirname14(dirname14(cache)), "config.toml"), home = dirname14(dirname14(dirname14(cache)));
41716
+ const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
41717
+ const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join21(home, path));
41718
+ if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep4) || input.sourceRoot.startsWith(root2 + sep4)) || binding.sources.some((source) => basename7(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep4)))))
41719
+ throw new Error("Native installation input overlaps configuration or loading roots");
41720
+ if (proof.plugins.some((input) => dirname14(dirname14(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join21(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
41721
+ throw new Error("Missing native installation input source witness");
41722
+ return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
41723
+ const text2 = read2(path);
41724
+ if (text2 === null)
41725
+ throw new Error("Missing native installation input");
41726
+ return text2;
41727
+ });
41728
+ }
41524
41729
  function assertMarketplaceBinding(binding, source) {
41525
41730
  if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename7(source.path) !== "config.toml"))
41526
41731
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
@@ -41531,8 +41736,11 @@ function assertMarketplaceBinding(binding, source) {
41531
41736
  }
41532
41737
  function rebindAgentDiscovery(binding, changes) {
41533
41738
  const budget = discoveryByteBudget();
41739
+ const installationRoots = codexInstallationRoots(binding);
41534
41740
  return { ...binding, sources: binding.sources.map((source) => {
41535
41741
  assertMarketplaceBinding(binding, source);
41742
+ if (!changes.has(source.path) && installationRoots.some((root2) => source.path.startsWith(root2 + sep4)))
41743
+ return source;
41536
41744
  const sha2562 = projected(source, changes, budget);
41537
41745
  if (!changes.has(source.path) && sha2562 !== source.sha256)
41538
41746
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
@@ -42140,10 +42348,75 @@ function disableCodexBundledSkills(text2) {
42140
42348
  // src/lib/agent-integration.ts
42141
42349
  var sha4 = (data) => createHash18("sha256").update(data).digest("hex");
42142
42350
  var ROOTS = NATIVE_SKILL_ROOTS;
42351
+ function hasNoDarwinAcl(fd) {
42352
+ try {
42353
+ const { dlopen, read: read3 } = __require("bun:ffi");
42354
+ const library = dlopen("/usr/lib/libSystem.B.dylib", {
42355
+ acl_get_fd: { args: ["i32"], returns: "ptr" },
42356
+ acl_free: { args: ["ptr"], returns: "i32" },
42357
+ __error: { args: [], returns: "ptr" }
42358
+ });
42359
+ try {
42360
+ const errno = library.symbols.__error();
42361
+ if (!errno)
42362
+ return false;
42363
+ const acl = library.symbols.acl_get_fd(fd);
42364
+ if (acl) {
42365
+ library.symbols.acl_free(acl);
42366
+ return false;
42367
+ }
42368
+ return (acl === null || acl === 0) && read3.i32(errno) === 2;
42369
+ } finally {
42370
+ library.close();
42371
+ }
42372
+ } catch {
42373
+ return false;
42374
+ }
42375
+ }
42376
+ function isSystemRootAlias(path) {
42377
+ if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(path))
42378
+ return false;
42379
+ const target = `/private${path}`, link = `private${path}`;
42380
+ const identity4 = (stat2) => `${stat2.dev}:${stat2.ino}:${stat2.ctimeNs}:${stat2.uid}:${stat2.mode}`;
42381
+ try {
42382
+ const before = lstatSync19(path, { bigint: true });
42383
+ if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync4(path) !== link)
42384
+ return false;
42385
+ const directories = ["/", "/private", target].map((directory) => {
42386
+ const stat2 = lstatSync19(directory, { bigint: true });
42387
+ if (!stat2.isDirectory() || stat2.uid !== 0n)
42388
+ throw new Error("Untrusted system alias directory");
42389
+ if (directory !== "/private/tmp") {
42390
+ if ((stat2.mode & 0o022n) !== 0n)
42391
+ throw new Error("Writable system alias directory");
42392
+ } else if ((stat2.mode & 0o1000n) === 0n)
42393
+ throw new Error("Unprotected system temporary directory");
42394
+ const fd = openSync16(directory, constants12.O_RDONLY | constants12.O_DIRECTORY | constants12.O_NOFOLLOW);
42395
+ try {
42396
+ if (identity4(fstatSync16(fd, { bigint: true })) !== identity4(stat2) || !hasNoDarwinAcl(fd) || identity4(fstatSync16(fd, { bigint: true })) !== identity4(stat2))
42397
+ throw new Error("Unverified system alias directory ACL");
42398
+ } finally {
42399
+ closeSync16(fd);
42400
+ }
42401
+ return { directory, identity: identity4(stat2) };
42402
+ });
42403
+ return realpathSync7(path) === target && readlinkSync4(path) === link && identity4(lstatSync19(path, { bigint: true })) === identity4(before) && directories.every((item) => identity4(lstatSync19(item.directory, { bigint: true })) === item.identity);
42404
+ } catch {
42405
+ return false;
42406
+ }
42407
+ }
42408
+ function canonicalSystemPath(path) {
42409
+ const absolute = resolve15(path), root2 = `/${absolute.split(sep5)[1]}`;
42410
+ if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root2) || !lstatSync19(root2, { throwIfNoEntry: false })?.isSymbolicLink())
42411
+ return absolute;
42412
+ if (!isSystemRootAlias(root2))
42413
+ throw new Error(`Refusing symlink path: ${root2}`);
42414
+ return `/private${absolute}`;
42415
+ }
42143
42416
  function assertSafePath(path) {
42144
42417
  let cursor = resolve15(path);
42145
42418
  while (true) {
42146
- if (lstatSync19(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
42419
+ if (lstatSync19(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
42147
42420
  throw new Error(`Refusing symlink path: ${cursor}`);
42148
42421
  const parent = dirname15(cursor);
42149
42422
  if (parent === cursor)
@@ -42179,7 +42452,7 @@ function rootAliases(home, allowed = false) {
42179
42452
  return result;
42180
42453
  }
42181
42454
  function canonicalAgentPath(path, aliases) {
42182
- const absolute = resolve15(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep5));
42455
+ const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep5));
42183
42456
  return binding ? join22(binding.target, relative4(binding.alias, absolute)) : absolute;
42184
42457
  }
42185
42458
  function disabledCodexSkillPaths(config, aliases) {
@@ -42277,17 +42550,19 @@ function selectNativeMigrationTargets(inventory, manifest) {
42277
42550
  const identities = new Set;
42278
42551
  const selected = [];
42279
42552
  for (const target of manifest.targets) {
42280
- const projectRoot = resolve15(target.projectRoot), normalized = relative4(projectRoot, resolve15(projectRoot, target.path));
42281
- if (!isAbsolute17(target.projectRoot) || projectRoot !== target.projectRoot || !existsSync6(projectRoot) || !lstatSync19(projectRoot).isDirectory())
42553
+ if (!isAbsolute17(target.projectRoot) || resolve15(target.projectRoot) !== target.projectRoot)
42554
+ throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
42555
+ const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative4(projectRoot, resolve15(projectRoot, target.path));
42556
+ if (!existsSync6(projectRoot) || !lstatSync19(projectRoot).isDirectory())
42282
42557
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
42283
42558
  assertSafePath(projectRoot);
42284
42559
  if (isAbsolute17(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep5}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
42285
42560
  throw new Error(`Invalid native migration target: ${target.path}`);
42286
- const identity4 = `${target.agent}\x00${projectRoot}\x00${normalized}`;
42561
+ const identity4 = `${target.agent}\x00${canonicalSystemPath(projectRoot)}\x00${normalized}`;
42287
42562
  if (identities.has(identity4))
42288
42563
  throw new Error(`Duplicate native migration target: ${target.path}`);
42289
42564
  identities.add(identity4);
42290
- const expected = resolve15(target.projectRoot, target.path);
42565
+ const expected = canonicalSystemPath(resolve15(target.projectRoot, target.path));
42291
42566
  const matches = inventory.filter((entry2) => entry2.agent === target.agent && resolve15(entry2.path) === expected);
42292
42567
  if (matches.length !== 1)
42293
42568
  throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
@@ -42303,7 +42578,7 @@ function selectNativeMigrationTargets(inventory, manifest) {
42303
42578
  function projectAncestorDirectories(projects) {
42304
42579
  const directories = new Set;
42305
42580
  for (const project of projects) {
42306
- for (let path = resolve15(project), depth = 0;; depth++) {
42581
+ for (let path = canonicalSystemPath(project), depth = 0;; depth++) {
42307
42582
  if (depth >= 100)
42308
42583
  throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
42309
42584
  directories.add(path);
@@ -42316,11 +42591,13 @@ function projectAncestorDirectories(projects) {
42316
42591
  return [...directories];
42317
42592
  }
42318
42593
  function inventoryNativeSkills(home = homedir3(), options = {}) {
42594
+ home = canonicalSystemPath(home);
42319
42595
  if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute17(options.reviewedCacheAlias) || resolve15(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
42320
42596
  throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
42321
42597
  if (options.disabledVendorPaths?.some((path) => !isAbsolute17(path) || resolve15(path) !== path))
42322
42598
  throw new Error("Disabled vendor paths must be exact absolute paths");
42323
- const disabledVendorPaths = new Set(options.disabledVendorPaths ?? []);
42599
+ const reviewedCacheAlias = options.reviewedCacheAlias === undefined ? undefined : canonicalSystemPath(options.reviewedCacheAlias);
42600
+ const disabledVendorPaths = new Set((options.disabledVendorPaths ?? []).map(canonicalSystemPath));
42324
42601
  const aliases = rootAliases(home, options.allowRootAliases);
42325
42602
  const selectedAgents = options.agents ? new Set(options.agents) : undefined;
42326
42603
  const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
@@ -42331,6 +42608,9 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
42331
42608
  for (const [agent, path] of rootDefinitions)
42332
42609
  roots.push([agent, canonicalAgentPath(join22(project, path), aliases)]);
42333
42610
  }
42611
+ const installationInputs = options.codexInstallationInputRoots ?? [];
42612
+ if (installationInputs.some((input) => resolve15(input) !== input || realpathSync7(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep5) || input.startsWith(path + sep5)))))
42613
+ throw new Error("Native installation input overlaps an unconditional skill-loading root");
42334
42614
  const entries = [], seen = new Set;
42335
42615
  const cacheScans = new Map, maxAliasProofEntries = 1e4;
42336
42616
  let discoveryEntries = 0, discoveryPathBytes = 0;
@@ -42343,8 +42623,11 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
42343
42623
  }
42344
42624
  let reviewedCacheAliasFound = false;
42345
42625
  function verifiedSiblingCacheAlias(agent, path, parent) {
42346
- const link = readlinkSync4(path), target = resolve15(parent, link);
42347
- if (dirname15(target) !== parent || (isAbsolute17(link) ? link !== target : dirname15(link) !== "."))
42626
+ const link = readlinkSync4(path), lexicalTarget = resolve15(parent, link);
42627
+ if (isAbsolute17(link) ? link !== lexicalTarget : dirname15(link) !== ".")
42628
+ return false;
42629
+ const target = canonicalSystemPath(lexicalTarget);
42630
+ if (dirname15(target) !== parent)
42348
42631
  return false;
42349
42632
  if (!lstatSync19(target, { throwIfNoEntry: false })?.isDirectory())
42350
42633
  return false;
@@ -42353,20 +42636,22 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
42353
42636
  return false;
42354
42637
  if (scan.hasSkills) {
42355
42638
  const targetSkills = entries.filter((entry) => entry.agent === agent && entry.vendor && (entry.path === target || entry.path.startsWith(target + sep5)));
42356
- if (path === options.reviewedCacheAlias && targetSkills.length > 0)
42639
+ if (path === reviewedCacheAlias && targetSkills.length > 0)
42357
42640
  reviewedCacheAliasFound = true;
42358
42641
  if (agent === "codex" && targetSkills.length > 0 && targetSkills.every((entry) => disabledVendorPaths.has(entry.path) || options.disabledVendorSkill?.(entry) === true))
42359
42642
  return true;
42360
- if (path !== options.reviewedCacheAlias)
42643
+ if (path !== reviewedCacheAlias)
42361
42644
  return false;
42362
42645
  }
42363
42646
  return true;
42364
42647
  }
42365
- function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false) {
42648
+ function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false, configuredRoot = false) {
42366
42649
  if (!admitted)
42367
42650
  admit(path);
42368
42651
  const scan = { complete: true, hasSkills: false, entries: 1 };
42369
42652
  assertSafePath(path);
42653
+ if (configuredRoot && agent === "codex" && installationInputs.includes(path))
42654
+ return scan;
42370
42655
  if (!existsSync6(path))
42371
42656
  return scan;
42372
42657
  const stat2 = lstatSync19(path);
@@ -42422,7 +42707,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
42422
42707
  continue;
42423
42708
  }
42424
42709
  }
42425
- const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true);
42710
+ const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true, configuredRoot);
42426
42711
  scan.complete &&= childScan.complete;
42427
42712
  scan.hasSkills ||= childScan.hasSkills;
42428
42713
  scan.entries += childScan.entries;
@@ -42431,10 +42716,10 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
42431
42716
  cacheScans.set(path, scan);
42432
42717
  return scan;
42433
42718
  }
42434
- function scanRoot(agent, path, vendor = false, pluginCache = false) {
42719
+ function scanRoot(agent, path, vendor = false, pluginCache = false, configuredRoot = false) {
42435
42720
  if (agent === "hermes" || options.guardHermes && `${path}${sep5}`.includes(`${sep5}.agents${sep5}skills${sep5}`))
42436
42721
  assertNoHermesLegacyShadow(path);
42437
- if (!visit2(agent, path, vendor, 0, pluginCache).complete)
42722
+ if (!visit2(agent, path, vendor, 0, pluginCache, false, configuredRoot).complete)
42438
42723
  throw new Error(`Native skill discovery limit exceeded; review this root before continuing: ${path}`);
42439
42724
  }
42440
42725
  for (const [agent, path] of roots)
@@ -42450,7 +42735,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
42450
42735
  const configured = options.configured ? INTEGRATION_AGENTS.filter(includesAgent).flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
42451
42736
  for (const root2 of [...options.agentRoots ?? [], ...configured])
42452
42737
  if (includesAgent(root2.agent))
42453
- scanRoot(root2.agent, canonicalAgentPath(root2.path, aliases), true);
42738
+ scanRoot(root2.agent, canonicalAgentPath(root2.path, aliases), true, false, true);
42454
42739
  if (options.reviewedCacheAlias !== undefined && !reviewedCacheAliasFound)
42455
42740
  throw new Error("Reviewed cache alias was not an inventoried skill-containing sibling alias");
42456
42741
  recheckRootAliases(aliases);
@@ -42590,6 +42875,11 @@ enabled = false
42590
42875
  Bun.TOML.parse(result);
42591
42876
  return result;
42592
42877
  }
42878
+ function assertCodexInstallationInputs(home, projectDirs, inputs) {
42879
+ const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve15(directory, path)));
42880
+ if (inputs.some((input) => nativeRoots.some((root2) => root2 === input || root2.startsWith(input + sep5) || input.startsWith(root2 + sep5))))
42881
+ throw new Error("Native installation input overlaps an unconditional skill-loading root");
42882
+ }
42593
42883
  function planAgentIntegration(options) {
42594
42884
  const home = options.home ?? homedir3(), dataDir2 = options.dataDir ?? getDataDirReadOnly();
42595
42885
  const aliases = rootAliases(home, options.allowRootAliases);
@@ -42628,11 +42918,16 @@ function planAgentIntegration(options) {
42628
42918
  const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(join22(home, ".codex", "config.toml"), aliases)) ?? "") : {};
42629
42919
  const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, disabledVendorPaths: disabledCodexSkillPaths(codexConfig, aliases) });
42630
42920
  const observedNativeSources = [];
42631
- const codexPluginSkills = options.codexNativeCatalog ? reviewCodexPluginSkillControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join22(entry.path, "SKILL.md")), canonicalAgentPath(join22(home, ".codex/plugins/cache"), aliases), resolve15(options.projectDir ?? home), (path) => {
42921
+ const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join22(entry.path, "SKILL.md")), canonicalAgentPath(join22(home, ".codex/plugins/cache"), aliases), resolve15(options.projectDir ?? home), (path) => {
42632
42922
  const bytes = readNativeBytes(path, 1024 * 1024);
42633
42923
  observedNativeSources.push({ path, sha256: sha4(bytes) });
42634
42924
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
42635
- }, codexConfig.skills?.config ?? []) : policy.bridge?.codexPluginSkills ?? [];
42925
+ }, codexConfig.skills?.config ?? [], codexConfig.plugins) : { skills: policy.bridge?.codexPluginSkills ?? [], inactivePlugins: policy.bridge?.codexInactivePlugins ?? [], sourceInputs: policy.bridge?.discovery?.codex?.codexInstallationInputs?.plugins ?? [] };
42926
+ const codexPluginSkills = codexPluginControls.skills;
42927
+ const codexInactivePlugins = codexPluginControls.inactivePlugins;
42928
+ const codexDiscovery = discoveries.find((binding) => binding.agent === "codex");
42929
+ const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep5) || input.sourceRoot.startsWith(root2 + sep5)) && codexDiscovery.sources.some((source) => source.path === join22(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
42930
+ assertCodexInstallationInputs(home, [options.projectDir ?? home], codexPluginSourceInputs.map((input) => input.sourceRoot));
42636
42931
  const changes = [];
42637
42932
  for (const agent of [...new Set(options.agents)]) {
42638
42933
  if (!INTEGRATION_AGENTS.includes(agent))
@@ -42697,7 +42992,16 @@ function planAgentIntegration(options) {
42697
42992
  changes.push({ path: configPath, before: previous, after: next });
42698
42993
  }
42699
42994
  }
42700
- const discoveryAfter = discoveries.map((binding) => rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after]))));
42995
+ const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha4(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join22(home, ".codex/config.toml"), aliases))?.before === null ? null : sha4(readOptional(canonicalAgentPath(join22(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview;
42996
+ const discoveryAfter = discoveries.map((binding) => {
42997
+ const rebound = rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after])));
42998
+ if (binding.agent !== "codex")
42999
+ return rebound;
43000
+ const { codexInstallationInputs: previousInputs, ...current } = rebound;
43001
+ const inputs = codexPluginSourceInputs.map((input) => input.sourceRoot);
43002
+ const directories = options.codexNativeCatalog || !previousInputs ? captureDiscoveryDirectories((binding.directories ?? []).filter((directory) => inputs.some((root2) => root2.startsWith(directory.path + sep5))).map((directory) => directory.path), inputs) : previousInputs.directories;
43003
+ return codexPluginSourceInputs.length ? { ...current, codexInstallationInputs: { version: "codex-cli 0.160.0", catalogSha256: codexPluginSkillReview.catalogSha256, plugins: codexPluginSourceInputs, ...directories?.length ? { directories } : {} } } : current;
43004
+ });
42701
43005
  const nextPolicy = { ...policy, version: 1, loading: "cli", profileId, bridge: {
42702
43006
  ...policy.bridge,
42703
43007
  version: CLI_BRIDGE_VERSION,
@@ -42710,7 +43014,7 @@ function planAgentIntegration(options) {
42710
43014
  profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
42711
43015
  disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join22(home, ".codex", "skills", ".system"), aliases) + sep5)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
42712
43016
  rootAliases: aliases,
42713
- ...codexPluginSkills.length ? { codexPluginSkills, codexPluginSkillReview: options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha4(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join22(home, ".codex/config.toml"), aliases))?.before === null ? null : sha4(readOptional(canonicalAgentPath(join22(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview } : {},
43017
+ ...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
42714
43018
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
42715
43019
  } };
42716
43020
  const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
@@ -43201,7 +43505,12 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
43201
43505
  throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
43202
43506
  if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join22(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024))))
43203
43507
  throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
43204
- const disabledPlugin = (entry) => agent === "codex" && entry.vendor && isReviewedCodexPluginSkillDisabled(join22(entry.path, "SKILL.md"), canonicalAgentPath(join22(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], codexConfig.skills?.config, (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
43508
+ const disabledPlugin = (entry) => {
43509
+ if (agent !== "codex" || !entry.vendor)
43510
+ return false;
43511
+ const document2 = join22(entry.path, "SKILL.md"), cache = canonicalAgentPath(join22(home, ".codex/plugins/cache"), aliases), read3 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024));
43512
+ return isReviewedCodexPluginInactive(document2, cache, binding.codexInactivePlugins ?? [], codexConfig.plugins, codexConfig.skills?.config ?? [], read3) || isReviewedCodexPluginSkillDisabled(document2, cache, binding.codexPluginSkills ?? [], codexConfig.skills?.config, read3);
43513
+ };
43205
43514
  const disabledPaths = new Set(disabledCodexSkillPaths(codexConfig, aliases));
43206
43515
  const disabledVendorSkill = (entry) => {
43207
43516
  if (agent !== "codex" || !entry.vendor)
@@ -43238,7 +43547,15 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
43238
43547
  } catch (error) {
43239
43548
  throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
43240
43549
  }
43241
- const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", agents: [agent], projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0, disabledVendorPaths: agent === "codex" ? disabledCodexSkillPaths(codexConfig, aliases) : [], disabledVendorSkill: disabledPlugin });
43550
+ let installationInputRoots = [];
43551
+ if (agent === "codex") {
43552
+ try {
43553
+ installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(join22(home, ".codex/plugins/cache"), aliases), discovery.codexInstallationInputs?.plugins ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
43554
+ } catch {
43555
+ throw new Error("NATIVE_SKILL_DRIFT: native installation input identity changed; review discovery and catalog");
43556
+ }
43557
+ }
43558
+ const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", agents: [agent], projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0, disabledVendorPaths: agent === "codex" ? disabledCodexSkillPaths(codexConfig, aliases) : [], disabledVendorSkill: disabledPlugin, codexInstallationInputRoots: installationInputRoots });
43242
43559
  const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !disabledVendorSkill(entry));
43243
43560
  if (unexpected.length) {
43244
43561
  const paths = unexpected.slice(0, 3).map((entry) => {