@hasna/skills 0.10.24 → 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.
@@ -9,10 +9,37 @@ export interface CodexPluginSkillControl {
9
9
  mcpSha256?: string;
10
10
  remotePluginId?: string;
11
11
  }
12
+ /** A native nonremote installation disabled by the witnessed configuration.
13
+ * Its cache is inert; this makes no claim about reviewed hook capabilities. */
14
+ export interface CodexInactivePluginControl {
15
+ pluginId: string;
16
+ namespace: string;
17
+ pluginParent: string;
18
+ sourceType: "local" | "git" | "npm";
19
+ sourceSha256: string;
20
+ }
21
+ export interface CodexPluginSourceInput {
22
+ pluginId: string;
23
+ namespace: string;
24
+ pluginParent: string;
25
+ sourceRoot: string;
26
+ sourceSha256: string;
27
+ }
12
28
  type Read = (path: string) => string;
13
29
  /** Capability controls remain bound even after every native Skill body disappears. */
14
30
  export declare function reviewedCodexPluginCapabilitiesUnchanged(cache: string, controls: CodexPluginSkillControl[], read: Read): boolean;
15
- export declare function reviewCodexPluginSkillControls(catalog: CodexNativeSkillCatalog, documents: string[], cache: string, cwd: string, read: Read, rules: unknown): CodexPluginSkillControl[];
31
+ export declare function reviewCodexPluginControls(catalog: CodexNativeSkillCatalog, documents: string[], cache: string, cwd: string, read: Read, rules: unknown, pluginSettings?: unknown): {
32
+ skills: CodexPluginSkillControl[];
33
+ inactivePlugins: CodexInactivePluginControl[];
34
+ sourceInputs: CodexPluginSourceInput[];
35
+ };
36
+ /** Validate installation-input roles before omitting them from runtime inventory.
37
+ * Discovery witnesses still cover the sources; migration inventory stays full. */
38
+ export declare function reviewedCodexPluginSourceRoots(cache: string, inputs: CodexPluginSourceInput[], read: Read): string[];
39
+ export declare function reviewCodexPluginSkillControls(catalog: CodexNativeSkillCatalog, documents: string[], cache: string, cwd: string, read: Read, rules: unknown, pluginSettings?: unknown): CodexPluginSkillControl[];
40
+ /** Cache refresh cannot reactivate a config-controlled disabled installation.
41
+ * The caller verifies discovery/config witnesses before consuming this result. */
42
+ export declare function isReviewedCodexPluginInactive(document: string, cache: string, controls: CodexInactivePluginControl[], pluginSettings: unknown, rules: unknown, read: Read): boolean;
16
43
  export declare function isReviewedCodexPluginSkillDisabled(document: string, cache: string, controls: CodexPluginSkillControl[], rules: unknown, read: Read): boolean;
17
44
  export declare function disableReviewedCodexPluginNames(text: string, controls: CodexPluginSkillControl[]): string;
18
45
  export {};
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.24",
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
  });
@@ -38451,7 +38469,7 @@ function rootControls(root, read) {
38451
38469
  const appSha256 = present ? appControls(app, read) : undefined;
38452
38470
  return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
38453
38471
  }
38454
- function identity(document, cache, read) {
38472
+ function documentIdentity(document, cache, read) {
38455
38473
  if (!document.startsWith(cache + sep2) || !document.endsWith(sep2 + "SKILL.md"))
38456
38474
  refuse3();
38457
38475
  const text = read(document);
@@ -38479,11 +38497,33 @@ function identity(document, cache, read) {
38479
38497
  const path = join10(root, ".codex-plugin", "plugin.json");
38480
38498
  if (!lstatSync6(path, { throwIfNoEntry: false }))
38481
38499
  continue;
38482
- const controls = rootControls(root, read);
38483
- 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 };
38484
38517
  }
38485
38518
  refuse3();
38486
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
+ }
38487
38527
  function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read) {
38488
38528
  if (!Array.isArray(controls) || controls.length > 4096)
38489
38529
  return false;
@@ -38521,11 +38561,13 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read) {
38521
38561
  return false;
38522
38562
  }
38523
38563
  }
