@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/bin/index.js CHANGED
@@ -36868,7 +36868,7 @@ var package_default;
36868
36868
  var init_package = __esm(() => {
36869
36869
  package_default = {
36870
36870
  name: "@hasna/skills",
36871
- version: "0.10.23",
36871
+ version: "0.10.25",
36872
36872
  description: "Skills library for AI coding agents",
36873
36873
  type: "module",
36874
36874
  bin: {
@@ -39944,6 +39944,18 @@ function assertAgentPolicyCollections(policy) {
39944
39944
  if (control.appSha256 !== undefined)
39945
39945
  requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
39946
39946
  }
39947
+ if (bridge.codexInactivePlugins !== undefined) {
39948
+ const controls = array(bridge.codexInactivePlugins, 4096), ids = new Set;
39949
+ requireBound(controls.length === 0 || object(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === "codex-cli 0.160.0" && typeof bridge.codexPluginSkillReview.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(bridge.codexPluginSkillReview.catalogSha256));
39950
+ for (const control of controls) {
39951
+ requireBound(object(control) && Object.keys(control).every((key) => ["pluginId", "namespace", "pluginParent", "sourceType", "sourceSha256"].includes(key)));
39952
+ text(control.pluginId, 1024);
39953
+ text(control.namespace, 64);
39954
+ text(control.pluginParent);
39955
+ requireBound(!ids.has(control.pluginId) && isAbsolute2(control.pluginParent) && resolve3(control.pluginParent) === control.pluginParent && ["local", "git", "npm"].includes(control.sourceType) && typeof control.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(control.sourceSha256));
39956
+ ids.add(control.pluginId);
39957
+ }
39958
+ }
39947
39959
  if (bridge.disabledBuiltins !== undefined)
39948
39960
  for (const builtin of array(bridge.disabledBuiltins, AGENT_POLICY_LIMITS.builtinNames)) {
39949
39961
  requireBound(object(builtin));
@@ -39956,6 +39968,25 @@ function assertAgentPolicyCollections(policy) {
39956
39968
  text(agent, 128);
39957
39969
  requireBound(object(value));
39958
39970
  text(value.agent, 128);
39971
+ if (value.codexInstallationInputs !== undefined) {
39972
+ const proof = record(value.codexInstallationInputs, 4), inputs = array(proof.plugins, 4096), ids = new Set;
39973
+ 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 && object(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === proof.version && bridge.codexPluginSkillReview.catalogSha256 === proof.catalogSha256);
39974
+ for (const input of inputs) {
39975
+ requireBound(object(input) && Object.keys(input).every((key) => ["pluginId", "namespace", "pluginParent", "sourceRoot", "sourceSha256"].includes(key)));
39976
+ text(input.pluginId, 1024);
39977
+ text(input.namespace, 64);
39978
+ text(input.pluginParent);
39979
+ text(input.sourceRoot);
39980
+ requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) => isAbsolute2(path) && resolve3(path) === path) && typeof input.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(input.sourceSha256));
39981
+ ids.add(input.pluginId);
39982
+ }
39983
+ if (proof.directories !== undefined)
39984
+ for (const directory of array(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
39985
+ requireBound(object(directory));
39986
+ text(directory.path);
39987
+ requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
39988
+ }
39989
+ }
39959
39990
  for (const root of array(value.roots, AGENT_POLICY_LIMITS.discoveryRoots))
39960
39991
  text(root);
39961
39992
  for (const source of array(value.sources, AGENT_POLICY_LIMITS.discoverySources)) {
@@ -47235,7 +47266,7 @@ function safe(path) {
47235
47266
  function same(before2, after2) {
47236
47267
  return Boolean(after2 && after2.isDirectory() && before2.dev === after2.dev && before2.ino === after2.ino && before2.mtimeMs === after2.mtimeMs && before2.ctimeMs === after2.ctimeMs);
47237
47268
  }
47238
- function capture(path, budget) {
47269
+ function capture(path, budget, excludedRoots) {
47239
47270
  safe(path);
47240
47271
  const root = lstatSync4(path, { throwIfNoEntry: false });
47241
47272
  if (!root)
@@ -47264,7 +47295,7 @@ function capture(path, budget) {
47264
47295
  budget.bytes += Buffer.byteLength(row);
47265
47296
  need(budget.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
47266
47297
  hash.update(row);
47267
- if (stat.isDirectory())
47298
+ if (stat.isDirectory() && !excludedRoots.includes(child))
47268
47299
  walk(child, childRelative, stat, depth + 1);
47269
47300
  }
47270
47301
  need(same(before2, lstatSync4(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
@@ -47273,16 +47304,18 @@ function capture(path, budget) {
47273
47304
  safe(path);
47274
47305
  return { path, sha256: hash.digest("hex") };
47275
47306
  }
47276
- function captureDiscoveryDirectories(paths) {
47307
+ function captureDiscoveryDirectories(paths, excludedRoots = []) {
47277
47308
  need(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
47278
47309
  const budget = { entries: 0, bytes: 0 };
47279
- return paths.map((path) => capture(path, budget));
47310
+ for (const root of excludedRoots)
47311
+ safe(root);
47312
+ return paths.map((path) => capture(path, budget, excludedRoots));
47280
47313
  }
47281
- function verifyDiscoveryDirectories(directories) {
47314
+ function verifyDiscoveryDirectories(directories, excludedRoots = []) {
47282
47315
  need(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
47283
47316
  for (const directory of directories)
47284
47317
  need(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
47285
- const current = captureDiscoveryDirectories(directories.map((directory) => directory.path));
47318
+ const current = captureDiscoveryDirectories(directories.map((directory) => directory.path), excludedRoots);
47286
47319
  for (let i = 0;i < directories.length; i++)
47287
47320
  need(current[i].sha256 === directories[i].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`);
47288
47321
  }
@@ -51059,9 +51092,22 @@ var init_codex_hook_rpc = __esm(() => {
51059
51092
  // src/lib/codex-native-skill-catalog.ts
51060
51093
  import { realpathSync as realpathSync4 } from "fs";
51061
51094
  import { isAbsolute as isAbsolute10, normalize as normalize2 } from "path";
51095
+ function codexPluginSourceIsConfigControlled(plugin) {
51096
+ 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"];
51097
+ 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)));
51098
+ }
51062
51099
  function refuse() {
51063
51100
  throw new Error("CODEX_NATIVE_SKILL_CATALOG_INVALID");
51064
51101
  }
51102
+ function projectPluginSource(source) {
51103
+ if (!object4(source))
51104
+ return {};
51105
+ if (source.type === "remote")
51106
+ return { sourceType: "remote" };
51107
+ const optionalText = (value) => value === undefined || value === null || scalar(value, MAX_PATH_BYTES);
51108
+ 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;
51109
+ return valid ? { sourceType: source.type, sourceSha256: hashNativeJsonControls(JSON.stringify(source)), ...source.type === "local" ? { sourcePath: source.path } : {} } : {};
51110
+ }
51065
51111
  function projectCodexNativeSkillCatalog(response, cwd2) {
51066
51112
  if (!absolutePath2(cwd2) || !object4(response) || !Array.isArray(response.data) || response.data.length !== 1)
51067
51113
  refuse();
@@ -51091,7 +51137,10 @@ function projectCodexInstalledPlugins(response) {
51091
51137
  plugins.push({
51092
51138
  ...value,
51093
51139
  marketplace: marketplace.name,
51094
- sourceType: object4(value.source) && value.source.type === "remote" ? "remote" : undefined
51140
+ sourceType: undefined,
51141
+ sourceSha256: undefined,
51142
+ sourcePath: undefined,
51143
+ ...projectPluginSource(value.source)
51095
51144
  });
51096
51145
  }
51097
51146
  }
@@ -51102,7 +51151,7 @@ function projectCodexInstalledPluginEntries(value) {
51102
51151
  refuse();
51103
51152
  const ids = new Set;
51104
51153
  return value.map((item) => {
51105
- if (!object4(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")
51154
+ if (!object4(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) || normalize2(item.sourcePath) !== item.sourcePath || item.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: item.sourcePath })) : item.sourcePath !== undefined))
51106
51155
  refuse();
51107
51156
  const at2 = item.id.lastIndexOf("@");
51108
51157
  if (at2 < 1 || item.id.slice(0, at2) !== item.name || at2 === item.id.length - 1 || ids.has(item.id))
@@ -51114,7 +51163,9 @@ function projectCodexInstalledPluginEntries(value) {
51114
51163
  installed: item.installed,
51115
51164
  enabled: item.enabled,
51116
51165
  localVersion: item.localVersion ?? null,
51117
- ...item.sourceType === "remote" ? { sourceType: "remote" } : {},
51166
+ ...item.sourceType === undefined ? {} : { sourceType: item.sourceType },
51167
+ ...item.sourceSha256 === undefined ? {} : { sourceSha256: item.sourceSha256 },
51168
+ ...item.sourcePath === undefined ? {} : { sourcePath: item.sourcePath },
51118
51169
  ...item.remotePluginId == null ? {} : { remotePluginId: item.remotePluginId }
51119
51170
  };
51120
51171
  });
@@ -51184,6 +51235,7 @@ var MAX_SKILLS = 4096, MAX_NAME_BYTES = 1024, MAX_PATH_BYTES = 16384, MAX_PLUGIN
51184
51235
  var init_codex_native_skill_catalog = __esm(() => {
51185
51236
  init_codex_hook_rpc();
51186
51237
  init_codex_native_compatibility();
51238
+ init_claude_settings_witness();
51187
51239
  });
51188
51240
 
51189
51241
  // src/lib/codex-plugin-skill-controls.ts
@@ -51240,27 +51292,62 @@ function mcpControls(path, read2) {
51240
51292
  const object5 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
51241
51293
  const textField = (v, empty = false) => typeof v === "string" && (empty || Boolean(v.trim())) && Buffer.byteLength(v) <= 16384 && !/[\x00-\x1f\x7f]/.test(v);
51242
51294
  const envName = (v) => typeof v === "string" && /^[A-Za-z_][A-Za-z0-9_]{0,255}$/.test(v);
51295
+ const optional = (v, valid) => v === undefined || v === null || valid(v);
51296
+ const stringList = (v) => Array.isArray(v) && v.length <= 256 && v.every((item) => textField(item));
51297
+ const approval = (v) => ["auto", "prompt", "writes", "approve"].includes(v);
51298
+ const duration = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0;
51299
+ const positiveInteger = (v) => Number.isSafeInteger(v) && v > 0;
51300
+ const fields = [
51301
+ "command",
51302
+ "args",
51303
+ "cwd",
51304
+ "env",
51305
+ "env_vars",
51306
+ "enabled",
51307
+ "required",
51308
+ "environment_id",
51309
+ "startup_readiness",
51310
+ "supports_parallel_tool_calls",
51311
+ "tool_input_schema_max_bytes",
51312
+ "startup_timeout_sec",
51313
+ "startup_timeout_ms",
51314
+ "tool_timeout_sec",
51315
+ "default_tools_approval_mode",
51316
+ "enabled_tools",
51317
+ "disabled_tools",
51318
+ "omit_tools_from",
51319
+ "tools",
51320
+ "name"
51321
+ ];
51243
51322
  if (!object5(value) || Object.keys(value).length !== 1 || !object5(value.mcpServers))
51244
51323
  refuse2();
51245
51324
  const servers = Object.entries(value.mcpServers);
51246
51325
  if (!servers.length || servers.length > 256)
51247
51326
  refuse2();
51248
51327
  for (const [name, server] of servers) {
51249
- if (!identifier2(name) || !object5(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))
51328
+ if (!identifier2(name) || !object5(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) => object5(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))
51250
51329
  refuse2();
51251
- if (server.env_vars !== undefined) {
51330
+ if (server.env_vars !== undefined && server.env_vars !== null) {
51252
51331
  if (!Array.isArray(server.env_vars) || server.env_vars.length > 256)
51253
51332
  refuse2();
51254
51333
  const names = server.env_vars.map((entry) => {
51255
51334
  if (envName(entry))
51256
51335
  return entry;
51257
- if (!object5(entry) || !Object.keys(entry).every((key) => ["name", "source"].includes(key)) || !envName(entry.name) || entry.source !== undefined && entry.source !== "local")
51336
+ if (!object5(entry) || !Object.keys(entry).every((key) => ["name", "source"].includes(key)) || !envName(entry.name) || !optional(entry.source, (v) => v === "local"))
51258
51337
  refuse2();
51259
51338
  return entry.name;
51260
51339
  });
51261
51340
  if (new Set(names).size !== names.length)
51262
51341
  refuse2();
51263
51342
  }
51343
+ if (server.tools !== undefined && server.tools !== null) {
51344
+ if (!object5(server.tools) || Object.keys(server.tools).length > 256)
51345
+ refuse2();
51346
+ for (const [tool, config] of Object.entries(server.tools)) {
51347
+ if (!textField(tool) || !object5(config) || !Object.keys(config).every((key) => ["approval_mode", "output_token_limit"].includes(key)) || !optional(config.approval_mode, approval) || !optional(config.output_token_limit, positiveInteger))
51348
+ refuse2();
51349
+ }
51350
+ }
51264
51351
  }
51265
51352
  return sha2562;
51266
51353
  }
@@ -51297,7 +51384,7 @@ function rootControls(root, read2) {
51297
51384
  const appSha256 = present ? appControls(app, read2) : undefined;
51298
51385
  return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
51299
51386
  }
51300
- function identity4(document3, cache3, read2) {
51387
+ function documentIdentity(document3, cache3, read2) {
51301
51388
  if (!document3.startsWith(cache3 + sep4) || !document3.endsWith(sep4 + "SKILL.md"))
51302
51389
  refuse2();
51303
51390
  const text2 = read2(document3);
@@ -51325,11 +51412,33 @@ function identity4(document3, cache3, read2) {
51325
51412
  const path = join18(root, ".codex-plugin", "plugin.json");
51326
51413
  if (!lstatSync12(path, { throwIfNoEntry: false }))
51327
51414
  continue;
51328
- const controls = rootControls(root, read2);
51329
- return { name: `${controls.namespace}:${front.name}`, pluginParent: dirname10(root), ...controls };
51415
+ const manifestFile = lstatSync12(path);
51416
+ if (!lstatSync12(join18(root, ".codex-plugin")).isDirectory() || !manifestFile.isFile() || manifestFile.size > 1024 * 1024)
51417
+ refuse2();
51418
+ let manifest;
51419
+ try {
51420
+ const text3 = read2(path);
51421
+ hashNativeJsonControls(text3);
51422
+ manifest = JSON.parse(text3);
51423
+ } catch {
51424
+ refuse2();
51425
+ }
51426
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
51427
+ refuse2();
51428
+ const declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
51429
+ if (declaredVersion === null || (declaredVersion ? declaredVersion !== basename5(root) : basename5(root) !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
51430
+ refuse2();
51431
+ return { name: `${manifest.name}:${front.name}`, namespace: manifest.name, pluginParent: dirname10(root), root };
51330
51432
  }
51331
51433
  refuse2();
51332
51434
  }
51435
+ function identity4(document3, cache3, read2) {
51436
+ const parsed = documentIdentity(document3, cache3, read2);
51437
+ const controls = rootControls(parsed.root, read2);
51438
+ if (controls.namespace !== parsed.namespace)
51439
+ refuse2();
51440
+ return { name: parsed.name, pluginParent: parsed.pluginParent, ...controls };
51441
+ }
51333
51442
  function reviewedCodexPluginCapabilitiesUnchanged(cache3, controls, read2) {
51334
51443
  if (!Array.isArray(controls) || controls.length > 4096)
51335
51444
  return false;
@@ -51367,11 +51476,13 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache3, controls, read2) {
51367
51476
  return false;
51368
51477
  }
51369
51478
  }
51370
- function reviewCodexPluginSkillControls(catalog, documents, cache3, cwd2, read2, rules) {
51479
+ function reviewCodexPluginControls(catalog, documents, cache3, cwd2, read2, rules, pluginSettings) {
51371
51480
  if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd2)
51372
51481
  refuse2();
51373
51482
  const skills = projectCodexNativeSkillCatalog({ data: [{ cwd: cwd2, errors: [], skills: catalog.skills }] }, cwd2);
51374
- const allowed = new Set(documents), result2 = [];
51483
+ const allowed = new Set(documents), result2 = [], inertDocuments = new Set;
51484
+ const inactivePlugins = new Map;
51485
+ const sourceInputs = new Map;
51375
51486
  let installedPlugins;
51376
51487
  try {
51377
51488
  installedPlugins = projectCodexInstalledPluginEntries(catalog.plugins ?? []);
@@ -51382,25 +51493,48 @@ function reviewCodexPluginSkillControls(catalog, documents, cache3, cwd2, read2,
51382
51493
  for (const document3 of candidates) {
51383
51494
  if (!document3.startsWith(cache3 + sep4))
51384
51495
  continue;
51385
- const parsed = identity4(document3, cache3, read2);
51386
- if (parsed.name === "skills-cli" || parsed.name.endsWith(":skills-cli"))
51496
+ const located = documentIdentity(document3, cache3, read2);
51497
+ if (located.name === "skills-cli" || located.name.endsWith(":skills-cli"))
51387
51498
  refuse2();
51388
- const cacheParts = relative4(cache3, parsed.pluginParent).split(sep4);
51499
+ const cacheParts = relative4(cache3, located.pluginParent).split(sep4);
51389
51500
  if (cacheParts.length !== 2 || cacheParts.some((part) => !identifier2(part)))
51390
51501
  refuse2();
51391
51502
  const [marketplace, pluginName] = cacheParts;
51392
- if (pluginName !== parsed.namespace)
51503
+ if (pluginName !== located.namespace)
51393
51504
  refuse2();
51394
51505
  const expectedPluginId = `${pluginName}@${marketplace}`;
51395
51506
  const listed = skills.filter((skill) => skill.path === document3);
51396
51507
  if (listed.length > 1)
51397
51508
  refuse2();
51509
+ const installed = installedPlugins.filter((plugin) => plugin.id === expectedPluginId && plugin.name === pluginName);
51510
+ if (catalog.version === "codex-cli 0.160.0" && installed.length === 1 && installed[0].installed && installed[0].localVersion === basename5(located.root) && installed[0].sourceType === "local" && codexPluginSourceIsConfigControlled(installed[0])) {
51511
+ const plugin = installed[0], input = { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceRoot: plugin.sourcePath, sourceSha256: plugin.sourceSha256 };
51512
+ if (input.sourceRoot !== located.root && !input.sourceRoot.startsWith(cache3 + sep4) && (lstatSync12(input.sourceRoot, { throwIfNoEntry: false }) || documents.some((path) => path.startsWith(input.sourceRoot + sep4)))) {
51513
+ if (skills.some((skill) => skill.path === input.sourceRoot || skill.path.startsWith(input.sourceRoot + sep4)) || !sourceInputUnchanged(input, cache3, read2))
51514
+ refuse2();
51515
+ sourceInputs.set(expectedPluginId, input);
51516
+ }
51517
+ }
51518
+ const settings = pluginSettings && typeof pluginSettings === "object" && !Array.isArray(pluginSettings) ? pluginSettings : undefined;
51519
+ 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) {
51520
+ if (!Array.isArray(rules))
51521
+ refuse2();
51522
+ const exactPathDeny = rules.some((rule) => rule?.path === document3 && rule?.enabled === false);
51523
+ if (!lstatSync12(document3).isFile() || realpathSync5(document3) !== document3 || realpathSync5(located.root) !== located.root || realpathSync5(located.pluginParent) !== located.pluginParent || realpathSync5(join18(located.root, ".codex-plugin/plugin.json")) !== join18(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: document3, pluginId: expectedPluginId, enabled: true }, rules))
51524
+ refuse2();
51525
+ inertDocuments.add(document3);
51526
+ const plugin = installed[0];
51527
+ inactivePlugins.set(expectedPluginId, { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceType: plugin.sourceType, sourceSha256: plugin.sourceSha256 });
51528
+ continue;
51529
+ }
51530
+ const parsed = { name: located.name, pluginParent: located.pluginParent, ...rootControls(located.root, read2) };
51531
+ if (parsed.namespace !== located.namespace)
51532
+ refuse2();
51398
51533
  let remotePluginId;
51399
51534
  if (listed.length === 1) {
51400
51535
  if (listed[0].name !== parsed.name || listed[0].pluginId !== expectedPluginId)
51401
51536
  refuse2();
51402
51537
  } else {
51403
- const installed = installedPlugins.filter((plugin2) => plugin2.id === expectedPluginId && plugin2.name === pluginName);
51404
51538
  if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled)
51405
51539
  refuse2();
51406
51540
  const plugin = installed[0];
@@ -51418,9 +51552,48 @@ function reviewCodexPluginSkillControls(catalog, documents, cache3, cwd2, read2,
51418
51552
  }
51419
51553
  result2.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
51420
51554
  }
51421
- if (!result2.length && documents.some((document3) => document3.startsWith(cache3 + sep4)) || new Set(result2.map((item) => item.name)).size !== result2.length)
51555
+ if (!result2.length && documents.some((document3) => document3.startsWith(cache3 + sep4) && !inertDocuments.has(document3)) || new Set(result2.map((item) => item.name)).size !== result2.length)
51422
51556
  refuse2();
51423
- return result2;
51557
+ return { skills: result2, inactivePlugins: [...inactivePlugins.values()], sourceInputs: [...sourceInputs.values()] };
51558
+ }
51559
+ function sourceInputUnchanged(input, cache3, read2) {
51560
+ try {
51561
+ const parts = relative4(cache3, input.pluginParent).split(sep4);
51562
+ if (parts.length !== 2 || parts.some((part) => !identifier2(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 })) || resolve10(input.sourceRoot) !== input.sourceRoot || !lstatSync12(input.sourceRoot).isDirectory() || realpathSync5(input.sourceRoot) !== input.sourceRoot || input.sourceRoot === cache3 || input.sourceRoot.startsWith(cache3 + sep4) || cache3.startsWith(input.sourceRoot + sep4))
51563
+ return false;
51564
+ const path = join18(input.sourceRoot, ".codex-plugin/plugin.json"), stat = lstatSync12(path);
51565
+ if (!stat.isFile() || stat.size > 1024 * 1024 || realpathSync5(path) !== path)
51566
+ return false;
51567
+ const text2 = read2(path);
51568
+ hashNativeJsonControls(text2);
51569
+ const manifest = JSON.parse(text2);
51570
+ return Boolean(manifest && typeof manifest === "object" && !Array.isArray(manifest) && manifest.name === input.namespace);
51571
+ } catch {
51572
+ return false;
51573
+ }
51574
+ }
51575
+ function reviewedCodexPluginSourceRoots(cache3, inputs, read2) {
51576
+ 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, cache3, read2)))
51577
+ refuse2();
51578
+ const roots = inputs.map((input) => input.sourceRoot);
51579
+ if (roots.some((root) => roots.some((other) => root !== other && root.startsWith(other + sep4))))
51580
+ refuse2();
51581
+ return roots;
51582
+ }
51583
+ function isReviewedCodexPluginInactive(document3, cache3, controls, pluginSettings, rules, read2) {
51584
+ if (!Array.isArray(controls) || controls.length > 4096 || !pluginSettings || typeof pluginSettings !== "object" || Array.isArray(pluginSettings) || !Array.isArray(rules))
51585
+ return false;
51586
+ try {
51587
+ const parsed = documentIdentity(document3, cache3, read2), matches2 = controls.filter((control2) => control2.pluginParent === parsed.pluginParent && control2.namespace === parsed.namespace);
51588
+ if (matches2.length !== 1 || !lstatSync12(document3).isFile() || realpathSync5(document3) !== document3 || realpathSync5(parsed.root) !== parsed.root || realpathSync5(parsed.pluginParent) !== parsed.pluginParent || realpathSync5(join18(parsed.root, ".codex-plugin/plugin.json")) !== join18(parsed.root, ".codex-plugin/plugin.json"))
51589
+ return false;
51590
+ const control = matches2[0], parts = relative4(cache3, parsed.pluginParent).split(sep4);
51591
+ if (parts.length !== 2 || parts.some((part) => !identifier2(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)
51592
+ return false;
51593
+ return !rules.some((rule) => (rule?.path === document3 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule?.enabled !== false);
51594
+ } catch {
51595
+ return false;
51596
+ }
51424
51597
  }
51425
51598
  function isReviewedCodexPluginSkillDisabled(document3, cache3, controls, rules, read2) {
51426
51599
  if (!document3.startsWith(cache3 + sep4) || !Array.isArray(controls) || controls.length > 4096)
@@ -52121,10 +52294,75 @@ import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
52121
52294
  import { existsSync as existsSync12, lstatSync as lstatSync15, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync13, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync4, realpathSync as realpathSync6, renameSync as renameSync2, rmdirSync as rmdirSync2, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, chmodSync, openSync as openSync8, closeSync as closeSync8, fsyncSync as fsyncSync2, fstatSync as fstatSync8, readSync as readSync6, constants as constants8, linkSync } from "fs";
52122
52295
  import { dirname as dirname11, join as join21, resolve as resolve11, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
52123
52296
  import { homedir as homedir3 } from "os";
52297
+ function hasNoDarwinAcl(fd) {
52298
+ try {
52299
+ const { dlopen, read: read2 } = __require("bun:ffi");
52300
+ const library = dlopen("/usr/lib/libSystem.B.dylib", {
52301
+ acl_get_fd: { args: ["i32"], returns: "ptr" },
52302
+ acl_free: { args: ["ptr"], returns: "i32" },
52303
+ __error: { args: [], returns: "ptr" }
52304
+ });
52305
+ try {
52306
+ const errno = library.symbols.__error();
52307
+ if (!errno)
52308
+ return false;
52309
+ const acl = library.symbols.acl_get_fd(fd);
52310
+ if (acl) {
52311
+ library.symbols.acl_free(acl);
52312
+ return false;
52313
+ }
52314
+ return (acl === null || acl === 0) && read2.i32(errno) === 2;
52315
+ } finally {
52316
+ library.close();
52317
+ }
52318
+ } catch {
52319
+ return false;
52320
+ }
52321
+ }
52322
+ function isSystemRootAlias(path) {
52323
+ if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(path))
52324
+ return false;
52325
+ const target = `/private${path}`, link2 = `private${path}`;
52326
+ const identity5 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}`;
52327
+ try {
52328
+ const before2 = lstatSync15(path, { bigint: true });
52329
+ if (!before2.isSymbolicLink() || before2.uid !== 0n || readlinkSync4(path) !== link2)
52330
+ return false;
52331
+ const directories = ["/", "/private", target].map((directory) => {
52332
+ const stat = lstatSync15(directory, { bigint: true });
52333
+ if (!stat.isDirectory() || stat.uid !== 0n)
52334
+ throw new Error("Untrusted system alias directory");
52335
+ if (directory !== "/private/tmp") {
52336
+ if ((stat.mode & 0o022n) !== 0n)
52337
+ throw new Error("Writable system alias directory");
52338
+ } else if ((stat.mode & 0o1000n) === 0n)
52339
+ throw new Error("Unprotected system temporary directory");
52340
+ const fd = openSync8(directory, constants8.O_RDONLY | constants8.O_DIRECTORY | constants8.O_NOFOLLOW);
52341
+ try {
52342
+ if (identity5(fstatSync8(fd, { bigint: true })) !== identity5(stat) || !hasNoDarwinAcl(fd) || identity5(fstatSync8(fd, { bigint: true })) !== identity5(stat))
52343
+ throw new Error("Unverified system alias directory ACL");
52344
+ } finally {
52345
+ closeSync8(fd);
52346
+ }
52347
+ return { directory, identity: identity5(stat) };
52348
+ });
52349
+ return realpathSync6(path) === target && readlinkSync4(path) === link2 && identity5(lstatSync15(path, { bigint: true })) === identity5(before2) && directories.every((item) => identity5(lstatSync15(item.directory, { bigint: true })) === item.identity);
52350
+ } catch {
52351
+ return false;
52352
+ }
52353
+ }
52354
+ function canonicalSystemPath(path) {
52355
+ const absolute = resolve11(path), root = `/${absolute.split(sep5)[1]}`;
52356
+ if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync15(root, { throwIfNoEntry: false })?.isSymbolicLink())
52357
+ return absolute;
52358
+ if (!isSystemRootAlias(root))
52359
+ throw new Error(`Refusing symlink path: ${root}`);
52360
+ return `/private${absolute}`;
52361
+ }
52124
52362
  function assertSafePath(path) {
52125
52363
  let cursor = resolve11(path);
52126
52364
  while (true) {
52127
- if (lstatSync15(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
52365
+ if (lstatSync15(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
52128
52366
  throw new Error(`Refusing symlink path: ${cursor}`);
52129
52367
  const parent = dirname11(cursor);
52130
52368
  if (parent === cursor)
@@ -52160,7 +52398,7 @@ function rootAliases(home, allowed = false) {
52160
52398
  return result2;
52161
52399
  }
52162
52400
  function canonicalAgentPath(path, aliases) {
52163
- const absolute = resolve11(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep5));
52401
+ const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep5));
52164
52402
  return binding ? join21(binding.target, relative5(binding.alias, absolute)) : absolute;
52165
52403
  }
52166
52404
  function disabledCodexSkillPaths(config, aliases) {
@@ -52277,8 +52515,10 @@ function parseNativeMigrationTargetManifest(input) {
52277
52515
  throw new Error("Invalid native migration target fields");
52278
52516
  if (!ROOTS.some(([agent]) => agent === item.agent))
52279
52517
  throw new Error(`Unsupported native migration target agent: ${item.agent}`);
52280
- const projectRoot = resolve11(item.projectRoot);
52281
- if (!isAbsolute11(item.projectRoot) || projectRoot !== item.projectRoot || !existsSync12(projectRoot) || !lstatSync15(projectRoot).isDirectory())
52518
+ if (!isAbsolute11(item.projectRoot) || resolve11(item.projectRoot) !== item.projectRoot)
52519
+ throw new Error(`Native migration target project root must be an existing absolute directory: ${item.projectRoot}`);
52520
+ const projectRoot = canonicalSystemPath(item.projectRoot);
52521
+ if (!existsSync12(projectRoot) || !lstatSync15(projectRoot).isDirectory())
52282
52522
  throw new Error(`Native migration target project root must be an existing absolute directory: ${item.projectRoot}`);
52283
52523
  assertSafePath(projectRoot);
52284
52524
  if (isAbsolute11(item.path) || item.path.includes("\\") || item.path.length === 0 || item.path.includes("\x00"))
@@ -52288,7 +52528,7 @@ function parseNativeMigrationTargetManifest(input) {
52288
52528
  throw new Error(`Native migration target path escapes or is not normalized: ${item.path}`);
52289
52529
  if (!/^[0-9a-f]{64}$/.test(item.treeSha256))
52290
52530
  throw new Error(`Invalid native migration tree digest for ${item.path}`);
52291
- const identity5 = `${item.agent}\x00${projectRoot}\x00${normalized}`;
52531
+ const identity5 = `${item.agent}\x00${canonicalSystemPath(projectRoot)}\x00${normalized}`;
52292
52532
  if (identities.has(identity5))
52293
52533
  throw new Error(`Duplicate native migration target: ${item.path}`);
52294
52534
  identities.add(identity5);
@@ -52305,17 +52545,19 @@ function selectNativeMigrationTargets(inventory, manifest) {
52305
52545
  const identities = new Set;
52306
52546
  const selected = [];
52307
52547
  for (const target of manifest.targets) {
52308
- const projectRoot = resolve11(target.projectRoot), normalized = relative5(projectRoot, resolve11(projectRoot, target.path));
52309
- if (!isAbsolute11(target.projectRoot) || projectRoot !== target.projectRoot || !existsSync12(projectRoot) || !lstatSync15(projectRoot).isDirectory())
52548
+ if (!isAbsolute11(target.projectRoot) || resolve11(target.projectRoot) !== target.projectRoot)
52549
+ throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
52550
+ const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative5(projectRoot, resolve11(projectRoot, target.path));
52551
+ if (!existsSync12(projectRoot) || !lstatSync15(projectRoot).isDirectory())
52310
52552
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
52311
52553
  assertSafePath(projectRoot);
52312
52554
  if (isAbsolute11(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep5}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
52313
52555
  throw new Error(`Invalid native migration target: ${target.path}`);
52314
- const identity5 = `${target.agent}\x00${projectRoot}\x00${normalized}`;
52556
+ const identity5 = `${target.agent}\x00${canonicalSystemPath(projectRoot)}\x00${normalized}`;
52315
52557
  if (identities.has(identity5))
52316
52558
  throw new Error(`Duplicate native migration target: ${target.path}`);
52317
52559
  identities.add(identity5);
52318
- const expected = resolve11(target.projectRoot, target.path);
52560
+ const expected = canonicalSystemPath(resolve11(target.projectRoot, target.path));
52319
52561
  const matches2 = inventory.filter((entry2) => entry2.agent === target.agent && resolve11(entry2.path) === expected);
52320
52562
  if (matches2.length !== 1)
52321
52563
  throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
@@ -52331,7 +52573,7 @@ function selectNativeMigrationTargets(inventory, manifest) {
52331
52573
  function projectAncestorDirectories(projects) {
52332
52574
  const directories = new Set;
52333
52575
  for (const project of projects) {
52334
- for (let path = resolve11(project), depth = 0;; depth++) {
52576
+ for (let path = canonicalSystemPath(project), depth = 0;; depth++) {
52335
52577
  if (depth >= 100)
52336
52578
  throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
52337
52579
  directories.add(path);
@@ -52344,11 +52586,13 @@ function projectAncestorDirectories(projects) {
52344
52586
  return [...directories];
52345
52587
  }
52346
52588
  function inventoryNativeSkills(home = homedir3(), options = {}) {
52589
+ home = canonicalSystemPath(home);
52347
52590
  if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute11(options.reviewedCacheAlias) || resolve11(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
52348
52591
  throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
52349
52592
  if (options.disabledVendorPaths?.some((path) => !isAbsolute11(path) || resolve11(path) !== path))
52350
52593
  throw new Error("Disabled vendor paths must be exact absolute paths");
52351
- const disabledVendorPaths = new Set(options.disabledVendorPaths ?? []);
52594
+ const reviewedCacheAlias = options.reviewedCacheAlias === undefined ? undefined : canonicalSystemPath(options.reviewedCacheAlias);
52595
+ const disabledVendorPaths = new Set((options.disabledVendorPaths ?? []).map(canonicalSystemPath));
52352
52596
  const aliases = rootAliases(home, options.allowRootAliases);
52353
52597
  const selectedAgents = options.agents ? new Set(options.agents) : undefined;
52354
52598
  const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
@@ -52359,6 +52603,9 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
52359
52603
  for (const [agent, path] of rootDefinitions)
52360
52604
  roots.push([agent, canonicalAgentPath(join21(project, path), aliases)]);
52361
52605
  }
52606
+ const installationInputs = options.codexInstallationInputRoots ?? [];
52607
+ if (installationInputs.some((input) => resolve11(input) !== input || realpathSync6(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep5) || input.startsWith(path + sep5)))))
52608
+ throw new Error("Native installation input overlaps an unconditional skill-loading root");
52362
52609
  const entries = [], seen = new Set;
52363
52610
  const cacheScans = new Map, maxAliasProofEntries = 1e4;
52364
52611
  let discoveryEntries = 0, discoveryPathBytes = 0;
@@ -52371,8 +52618,11 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
52371
52618
  }
52372
52619
  let reviewedCacheAliasFound = false;
52373
52620
  function verifiedSiblingCacheAlias(agent, path, parent) {
52374
- const link2 = readlinkSync4(path), target = resolve11(parent, link2);
52375
- if (dirname11(target) !== parent || (isAbsolute11(link2) ? link2 !== target : dirname11(link2) !== "."))
52621
+ const link2 = readlinkSync4(path), lexicalTarget = resolve11(parent, link2);
52622
+ if (isAbsolute11(link2) ? link2 !== lexicalTarget : dirname11(link2) !== ".")
52623
+ return false;
52624
+ const target = canonicalSystemPath(lexicalTarget);
52625
+ if (dirname11(target) !== parent)
52376
52626
  return false;
52377
52627
  if (!lstatSync15(target, { throwIfNoEntry: false })?.isDirectory())
52378
52628
  return false;
@@ -52381,20 +52631,22 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
52381
52631
  return false;
52382
52632
  if (scan.hasSkills) {
52383
52633
  const targetSkills = entries.filter((entry) => entry.agent === agent && entry.vendor && (entry.path === target || entry.path.startsWith(target + sep5)));
52384
- if (path === options.reviewedCacheAlias && targetSkills.length > 0)
52634
+ if (path === reviewedCacheAlias && targetSkills.length > 0)
52385
52635
  reviewedCacheAliasFound = true;
52386
52636
  if (agent === "codex" && targetSkills.length > 0 && targetSkills.every((entry) => disabledVendorPaths.has(entry.path) || options.disabledVendorSkill?.(entry) === true))
52387
52637
  return true;
52388
- if (path !== options.reviewedCacheAlias)
52638
+ if (path !== reviewedCacheAlias)
52389
52639
  return false;
52390
52640
  }
52391
52641
  return true;
52392
52642
  }
52393
- function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false) {
52643
+ function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false, configuredRoot = false) {
52394
52644
  if (!admitted)
52395
52645
  admit(path);
52396
52646
  const scan = { complete: true, hasSkills: false, entries: 1 };
52397
52647
  assertSafePath(path);
52648
+ if (configuredRoot && agent === "codex" && installationInputs.includes(path))
52649
+ return scan;
52398
52650
  if (!existsSync12(path))
52399
52651
  return scan;
52400
52652
  const stat = lstatSync15(path);
@@ -52450,7 +52702,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
52450
52702
  continue;
52451
52703
  }
52452
52704
  }
52453
- const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true);
52705
+ const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true, configuredRoot);
52454
52706
  scan.complete &&= childScan.complete;
52455
52707
  scan.hasSkills ||= childScan.hasSkills;
52456
52708
  scan.entries += childScan.entries;
@@ -52459,10 +52711,10 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
52459
52711
  cacheScans.set(path, scan);
52460
52712
  return scan;
52461
52713
  }
52462
- function scanRoot(agent, path, vendor = false, pluginCache = false) {
52714
+ function scanRoot(agent, path, vendor = false, pluginCache = false, configuredRoot = false) {
52463
52715
  if (agent === "hermes" || options.guardHermes && `${path}${sep5}`.includes(`${sep5}.agents${sep5}skills${sep5}`))
52464
52716
  assertNoHermesLegacyShadow(path);
52465
- if (!visit2(agent, path, vendor, 0, pluginCache).complete)
52717
+ if (!visit2(agent, path, vendor, 0, pluginCache, false, configuredRoot).complete)
52466
52718
  throw new Error(`Native skill discovery limit exceeded; review this root before continuing: ${path}`);
52467
52719
  }
52468
52720
  for (const [agent, path] of roots)
@@ -52478,7 +52730,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
52478
52730
  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 }))) : [];
52479
52731
  for (const root of [...options.agentRoots ?? [], ...configured])
52480
52732
  if (includesAgent(root.agent))
52481
- scanRoot(root.agent, canonicalAgentPath(root.path, aliases), true);
52733
+ scanRoot(root.agent, canonicalAgentPath(root.path, aliases), true, false, true);
52482
52734
  if (options.reviewedCacheAlias !== undefined && !reviewedCacheAliasFound)
52483
52735
  throw new Error("Reviewed cache alias was not an inventoried skill-containing sibling alias");
52484
52736
  recheckRootAliases(aliases);
@@ -52618,6 +52870,11 @@ enabled = false
52618
52870
  Bun.TOML.parse(result2);
52619
52871
  return result2;
52620
52872
  }
52873
+ function assertCodexInstallationInputs(home, projectDirs, inputs) {
52874
+ const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve11(directory, path)));
52875
+ if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep5) || input.startsWith(root + sep5))))
52876
+ throw new Error("Native installation input overlaps an unconditional skill-loading root");
52877
+ }
52621
52878
  function planAgentIntegration(options) {
52622
52879
  const home = options.home ?? homedir3(), dataDir3 = options.dataDir ?? getDataDirReadOnly();
52623
52880
  const aliases = rootAliases(home, options.allowRootAliases);
@@ -52656,11 +52913,16 @@ function planAgentIntegration(options) {
52656
52913
  const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(join21(home, ".codex", "config.toml"), aliases)) ?? "") : {};
52657
52914
  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) });
52658
52915
  const observedNativeSources = [];
52659
- const codexPluginSkills = options.codexNativeCatalog ? reviewCodexPluginSkillControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join21(entry.path, "SKILL.md")), canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), resolve11(options.projectDir ?? home), (path) => {
52916
+ const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join21(entry.path, "SKILL.md")), canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), resolve11(options.projectDir ?? home), (path) => {
52660
52917
  const bytes = readNativeBytes(path, 1024 * 1024);
52661
52918
  observedNativeSources.push({ path, sha256: sha3(bytes) });
52662
52919
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
52663
- }, codexConfig.skills?.config ?? []) : policy.bridge?.codexPluginSkills ?? [];
52920
+ }, codexConfig.skills?.config ?? [], codexConfig.plugins) : { skills: policy.bridge?.codexPluginSkills ?? [], inactivePlugins: policy.bridge?.codexInactivePlugins ?? [], sourceInputs: policy.bridge?.discovery?.codex?.codexInstallationInputs?.plugins ?? [] };
52921
+ const codexPluginSkills = codexPluginControls.skills;
52922
+ const codexInactivePlugins = codexPluginControls.inactivePlugins;
52923
+ const codexDiscovery = discoveries.find((binding) => binding.agent === "codex");
52924
+ const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot + sep5) || input.sourceRoot.startsWith(root + sep5)) && codexDiscovery.sources.some((source) => source.path === join21(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
52925
+ assertCodexInstallationInputs(home, [options.projectDir ?? home], codexPluginSourceInputs.map((input) => input.sourceRoot));
52664
52926
  const changes = [];
52665
52927
  for (const agent of [...new Set(options.agents)]) {
52666
52928
  if (!INTEGRATION_AGENTS.includes(agent))
@@ -52725,7 +52987,16 @@ function planAgentIntegration(options) {
52725
52987
  changes.push({ path: configPath, before: previous, after: next });
52726
52988
  }
52727
52989
  }
52728
- const discoveryAfter = discoveries.map((binding) => rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after]))));
52990
+ const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(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(join21(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join21(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview;
52991
+ const discoveryAfter = discoveries.map((binding) => {
52992
+ const rebound = rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after])));
52993
+ if (binding.agent !== "codex")
52994
+ return rebound;
52995
+ const { codexInstallationInputs: previousInputs, ...current } = rebound;
52996
+ const inputs = codexPluginSourceInputs.map((input) => input.sourceRoot);
52997
+ const directories = options.codexNativeCatalog || !previousInputs ? captureDiscoveryDirectories((binding.directories ?? []).filter((directory) => inputs.some((root) => root.startsWith(directory.path + sep5))).map((directory) => directory.path), inputs) : previousInputs.directories;
52998
+ return codexPluginSourceInputs.length ? { ...current, codexInstallationInputs: { version: "codex-cli 0.160.0", catalogSha256: codexPluginSkillReview.catalogSha256, plugins: codexPluginSourceInputs, ...directories?.length ? { directories } : {} } } : current;
52999
+ });
52729
53000
  const nextPolicy = { ...policy, version: 1, loading: "cli", profileId, bridge: {
52730
53001
  ...policy.bridge,
52731
53002
  version: CLI_BRIDGE_VERSION,
@@ -52738,7 +53009,7 @@ function planAgentIntegration(options) {
52738
53009
  profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
52739
53010
  disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join21(home, ".codex", "skills", ".system"), aliases) + sep5)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
52740
53011
  rootAliases: aliases,
52741
- ...codexPluginSkills.length ? { codexPluginSkills, codexPluginSkillReview: options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(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(join21(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join21(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview } : {},
53012
+ ...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
52742
53013
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
52743
53014
  } };
52744
53015
  const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
@@ -53148,7 +53419,12 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
53148
53419
  throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
53149
53420
  if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024))))
53150
53421
  throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
53151
- const disabledPlugin = (entry) => agent === "codex" && entry.vendor && isReviewedCodexPluginSkillDisabled(join21(entry.path, "SKILL.md"), canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], codexConfig.skills?.config, (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
53422
+ const disabledPlugin = (entry) => {
53423
+ if (agent !== "codex" || !entry.vendor)
53424
+ return false;
53425
+ const document3 = join21(entry.path, "SKILL.md"), cache3 = canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024));
53426
+ return isReviewedCodexPluginInactive(document3, cache3, binding.codexInactivePlugins ?? [], codexConfig.plugins, codexConfig.skills?.config ?? [], read2) || isReviewedCodexPluginSkillDisabled(document3, cache3, binding.codexPluginSkills ?? [], codexConfig.skills?.config, read2);
53427
+ };
53152
53428
  const disabledPaths = new Set(disabledCodexSkillPaths(codexConfig, aliases));