38524
- function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read, rules) {
38564
+ function reviewCodexPluginControls(catalog, documents, cache, cwd, read, rules, pluginSettings) {
38525
38565
  if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd)
38526
38566
  refuse3();
38527
38567
  const skills = projectCodexNativeSkillCatalog({ data: [{ cwd, errors: [], skills: catalog.skills }] }, cwd);
38528
- 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;
38529
38571
  let installedPlugins;
38530
38572
  try {
38531
38573
  installedPlugins = projectCodexInstalledPluginEntries(catalog.plugins ?? []);
@@ -38536,25 +38578,48 @@ function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read, ru
38536
38578
  for (const document of candidates) {
38537
38579
  if (!document.startsWith(cache + sep2))
38538
38580
  continue;
38539
- const parsed = identity(document, cache, read);
38540
- 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"))
38541
38583
  refuse3();
38542
- const cacheParts = relative3(cache, parsed.pluginParent).split(sep2);
38584
+ const cacheParts = relative3(cache, located.pluginParent).split(sep2);
38543
38585
  if (cacheParts.length !== 2 || cacheParts.some((part) => !identifier3(part)))
38544
38586
  refuse3();
38545
38587
  const [marketplace, pluginName] = cacheParts;
38546
- if (pluginName !== parsed.namespace)
38588
+ if (pluginName !== located.namespace)
38547
38589
  refuse3();
38548
38590
  const expectedPluginId = `${pluginName}@${marketplace}`;
38549
38591
  const listed = skills.filter((skill) => skill.path === document);
38550
38592
  if (listed.length > 1)
38551
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();
38552
38618
  let remotePluginId;
38553
38619
  if (listed.length === 1) {
38554
38620
  if (listed[0].name !== parsed.name || listed[0].pluginId !== expectedPluginId)
38555
38621
  refuse3();
38556
38622
  } else {
38557
- const installed = installedPlugins.filter((plugin2) => plugin2.id === expectedPluginId && plugin2.name === pluginName);
38558
38623
  if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled)
38559
38624
  refuse3();
38560
38625
  const plugin = installed[0];
@@ -38572,9 +38637,48 @@ function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read, ru
38572
38637
  }
38573
38638
  result.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
38574
38639
  }
38575
- 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)
38576
38641
  refuse3();
38577
- 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
+ }
38578
38682
  }