53153
53429
  const disabledVendorSkill = (entry) => {
53154
53430
  if (agent !== "codex" || !entry.vendor)
@@ -53185,7 +53461,15 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
53185
53461
  } catch (error) {
53186
53462
  throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
53187
53463
  }
53188
- 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 });
53464
+ let installationInputRoots = [];
53465
+ if (agent === "codex") {
53466
+ try {
53467
+ installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), discovery.codexInstallationInputs?.plugins ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
53468
+ } catch {
53469
+ throw new Error("NATIVE_SKILL_DRIFT: native installation input identity changed; review discovery and catalog");
53470
+ }
53471
+ }
53472
+ 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 });
53189
53473
  const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !disabledVendorSkill(entry));
53190
53474
  if (unexpected.length) {
53191
53475
  const paths = unexpected.slice(0, 3).map((entry) => {
@@ -56557,8 +56841,16 @@ function verifyAgentDiscovery(binding, codexRecovery) {
56557
56841
  throw new Error("Invalid native discovery binding");
56558
56842
  if (binding.agent === "hermes" && !binding.directories?.length)
56559
56843
  throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
56560
- if (binding.directories !== undefined)
56561
- verifyDiscoveryDirectories(binding.directories);
56844
+ const installationRoots = codexInstallationRoots(binding);
56845
+ const installationInput = (path) => installationRoots.some((root2) => path === root2 || path.startsWith(root2 + sep6));
56846
+ if (binding.directories !== undefined) {
56847
+ const projected2 = binding.codexInstallationInputs?.directories ?? [];
56848
+ const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root2) => root2.startsWith(directory.path + sep6)));
56849
+ if (projected2.length !== ancestors2.length || projected2.some((directory) => !ancestors2.some((original) => original.path === directory.path)))
56850
+ throw new Error("Missing installation input directory projection");
56851
+ verifyDiscoveryDirectories(binding.directories.filter((directory) => !installationInput(directory.path) && !ancestors2.includes(directory)));
56852
+ verifyDiscoveryDirectories(projected2, installationRoots);
56853
+ }
56562
56854
  const budget = discoveryByteBudget();
56563
56855
  for (const source of binding.sources) {
56564
56856
  assertMarketplaceBinding(binding, source);
@@ -56568,6 +56860,10 @@ function verifyAgentDiscovery(binding, codexRecovery) {
56568
56860
  throw new Error("Invalid native discovery projection");
56569
56861
  if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
56570
56862
  throw new Error("Invalid native discovery digest");
56863
+ if (installationInput(source.path)) {
56864
+ safe4(source.path);
56865
+ continue;
56866
+ }
56571
56867
  const current = projected(source, undefined, budget);
56572
56868
  if (current !== source.sha256 && !verifiesCodexHookDiscoverySource(binding, source, current, codexRecovery))
56573
56869
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
@@ -56575,6 +56871,27 @@ function verifyAgentDiscovery(binding, codexRecovery) {
56575
56871
  for (const root2 of binding.roots)
56576
56872
  safe4(root2);
56577
56873
  }
56874
+ function codexInstallationRoots(binding) {
56875
+ const proof = binding.codexInstallationInputs;
56876
+ if (proof === undefined)
56877
+ return [];
56878
+ 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)
56879
+ throw new Error("Invalid native installation input proof");
56880
+ const cache3 = dirname17(dirname17(proof.plugins[0].pluginParent));
56881
+ const config = join32(dirname17(dirname17(cache3)), "config.toml"), home = dirname17(dirname17(dirname17(cache3)));
56882
+ const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
56883
+ const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join32(home, path));
56884
+ if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep6) || input.sourceRoot.startsWith(root2 + sep6)) || binding.sources.some((source) => basename9(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep6)))))
56885
+ throw new Error("Native installation input overlaps configuration or loading roots");
56886
+ if (proof.plugins.some((input) => dirname17(dirname17(input.pluginParent)) !== cache3 || !binding.sources.some((source) => source.path === join32(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
56887
+ throw new Error("Missing native installation input source witness");
56888
+ return reviewedCodexPluginSourceRoots(cache3, proof.plugins, (path) => {
56889
+ const text3 = read2(path);
56890
+ if (text3 === null)
56891
+ throw new Error("Missing native installation input");
56892
+ return text3;
56893
+ });
56894
+ }
56578
56895
  function assertMarketplaceBinding(binding, source) {
56579
56896
  if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
56580
56897
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
@@ -56585,8 +56902,11 @@ function assertMarketplaceBinding(binding, source) {
56585
56902
  }
56586
56903
  function rebindAgentDiscovery(binding, changes) {
56587
56904
  const budget = discoveryByteBudget();
56905
+ const installationRoots = codexInstallationRoots(binding);
56588
56906
  return { ...binding, sources: binding.sources.map((source) => {
56589
56907
  assertMarketplaceBinding(binding, source);
56908
+ if (!changes.has(source.path) && installationRoots.some((root2) => source.path.startsWith(root2 + sep6)))
56909
+ return source;
56590
56910
  const sha2562 = projected(source, changes, budget);
56591
56911
  if (!changes.has(source.path) && sha2562 !== source.sha256)
56592
56912
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
@@ -56857,6 +57177,8 @@ var init_agent_discovery = __esm(() => {
56857
57177
  init_claude_settings_witness();
56858
57178
  init_codex_hook_discovery_recovery();
56859
57179
  init_codex_settings_witness();
57180
+ init_codex_plugin_skill_controls();
57181
+ init_native_discovery_roots();
56860
57182
  init_agent_discovery_directories();
56861
57183
  });
56862
57184