38579
38683
  function isReviewedCodexPluginSkillDisabled(document, cache, controls, rules, read) {
38580
38684
  if (!document.startsWith(cache + sep2) || !Array.isArray(controls) || controls.length > 4096)
@@ -38950,6 +39054,18 @@ function assertAgentPolicyCollections(policy) {
38950
39054
  if (control.appSha256 !== undefined)
38951
39055
  requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
38952
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
+ }
38953
39069
  if (bridge.disabledBuiltins !== undefined)
38954
39070
  for (const builtin of array(bridge.disabledBuiltins, AGENT_POLICY_LIMITS.builtinNames)) {
38955
39071
  requireBound(object3(builtin));
@@ -38962,6 +39078,25 @@ function assertAgentPolicyCollections(policy) {
38962
39078
  text(agent, 128);
38963
39079
  requireBound(object3(value));
38964
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
+ }
38965
39100
  for (const root of array(value.roots, AGENT_POLICY_LIMITS.discoveryRoots))
38966
39101
  text(root);
38967
39102
  for (const source of array(value.sources, AGENT_POLICY_LIMITS.discoverySources)) {
@@ -39404,7 +39539,7 @@ function safe(path) {
39404
39539
  function same(before, after) {
39405
39540
  return Boolean(after && after.isDirectory() && before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs);
39406
39541
  }
39407
- function capture(path, budget) {
39542
+ function capture(path, budget, excludedRoots) {
39408
39543
  safe(path);
39409
39544
  const root = lstatSync10(path, { throwIfNoEntry: false });
39410
39545
  if (!root)
@@ -39433,7 +39568,7 @@ function capture(path, budget) {
39433
39568
  budget.bytes += Buffer.byteLength(row);
39434
39569
  need4(budget.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
39435
39570
  hash2.update(row);
39436
- if (stat.isDirectory())
39571
+ if (stat.isDirectory() && !excludedRoots.includes(child))
39437
39572
  walk2(child, childRelative, stat, depth + 1);
39438
39573
  }
39439
39574
  need4(same(before, lstatSync10(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
@@ -39442,16 +39577,18 @@ function capture(path, budget) {
39442
39577
  safe(path);
39443
39578
  return { path, sha256: hash2.digest("hex") };
39444
39579
  }
39445
- function captureDiscoveryDirectories(paths) {
39580
+ function captureDiscoveryDirectories(paths, excludedRoots = []) {
39446
39581
  need4(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
39447
39582
  const budget = { entries: 0, bytes: 0 };
39448
- 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));
39449
39586
  }
39450
- function verifyDiscoveryDirectories(directories) {
39587
+ function verifyDiscoveryDirectories(directories, excludedRoots = []) {
39451
39588
  need4(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
39452
39589
  for (const directory of directories)
39453
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");
39454
- const current = captureDiscoveryDirectories(directories.map((directory) => directory.path));
39591
+ const current = captureDiscoveryDirectories(directories.map((directory) => directory.path), excludedRoots);
39455
39592
  for (let i = 0;i < directories.length; i++)
39456
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}`);
39457
39594
  }
@@ -41538,8 +41675,16 @@ function verifyAgentDiscovery(binding, codexRecovery) {
41538
41675
  throw new Error("Invalid native discovery binding");
41539
41676
  if (binding.agent === "hermes" && !binding.directories?.length)
41540
41677
  throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
41541
- if (binding.directories !== undefined)
41542
- 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
+ }
41543
41688
  const budget = discoveryByteBudget();
41544
41689
  for (const source of binding.sources) {
41545
41690
  assertMarketplaceBinding(binding, source);
@@ -41549,6 +41694,10 @@ function verifyAgentDiscovery(binding, codexRecovery) {
41549
41694
  throw new Error("Invalid native discovery projection");
41550
41695
  if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
41551
41696
  throw new Error("Invalid native discovery digest");
41697
+ if (installationInput(source.path)) {
41698
+ safe4(source.path);
41699
+ continue;
41700
+ }
41552
41701
  const current = projected(source, undefined, budget);
41553
41702
  if (current !== source.sha256 && !verifiesCodexHookDiscoverySource(binding, source, current, codexRecovery))
41554
41703
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
@@ -41556,6 +41705,27 @@ function verifyAgentDiscovery(binding, codexRecovery) {
41556
41705
  for (const root2 of binding.roots)
41557
41706
  safe4(root2);
41558
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
+ }
41559
41729
  function assertMarketplaceBinding(binding, source) {
41560
41730
  if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename7(source.path) !== "config.toml"))
41561
41731
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
@@ -41566,8 +41736,11 @@ function assertMarketplaceBinding(binding, source) {
41566
41736
  }
41567
41737
  function rebindAgentDiscovery(binding, changes) {
41568
41738
  const budget = discoveryByteBudget();
41739
+ const installationRoots = codexInstallationRoots(binding);
41569
41740
  return { ...binding, sources: binding.sources.map((source) => {
41570
41741
  assertMarketplaceBinding(binding, source);
41742
+ if (!changes.has(source.path) && installationRoots.some((root2) => source.path.startsWith(root2 + sep4)))
41743
+ return source;
41571
41744
  const sha2562 = projected(source, changes, budget);
41572
41745
  if (!changes.has(source.path) && sha2562 !== source.sha256)
41573
41746
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
@@ -42435,6 +42608,9 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
42435
42608
  for (const [agent, path] of rootDefinitions)
42436
42609
  roots.push([agent, canonicalAgentPath(join22(project, path), aliases)]);
42437
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");
42438
42614
  const entries = [], seen = new Set;
42439
42615
  const cacheScans = new Map, maxAliasProofEntries = 1e4;
42440
42616
  let discoveryEntries = 0, discoveryPathBytes = 0;
@@ -42469,11 +42645,13 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
42469
42645
  }
42470
42646
  return true;
42471
42647
  }
42472
- 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) {
42473
42649
  if (!admitted)
42474
42650
  admit(path);
42475
42651
  const scan = { complete: true, hasSkills: false, entries: 1 };
42476
42652
  assertSafePath(path);
42653
+ if (configuredRoot && agent === "codex" && installationInputs.includes(path))
42654
+ return scan;
42477
42655
  if (!existsSync6(path))
42478
42656
  return scan;
42479
42657
  const stat2 = lstatSync19(path);
@@ -42529,7 +42707,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
42529
42707
  continue;
42530
42708
  }
42531
42709
  }
42532
- const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true);
42710
+ const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true, configuredRoot);
42533
42711
  scan.complete &&= childScan.complete;
42534
42712
  scan.hasSkills ||= childScan.hasSkills;
42535
42713
  scan.entries += childScan.entries;
@@ -42538,10 +42716,10 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
42538
42716
  cacheScans.set(path, scan);
42539
42717
  return scan;
42540
42718
  }
42541
- function scanRoot(agent, path, vendor = false, pluginCache = false) {
42719
+ function scanRoot(agent, path, vendor = false, pluginCache = false, configuredRoot = false) {
42542
42720
  if (agent === "hermes" || options.guardHermes && `${path}${sep5}`.includes(`${sep5}.agents${sep5}skills${sep5}`))
42543
42721
  assertNoHermesLegacyShadow(path);
42544
- if (!visit2(agent, path, vendor, 0, pluginCache).complete)
42722
+ if (!visit2(agent, path, vendor, 0, pluginCache, false, configuredRoot).complete)
42545
42723
  throw new Error(`Native skill discovery limit exceeded; review this root before continuing: ${path}`);
42546
42724
  }
42547
42725
  for (const [agent, path] of roots)
@@ -42557,7 +42735,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
42557
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 }))) : [];
42558
42736
  for (const root2 of [...options.agentRoots ?? [], ...configured])
42559
42737
  if (includesAgent(root2.agent))
42560
- scanRoot(root2.agent, canonicalAgentPath(root2.path, aliases), true);
42738
+ scanRoot(root2.agent, canonicalAgentPath(root2.path, aliases), true, false, true);
42561
42739
  if (options.reviewedCacheAlias !== undefined && !reviewedCacheAliasFound)
42562
42740
  throw new Error("Reviewed cache alias was not an inventoried skill-containing sibling alias");
42563
42741
  recheckRootAliases(aliases);
@@ -42697,6 +42875,11 @@ enabled = false
42697
42875
  Bun.TOML.parse(result);
42698
42876
  return result;
42699
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
+ }
42700
42883
  function planAgentIntegration(options) {
42701
42884
  const home = options.home ?? homedir3(), dataDir2 = options.dataDir ?? getDataDirReadOnly();
42702
42885
  const aliases = rootAliases(home, options.allowRootAliases);
@@ -42735,11 +42918,16 @@ function planAgentIntegration(options) {
42735
42918
  const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(join22(home, ".codex", "config.toml"), aliases)) ?? "") : {};
42736
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) });
42737
42920
  const observedNativeSources = [];
42738
- 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) => {
42739
42922
  const bytes = readNativeBytes(path, 1024 * 1024);
42740
42923
  observedNativeSources.push({ path, sha256: sha4(bytes) });
42741
42924
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
42742
- }, 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));
42743
42931
  const changes = [];
42744
42932
  for (const agent of [...new Set(options.agents)]) {
42745
42933
  if (!INTEGRATION_AGENTS.includes(agent))
@@ -42804,7 +42992,16 @@ function planAgentIntegration(options) {
42804
42992
  changes.push({ path: configPath, before: previous, after: next });
42805
42993
  }
42806
42994
  }
42807
- 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
+ });
42808
43005
  const nextPolicy = { ...policy, version: 1, loading: "cli", profileId, bridge: {
42809
43006
  ...policy.bridge,
42810
43007
  version: CLI_BRIDGE_VERSION,
@@ -42817,7 +43014,7 @@ function planAgentIntegration(options) {
42817
43014
  profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
42818
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 ?? [],
42819
43016
  rootAliases: aliases,
42820
- ...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 } : {},
42821
43018
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
42822
43019
  } };
42823
43020
  const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
@@ -43308,7 +43505,12 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
43308
43505
  throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
43309
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))))
43310
43507
  throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
43311
- 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
+ };
43312
43514
  const disabledPaths = new Set(disabledCodexSkillPaths(codexConfig, aliases));
43313
43515
  const disabledVendorSkill = (entry) => {
43314
43516
  if (agent !== "codex" || !entry.vendor)
@@ -43345,7 +43547,15 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
43345
43547
  } catch (error) {
43346
43548
  throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
43347
43549
  }
43348
- 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 });
43349
43559
  const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !disabledVendorSkill(entry));
43350
43560
  if (unexpected.length) {
43351
43561
  const paths = unexpected.slice(0, 3).map((entry) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/skills",
3
- "version": "0.10.24",
3
+ "version": "0.10.25",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {