@hasna/skills 0.10.50 → 0.10.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5267,8 +5267,8 @@ var require_cst_visit = __commonJS((exports) => {
5267
5267
  visit.REMOVE = REMOVE;
5268
5268
  visit.itemAtPath = (cst, path) => {
5269
5269
  let item = cst;
5270
- for (const [field, index] of path) {
5271
- const tok = item?.[field];
5270
+ for (const [field2, index] of path) {
5271
+ const tok = item?.[field2];
5272
5272
  if (tok && "items" in tok) {
5273
5273
  item = tok.items[index];
5274
5274
  } else
@@ -5278,8 +5278,8 @@ var require_cst_visit = __commonJS((exports) => {
5278
5278
  };
5279
5279
  visit.parentCollection = (cst, path) => {
5280
5280
  const parent = visit.itemAtPath(cst, path.slice(0, -1));
5281
- const field = path[path.length - 1][0];
5282
- const coll = parent?.[field];
5281
+ const field2 = path[path.length - 1][0];
5282
+ const coll = parent?.[field2];
5283
5283
  if (coll && "items" in coll)
5284
5284
  return coll;
5285
5285
  throw new Error("Parent collection not found");
@@ -5288,11 +5288,11 @@ var require_cst_visit = __commonJS((exports) => {
5288
5288
  let ctrl = visitor(item, path);
5289
5289
  if (typeof ctrl === "symbol")
5290
5290
  return ctrl;
5291
- for (const field of ["key", "value"]) {
5292
- const token = item[field];
5291
+ for (const field2 of ["key", "value"]) {
5292
+ const token = item[field2];
5293
5293
  if (token && "items" in token) {
5294
5294
  for (let i = 0;i < token.items.length; ++i) {
5295
- const ci = _visit(Object.freeze(path.concat([[field, i]])), token.items[i], visitor);
5295
+ const ci = _visit(Object.freeze(path.concat([[field2, i]])), token.items[i], visitor);
5296
5296
  if (typeof ci === "number")
5297
5297
  i = ci - 1;
5298
5298
  else if (ci === BREAK)
@@ -5302,7 +5302,7 @@ var require_cst_visit = __commonJS((exports) => {
5302
5302
  i -= 1;
5303
5303
  }
5304
5304
  }
5305
- if (typeof ctrl === "function" && field === "key")
5305
+ if (typeof ctrl === "function" && field2 === "key")
5306
5306
  ctrl = ctrl(item, path);
5307
5307
  }
5308
5308
  }
@@ -5728,9 +5728,9 @@ var require_lexer = __commonJS((exports) => {
5728
5728
  }
5729
5729
  }
5730
5730
  *parseQuotedScalar() {
5731
- const quote = this.charAt(0);
5732
- let end = this.buffer.indexOf(quote, this.pos + 1);
5733
- if (quote === "'") {
5731
+ const quote2 = this.charAt(0);
5732
+ let end = this.buffer.indexOf(quote2, this.pos + 1);
5733
+ if (quote2 === "'") {
5734
5734
  while (end !== -1 && this.buffer[end + 1] === "'")
5735
5735
  end = this.buffer.indexOf("'", end + 2);
5736
5736
  } else {
@@ -7262,7 +7262,7 @@ import {
7262
7262
  statSync as statSync11,
7263
7263
  writeFileSync as writeFileSync9
7264
7264
  } from "fs";
7265
- import { basename as basename12, dirname as dirname21, isAbsolute as isAbsolute20, join as join34, normalize as normalize4, relative as relative7, resolve as resolve20, sep as sep10 } from "path";
7265
+ import { basename as basename13, dirname as dirname22, isAbsolute as isAbsolute22, join as join34, normalize as normalize4, relative as relative7, resolve as resolve22, sep as sep10 } from "path";
7266
7266
 
7267
7267
  // src/lib/registry-data/index.ts
7268
7268
  var SKILLS = [];
@@ -8922,7 +8922,7 @@ var NATIVE_SKILL_ROOTS = [
8922
8922
  // src/lib/authoring-path.ts
8923
8923
  import { lstatSync as lstatSync26, realpathSync as realpathSync12 } from "fs";
8924
8924
  import { homedir as homedir6 } from "os";
8925
- import { basename as basename11, dirname as dirname20, join as join33, resolve as resolve19, sep as sep9 } from "path";
8925
+ import { basename as basename12, dirname as dirname21, join as join33, resolve as resolve21, sep as sep9 } from "path";
8926
8926
 
8927
8927
  // src/lib/agent-adapters.ts
8928
8928
  var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes", "sumi"];
@@ -9126,6 +9126,10 @@ function parse(text) {
9126
9126
  need(root.entries.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.rows, "exceeds its settings field limit");
9127
9127
  return root;
9128
9128
  }
9129
+ function parseNativeJsonObject(text) {
9130
+ need(typeof text === "string" && Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
9131
+ return parse(text);
9132
+ }
9129
9133
  var BOOLEAN_PREFERENCES = new Set([
9130
9134
  "autoScrollEnabled",
9131
9135
  "axScreenReader",
@@ -9379,7 +9383,222 @@ function upgradeSumiSettingsWitness(previous, reviewedSettingsPath) {
9379
9383
  }
9380
9384
 
9381
9385
  // src/lib/agent-policy-limits.ts
9382
- import { basename as basename3, isAbsolute as isAbsolute3, join as join7, resolve as resolve3, sep as sep2 } from "path";
9386
+ import { basename as basename4, isAbsolute as isAbsolute4, join as join7, resolve as resolve4, sep as sep2 } from "path";
9387
+
9388
+ // src/lib/codex-native-compatibility.ts
9389
+ var MEASURED_CODEX_CAPABILITIES = {
9390
+ "codex-cli 0.153.0": ["hooks"],
9391
+ "codex-cli 0.154.0": ["hooks"],
9392
+ "codex-cli 0.155.0": ["hooks"],
9393
+ "codex-cli 0.155.1": ["hooks"],
9394
+ "codex-cli 0.156.1": ["hooks"],
9395
+ "codex-cli 0.157.0": ["hooks"],
9396
+ "codex-cli 0.157.1": ["hooks"],
9397
+ "codex-cli 0.158.0": ["hooks"],
9398
+ "codex-cli 0.159.0": ["hooks"],
9399
+ "codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
9400
+ "codex-cli 0.160.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
9401
+ "codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
9402
+ };
9403
+ function supportsCodexNativeCapability(version, capability) {
9404
+ return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
9405
+ }
9406
+ var SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
9407
+
9408
+ // src/lib/claude-marketplace-entry-witness.ts
9409
+ import { createHash as createHash4 } from "crypto";
9410
+ import { basename as basename3, dirname as dirname4, isAbsolute as isAbsolute3, resolve as resolve3 } from "path";
9411
+ var CLAUDE_MARKETPLACE_ENTRY_HASH_MODE = "claude-marketplace-entry-v1";
9412
+ var DOMAIN = "hasna.skills.claude-marketplace-entry.v1\x00";
9413
+ var PATH_CHARACTERS = 4096;
9414
+ var NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
9415
+ function need2(value, reason) {
9416
+ if (!value)
9417
+ throw new Error(`Claude marketplace entry witness ${reason}`);
9418
+ }
9419
+ var bounded = (key) => key.length > 64 ? `${key.slice(0, 64)}...` : key;
9420
+ var quote = (text) => JSON.stringify(text).replace(/[^\x20-\x7e]/g, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
9421
+ var child = (path, key) => path + (/^[A-Za-z0-9_$-]{1,64}$/.test(key) ? `.${key}` : `[${JSON.stringify(bounded(key))}]`);
9422
+ function unknownKey(selector, path, key) {
9423
+ throw new Error(path === null ? `${CLAUDE_MARKETPLACE_ENTRY_HASH_MODE}: unknown top-level key ${quote(bounded(key))} in ${selector.marketplace}` : `${CLAUDE_MARKETPLACE_ENTRY_HASH_MODE}: unknown key ${quote(child(path, key))} in ${selector.marketplace}`);
9424
+ }
9425
+ function knownKeys(object, allowed, selector, path) {
9426
+ for (const [key] of object.entries)
9427
+ if (!allowed.has(key))
9428
+ unknownKey(selector, path, key);
9429
+ }
9430
+ var reason = (error) => String(error?.message ?? error).replace(/^Claude settings witness /, "");
9431
+ var MARKETPLACE_KEYS = new Set(["$schema", "name", "owner", "plugins", "description", "version", "metadata", "forceRemoveDeletedPlugins", "allowCrossMarketplaceDependenciesOn", "renames"]);
9432
+ var MARKETPLACE_METADATA_KEYS = new Set(["description", "version", "pluginRoot"]);
9433
+ var ENTRY_BOUND_KEYS = new Set([
9434
+ "name",
9435
+ "source",
9436
+ "strict",
9437
+ "defaultEnabled",
9438
+ "dependencies",
9439
+ "relevance",
9440
+ "headers",
9441
+ "headersHelper",
9442
+ "settings",
9443
+ "userConfig",
9444
+ "types",
9445
+ "channels",
9446
+ "skills",
9447
+ "commands",
9448
+ "agents",
9449
+ "hooks",
9450
+ "mcpServers",
9451
+ "lspServers",
9452
+ "outputStyles",
9453
+ "workflows",
9454
+ "experimental",
9455
+ "themes",
9456
+ "monitors"
9457
+ ]);
9458
+ var ENTRY_OMITTED_KEYS = new Set(["$schema", "description", "version", "author", "homepage", "repository", "license", "keywords", "category", "tags", "displayName", "metadata"]);
9459
+ var EXPERIMENTAL_KEYS = new Set(["themes", "monitors", "evals"]);
9460
+ var SOURCE_KEYS = Object.freeze({
9461
+ github: ["source", "repo", "ref", "sha"],
9462
+ url: ["source", "url", "ref", "sha", "path"],
9463
+ "git-subdir": ["source", "url", "path", "ref", "sha"],
9464
+ npm: ["source", "package", "version", "registry"],
9465
+ archive: ["source", "url", "sha256"],
9466
+ command: ["source", "command", "timeout", "mode"]
9467
+ });
9468
+ var field = (object, key) => object.entries.find(([name]) => name === key)?.[1];
9469
+ var isString = (value) => value?.kind === "string";
9470
+ var isBoolean = (value) => value?.kind === "literal" && (value.value === "true" || value.value === "false");
9471
+ var stringArray = (value) => value?.kind === "array" && value.items.every(isString);
9472
+ var stringObject = (value) => value?.kind === "object" && value.entries.every(([, item]) => isString(item));
9473
+ function claudeMarketplaceEntrySourceValid(source) {
9474
+ const path = source?.path;
9475
+ return typeof path === "string" && path.length > 0 && path.length <= PATH_CHARACTERS && !/[\x00-\x1f\x7f]/.test(path) && isAbsolute3(path) && resolve3(path) === path && basename3(path) === "marketplace.json" && basename3(dirname4(path)) === ".claude-plugin" && typeof source.marketplace === "string" && NAME.test(source.marketplace) && !source.marketplace.includes("..") && typeof source.plugin === "string" && NAME.test(source.plugin);
9476
+ }
9477
+ function canonical2(value) {
9478
+ if (value.kind === "string")
9479
+ return JSON.stringify(value.value);
9480
+ if (value.kind === "number" || value.kind === "literal")
9481
+ return value.value;
9482
+ if (value.kind === "array")
9483
+ return "[" + value.items.map(canonical2).join(",") + "]";
9484
+ return "{" + [...value.entries].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, item]) => JSON.stringify(key) + ":" + canonical2(item)).join(",") + "}";
9485
+ }
9486
+ function validateMarketplace(root, selector) {
9487
+ knownKeys(root, MARKETPLACE_KEYS, selector, null);
9488
+ const name = field(root, "name");
9489
+ need2(isString(name) && name.value === selector.marketplace, "marketplace name does not match the reviewed binding");
9490
+ for (const key of ["$schema", "description", "version"]) {
9491
+ const value = field(root, key);
9492
+ need2(value === undefined || isString(value), `has an invalid marketplace ${key}`);
9493
+ }
9494
+ const owner = field(root, "owner");
9495
+ need2(owner === undefined || owner.kind === "object", "has an invalid marketplace owner");
9496
+ const metadata = field(root, "metadata");
9497
+ if (metadata !== undefined) {
9498
+ need2(metadata.kind === "object", "has an invalid marketplace metadata object");
9499
+ knownKeys(metadata, MARKETPLACE_METADATA_KEYS, selector, "metadata");
9500
+ for (const [key, value] of metadata.entries)
9501
+ need2(isString(value), `has an invalid marketplace metadata.${key}`);
9502
+ }
9503
+ const remove = field(root, "forceRemoveDeletedPlugins");
9504
+ need2(remove === undefined || isBoolean(remove), "has an invalid forceRemoveDeletedPlugins value");
9505
+ const cross = field(root, "allowCrossMarketplaceDependenciesOn");
9506
+ need2(cross === undefined || stringArray(cross), "has an invalid allowCrossMarketplaceDependenciesOn value");
9507
+ const renames = field(root, "renames");
9508
+ if (renames !== undefined) {
9509
+ need2(renames.kind === "object" && renames.entries.every(([, target]) => isString(target) || target.kind === "literal" && target.value === "null"), "has an invalid renames map");
9510
+ need2(!renames.entries.some(([from]) => from.toLowerCase() === selector.plugin.toLowerCase()), "plugin id is redirected by the marketplace renames map");
9511
+ }
9512
+ }
9513
+ function validateSource(source, selector, path) {
9514
+ if (isString(source)) {
9515
+ need2(source.value.length > 0, "has an empty plugin source");
9516
+ return;
9517
+ }
9518
+ need2(source?.kind === "object", "has an invalid plugin source");
9519
+ const type = field(source, "source");
9520
+ need2(isString(type) && Object.hasOwn(SOURCE_KEYS, type.value), "has an unreviewed plugin source type");
9521
+ knownKeys(source, new Set(SOURCE_KEYS[type.value]), selector, path);
9522
+ }
9523
+ function projectEntry(entry, selector) {
9524
+ const path = `plugins[${selector.plugin}]`;
9525
+ knownKeys(entry, { has: (key) => ENTRY_BOUND_KEYS.has(key) || ENTRY_OMITTED_KEYS.has(key) }, selector, path);
9526
+ const name = field(entry, "name");
9527
+ need2(isString(name) && name.value === selector.plugin, "selected entry name does not match");
9528
+ validateSource(field(entry, "source"), selector, `${path}.source`);
9529
+ for (const key of ["strict", "defaultEnabled"]) {
9530
+ const value = field(entry, key);
9531
+ need2(value === undefined || isBoolean(value), `has an invalid entry ${key}`);
9532
+ }
9533
+ const headersHelper = field(entry, "headersHelper");
9534
+ need2(headersHelper === undefined || isString(headersHelper), "has an invalid entry headersHelper");
9535
+ const experimental = field(entry, "experimental");
9536
+ if (experimental !== undefined) {
9537
+ need2(experimental.kind === "object", "has an invalid entry experimental object");
9538
+ knownKeys(experimental, EXPERIMENTAL_KEYS, selector, `${path}.experimental`);
9539
+ }
9540
+ for (const key of ["$schema", "description", "version", "homepage", "license", "category", "displayName"]) {
9541
+ const value = field(entry, key);
9542
+ need2(value === undefined || isString(value), `has an invalid entry ${key}`);
9543
+ }
9544
+ for (const [key, keys] of [["author", new Set(["name", "email", "url"])], ["repository", new Set(["type", "url", "directory"])]]) {
9545
+ const value = field(entry, key);
9546
+ if (value?.kind === "object")
9547
+ knownKeys(value, keys, selector, `${path}.${key}`);
9548
+ need2(value === undefined || isString(value) || stringObject(value), `has an invalid entry ${key}`);
9549
+ }
9550
+ for (const key of ["keywords", "tags"]) {
9551
+ const value = field(entry, key);
9552
+ need2(value === undefined || stringArray(value), `has an invalid entry ${key}`);
9553
+ }
9554
+ const metadata = field(entry, "metadata");
9555
+ need2(metadata === undefined || metadata.kind === "object", "has an invalid entry metadata object");
9556
+ return { kind: "object", entries: entry.entries.filter(([key]) => ENTRY_BOUND_KEYS.has(key)) };
9557
+ }
9558
+ function hashClaudeMarketplaceEntry(text, selector) {
9559
+ need2(selector && typeof selector.marketplace === "string" && NAME.test(selector.marketplace) && !selector.marketplace.includes("..") && typeof selector.plugin === "string" && NAME.test(selector.plugin), "requires an exact marketplace and plugin name");
9560
+ let root;
9561
+ try {
9562
+ root = parseNativeJsonObject(text);
9563
+ } catch (error) {
9564
+ throw new Error(`Claude marketplace entry witness cannot parse marketplace.json: ${reason(error)}`);
9565
+ }
9566
+ validateMarketplace(root, selector);
9567
+ const plugins = field(root, "plugins");
9568
+ need2(plugins?.kind === "array", "requires a plugins array");
9569
+ const entryName = (item) => {
9570
+ if (item.kind !== "object")
9571
+ return;
9572
+ const name = field(item, "name");
9573
+ return isString(name) ? name.value : undefined;
9574
+ };
9575
+ const exact = plugins.items.filter((item) => entryName(item) === selector.plugin);
9576
+ const folded = plugins.items.filter((item) => entryName(item)?.toLowerCase() === selector.plugin.toLowerCase());
9577
+ need2(exact.length > 0, "selected plugin entry is missing");
9578
+ need2(exact.length === 1 && folded.length === 1, "selected plugin entry is duplicated or ambiguous");
9579
+ const entry = projectEntry(exact[0], selector);
9580
+ const metadata = field(root, "metadata"), pluginRoot = metadata?.kind === "object" ? field(metadata, "pluginRoot") : undefined;
9581
+ const nullValue = { kind: "literal", value: "null" };
9582
+ const marketplace = { kind: "object", entries: [
9583
+ ["name", field(root, "name")],
9584
+ ["pluginRoot", pluginRoot ?? nullValue],
9585
+ ["allowCrossMarketplaceDependenciesOn", field(root, "allowCrossMarketplaceDependenciesOn") ?? nullValue]
9586
+ ] };
9587
+ const projection = { kind: "object", entries: [["marketplace", marketplace], ["plugin", entry]] };
9588
+ return createHash4("sha256").update(DOMAIN).update(canonical2(projection)).digest("hex");
9589
+ }
9590
+ function captureClaudeMarketplaceEntry(path, marketplace, plugin, budget2 = { remaining: 256 * 1024 * 1024 }) {
9591
+ need2(claudeMarketplaceEntrySourceValid({ path, marketplace, plugin }), "requires a normalized absolute .claude-plugin/marketplace.json path and exact names");
9592
+ let text;
9593
+ try {
9594
+ text = readNativeSettingsWitnessFile(path, budget2, "marketplace.json");
9595
+ } catch (error) {
9596
+ throw new Error(`Claude marketplace entry witness cannot read marketplace.json: ${reason(error)}`);
9597
+ }
9598
+ return { path, hashMode: CLAUDE_MARKETPLACE_ENTRY_HASH_MODE, marketplace, plugin, sha256: hashClaudeMarketplaceEntry(text, { marketplace, plugin }) };
9599
+ }
9600
+
9601
+ // src/lib/agent-policy-limits.ts
9383
9602
  var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryPathLinks: 40, discoveryPathSteps: 256, discoveryPathSourceMetadataBytes: 64 * 1024, discoveryPathTotalMetadataBytes: 8 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
9384
9603
  function object(value) {
9385
9604
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -9433,7 +9652,7 @@ function assertAgentPolicyCollections(policy) {
9433
9652
  text(control.pluginId, 1024);
9434
9653
  text(control.namespace, 64);
9435
9654
  text(control.pluginParent);
9436
- requireBound(isAbsolute3(control.pluginParent) && resolve3(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
9655
+ requireBound(isAbsolute4(control.pluginParent) && resolve4(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
9437
9656
  if (control.remotePluginId !== undefined)
9438
9657
  requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
9439
9658
  if (control.skillsOnly !== undefined)
@@ -9445,16 +9664,24 @@ function assertAgentPolicyCollections(policy) {
9445
9664
  }
9446
9665
  if (bridge.codexInactivePlugins !== undefined) {
9447
9666
  const controls = array(bridge.codexInactivePlugins, 4096), ids = new Set;
9448
- 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));
9667
+ requireBound(controls.length === 0 || object(bridge.codexPluginSkillReview) && supportsCodexNativeCapability(bridge.codexPluginSkillReview.version, "installed-plugin-review") && typeof bridge.codexPluginSkillReview.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(bridge.codexPluginSkillReview.catalogSha256));
9449
9668
  for (const control of controls) {
9450
9669
  requireBound(object(control) && Object.keys(control).every((key) => ["pluginId", "namespace", "pluginParent", "sourceType", "sourceSha256"].includes(key)));
9451
9670
  text(control.pluginId, 1024);
9452
9671
  text(control.namespace, 64);
9453
9672
  text(control.pluginParent);
9454
- requireBound(!ids.has(control.pluginId) && isAbsolute3(control.pluginParent) && resolve3(control.pluginParent) === control.pluginParent && ["local", "git", "npm"].includes(control.sourceType) && typeof control.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(control.sourceSha256));
9673
+ requireBound(!ids.has(control.pluginId) && isAbsolute4(control.pluginParent) && resolve4(control.pluginParent) === control.pluginParent && ["local", "git", "npm"].includes(control.sourceType) && typeof control.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(control.sourceSha256));
9455
9674
  ids.add(control.pluginId);
9456
9675
  }
9457
9676
  }
9677
+ if (bridge.discoveryExecutables !== undefined)
9678
+ for (const [agent, executable] of Object.entries(record(bridge.discoveryExecutables, AGENT_POLICY_LIMITS.agents))) {
9679
+ requireBound(agent === "gemini" && object(executable) && Object.keys(executable).length === 3 && executable.command === "gemini");
9680
+ for (const path of [executable.path, executable.target]) {
9681
+ text(path);
9682
+ requireBound(!/[\x00-\x1f\x7f-\x9f\u200e\u200f\u202a-\u202e\u2066-\u2069]/.test(path) && isAbsolute4(path) && resolve4(path) === path);
9683
+ }
9684
+ }
9458
9685
  if (bridge.disabledBuiltins !== undefined)
9459
9686
  for (const builtin of array(bridge.disabledBuiltins, AGENT_POLICY_LIMITS.builtinNames)) {
9460
9687
  requireBound(object(builtin));
@@ -9480,11 +9707,11 @@ function assertAgentPolicyCollections(policy) {
9480
9707
  requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).every((key) => ["roots", "parents", "directories"].includes(key)) && roots.length + parents2.length > 0 && new Set(roots).size === roots.length && new Set(parents2).size === parents2.length);
9481
9708
  for (const root of roots) {
9482
9709
  text(root);
9483
- requireBound(isAbsolute3(root) && resolve3(root) === root);
9710
+ requireBound(isAbsolute4(root) && resolve4(root) === root);
9484
9711
  }
9485
9712
  for (const parent of parents2) {
9486
9713
  text(parent);
9487
- requireBound(isAbsolute3(parent) && resolve3(parent) === parent);
9714
+ requireBound(isAbsolute4(parent) && resolve4(parent) === parent);
9488
9715
  }
9489
9716
  for (const directory of array(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
9490
9717
  requireBound(object(directory));
@@ -9497,14 +9724,14 @@ function assertAgentPolicyCollections(policy) {
9497
9724
  }
9498
9725
  if (value.codexInstallationInputs !== undefined) {
9499
9726
  const proof = record(value.codexInstallationInputs, 4), inputs = array(proof.plugins, 4096), ids = new Set;
9500
- 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);
9727
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && supportsCodexNativeCapability(proof.version, "installed-plugin-review") && 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);
9501
9728
  for (const input of inputs) {
9502
9729
  requireBound(object(input) && Object.keys(input).every((key) => ["pluginId", "namespace", "pluginParent", "sourceRoot", "sourceSha256"].includes(key)));
9503
9730
  text(input.pluginId, 1024);
9504
9731
  text(input.namespace, 64);
9505
9732
  text(input.pluginParent);
9506
9733
  text(input.sourceRoot);
9507
- requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) => isAbsolute3(path) && resolve3(path) === path) && typeof input.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(input.sourceSha256));
9734
+ requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) => isAbsolute4(path) && resolve4(path) === path) && typeof input.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(input.sourceSha256));
9508
9735
  ids.add(input.pluginId);
9509
9736
  }
9510
9737
  if (proof.directories !== undefined)
@@ -9521,17 +9748,19 @@ function assertAgentPolicyCollections(policy) {
9521
9748
  text(source.path);
9522
9749
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
9523
9750
  if (source.hashMode !== undefined)
9524
- requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
9751
+ requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1", "claude-marketplace-entry-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
9525
9752
  if (source.hashMode === "claude-plugin-manifest-v1")
9526
- requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && source.path.endsWith(join7(sep2, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
9753
+ requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && source.path.endsWith(join7(sep2, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
9527
9754
  if (source.hashMode === "sumi-settings-v1")
9528
- requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "sumi.json");
9755
+ requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "sumi.json");
9529
9756
  if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
9530
- requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "config.toml");
9757
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "config.toml");
9531
9758
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
9532
- requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "settings.json");
9759
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "settings.json");
9533
9760
  if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
9534
- requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "known_marketplaces.json");
9761
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "known_marketplaces.json");
9762
+ if (source.hashMode === "claude-marketplace-entry-v1")
9763
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && claudeMarketplaceEntrySourceValid(source));
9535
9764
  if (source.hashMode === "claude-plugin-registry") {
9536
9765
  requireBound(agent === "claude");
9537
9766
  const managed = array(source.managedPlugins, 64);
@@ -9546,8 +9775,8 @@ function assertAgentPolicyCollections(policy) {
9546
9775
  if (source.format !== undefined)
9547
9776
  requireBound(["json", "toml", "yaml"].includes(source.format));
9548
9777
  if (source.fields !== undefined)
9549
- for (const field of array(source.fields, AGENT_POLICY_LIMITS.fields))
9550
- text(field, 256);
9778
+ for (const field2 of array(source.fields, AGENT_POLICY_LIMITS.fields))
9779
+ text(field2, 256);
9551
9780
  }
9552
9781
  if (value.directories !== undefined)
9553
9782
  for (const directory of array(value.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
@@ -9562,9 +9791,9 @@ function assertAgentPolicyCollections(policy) {
9562
9791
  }
9563
9792
 
9564
9793
  // src/lib/agent-discovery.ts
9565
- import { createHash as createHash24 } from "crypto";
9794
+ import { createHash as createHash25 } from "crypto";
9566
9795
  import { lstatSync as lstatSync25, readFileSync as readFileSync20, readdirSync as readdirSync10, realpathSync as realpathSync11 } from "fs";
9567
- import { basename as basename10, dirname as dirname19, isAbsolute as isAbsolute19, join as join32, resolve as resolve18, sep as sep8 } from "path";
9796
+ import { basename as basename11, dirname as dirname20, isAbsolute as isAbsolute21, join as join32, resolve as resolve20, sep as sep8 } from "path";
9568
9797
  import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
9569
9798
 
9570
9799
  // node_modules/yaml/dist/index.js
@@ -9614,7 +9843,7 @@ var $visit = visit.visit;
9614
9843
  var $visitAsync = visit.visitAsync;
9615
9844
 
9616
9845
  // src/lib/agent-hermes.ts
9617
- import { dirname as dirname4, isAbsolute as isAbsolute4, join as join8, resolve as resolve4 } from "path";
9846
+ import { dirname as dirname5, isAbsolute as isAbsolute5, join as join8, resolve as resolve5 } from "path";
9618
9847
  import { lstatSync as lstatSync4, readFileSync as readFileSync6, opendirSync } from "fs";
9619
9848
  var HERMES_OPT_OUT = ".no-bundled-skills";
9620
9849
  var EVENTS = ["pre_llm_call", "pre_tool_call"];
@@ -9652,7 +9881,7 @@ function quoted(value) {
9652
9881
  return `'${value.replace(/'/g, `'\\''`)}'`;
9653
9882
  }
9654
9883
  function hermesHookDefinitions(supervisor) {
9655
- if (!isAbsolute4(supervisor.runtime) || !isAbsolute4(supervisor.path))
9884
+ if (!isAbsolute5(supervisor.runtime) || !isAbsolute5(supervisor.path))
9656
9885
  throw new Error("Expected absolute Hermes supervisor paths");
9657
9886
  const entry = (event) => ({ command: `${quoted(supervisor.runtime)} ${quoted(supervisor.path)} --event ${event}`, timeout: 15, ...event === "pre_tool_call" ? { fail_closed: true } : {} });
9658
9887
  return { pre_llm_call: entry("pre_llm_call"), pre_tool_call: entry("pre_tool_call") };
@@ -9688,16 +9917,16 @@ function assertHermesEnvironment(home) {
9688
9917
  throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
9689
9918
  }
9690
9919
  const selected = process.env.HERMES_HOME?.trim();
9691
- if (selected && (!isAbsolute4(selected) || resolve4(selected) !== join8(resolve4(home), ".hermes")))
9920
+ if (selected && (!isAbsolute5(selected) || resolve5(selected) !== join8(resolve5(home), ".hermes")))
9692
9921
  throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
9693
9922
  if (process.env.HERMES_ENABLE_PROJECT_PLUGINS?.trim() && !["0", "false", "no", "off"].includes(process.env.HERMES_ENABLE_PROJECT_PLUGINS.trim().toLowerCase()))
9694
9923
  throw new Error("NATIVE_SKILL_DRIFT: Hermes project plugins require a dedicated discovery review");
9695
9924
  }
9696
9925
  function read(path) {
9697
- for (let current = path;; current = dirname4(current)) {
9926
+ for (let current = path;; current = dirname5(current)) {
9698
9927
  if (lstatSync4(current, { throwIfNoEntry: false })?.isSymbolicLink())
9699
9928
  throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes protection input");
9700
- if (dirname4(current) === current)
9929
+ if (dirname5(current) === current)
9701
9930
  break;
9702
9931
  }
9703
9932
  const stat = lstatSync4(path, { throwIfNoEntry: false });
@@ -9711,7 +9940,7 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
9711
9940
  assertHermesEnvironment(home);
9712
9941
  if (read(join8(home, ".hermes", HERMES_OPT_OUT)) === null)
9713
9942
  throw new Error("NATIVE_SKILL_DRIFT: Hermes bundled skill reseeding is not disabled; run skills hook install");
9714
- if (!supervisor || !isAbsolute4(supervisor.path) || supervisor.runtime !== process.execPath || read(supervisor.path) !== renderHermesSupervisor(command, profile))
9943
+ if (!supervisor || !isAbsolute5(supervisor.path) || supervisor.runtime !== process.execPath || read(supervisor.path) !== renderHermesSupervisor(command, profile))
9715
9944
  throw new Error("NATIVE_SKILL_DRIFT: Hermes owned supervisor changed; run skills hook install");
9716
9945
  const raw = read(join8(home, ".hermes/shell-hooks-allowlist.json"));
9717
9946
  let trust;
@@ -9737,10 +9966,10 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
9737
9966
  }
9738
9967
  function assertNoHermesLegacyShadow(root) {
9739
9968
  let entries = 0;
9740
- for (let cursor = resolve4(root);; cursor = dirname4(cursor)) {
9969
+ for (let cursor = resolve5(root);; cursor = dirname5(cursor)) {
9741
9970
  if (lstatSync4(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
9742
9971
  throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes legacy discovery root");
9743
- if (dirname4(cursor) === cursor)
9972
+ if (dirname5(cursor) === cursor)
9744
9973
  break;
9745
9974
  }
9746
9975
  function scan(path, depth) {
@@ -9761,10 +9990,10 @@ function assertNoHermesLegacyShadow(root) {
9761
9990
  const hasDescriptor = Boolean(lstatSync4(join8(path, "SKILL.md"), { throwIfNoEntry: false }));
9762
9991
  const directory = opendirSync(path);
9763
9992
  try {
9764
- for (let child = directory.readSync();child; child = directory.readSync()) {
9765
- if (hasDescriptor && ["references", "templates", "assets", "scripts"].includes(child.name))
9993
+ for (let child2 = directory.readSync();child2; child2 = directory.readSync()) {
9994
+ if (hasDescriptor && ["references", "templates", "assets", "scripts"].includes(child2.name))
9766
9995
  continue;
9767
- scan(join8(path, child.name), depth + 1);
9996
+ scan(join8(path, child2.name), depth + 1);
9768
9997
  }
9769
9998
  } finally {
9770
9999
  directory.closeSync();
@@ -9821,18 +10050,18 @@ if (failed) process.exitCode = 2;
9821
10050
  }
9822
10051
 
9823
10052
  // src/lib/agent-discovery-directories.ts
9824
- import { createHash as createHash4 } from "crypto";
10053
+ import { createHash as createHash5 } from "crypto";
9825
10054
  import { lstatSync as lstatSync5, opendirSync as opendirSync2 } from "fs";
9826
- import { dirname as dirname5, isAbsolute as isAbsolute5, join as join9, resolve as resolve5 } from "path";
9827
- function need2(value, message) {
10055
+ import { dirname as dirname6, isAbsolute as isAbsolute6, join as join9, resolve as resolve6 } from "path";
10056
+ function need3(value, message) {
9828
10057
  if (!value)
9829
10058
  throw new Error(message);
9830
10059
  }
9831
10060
  function safe(path) {
9832
- need2(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute5(path) && resolve5(path) === path && !path.includes("\x00"), "Expected a canonical absolute discovery directory path");
9833
- for (let cursor = path;; cursor = dirname5(cursor)) {
9834
- need2(!lstatSync5(cursor, { throwIfNoEntry: false })?.isSymbolicLink(), "Refusing symlink native discovery directory");
9835
- if (dirname5(cursor) === cursor)
10061
+ need3(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute6(path) && resolve6(path) === path && !path.includes("\x00"), "Expected a canonical absolute discovery directory path");
10062
+ for (let cursor = path;; cursor = dirname6(cursor)) {
10063
+ need3(!lstatSync5(cursor, { throwIfNoEntry: false })?.isSymbolicLink(), "Refusing symlink native discovery directory");
10064
+ if (dirname6(cursor) === cursor)
9836
10065
  break;
9837
10066
  }
9838
10067
  }
@@ -9846,45 +10075,45 @@ function capture(path, budget2, excludedRoots, omittedRoots = [], rows) {
9846
10075
  const root = lstatSync5(path, { throwIfNoEntry: false });
9847
10076
  if (!root)
9848
10077
  return { path, sha256: null };
9849
- need2(root.isDirectory(), "Unsupported native discovery directory input");
9850
- const hash = createHash4("sha256");
10078
+ need3(root.isDirectory(), "Unsupported native discovery directory input");
10079
+ const hash = createHash5("sha256");
9851
10080
  function walk(current, relative2, before, depth) {
9852
- need2(depth <= 64, "Native discovery directory depth limit exceeded");
10081
+ need3(depth <= 64, "Native discovery directory depth limit exceeded");
9853
10082
  const names = [], directory = opendirSync2(current);
9854
10083
  try {
9855
10084
  for (let entry = directory.readSync();entry; entry = directory.readSync()) {
9856
- need2(++budget2.entries <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Native discovery directory entry limit exceeded");
10085
+ need3(++budget2.entries <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Native discovery directory entry limit exceeded");
9857
10086
  names.push(entry.name);
9858
10087
  }
9859
10088
  } finally {
9860
10089
  directory.closeSync();
9861
10090
  }
9862
- need2(new Set(names).size === names.length, "Ambiguous native discovery directory names");
10091
+ need3(new Set(names).size === names.length, "Ambiguous native discovery directory names");
9863
10092
  for (const name of names.sort()) {
9864
- const child = join9(current, name), childRelative = relative2 ? `${relative2}/${name}` : name;
9865
- if (omittedRoots.includes(child))
10093
+ const child2 = join9(current, name), childRelative = relative2 ? `${relative2}/${name}` : name;
10094
+ if (omittedRoots.includes(child2))
9866
10095
  continue;
9867
- need2(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
9868
- const stat = lstatSync5(child);
9869
- need2(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
10096
+ need3(child2.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
10097
+ const stat = lstatSync5(child2);
10098
+ need3(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
9870
10099
  const row = JSON.stringify([childRelative, stat.isDirectory() ? "directory" : "file"]) + `
9871
10100
  `;
9872
10101
  budget2.bytes += Buffer.byteLength(row);
9873
- need2(budget2.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
10102
+ need3(budget2.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
9874
10103
  hash.update(row);
9875
10104
  rows?.push(row);
9876
- if (stat.isDirectory() && !excludedRoots.includes(child))
9877
- walk(child, childRelative, stat, depth + 1);
10105
+ if (stat.isDirectory() && !excludedRoots.includes(child2))
10106
+ walk(child2, childRelative, stat, depth + 1);
9878
10107
  }
9879
- need2(same(before, lstatSync5(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
10108
+ need3(same(before, lstatSync5(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
9880
10109
  }
9881
10110
  walk(path, "", root, 0);
9882
10111
  safe(path);
9883
10112
  return { path, sha256: hash.digest("hex") };
9884
10113
  }
9885
10114
  function captureDiscoveryDirectoryProjection(paths, omittedRoots) {
9886
- need2(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid projected discovery directory collection");
9887
- need2(omittedRoots.length <= AGENT_POLICY_LIMITS.discoveryRoots && new Set(omittedRoots).size === omittedRoots.length, "Invalid inactive discovery roots");
10115
+ need3(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid projected discovery directory collection");
10116
+ need3(omittedRoots.length <= AGENT_POLICY_LIMITS.discoveryRoots && new Set(omittedRoots).size === omittedRoots.length, "Invalid inactive discovery roots");
9888
10117
  for (const root of omittedRoots)
9889
10118
  safe(root);
9890
10119
  const budget2 = { entries: 0, bytes: 0 };
@@ -9894,62 +10123,62 @@ function captureDiscoveryDirectoryProjection(paths, omittedRoots) {
9894
10123
  });
9895
10124
  }
9896
10125
  function verifyDiscoveryDirectoryProjection(directories, omittedRoots) {
9897
- need2(directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid projected discovery directory collection");
10126
+ need3(directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid projected discovery directory collection");
9898
10127
  let count = 0, bytes = 0;
9899
10128
  const expected = directories.map((directory) => {
9900
10129
  safe(directory.path);
9901
- need2(Array.isArray(directory.entries), "Missing reviewed directory membership inventory");
9902
- const hash = createHash4("sha256");
10130
+ need3(Array.isArray(directory.entries), "Missing reviewed directory membership inventory");
10131
+ const hash = createHash5("sha256");
9903
10132
  const kept = [];
9904
10133
  for (const row of directory.entries) {
9905
- need2(typeof row === "string" && ++count <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Invalid reviewed directory membership rows");
10134
+ need3(typeof row === "string" && ++count <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Invalid reviewed directory membership rows");
9906
10135
  bytes += Buffer.byteLength(row);
9907
- need2(bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Reviewed directory membership byte limit exceeded");
10136
+ need3(bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Reviewed directory membership byte limit exceeded");
9908
10137
  const value = JSON.parse(row);
9909
- need2(Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && value[0] && !isAbsolute5(value[0]) && !value[0].split("/").some((part) => !part || part === "." || part === "..") && ["file", "directory"].includes(value[1]) && row === JSON.stringify(value) + `
10138
+ need3(Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && value[0] && !isAbsolute6(value[0]) && !value[0].split("/").some((part) => !part || part === "." || part === "..") && ["file", "directory"].includes(value[1]) && row === JSON.stringify(value) + `
9910
10139
  `, "Invalid reviewed directory member");
9911
10140
  hash.update(row);
9912
10141
  const path = join9(directory.path, value[0]);
9913
10142
  if (!omittedRoots.some((root) => path === root || path.startsWith(root + "/")))
9914
10143
  kept.push(row);
9915
10144
  }
9916
- need2(directory.sha256 === null ? directory.entries.length === 0 : hash.digest("hex") === directory.sha256, "Reviewed directory inventory does not match its fingerprint");
10145
+ need3(directory.sha256 === null ? directory.entries.length === 0 : hash.digest("hex") === directory.sha256, "Reviewed directory inventory does not match its fingerprint");
9917
10146
  const absent = omittedRoots.some((root) => directory.path === root || directory.path.startsWith(root + "/"));
9918
- return { path: directory.path, sha256: absent || directory.sha256 === null ? null : createHash4("sha256").update(kept.join("")).digest("hex") };
10147
+ return { path: directory.path, sha256: absent || directory.sha256 === null ? null : createHash5("sha256").update(kept.join("")).digest("hex") };
9919
10148
  });
9920
10149
  const current = captureDiscoveryDirectoryProjection(directories.map((item) => item.path), omittedRoots);
9921
- need2(current.every((item, index) => item.sha256 === expected[index].sha256), "Native discovery directory membership changed outside retired Codex roots");
10150
+ need3(current.every((item, index) => item.sha256 === expected[index].sha256), "Native discovery directory membership changed outside retired Codex roots");
9922
10151
  }
9923
10152
  function captureDiscoveryDirectories(paths, excludedRoots = []) {
9924
- need2(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
10153
+ need3(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
9925
10154
  const budget2 = { entries: 0, bytes: 0 };
9926
10155
  for (const root of excludedRoots)
9927
10156
  safe(root);
9928
10157
  return paths.map((path) => capture(path, budget2, excludedRoots));
9929
10158
  }
9930
10159
  function verifyDiscoveryDirectories(directories, excludedRoots = []) {
9931
- need2(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
10160
+ need3(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
9932
10161
  for (const directory of directories)
9933
- need2(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
10162
+ need3(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
9934
10163
  const current = captureDiscoveryDirectories(directories.map((directory) => directory.path), excludedRoots);
9935
10164
  for (let i = 0;i < directories.length; i++)
9936
- need2(current[i].sha256 === directories[i].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`);
10165
+ need3(current[i].sha256 === directories[i].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`);
9937
10166
  }
9938
10167
 
9939
10168
  // src/lib/agent-discovery-bytes.ts
9940
- import { createHash as createHash5 } from "crypto";
10169
+ import { createHash as createHash6 } from "crypto";
9941
10170
  import { closeSync as closeSync2, constants as constants2, fstatSync as fstatSync2, lstatSync as lstatSync6, openSync as openSync2, readSync as readSync2 } from "fs";
9942
- import { dirname as dirname6, isAbsolute as isAbsolute6, resolve as resolve6 } from "path";
10171
+ import { dirname as dirname7, isAbsolute as isAbsolute7, resolve as resolve7 } from "path";
9943
10172
  function discoveryByteBudget() {
9944
10173
  return { remaining: AGENT_POLICY_LIMITS.discoveryRawTotalBytes };
9945
10174
  }
9946
10175
  function safe2(path) {
9947
- if (typeof path !== "string" || !isAbsolute6(path) || path !== resolve6(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
10176
+ if (typeof path !== "string" || !isAbsolute7(path) || path !== resolve7(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
9948
10177
  throw new Error("Expected a canonical absolute raw discovery path");
9949
- for (let cursor = path;; cursor = dirname6(cursor)) {
10178
+ for (let cursor = path;; cursor = dirname7(cursor)) {
9950
10179
  if (lstatSync6(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
9951
10180
  throw new Error("Refusing symlink raw discovery input");
9952
- if (dirname6(cursor) === cursor)
10181
+ if (dirname7(cursor) === cursor)
9953
10182
  break;
9954
10183
  }
9955
10184
  }
@@ -9968,7 +10197,7 @@ function hashRawDiscoveryFile(path, budget2, changes) {
9968
10197
  if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
9969
10198
  throw new Error("Raw discovery file byte limit exceeded");
9970
10199
  consume(budget2, bytes);
9971
- return createHash5("sha256").update(text2).digest("hex");
10200
+ return createHash6("sha256").update(text2).digest("hex");
9972
10201
  }
9973
10202
  const initial = lstatSync6(path, { throwIfNoEntry: false });
9974
10203
  if (!initial)
@@ -9982,7 +10211,7 @@ function hashRawDiscoveryFile(path, budget2, changes) {
9982
10211
  const opened = fstatSync2(fd);
9983
10212
  if (!opened.isFile() || !same2(initial, opened))
9984
10213
  throw new Error("Raw discovery input changed during open");
9985
- const hash = createHash5("sha256"), buffer = Buffer.alloc(64 * 1024);
10214
+ const hash = createHash6("sha256"), buffer = Buffer.alloc(64 * 1024);
9986
10215
  let bytes = 0;
9987
10216
  while (true) {
9988
10217
  const count = readSync2(fd, buffer, 0, Math.min(buffer.length, AGENT_POLICY_LIMITS.discoveryRawSourceBytes - bytes + 1), null);
@@ -10005,9 +10234,9 @@ function hashRawDiscoveryFile(path, budget2, changes) {
10005
10234
  }
10006
10235
 
10007
10236
  // src/lib/agent-discovery-path-bytes.ts
10008
- import { createHash as createHash6 } from "crypto";
10237
+ import { createHash as createHash7 } from "crypto";
10009
10238
  import { lstatSync as lstatSync7, readlinkSync } from "fs";
10010
- import { dirname as dirname7, isAbsolute as isAbsolute7, join as join10, parse as parse2, resolve as resolve7, sep as sep3 } from "path";
10239
+ import { dirname as dirname8, isAbsolute as isAbsolute8, join as join10, parse as parse2, resolve as resolve8, sep as sep3 } from "path";
10011
10240
  function identity2(stat, mutable = false) {
10012
10241
  return {
10013
10242
  dev: String(stat.dev),
@@ -10018,12 +10247,12 @@ function identity2(stat, mutable = false) {
10018
10247
  ...mutable ? { size: String(stat.size), mtimeNs: String(stat.mtimeNs), ctimeNs: String(stat.ctimeNs) } : {}
10019
10248
  };
10020
10249
  }
10021
- function canonical2(path) {
10022
- if (typeof path !== "string" || !isAbsolute7(path) || path !== resolve7(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00") || Buffer.from(path).toString("utf8") !== path)
10250
+ function canonical3(path) {
10251
+ if (typeof path !== "string" || !isAbsolute8(path) || path !== resolve8(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00") || Buffer.from(path).toString("utf8") !== path)
10023
10252
  throw new Error("Expected a canonical absolute discovery path");
10024
10253
  }
10025
10254
  function trace(path) {
10026
- canonical2(path);
10255
+ canonical3(path);
10027
10256
  const rows = [], paths = [];
10028
10257
  let metadataBytes = 0, steps = 0, links = 0, cursor = parse2(path).root, pending = path.slice(cursor.length).split(sep3);
10029
10258
  function record2(path2, row) {
@@ -10048,7 +10277,7 @@ function trace(path) {
10048
10277
  if (!component || component === ".")
10049
10278
  continue;
10050
10279
  if (component === "..") {
10051
- cursor = dirname7(cursor);
10280
+ cursor = dirname8(cursor);
10052
10281
  continue;
10053
10282
  }
10054
10283
  const candidate = join10(cursor, component);
@@ -10068,11 +10297,11 @@ function trace(path) {
10068
10297
  if (!target || target.includes("\x00") || Buffer.byteLength(target) > AGENT_POLICY_LIMITS.pathCharacters || !after.isSymbolicLink() || JSON.stringify(identity2(stat, true)) !== JSON.stringify(identity2(after, true)))
10069
10298
  throw new Error("Discovery link changed during read");
10070
10299
  record2(candidate, { type: "link", target, ...identity2(stat, true) });
10071
- if (isAbsolute7(target)) {
10300
+ if (isAbsolute8(target)) {
10072
10301
  cursor = parse2(target).root;
10073
10302
  root();
10074
10303
  }
10075
- const remainder = isAbsolute7(target) ? target.slice(parse2(target).root.length) : target;
10304
+ const remainder = isAbsolute8(target) ? target.slice(parse2(target).root.length) : target;
10076
10305
  pending = [...remainder.split(sep3), ...pending];
10077
10306
  if (pending.length + steps > AGENT_POLICY_LIMITS.discoveryPathSteps)
10078
10307
  throw new Error("Discovery path step limit exceeded");
@@ -10115,15 +10344,15 @@ function hashDiscoveryPathFile(path, budget2, changes) {
10115
10344
  const after = trace(path);
10116
10345
  if (JSON.stringify(before.rows) !== JSON.stringify(after.rows) || before.file !== after.file)
10117
10346
  throw new Error("Discovery path input changed during read");
10118
- return createHash6("sha256").update(JSON.stringify({ version: 1, path, rows: before.rows, content })).digest("hex");
10347
+ return createHash7("sha256").update(JSON.stringify({ version: 1, path, rows: before.rows, content })).digest("hex");
10119
10348
  }
10120
10349
 
10121
10350
  // src/lib/claude-plugin-manifest-witness.ts
10122
- import { createHash as createHash7 } from "crypto";
10351
+ import { createHash as createHash8 } from "crypto";
10123
10352
  var CLAUDE_PLUGIN_MANIFEST_LIMITS = Object.freeze({ bytes: 1024 * 1024, depth: 32, nodes: 65536, stringCharacters: 16384 });
10124
- function requireValue(value, reason) {
10353
+ function requireValue(value, reason2) {
10125
10354
  if (!value)
10126
- throw new Error("Claude plugin manifest " + reason);
10355
+ throw new Error("Claude plugin manifest " + reason2);
10127
10356
  }
10128
10357
  function parse3(text2) {
10129
10358
  requireValue(Buffer.byteLength(text2, "utf8") <= CLAUDE_PLUGIN_MANIFEST_LIMITS.bytes, "exceeds its byte limit");
@@ -10199,7 +10428,7 @@ function parse3(text2) {
10199
10428
  requireValue(at === text2.length && root.kind === "object", "requires a JSON object without trailing content");
10200
10429
  return root;
10201
10430
  }
10202
- function canonical3(value) {
10431
+ function canonical4(value) {
10203
10432
  if (value.kind === "string")
10204
10433
  return JSON.stringify(value.value);
10205
10434
  if (value.kind === "number")
@@ -10207,19 +10436,19 @@ function canonical3(value) {
10207
10436
  if (value.kind === "literal")
10208
10437
  return value.value;
10209
10438
  if (value.kind === "array")
10210
- return "[" + value.items.map(canonical3).join(",") + "]";
10211
- return "{" + value.entries.map(([key, item]) => JSON.stringify(key) + ":" + canonical3(item)).join(",") + "}";
10439
+ return "[" + value.items.map(canonical4).join(",") + "]";
10440
+ return "{" + value.entries.map(([key, item]) => JSON.stringify(key) + ":" + canonical4(item)).join(",") + "}";
10212
10441
  }
10213
10442
  var DESCRIPTIVE_FIELDS = new Set(["description", "version", "author", "homepage", "repository", "license", "keywords"]);
10214
- function validateText(value, field) {
10215
- requireValue(value?.kind === "string" && value.value.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters, "has an invalid " + field + " field");
10443
+ function validateText(value, field2) {
10444
+ requireValue(value?.kind === "string" && value.value.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters, "has an invalid " + field2 + " field");
10216
10445
  }
10217
10446
  function validateDescriptiveFields(manifest) {
10218
10447
  const values = new Map(manifest.entries);
10219
- for (const field of ["description", "version", "homepage", "license"]) {
10220
- const value = values.get(field);
10448
+ for (const field2 of ["description", "version", "homepage", "license"]) {
10449
+ const value = values.get(field2);
10221
10450
  if (value !== undefined)
10222
- validateText(value, field);
10451
+ validateText(value, field2);
10223
10452
  }
10224
10453
  const author = values.get("author");
10225
10454
  if (author !== undefined) {
@@ -10244,10 +10473,10 @@ function hashClaudePluginManifest(text2) {
10244
10473
  const manifest = parse3(text2);
10245
10474
  validateDescriptiveFields(manifest);
10246
10475
  const discovery = { kind: "object", entries: manifest.entries.filter(([key]) => !DESCRIPTIVE_FIELDS.has(key)) };
10247
- return createHash7("sha256").update(canonical3(discovery)).digest("hex");
10476
+ return createHash8("sha256").update(canonical4(discovery)).digest("hex");
10248
10477
  }
10249
10478
  function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
10250
- if (!/^[a-f0-9]{64}$/.test(reviewedSha256) || createHash7("sha256").update(text2).digest("hex") !== reviewedSha256) {
10479
+ if (!/^[a-f0-9]{64}$/.test(reviewedSha256) || createHash8("sha256").update(text2).digest("hex") !== reviewedSha256) {
10251
10480
  throw new Error("Reviewed Claude plugin manifest changed before semantic projection");
10252
10481
  }
10253
10482
  return hashClaudePluginManifest(text2);
@@ -10255,22 +10484,22 @@ function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
10255
10484
 
10256
10485
  // src/lib/plugin-discovery.ts
10257
10486
  import { closeSync as closeSync13, constants as constants13, fstatSync as fstatSync13, lstatSync as lstatSync23, openSync as openSync13, opendirSync as opendirSync5, readSync as readSync10 } from "fs";
10258
- import { basename as basename8, dirname as dirname17, isAbsolute as isAbsolute17, join as join31, resolve as resolve16 } from "path";
10487
+ import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute19, join as join31, resolve as resolve18 } from "path";
10259
10488
 
10260
10489
  // src/lib/plugin-admission.ts
10261
10490
  import { homedir as homedir5 } from "os";
10262
- import { isAbsolute as isAbsolute16, join as join30, resolve as resolve15 } from "path";
10491
+ import { isAbsolute as isAbsolute18, join as join30, resolve as resolve17 } from "path";
10263
10492
 
10264
10493
  // src/lib/selection-cache.ts
10265
- import { createHash as createHash9, randomUUID } from "crypto";
10494
+ import { createHash as createHash10, randomUUID } from "crypto";
10266
10495
  import { spawnSync } from "child_process";
10267
10496
  import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, fsyncSync, linkSync, lstatSync as lstatSync8, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync8, readlinkSync as readlinkSync2, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
10268
10497
  import { hostname } from "os";
10269
- import { dirname as dirname8, join as join12, parse as parse4, resolve as resolve8 } from "path";
10498
+ import { dirname as dirname9, join as join12, parse as parse4, resolve as resolve9 } from "path";
10270
10499
  import { isDeepStrictEqual } from "util";
10271
10500
 
10272
10501
  // src/lib/skill-bundle.ts
10273
- import { createHash as createHash8 } from "crypto";
10502
+ import { createHash as createHash9 } from "crypto";
10274
10503
  import { readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
10275
10504
  import { join as join11, relative as relative2 } from "path";
10276
10505
  import { createGunzip } from "zlib";
@@ -10425,7 +10654,7 @@ function ownBytes(view) {
10425
10654
  return out;
10426
10655
  }
10427
10656
  function sha256Hex(bytes) {
10428
- return createHash8("sha256").update(bytes).digest("hex");
10657
+ return createHash9("sha256").update(bytes).digest("hex");
10429
10658
  }
10430
10659
  function collectSkillBundleEntries(dir) {
10431
10660
  const entries = [];
@@ -10621,12 +10850,12 @@ function inspectionLimits(options) {
10621
10850
  for (const key of Object.keys(options.limits ?? {})) {
10622
10851
  if (!Object.hasOwn(limits, key))
10623
10852
  throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Unknown bundle limit");
10624
- const field = key;
10625
- const value = options.limits[field];
10626
- if (!Number.isSafeInteger(value) || value <= 0 || value > limits[field]) {
10853
+ const field2 = key;
10854
+ const value = options.limits[field2];
10855
+ if (!Number.isSafeInteger(value) || value <= 0 || value > limits[field2]) {
10627
10856
  throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Bundle limits must be positive integers within the hard ceilings");
10628
10857
  }
10629
- limits[field] = value;
10858
+ limits[field2] = value;
10630
10859
  }
10631
10860
  return limits;
10632
10861
  }
@@ -10656,7 +10885,14 @@ async function inspectSkillBundle(bundle, options = {}) {
10656
10885
  decoder.destroy(terminalError);
10657
10886
  };
10658
10887
  const onAbort = () => stop("BUNDLE_ABORTED");
10659
- const timer = setTimeout(() => stop("BUNDLE_TIMEOUT"), Math.max(1, deadline - performance.now()));
10888
+ const expire = () => {
10889
+ const remaining = deadline - performance.now();
10890
+ if (remaining > 0)
10891
+ timer = setTimeout(expire, Math.max(1, Math.ceil(remaining)));
10892
+ else
10893
+ stop("BUNDLE_TIMEOUT");
10894
+ };
10895
+ let timer = setTimeout(expire, Math.max(1, Math.ceil(deadline - performance.now())));
10660
10896
  signal?.addEventListener("abort", onAbort, { once: true });
10661
10897
  let decompressedByteSize = 0;
10662
10898
  let bytesSinceYield = 0;
@@ -10671,7 +10907,7 @@ async function inspectSkillBundle(bundle, options = {}) {
10671
10907
  parser2.push(chunk);
10672
10908
  bytesSinceYield += chunk.byteLength;
10673
10909
  if (bytesSinceYield >= 256 * 1024) {
10674
- await new Promise((resolve8) => setTimeout(resolve8, 0));
10910
+ await new Promise((resolve9) => setTimeout(resolve9, 0));
10675
10911
  bytesSinceYield = 0;
10676
10912
  check();
10677
10913
  }
@@ -10808,8 +11044,8 @@ class BoundedTarReader {
10808
11044
  }
10809
11045
  }
10810
11046
  }
10811
- function tarOctal(field) {
10812
- const text2 = new TextDecoder().decode(field);
11047
+ function tarOctal(field2) {
11048
+ const text2 = new TextDecoder().decode(field2);
10813
11049
  if (!/^[0-7]+[\0 ]*$/.test(text2))
10814
11050
  invalidBundle("Invalid tar octal field");
10815
11051
  const value = Number.parseInt(text2, 8);
@@ -10900,8 +11136,8 @@ function profileSelectionSnapshot(selections) {
10900
11136
  // src/lib/selection-cache.ts
10901
11137
  class SkillSelectionError extends Error {
10902
11138
  code;
10903
- constructor(code, message) {
10904
- super(message);
11139
+ constructor(code, message, options) {
11140
+ super(message, options);
10905
11141
  this.code = code;
10906
11142
  this.name = "SkillSelectionError";
10907
11143
  }
@@ -10909,14 +11145,14 @@ class SkillSelectionError extends Error {
10909
11145
  var MAX_CACHED_PROFILE_AGE_MS = 24 * 60 * 60 * 1000;
10910
11146
  var SELECTION_LOCK_FILE = "selection.lock.json";
10911
11147
  function selectionCacheRoot(options = {}) {
10912
- return resolve8(options.cacheDir ?? join12(getDataDirReadOnly(), "selection-cache"));
11148
+ return resolve9(options.cacheDir ?? join12(getDataDirReadOnly(), "selection-cache"));
10913
11149
  }
10914
11150
  function selectionKey(selection) {
10915
11151
  validateSelection(selection);
10916
11152
  return hash([selection.authority, selection.workspaceId, selection.slug, selection.version, selection.bundleDigest]);
10917
11153
  }
10918
11154
  function hash(value) {
10919
- return createHash9("sha256").update(JSON.stringify(value)).digest("hex");
11155
+ return createHash10("sha256").update(JSON.stringify(value)).digest("hex");
10920
11156
  }
10921
11157
  function validateSelection(selection) {
10922
11158
  if (!selection || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(selection.slug) || !isValidSkillVersion(selection.version) || !/^sha256:[a-f0-9]{64}$/.test(selection.bundleDigest) || typeof selection.workspaceId !== "string" || !selection.workspaceId.trim() || selection.authorizationEpoch !== undefined && (typeof selection.authorizationEpoch !== "string" || !/^[a-f0-9]{32}$/.test(selection.authorizationEpoch)) || typeof selection.profileRevision !== "string" || !selection.profileRevision.trim()) {
@@ -10956,7 +11192,7 @@ function selectionBundlePath(selection, options = {}) {
10956
11192
  return join12(selectionCacheRoot(options), "objects", hash(selection.authority), hash(selection.workspaceId), `${selection.bundleDigest.slice(7)}.tar.gz`);
10957
11193
  }
10958
11194
  function assertRegularPath(path, createParents = false) {
10959
- const absolute = resolve8(path);
11195
+ const absolute = resolve9(path);
10960
11196
  const root = parse4(absolute).root;
10961
11197
  let cursor = root;
10962
11198
  const segments = absolute.slice(root.length).split("/");
@@ -11013,7 +11249,7 @@ function writeSelectionJson(path, value) {
11013
11249
  }
11014
11250
  function atomicWrite(path, bytes, mode, durable = false) {
11015
11251
  assertRegularPath(path, true);
11016
- const temporary = join12(dirname8(path), `.selection-${randomUUID()}.tmp`);
11252
+ const temporary = join12(dirname9(path), `.selection-${randomUUID()}.tmp`);
11017
11253
  try {
11018
11254
  const fd = openSync3(temporary, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, mode);
11019
11255
  try {
@@ -11025,7 +11261,7 @@ function atomicWrite(path, bytes, mode, durable = false) {
11025
11261
  }
11026
11262
  renameSync(temporary, path);
11027
11263
  if (durable)
11028
- syncDirectory(dirname8(path));
11264
+ syncDirectory(dirname9(path));
11029
11265
  } finally {
11030
11266
  try {
11031
11267
  unlinkSync(temporary);
@@ -11035,8 +11271,8 @@ function atomicWrite(path, bytes, mode, durable = false) {
11035
11271
  }
11036
11272
  }
11037
11273
  }
11038
- function readSelectionJson(path) {
11039
- const bytes = readRegularFile(path, MAX_PROFILE_DOCUMENT_BYTES);
11274
+ function readSelectionJson(path, maximumBytes = MAX_PROFILE_DOCUMENT_BYTES) {
11275
+ const bytes = readRegularFile(path, Math.min(maximumBytes, MAX_PROFILE_DOCUMENT_BYTES));
11040
11276
  if (!bytes)
11041
11277
  return null;
11042
11278
  try {
@@ -11048,10 +11284,10 @@ function readSelectionJson(path) {
11048
11284
  async function verifiedEntries(selection, bytes, signal, deadline) {
11049
11285
  if (`sha256:${sha256Hex(bytes)}` !== selection.bundleDigest)
11050
11286
  throw new SkillSelectionError("BUNDLE_DIGEST_MISMATCH", "The skill bundle does not match its selected digest.");
11051
- const remaining = deadline === undefined ? undefined : Math.floor(deadline - performance.now());
11287
+ const remaining = deadline === undefined ? undefined : Math.ceil(deadline - performance.now());
11052
11288
  signal?.throwIfAborted();
11053
11289
  if (remaining !== undefined && remaining <= 0)
11054
- throw new SkillSelectionError("SKILLS_API_UNAVAILABLE", "Skills session authorization deadline exceeded.");
11290
+ throw new SkillBundleInspectionError("BUNDLE_TIMEOUT", "Bundle inspection deadline exceeded");
11055
11291
  const bundle = await inspectSkillBundle(bytes, { signal, ...remaining === undefined ? {} : { limits: { timeoutMs: remaining } } });
11056
11292
  return bundle.entries;
11057
11293
  }
@@ -11157,7 +11393,7 @@ function assertFreshCachedProfile(receipt, options = {}) {
11157
11393
  }
11158
11394
  }
11159
11395
  function projectSelectionLockPath(projectDir) {
11160
- return join12(resolve8(projectDir), ".skills", SELECTION_LOCK_FILE);
11396
+ return join12(resolve9(projectDir), ".skills", SELECTION_LOCK_FILE);
11161
11397
  }
11162
11398
  function readProjectSelection(projectDir) {
11163
11399
  const receipt = readSelectionJson(projectSelectionLockPath(projectDir));
@@ -11316,13 +11552,13 @@ function publishSessionLock(path, marker) {
11316
11552
  fsyncSync(fd);
11317
11553
  linkSync(stagingPath, path);
11318
11554
  published = true;
11319
- syncDirectory(dirname8(path));
11555
+ syncDirectory(dirname9(path));
11320
11556
  const staged = lstatSync8(stagingPath);
11321
11557
  if (!staged.isFile() || staged.dev !== stat.dev || staged.ino !== stat.ino) {
11322
11558
  throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "The initialized lock staging inode changed before publication completed.");
11323
11559
  }
11324
11560
  unlinkSync(stagingPath);
11325
- syncDirectory(dirname8(path));
11561
+ syncDirectory(dirname9(path));
11326
11562
  handedOff = true;
11327
11563
  return { path, stagingPath, fd, dev: stat.dev, ino: stat.ino };
11328
11564
  } finally {
@@ -11367,14 +11603,14 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
11367
11603
  writeFileSync3(guardFd, `${JSON.stringify({ schemaVersion: 1, pid: process.pid, operationId: randomUUID() })}
11368
11604
  `);
11369
11605
  fsyncSync(guardFd);
11370
- const cacheOptions = { cacheDir: dirname8(dirname8(lock.path)) };
11606
+ const cacheOptions = { cacheDir: dirname9(dirname9(lock.path)) };
11371
11607
  const current = readSessionLock(lock.marker.sessionId, cacheOptions);
11372
11608
  if (!current || current.dev !== lock.dev || current.ino !== lock.ino || current.sha256 !== lock.sha256 || current.mtimeMs !== lock.mtimeMs || !writerIsGone(current, explicitLocalHost))
11373
11609
  return null;
11374
11610
  const currentReceipt = readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions);
11375
11611
  if (currentReceipt?.sha256 !== receipt?.sha256 || currentReceipt?.generation !== receipt?.generation)
11376
11612
  return null;
11377
- const directory = join12(dirname8(dirname8(lock.path)), "session-lock-recoveries", randomUUID());
11613
+ const directory = join12(dirname9(dirname9(lock.path)), "session-lock-recoveries", randomUUID());
11378
11614
  const archivePath = join12(directory, "original.write-lock");
11379
11615
  const recordPath = join12(directory, "recovery.json");
11380
11616
  assertRegularPath(archivePath, true);
@@ -11391,7 +11627,7 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
11391
11627
  })}
11392
11628
  `));
11393
11629
  syncDirectory(directory);
11394
- syncDirectory(dirname8(directory));
11630
+ syncDirectory(dirname9(directory));
11395
11631
  const finalLock = readSessionLock(lock.marker.sessionId, cacheOptions);
11396
11632
  if (!finalLock || finalLock.dev !== lock.dev || finalLock.ino !== lock.ino || finalLock.sha256 !== lock.sha256 || readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions)?.sha256 !== receipt?.sha256)
11397
11633
  return null;
@@ -11406,7 +11642,7 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
11406
11642
  archivedPaths.push(publicationPath);
11407
11643
  }
11408
11644
  syncDirectory(directory);
11409
- syncDirectory(dirname8(lock.path));
11645
+ syncDirectory(dirname9(lock.path));
11410
11646
  for (const path of archivedPaths) {
11411
11647
  const before = lstatSync8(path, { throwIfNoEntry: false });
11412
11648
  const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
@@ -11576,7 +11812,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11576
11812
  archiveBytes(receiptPath, Buffer.from(`${JSON.stringify(receipt)}
11577
11813
  `));
11578
11814
  syncDirectory(directory);
11579
- syncDirectory(dirname8(directory));
11815
+ syncDirectory(dirname9(directory));
11580
11816
  syncDirectory(selectionCacheRoot(options));
11581
11817
  if (sha256Hex(readRegularFile(archivePath, MAX_PROFILE_DOCUMENT_BYTES)) !== before.sha256 || !sameSnapshot(readSkillSessionSnapshot(replacement.sessionId, options), expected)) {
11582
11818
  throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The session or its preservation archive changed before replacement; retained evidence must be reviewed.");
@@ -11596,7 +11832,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11596
11832
  // package.json
11597
11833
  var package_default = {
11598
11834
  name: "@hasna/skills",
11599
- version: "0.10.50",
11835
+ version: "0.10.52",
11600
11836
  description: "Skills library for AI coding agents",
11601
11837
  type: "module",
11602
11838
  bin: {
@@ -11733,12 +11969,12 @@ import { spawnSync as spawnSync2 } from "child_process";
11733
11969
  import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
11734
11970
  import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
11735
11971
  import { hostname as osHostname } from "os";
11736
- import { isAbsolute as isAbsolute8, join as join22 } from "path";
11972
+ import { isAbsolute as isAbsolute9, join as join22 } from "path";
11737
11973
  import { createRequire } from "module";
11738
11974
  import { fileURLToPath as fileURLToPath2 } from "url";
11739
11975
  import { closeSync as closeSync4, fstatSync as fstatSync4, lstatSync as lstatSync9, openSync as openSync4, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync6 } from "fs";
11740
11976
  import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
11741
- import { basename as basename4, dirname as dirname9, join as join13, relative as relative3, sep as sep4 } from "path";
11977
+ import { basename as basename5, dirname as dirname10, join as join13, relative as relative3, sep as sep4 } from "path";
11742
11978
  import { fileURLToPath, pathToFileURL } from "url";
11743
11979
  function envToken(name) {
11744
11980
  return name.toUpperCase().replace(/-/g, "_");
@@ -11844,9 +12080,9 @@ function entryPoint(directory) {
11844
12080
  return pathToFileURL(file).href;
11845
12081
  }
11846
12082
  function resolveInstalledSecrets(parentUrl) {
11847
- let directory = dirname9(fileURLToPath(parentUrl));
12083
+ let directory = dirname10(fileURLToPath(parentUrl));
11848
12084
  for (;; ) {
11849
- if (basename4(directory) !== "node_modules") {
12085
+ if (basename5(directory) !== "node_modules") {
11850
12086
  const candidate = join13(directory, "node_modules", PACKAGE);
11851
12087
  let absent = false;
11852
12088
  try {
@@ -11859,7 +12095,7 @@ function resolveInstalledSecrets(parentUrl) {
11859
12095
  if (!absent)
11860
12096
  return entryPoint(candidate);
11861
12097
  }
11862
- const parent = dirname9(directory);
12098
+ const parent = dirname10(directory);
11863
12099
  if (parent === directory)
11864
12100
  throw new Error("Secrets SDK is not installed for this consumer");
11865
12101
  directory = parent;
@@ -11879,8 +12115,8 @@ class CredentialResolutionError extends Error {
11879
12115
 
11880
12116
  class CredentialFileUnsafeError extends Error {
11881
12117
  path;
11882
- constructor(path, reason) {
11883
- super(`Refusing unsafe credential/config file ${path}: ${reason}.`);
12118
+ constructor(path, reason2) {
12119
+ super(`Refusing unsafe credential/config file ${path}: ${reason2}.`);
11884
12120
  this.name = "CredentialFileUnsafeError";
11885
12121
  this.path = path;
11886
12122
  }
@@ -11906,7 +12142,7 @@ function homeDir(env) {
11906
12142
  }
11907
12143
  function absoluteOverride(env, key) {
11908
12144
  const value = env[key]?.trim();
11909
- return value && isAbsolute8(value) ? value : null;
12145
+ return value && isAbsolute9(value) ? value : null;
11910
12146
  }
11911
12147
  function hasnaHomeDir(env) {
11912
12148
  const override = absoluteOverride(env, HASNA_HOME_ENV_KEY);
@@ -11952,9 +12188,9 @@ function parseEnvFile(text2) {
11952
12188
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
11953
12189
  continue;
11954
12190
  let value = withoutExport.slice(equals + 1).trim();
11955
- const quote = value[0];
11956
- if (quote === '"' || quote === "'") {
11957
- if (value.length < 2 || !value.endsWith(quote)) {
12191
+ const quote2 = value[0];
12192
+ if (quote2 === '"' || quote2 === "'") {
12193
+ if (value.length < 2 || !value.endsWith(quote2)) {
11958
12194
  unusable.add(key);
11959
12195
  continue;
11960
12196
  }
@@ -11978,8 +12214,8 @@ function configFileReadsCoherent(before, after) {
11978
12214
  return before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs;
11979
12215
  }
11980
12216
  function readAppConfigFile(path) {
11981
- const unsafe = (reason) => {
11982
- throw new CredentialFileUnsafeError(path, reason);
12217
+ const unsafe = (reason2) => {
12218
+ throw new CredentialFileUnsafeError(path, reason2);
11983
12219
  };
11984
12220
  let fd = -1;
11985
12221
  try {
@@ -12175,8 +12411,8 @@ function readKeychainItem(name, env, kind, options) {
12175
12411
  try {
12176
12412
  result = run(["find-generic-password", "-a", account, "-s", service, "-w"]);
12177
12413
  } catch (error) {
12178
- const reason = keychainFailureHint(error instanceof Error ? error.message : String(error));
12179
- throw new CredentialResolutionError(name, `The Keychain lookup for ${source} could not run${reason}. A Keychain failure is never resolved ` + `around: fix the keychain, or delete the item to fall through to the credential on disk.`, [source]);
12414
+ const reason2 = keychainFailureHint(error instanceof Error ? error.message : String(error));
12415
+ throw new CredentialResolutionError(name, `The Keychain lookup for ${source} could not run${reason2}. A Keychain failure is never resolved ` + `around: fix the keychain, or delete the item to fall through to the credential on disk.`, [source]);
12180
12416
  }
12181
12417
  if (result.status === KEYCHAIN_ITEM_NOT_FOUND_STATUS)
12182
12418
  return null;
@@ -12994,7 +13230,7 @@ class MissingSkillsFleetError extends Error {
12994
13230
  }
12995
13231
 
12996
13232
  // src/lib/remote-files.ts
12997
- import { createHash as createHash10 } from "crypto";
13233
+ import { createHash as createHash11 } from "crypto";
12998
13234
  var MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
12999
13235
  function describeRemoteFiles(files) {
13000
13236
  if (files.length > 10)
@@ -13012,7 +13248,7 @@ function describeRemoteFiles(files) {
13012
13248
  });
13013
13249
  }
13014
13250
  function sha256(bytes) {
13015
- return createHash10("sha256").update(bytes).digest("hex");
13251
+ return createHash11("sha256").update(bytes).digest("hex");
13016
13252
  }
13017
13253
  async function readBoundedResponse(response, maximum, signal) {
13018
13254
  if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
@@ -13070,6 +13306,10 @@ function identifier(value) {
13070
13306
  function invalid() {
13071
13307
  throw new Error("The Skills API returned an invalid profile response");
13072
13308
  }
13309
+ function unavailable(error, message) {
13310
+ const name = error?.name;
13311
+ return new SkillSelectionError("SKILLS_API_UNAVAILABLE", message, name === "AbortError" || name === "TimeoutError" ? { cause: new DOMException("The Skills API request was cut short", name) } : undefined);
13312
+ }
13073
13313
  function validateSelection2(value) {
13074
13314
  if (!object3(value) || !identifier(value.slug) || typeof value.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(value.version) || value.version.includes("..") || typeof value.bundleDigest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value.bundleDigest))
13075
13315
  invalid();
@@ -13117,8 +13357,8 @@ class HttpProfileClient {
13117
13357
  ...init.headers
13118
13358
  }
13119
13359
  });
13120
- } catch {
13121
- throw new SkillSelectionError("SKILLS_API_UNAVAILABLE", "Unable to reach the configured Skills API");
13360
+ } catch (error) {
13361
+ throw unavailable(error, "Unable to reach the configured Skills API");
13122
13362
  }
13123
13363
  if (!response.ok) {
13124
13364
  response.body?.cancel().catch(() => {});
@@ -13135,7 +13375,9 @@ class HttpProfileClient {
13135
13375
  async read(response, limit = MAX_PROFILE_DOCUMENT_BYTES) {
13136
13376
  try {
13137
13377
  return JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, limit, this.signal)));
13138
- } catch {
13378
+ } catch (error) {
13379
+ if (this.signal?.aborted)
13380
+ throw unavailable(this.signal.reason ?? error, "The Skills API response was cut short");
13139
13381
  invalid();
13140
13382
  }
13141
13383
  }
@@ -13222,17 +13464,17 @@ import { constants as constants5, closeSync as closeSync6, fstatSync as fstatSyn
13222
13464
  import { join as join14 } from "path";
13223
13465
 
13224
13466
  // src/lib/preparation-process.ts
13225
- function capturePreparationProcess(child, platform = process.platform, signalGroup = (pid, signal) => process.kill(pid, signal)) {
13467
+ function capturePreparationProcess(child2, platform = process.platform, signalGroup = (pid, signal) => process.kill(pid, signal)) {
13226
13468
  let spawnedPid;
13227
13469
  try {
13228
- spawnedPid = child.pid;
13470
+ spawnedPid = child2.pid;
13229
13471
  } catch {}
13230
13472
  const groupId = typeof spawnedPid === "number" && Number.isSafeInteger(spawnedPid) && spawnedPid > 1 ? spawnedPid : undefined;
13231
13473
  const canSignalGroup = platform !== "win32" && groupId !== undefined;
13232
13474
  const killLiveChild = () => {
13233
- if (child.exitCode === null && child.signalCode === null) {
13475
+ if (child2.exitCode === null && child2.signalCode === null) {
13234
13476
  try {
13235
- child.kill("SIGKILL");
13477
+ child2.kill("SIGKILL");
13236
13478
  } catch {}
13237
13479
  }
13238
13480
  };
@@ -13431,20 +13673,20 @@ import { join as join28 } from "path";
13431
13673
  // src/lib/codex-corpus-write.ts
13432
13674
  import { KernelLock } from "@hasna/contracts/kernel-lock";
13433
13675
  import { execFileSync } from "child_process";
13434
- import { createHash as createHash11 } from "crypto";
13676
+ import { createHash as createHash12 } from "crypto";
13435
13677
  import { accessSync, constants as constants6, lstatSync as lstatSync12, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync7 } from "fs";
13436
- import { basename as basename5, delimiter, dirname as dirname10, isAbsolute as isAbsolute9, join as join15, relative as relative4, resolve as resolve9, sep as sep5 } from "path";
13678
+ import { basename as basename6, delimiter, dirname as dirname11, isAbsolute as isAbsolute10, join as join15, relative as relative4, resolve as resolve10, sep as sep5 } from "path";
13437
13679
  var KEY = ".native-corpus-admission";
13438
13680
  var fail = () => {
13439
13681
  throw new Error("CODEX_CORPUS_ADMISSION_REQUIRED: verified native admission and its existing shared lock are required");
13440
13682
  };
13441
13683
  function physicalTarget(path) {
13442
13684
  const suffix = [];
13443
- for (let cursor = path;; cursor = dirname10(cursor)) {
13685
+ for (let cursor = path;; cursor = dirname11(cursor)) {
13444
13686
  try {
13445
13687
  return join15(realpathSync3(cursor), ...suffix);
13446
13688
  } catch (error) {
13447
- if (error.code !== "ENOENT" || cursor === dirname10(cursor))
13689
+ if (error.code !== "ENOENT" || cursor === dirname11(cursor))
13448
13690
  throw error;
13449
13691
  try {
13450
13692
  if (lstatSync12(cursor).isSymbolicLink())
@@ -13453,14 +13695,14 @@ function physicalTarget(path) {
13453
13695
  if (statError.code !== "ENOENT")
13454
13696
  throw statError;
13455
13697
  }
13456
- suffix.unshift(basename5(cursor));
13698
+ suffix.unshift(basename6(cursor));
13457
13699
  }
13458
13700
  }
13459
13701
  }
13460
13702
  function codexCorpusRootForPath(path) {
13461
- const target = resolve9(path), physical = physicalTarget(target);
13703
+ const target = resolve10(path), physical = physicalTarget(target);
13462
13704
  const selected = process.env.CODEX_HOME;
13463
- const selectedRoots = selected && isAbsolute9(selected) ? [resolve9(selected), physicalTarget(resolve9(selected))] : [];
13705
+ const selectedRoots = selected && isAbsolute10(selected) ? [resolve10(selected), physicalTarget(resolve10(selected))] : [];
13464
13706
  const classify = (candidate) => {
13465
13707
  const checked = (root2) => {
13466
13708
  if (candidate === root2 || candidate === join15(root2, `${KEY}.flock-v1`))
@@ -13469,10 +13711,10 @@ function codexCorpusRootForPath(path) {
13469
13711
  };
13470
13712
  for (const root2 of selectedRoots) {
13471
13713
  const distance = relative4(root2, candidate);
13472
- if (distance === "" || !distance.startsWith(`..${sep5}`) && distance !== ".." && !isAbsolute9(distance))
13714
+ if (distance === "" || !distance.startsWith(`..${sep5}`) && distance !== ".." && !isAbsolute10(distance))
13473
13715
  return checked(root2);
13474
13716
  }
13475
- for (let cursor = candidate;cursor !== dirname10(cursor); cursor = dirname10(cursor)) {
13717
+ for (let cursor = candidate;cursor !== dirname11(cursor); cursor = dirname11(cursor)) {
13476
13718
  if (cursor.endsWith(`${sep5}.codex`))
13477
13719
  return checked(cursor);
13478
13720
  }
@@ -13498,10 +13740,10 @@ function executable(options) {
13498
13740
  if (typeof command !== "string" || !command || command.includes("\x00") || options.codexSha256 !== undefined && !/^[a-f0-9]{64}$/.test(options.codexSha256))
13499
13741
  return fail();
13500
13742
  let found;
13501
- if (isAbsolute9(command))
13743
+ if (isAbsolute10(command))
13502
13744
  found = command;
13503
13745
  else if (!command.includes(sep5)) {
13504
- found = (process.env.PATH ?? "").split(delimiter).filter(isAbsolute9).map((dir) => join15(dir, command)).find((candidate) => {
13746
+ found = (process.env.PATH ?? "").split(delimiter).filter(isAbsolute10).map((dir) => join15(dir, command)).find((candidate) => {
13505
13747
  try {
13506
13748
  accessSync(candidate, constants6.X_OK);
13507
13749
  return true;
@@ -13515,7 +13757,7 @@ function executable(options) {
13515
13757
  const path = realpathSync3(found), initial = statSync7(path, { bigint: true });
13516
13758
  if (!initial.isFile() || (initial.mode & 0o022n) !== 0n || (initial.mode & 0o111n) === 0n || initial.uid !== BigInt(process.getuid()) && initial.uid !== 0n)
13517
13759
  return fail();
13518
- const hash2 = () => createHash11("sha256").update(readFileSync11(path)).digest("hex");
13760
+ const hash2 = () => createHash12("sha256").update(readFileSync11(path)).digest("hex");
13519
13761
  const digest = hash2();
13520
13762
  if (options.codexSha256 && digest !== options.codexSha256)
13521
13763
  return fail();
@@ -13539,7 +13781,7 @@ function acquireCodexCorpusWrite(roots, options) {
13539
13781
  }
13540
13782
  };
13541
13783
  try {
13542
- if (roots.some((root) => typeof root !== "string" || !isAbsolute9(root) || root.includes("\x00")))
13784
+ if (roots.some((root) => typeof root !== "string" || !isAbsolute10(root) || root.includes("\x00")))
13543
13785
  fail();
13544
13786
  const inputs = [...new Set(roots)].map((input) => ({ input, root: realpathSync3(input) })).sort((a, b) => a.root.localeCompare(b.root));
13545
13787
  for (const { input, root } of inputs) {
@@ -13609,10 +13851,10 @@ import {
13609
13851
  writeFileSync as writeFileSync7
13610
13852
  } from "fs";
13611
13853
  import { homedir as homedir3 } from "os";
13612
- import { basename as basename7, dirname as dirname15, join as join25 } from "path";
13854
+ import { basename as basename8, dirname as dirname16, join as join25 } from "path";
13613
13855
 
13614
13856
  // src/lib/codex-plugin-skill-controls.ts
13615
- import { createHash as createHash12 } from "crypto";
13857
+ import { createHash as createHash13 } from "crypto";
13616
13858
 
13617
13859
  // src/lib/codex-local-mcp-controls.ts
13618
13860
  function isCodexLocalStdioMcpServer(name, server) {
@@ -13681,43 +13923,24 @@ function isCodexLocalStdioMcpServer(name, server) {
13681
13923
  }
13682
13924
 
13683
13925
  // src/lib/codex-plugin-skill-controls.ts
13684
- import { basename as basename6, dirname as dirname11, join as join17, relative as relative5, resolve as resolve10, sep as sep6 } from "path";
13926
+ import { basename as basename7, dirname as dirname12, join as join17, relative as relative5, resolve as resolve11, sep as sep6 } from "path";
13685
13927
  import { lstatSync as lstatSync13, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync6 } from "fs";
13686
13928
 
13687
- // src/lib/codex-native-compatibility.ts
13688
- var MEASURED_CODEX_CAPABILITIES = {
13689
- "codex-cli 0.153.0": ["hooks"],
13690
- "codex-cli 0.154.0": ["hooks"],
13691
- "codex-cli 0.155.0": ["hooks"],
13692
- "codex-cli 0.155.1": ["hooks"],
13693
- "codex-cli 0.156.1": ["hooks"],
13694
- "codex-cli 0.157.0": ["hooks"],
13695
- "codex-cli 0.157.1": ["hooks"],
13696
- "codex-cli 0.158.0": ["hooks"],
13697
- "codex-cli 0.159.0": ["hooks"],
13698
- "codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
13699
- "codex-cli 0.160.0": ["hooks", "qualified-skill-catalog"]
13700
- };
13701
- function supportsCodexNativeCapability(version, capability) {
13702
- return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
13703
- }
13704
- var SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
13705
-
13706
13929
  // src/lib/codex-native-skill-catalog.ts
13707
13930
  import { realpathSync as realpathSync5 } from "fs";
13708
- import { isAbsolute as isAbsolute11, normalize as normalize2 } from "path";
13931
+ import { isAbsolute as isAbsolute12, normalize as normalize2 } from "path";
13709
13932
 
13710
13933
  // src/lib/codex-hook-rpc.ts
13711
13934
  import { join as join16 } from "path";
13712
13935
  import { execFile, spawn } from "child_process";
13713
13936
  import { realpathSync as realpathSync4, statSync as statSync8 } from "fs";
13714
- import { isAbsolute as isAbsolute10 } from "path";
13937
+ import { isAbsolute as isAbsolute11 } from "path";
13715
13938
  import { promisify } from "util";
13716
13939
  async function connectCodexHookRpc(options) {
13717
13940
  const timeoutMs = options.timeoutMs ?? 20000;
13718
13941
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 20000)
13719
13942
  throw new Error("CODEX_HOOK_TRUST_INVALID_TIMEOUT");
13720
- const found = isAbsolute10(options.command) ? options.command : Bun.which(options.command, { PATH: process.env.PATH });
13943
+ const found = isAbsolute11(options.command) ? options.command : Bun.which(options.command, { PATH: process.env.PATH });
13721
13944
  if (!found)
13722
13945
  throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE: install Codex or pass --codex-command");
13723
13946
  const binary = realpathSync4(found), stat = statSync8(binary);
@@ -13741,16 +13964,16 @@ async function connectCodexHookRpc(options) {
13741
13964
  else
13742
13965
  env.CODEX_HOME = options.codexHome;
13743
13966
  const corpusLease = acquireCodexCorpusWrite([options.codexHome ?? join16(options.home, ".codex")], { codexCommand: binary });
13744
- const child = spawn(binary, ["app-server", "--strict-config", "--stdio"], {
13967
+ const child2 = spawn(binary, ["app-server", "--strict-config", "--stdio"], {
13745
13968
  cwd: options.home,
13746
13969
  env,
13747
13970
  stdio: ["pipe", "pipe", "pipe"]
13748
13971
  });
13749
13972
  let childClosed = false;
13750
- const actualClose = new Promise((resolve10) => child.once("close", () => {
13973
+ const actualClose = new Promise((resolve11) => child2.once("close", () => {
13751
13974
  childClosed = true;
13752
13975
  corpusLease.close();
13753
- resolve10();
13976
+ resolve11();
13754
13977
  }));
13755
13978
  const pending = new Map;
13756
13979
  let nextId = 1, buffer = Buffer.alloc(0), stopped = false, diagnosticBytes = 0;
@@ -13762,20 +13985,20 @@ async function connectCodexHookRpc(options) {
13762
13985
  }
13763
13986
  pending.clear();
13764
13987
  };
13765
- child.on("error", fail2);
13766
- child.on("exit", fail2);
13767
- child.stdin.on("error", fail2);
13768
- child.stderr.on("data", (bytes) => {
13988
+ child2.on("error", fail2);
13989
+ child2.on("exit", fail2);
13990
+ child2.stdin.on("error", fail2);
13991
+ child2.stderr.on("data", (bytes) => {
13769
13992
  diagnosticBytes += bytes.length;
13770
13993
  if (diagnosticBytes > 2 * 1024 * 1024) {
13771
13994
  fail2();
13772
- child.kill();
13995
+ child2.kill();
13773
13996
  }
13774
13997
  });
13775
- child.stdout.on("data", (bytes) => {
13998
+ child2.stdout.on("data", (bytes) => {
13776
13999
  if (buffer.length + bytes.length > 8 * 1024 * 1024) {
13777
14000
  fail2();
13778
- child.kill();
14001
+ child2.kill();
13779
14002
  return;
13780
14003
  }
13781
14004
  buffer = Buffer.concat([buffer, bytes]);
@@ -13790,12 +14013,12 @@ async function connectCodexHookRpc(options) {
13790
14013
  value = JSON.parse(line.toString("utf8"));
13791
14014
  } catch {
13792
14015
  fail2();
13793
- child.kill();
14016
+ child2.kill();
13794
14017
  return;
13795
14018
  }
13796
14019
  if (!value || typeof value !== "object" || Array.isArray(value)) {
13797
14020
  fail2();
13798
- child.kill();
14021
+ child2.kill();
13799
14022
  return;
13800
14023
  }
13801
14024
  const waiting = pending.get(value.id);
@@ -13811,29 +14034,29 @@ async function connectCodexHookRpc(options) {
13811
14034
  });
13812
14035
  const rpc = {
13813
14036
  version,
13814
- processId: child.pid,
14037
+ processId: child2.pid,
13815
14038
  request(method, params) {
13816
14039
  if (stopped)
13817
14040
  return Promise.reject(new Error("CODEX_HOOK_TRUST_NATIVE_RPC_CLOSED"));
13818
- return new Promise((resolve10, reject) => {
14041
+ return new Promise((resolve11, reject) => {
13819
14042
  const id = nextId++;
13820
14043
  const timer = setTimeout(() => {
13821
14044
  fail2();
13822
- child.kill();
14045
+ child2.kill();
13823
14046
  }, timeoutMs);
13824
- pending.set(id, { resolve: resolve10, reject, timer });
13825
- child.stdin.write(JSON.stringify({ id, method, params }) + `
14047
+ pending.set(id, { resolve: resolve11, reject, timer });
14048
+ child2.stdin.write(JSON.stringify({ id, method, params }) + `
13826
14049
  `);
13827
14050
  });
13828
14051
  },
13829
14052
  async close() {
13830
14053
  fail2();
13831
- child.stdin.end();
14054
+ child2.stdin.end();
13832
14055
  if (childClosed)
13833
14056
  return;
13834
- await new Promise((resolve10, reject) => {
14057
+ await new Promise((resolve11, reject) => {
13835
14058
  const terminate = setTimeout(() => {
13836
- child.kill();
14059
+ child2.kill();
13837
14060
  }, 1000);
13838
14061
  const deadline = setTimeout(() => {
13839
14062
  clearTimeout(terminate);
@@ -13842,7 +14065,7 @@ async function connectCodexHookRpc(options) {
13842
14065
  actualClose.then(() => {
13843
14066
  clearTimeout(terminate);
13844
14067
  clearTimeout(deadline);
13845
- resolve10();
14068
+ resolve11();
13846
14069
  });
13847
14070
  });
13848
14071
  }
@@ -13852,7 +14075,7 @@ async function connectCodexHookRpc(options) {
13852
14075
  const nativeVersion = version.slice("codex-cli ".length);
13853
14076
  if (typeof initialized?.userAgent !== "string" || !(initialized.userAgent === `skills-native-hook-enrollment/${nativeVersion}` || initialized.userAgent.startsWith(`skills-native-hook-enrollment/${nativeVersion} `)))
13854
14077
  throw new Error("CODEX_HOOK_TRUST_NATIVE_VERSION_MISMATCH");
13855
- child.stdin.write(`{"method":"initialized"}
14078
+ child2.stdin.write(`{"method":"initialized"}
13856
14079
  `);
13857
14080
  return rpc;
13858
14081
  } catch {
@@ -13876,7 +14099,7 @@ function refuse() {
13876
14099
  var object4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
13877
14100
  var scalar = (value, max) => typeof value === "string" && value.length > 0 && value.trim() === value && Buffer.byteLength(value, "utf8") <= max && !/[\u0000-\u001f\u007f]/u.test(value);
13878
14101
  var remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value);
13879
- var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute11(value);
14102
+ var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute12(value);
13880
14103
  function projectPluginSource(source) {
13881
14104
  if (!object4(source))
13882
14105
  return {};
@@ -14075,7 +14298,7 @@ function mcpControls(path, read2) {
14075
14298
  function isBundledCleanupHooks(root, namespace, value) {
14076
14299
  const object5 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
14077
14300
  const exactKeys = (v, keys) => Object.keys(v).length === keys.length && keys.every((key) => Object.hasOwn(v, key));
14078
- if (basename6(dirname11(root)) !== namespace || basename6(dirname11(dirname11(root))) !== "openai-bundled")
14301
+ if (basename7(dirname12(root)) !== namespace || basename7(dirname12(dirname12(root))) !== "openai-bundled")
14079
14302
  return false;
14080
14303
  const server = namespace === "unified-computer-use" ? "cua_repl" : ["browser", "chrome", "chrome-dev", "chrome-internal", "computer-use"].includes(namespace) ? "node_repl" : undefined;
14081
14304
  if (!server || !object5(value) || !exactKeys(value, ["hooks"]) || !object5(value.hooks))
@@ -14109,7 +14332,7 @@ function rootControls(root, read2) {
14109
14332
  }
14110
14333
  if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
14111
14334
  refuse2();
14112
- const cacheVersion = basename6(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
14335
+ const cacheVersion = basename7(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
14113
14336
  if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
14114
14337
  refuse2();
14115
14338
  if (manifest.hooks !== undefined && !isBundledCleanupHooks(root, manifest.name, manifest.hooks) || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync13(join17(root, "hooks"), { throwIfNoEntry: false }))
@@ -14154,7 +14377,7 @@ function documentIdentity(document2, cache, read2) {
14154
14377
  }
14155
14378
  if (!front || typeof front !== "object" || !identifier2(front.name) || typeof front.description !== "string" || !front.description.trim())
14156
14379
  refuse2();
14157
- for (let root = dirname11(document2), depth = 0;root.startsWith(cache + sep6) && depth < 12; root = dirname11(root), depth++) {
14380
+ for (let root = dirname12(document2), depth = 0;root.startsWith(cache + sep6) && depth < 12; root = dirname12(root), depth++) {
14158
14381
  const path = join17(root, ".codex-plugin", "plugin.json");
14159
14382
  if (!lstatSync13(path, { throwIfNoEntry: false }))
14160
14383
  continue;
@@ -14172,9 +14395,9 @@ function documentIdentity(document2, cache, read2) {
14172
14395
  if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
14173
14396
  refuse2();
14174
14397
  const declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
14175
- if (declaredVersion === null || (declaredVersion ? declaredVersion !== basename6(root) : basename6(root) !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
14398
+ if (declaredVersion === null || (declaredVersion ? declaredVersion !== basename7(root) : basename7(root) !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
14176
14399
  refuse2();
14177
- return { name: `${manifest.name}:${front.name}`, namespace: manifest.name, pluginParent: dirname11(root), root };
14400
+ return { name: `${manifest.name}:${front.name}`, namespace: manifest.name, pluginParent: dirname12(root), root };
14178
14401
  }
14179
14402
  refuse2();
14180
14403
  }
@@ -14203,13 +14426,13 @@ function classifyCodexPluginCacheDocument(document2, cache, read2) {
14203
14426
  function reviewedDisabledCodexPluginParent(cache, parent, controls, rules) {
14204
14427
  try {
14205
14428
  const parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
14206
- if (resolve10(parent) !== parent || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !enrolled.length || !Array.isArray(rules))
14429
+ if (resolve11(parent) !== parent || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !enrolled.length || !Array.isArray(rules))
14207
14430
  return false;
14208
- for (let cursor = parent;; cursor = dirname11(cursor)) {
14431
+ for (let cursor = parent;; cursor = dirname12(cursor)) {
14209
14432
  const stat = lstatSync13(cursor, { throwIfNoEntry: false });
14210
14433
  if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
14211
14434
  return false;
14212
- if (dirname11(cursor) === cursor)
14435
+ if (dirname12(cursor) === cursor)
14213
14436
  break;
14214
14437
  }
14215
14438
  return enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path.startsWith(parent + sep6))) && rule.enabled !== false));
@@ -14222,14 +14445,14 @@ function absentDisabledCodexPluginParent(cache, parent, controls, rules) {
14222
14445
  }
14223
14446
  function reviewedRetiredCodexRoot(cache, root, controls, rules, read2, allowAbsent = false) {
14224
14447
  try {
14225
- const parent = dirname11(root), parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
14226
- if (resolve10(root) !== root || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !basename6(root) || basename6(root) === "latest" || !enrolled.length || !Array.isArray(rules))
14448
+ const parent = dirname12(root), parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
14449
+ if (resolve11(root) !== root || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !basename7(root) || basename7(root) === "latest" || !enrolled.length || !Array.isArray(rules))
14227
14450
  return false;
14228
- for (let cursor = root;; cursor = dirname11(cursor)) {
14451
+ for (let cursor = root;; cursor = dirname12(cursor)) {
14229
14452
  const stat = lstatSync13(cursor, { throwIfNoEntry: false });
14230
14453
  if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
14231
14454
  return false;
14232
- if (dirname11(cursor) === cursor)
14455
+ if (dirname12(cursor) === cursor)
14233
14456
  break;
14234
14457
  }
14235
14458
  if (!enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path === root || rule.path.startsWith(root + sep6))) && rule.enabled !== false)))
@@ -14276,8 +14499,8 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2, rules)
14276
14499
  for (const name of names) {
14277
14500
  let root = join17(parent, name), stat = lstatSync13(root);
14278
14501
  if (stat.isSymbolicLink()) {
14279
- const target = resolve10(parent, readlinkSync3(root));
14280
- if (name !== "latest" || dirname11(target) !== parent || !lstatSync13(target).isDirectory() || realpathSync6(target) !== target)
14502
+ const target = resolve11(parent, readlinkSync3(root));
14503
+ if (name !== "latest" || dirname12(target) !== parent || !lstatSync13(target).isDirectory() || realpathSync6(target) !== target)
14281
14504
  return false;
14282
14505
  root = target;
14283
14506
  stat = lstatSync13(root);
@@ -14297,6 +14520,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2, rules)
14297
14520
  function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules, pluginSettings, plannedRules, denials = []) {
14298
14521
  if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd)
14299
14522
  refuse2();
14523
+ const installedPluginReview = supportsCodexNativeCapability(catalog.version, "installed-plugin-review");
14300
14524
  if (!Array.isArray(denials) || denials.length > 4096 || new Set(denials.map((item) => item?.path)).size !== denials.length || denials.some((item) => !item || Object.keys(item).sort().join(",") !== "name,path,sha256" || typeof item.name !== "string" || typeof item.path !== "string" || !/^[a-f0-9]{64}$/.test(item.sha256)))
14301
14525
  refuse2();
14302
14526
  const usedDenials = new Set;
@@ -14328,7 +14552,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14328
14552
  if (listed.length > 1)
14329
14553
  refuse2();
14330
14554
  const installed = installedPlugins.filter((plugin) => plugin.id === expectedPluginId && plugin.name === pluginName);
14331
- if (catalog.version === "codex-cli 0.160.0" && installed.length === 1 && installed[0].installed && installed[0].localVersion === basename6(located.root) && installed[0].sourceType === "local" && codexPluginSourceIsConfigControlled(installed[0])) {
14555
+ if (installedPluginReview && installed.length === 1 && installed[0].installed && installed[0].localVersion === basename7(located.root) && installed[0].sourceType === "local" && codexPluginSourceIsConfigControlled(installed[0])) {
14332
14556
  const plugin = installed[0], input = { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceRoot: plugin.sourcePath, sourceSha256: plugin.sourceSha256 };
14333
14557
  if (input.sourceRoot !== located.root && !input.sourceRoot.startsWith(cache + sep6) && (lstatSync13(input.sourceRoot, { throwIfNoEntry: false }) || documents.some((path) => path.startsWith(input.sourceRoot + sep6)))) {
14334
14558
  if (skills.some((skill) => skill.path === input.sourceRoot || skill.path.startsWith(input.sourceRoot + sep6)) || !sourceInputUnchanged(input, cache, read2))
@@ -14337,7 +14561,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14337
14561
  }
14338
14562
  }
14339
14563
  const settings = pluginSettings && typeof pluginSettings === "object" && !Array.isArray(pluginSettings) ? pluginSettings : undefined;
14340
- 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) {
14564
+ if (installedPluginReview && !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) {
14341
14565
  if (!Array.isArray(rules))
14342
14566
  refuse2();
14343
14567
  const exactPathDeny = rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
@@ -14360,7 +14584,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14360
14584
  refuse2();
14361
14585
  const plugin = installed[0];
14362
14586
  if (plugin.localVersion !== null) {
14363
- if (plugin.localVersion !== basename6(dirname11(dirname11(dirname11(document2)))))
14587
+ if (plugin.localVersion !== basename7(dirname12(dirname12(dirname12(document2)))))
14364
14588
  continue;
14365
14589
  } else {
14366
14590
  if (plugin.sourceType !== "remote" || !plugin.remotePluginId || !remoteInstallationMatches(parsed.pluginParent, plugin.remotePluginId, read2) || !lstatSync13(document2).isFile() || realpathSync6(document2) !== document2)
@@ -14370,12 +14594,12 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14370
14594
  const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
14371
14595
  const nativeSkill = { name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true };
14372
14596
  const denial = denials.find((item) => item.path === document2);
14373
- const explicitDeny = denial !== undefined && remotePluginId !== undefined && catalog.version === "codex-cli 0.160.0" && denial.name === parsed.name && denial.sha256 === createHash12("sha256").update(read2(document2)).digest("hex") && Array.isArray(rules) && !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document2 && rule.enabled === false);
14597
+ const explicitDeny = denial !== undefined && remotePluginId !== undefined && installedPluginReview && denial.name === parsed.name && denial.sha256 === createHash13("sha256").update(read2(document2)).digest("hex") && Array.isArray(rules) && !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document2 && rule.enabled === false);
14374
14598
  if (denial && !explicitDeny)
14375
14599
  refuse2();
14376
14600
  if (explicitDeny)
14377
14601
  usedDenials.add(document2);
14378
- const plannedPathDeny = catalog.version === "codex-cli 0.160.0" && remotePluginId !== undefined && Array.isArray(rules) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === parsed.name && rule.enabled === false) && !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document2 && rule.enabled === false) && isCodexNativeSkillDisabled(nativeSkill, plannedRules);
14602
+ const plannedPathDeny = installedPluginReview && remotePluginId !== undefined && Array.isArray(rules) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === parsed.name && rule.enabled === false) && !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document2 && rule.enabled === false) && isCodexNativeSkillDisabled(nativeSkill, plannedRules);
14379
14603
  if (!explicitDeny && (!exactPathDeny && !plannedPathDeny || !isCodexNativeSkillDisabled(nativeSkill, rules)))
14380
14604
  refuse2();
14381
14605
  }
@@ -14391,7 +14615,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14391
14615
  function sourceInputUnchanged(input, cache, read2) {
14392
14616
  try {
14393
14617
  const parts = relative5(cache, input.pluginParent).split(sep6);
14394
- 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 || !lstatSync13(input.sourceRoot).isDirectory() || realpathSync6(input.sourceRoot) !== input.sourceRoot || input.sourceRoot === cache || input.sourceRoot.startsWith(cache + sep6) || cache.startsWith(input.sourceRoot + sep6))
14618
+ 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 })) || resolve11(input.sourceRoot) !== input.sourceRoot || !lstatSync13(input.sourceRoot).isDirectory() || realpathSync6(input.sourceRoot) !== input.sourceRoot || input.sourceRoot === cache || input.sourceRoot.startsWith(cache + sep6) || cache.startsWith(input.sourceRoot + sep6))
14395
14619
  return false;
14396
14620
  const path = join17(input.sourceRoot, ".codex-plugin/plugin.json"), stat = lstatSync13(path);
14397
14621
  if (!stat.isFile() || stat.size > 1024 * 1024 || realpathSync6(path) !== path)
@@ -14457,9 +14681,9 @@ enabled = false
14457
14681
  }
14458
14682
 
14459
14683
  // src/lib/codex-native-skill-policy.ts
14460
- import { createHash as createHash13, randomUUID as randomUUID2 } from "crypto";
14684
+ import { createHash as createHash14, randomUUID as randomUUID2 } from "crypto";
14461
14685
  import { closeSync as closeSync7, constants as constants7, fstatSync as fstatSync7, fsyncSync as fsyncSync2, lstatSync as lstatSync14, mkdirSync as mkdirSync5, openSync as openSync7, readFileSync as readFileSync12, readSync as readSync5, readlinkSync as readlinkSync4, realpathSync as realpathSync7, renameSync as renameSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
14462
- import { dirname as dirname12, isAbsolute as isAbsolute12, join as join18, resolve as resolve11 } from "path";
14686
+ import { dirname as dirname13, isAbsolute as isAbsolute13, join as join18, resolve as resolve12 } from "path";
14463
14687
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
14464
14688
  var CODEX_NATIVE_POLICY_CAPABILITY = "host-path-allowlist-v1";
14465
14689
  var CODEX_NATIVE_POLICY_RECEIPT_SCHEMA = "skills.codex-native-policy-acceptance/v1";
@@ -14482,9 +14706,9 @@ function refuse3(code, detail) {
14482
14706
  throw new Error(`${code}: ${detail}`);
14483
14707
  }
14484
14708
  var object5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
14485
- function boundedId(value, field) {
14709
+ function boundedId(value, field2) {
14486
14710
  if (typeof value !== "string" || !value || value.length > MAX_ENVELOPE_ID_LENGTH || value.trim() !== value || /[\u0000-\u001f\u007f]/.test(value))
14487
- refuse3("NATIVE_SKILL_POLICY_INVALID", `${field} must be a bounded non-empty string`);
14711
+ refuse3("NATIVE_SKILL_POLICY_INVALID", `${field2} must be a bounded non-empty string`);
14488
14712
  return value;
14489
14713
  }
14490
14714
  function parseCodexNativePolicyTrust(value) {
@@ -14507,9 +14731,9 @@ function parseCodexNativeHookEnvelope(envelope, bridgeDocument) {
14507
14731
  if (!object5(value))
14508
14732
  refuse3("NATIVE_SKILL_POLICY_INVALID", "native_skill_policy must be an object");
14509
14733
  const keys = Object.keys(value);
14510
- for (const field of POLICY_FIELDS)
14511
- if (!Object.hasOwn(value, field))
14512
- refuse3("NATIVE_SKILL_POLICY_INVALID", `native_skill_policy.${field} is missing`);
14734
+ for (const field2 of POLICY_FIELDS)
14735
+ if (!Object.hasOwn(value, field2))
14736
+ refuse3("NATIVE_SKILL_POLICY_INVALID", `native_skill_policy.${field2} is missing`);
14513
14737
  if (keys.length !== POLICY_FIELDS.length)
14514
14738
  refuse3("NATIVE_SKILL_POLICY_INVALID", "native_skill_policy carries unknown fields");
14515
14739
  if (value.capability !== CODEX_NATIVE_POLICY_CAPABILITY)
@@ -14573,19 +14797,19 @@ function readBounded(path, maximum, what) {
14573
14797
  }
14574
14798
  }
14575
14799
  function verifyCodexNativeBridgeDocument(path, expectedContent, expectedSha256) {
14576
- if (!isAbsolute12(path) || resolve11(path) !== path || !path.endsWith("/SKILL.md"))
14800
+ if (!isAbsolute13(path) || resolve12(path) !== path || !path.endsWith("/SKILL.md"))
14577
14801
  refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document path is not an exact absolute SKILL.md path");
14578
- for (let cursor = path;; cursor = dirname12(cursor)) {
14802
+ for (let cursor = path;; cursor = dirname13(cursor)) {
14579
14803
  const stat = lstatSync14(cursor, { throwIfNoEntry: false });
14580
14804
  if (!stat || stat.isSymbolicLink() || (cursor === path ? !stat.isFile() : !stat.isDirectory()))
14581
14805
  refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", `the bridge path has a missing, linked or unexpected component: ${cursor}`);
14582
- if (dirname12(cursor) === cursor)
14806
+ if (dirname13(cursor) === cursor)
14583
14807
  break;
14584
14808
  }
14585
14809
  if (realpathSync7.native(path) !== path)
14586
14810
  refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document real path differs from its lexical path");
14587
14811
  const bytes = readBounded(path, MAX_BRIDGE_DOCUMENT_BYTES, "the bridge document");
14588
- const sha2562 = createHash13("sha256").update(bytes).digest("hex");
14812
+ const sha2562 = createHash14("sha256").update(bytes).digest("hex");
14589
14813
  if (!bytes.equals(Buffer.from(expectedContent, "utf8")) || sha2562 !== expectedSha256)
14590
14814
  refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document bytes differ from the managed bridge");
14591
14815
  return { path, sha256: sha2562 };
@@ -14651,13 +14875,13 @@ function assertExecutableWitnessStat(stat, currentUid) {
14651
14875
  return { dev: String(stat.dev), ino: String(stat.ino), size: String(stat.size), mtimeNs: String(stat.mtimeNs), ctimeNs: String(stat.ctimeNs), uid: String(stat.uid) };
14652
14876
  }
14653
14877
  function executableWitness(path) {
14654
- if (!isAbsolute12(path) || resolve11(path) !== path)
14878
+ if (!isAbsolute13(path) || resolve12(path) !== path)
14655
14879
  refuse3("NATIVE_SKILL_POLICY_PROCESS_UNBOUND", "the consumer executable path is not an exact absolute path");
14656
- for (let cursor = dirname12(path);; cursor = dirname12(cursor)) {
14880
+ for (let cursor = dirname13(path);; cursor = dirname13(cursor)) {
14657
14881
  const stat2 = lstatSync14(cursor, { throwIfNoEntry: false });
14658
14882
  if (!stat2 || stat2.isSymbolicLink() || !stat2.isDirectory())
14659
14883
  refuse3("NATIVE_SKILL_POLICY_PROCESS_UNBOUND", `the consumer executable path has a missing, linked or unexpected component: ${cursor}`);
14660
- if (dirname12(cursor) === cursor)
14884
+ if (dirname13(cursor) === cursor)
14661
14885
  break;
14662
14886
  }
14663
14887
  const stat = lstatSync14(path, { throwIfNoEntry: false, bigint: true });
@@ -14668,7 +14892,7 @@ function executableWitness(path) {
14668
14892
  var witnessKey = (path, witness) => JSON.stringify([path, witness.dev, witness.ino, witness.size, witness.mtimeNs, witness.ctimeNs, witness.uid]);
14669
14893
  var executableDigestMemory = new Map;
14670
14894
  function executableCachePath(dataDir2) {
14671
- return join18(resolve11(dataDir2), "agent-hooks", "codex-native-policy-executable-cache.json");
14895
+ return join18(resolve12(dataDir2), "agent-hooks", "codex-native-policy-executable-cache.json");
14672
14896
  }
14673
14897
  var currentUid = () => process.getuid?.() ?? -1;
14674
14898
  function readTrustedCacheBytes(path, ownerUid) {
@@ -14713,16 +14937,16 @@ function readExecutableCache(dataDir2, ownerUid = currentUid()) {
14713
14937
  if (!object5(value) || value.schema !== CODEX_NATIVE_POLICY_EXECUTABLE_CACHE_SCHEMA || !Array.isArray(value.entries) || value.entries.length > 16)
14714
14938
  return [];
14715
14939
  const fields = ["path", "sha256", "dev", "ino", "size", "mtimeNs", "ctimeNs", "uid"];
14716
- return value.entries.filter((entry) => object5(entry) && Object.keys(entry).length === fields.length && fields.every((field) => typeof entry[field] === "string") && HEX_DIGEST.test(entry.sha256) && isAbsolute12(entry.path) && ["dev", "ino", "size", "mtimeNs", "ctimeNs", "uid"].every((field) => /^\d{1,30}$/.test(entry[field])));
14940
+ return value.entries.filter((entry) => object5(entry) && Object.keys(entry).length === fields.length && fields.every((field2) => typeof entry[field2] === "string") && HEX_DIGEST.test(entry.sha256) && isAbsolute13(entry.path) && ["dev", "ino", "size", "mtimeNs", "ctimeNs", "uid"].every((field2) => /^\d{1,30}$/.test(entry[field2])));
14717
14941
  } catch {
14718
14942
  return [];
14719
14943
  }
14720
14944
  }
14721
14945
  function writeExecutableCache(dataDir2, entries) {
14722
- const path = executableCachePath(dataDir2), directory = dirname12(path), temporary = `${path}.skills-${randomUUID2()}`;
14946
+ const path = executableCachePath(dataDir2), directory = dirname13(path), temporary = `${path}.skills-${randomUUID2()}`;
14723
14947
  let descriptor;
14724
14948
  try {
14725
- const parent = lstatSync14(dirname12(directory), { throwIfNoEntry: false });
14949
+ const parent = lstatSync14(dirname13(directory), { throwIfNoEntry: false });
14726
14950
  if (!parent || parent.isSymbolicLink() || !parent.isDirectory())
14727
14951
  return;
14728
14952
  const existing = lstatSync14(directory, { throwIfNoEntry: false });
@@ -14790,16 +15014,16 @@ function verifyCodexNativeExecutable(inspector, processId, trust, cache = {}, ex
14790
15014
  }
14791
15015
  function assertOperatorTrustRoot(dataDir2) {
14792
15016
  const check = (path, what, required) => {
14793
- if (!isAbsolute12(path) || resolve11(path) !== path)
15017
+ if (!isAbsolute13(path) || resolve12(path) !== path)
14794
15018
  refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} path is not an exact absolute path`);
14795
- for (let cursor = dirname12(path);; cursor = dirname12(cursor)) {
15019
+ for (let cursor = dirname13(path);; cursor = dirname13(cursor)) {
14796
15020
  const stat2 = lstatSync14(cursor, { throwIfNoEntry: false });
14797
15021
  if (!stat2) {
14798
15022
  if (required)
14799
15023
  refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} has a missing path component: ${cursor}`);
14800
15024
  } else if (stat2.isSymbolicLink() || !stat2.isDirectory())
14801
15025
  refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} has a linked or unexpected path component: ${cursor}`);
14802
- if (dirname12(cursor) === cursor)
15026
+ if (dirname13(cursor) === cursor)
14803
15027
  break;
14804
15028
  }
14805
15029
  const stat = lstatSync14(path, { throwIfNoEntry: false, bigint: true });
@@ -14829,9 +15053,9 @@ function assertOperatorTrustRoot(dataDir2) {
14829
15053
  if ((stat.mode & 0o022n) !== 0n)
14830
15054
  refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} is group- or world-writable`);
14831
15055
  };
14832
- checkDirectory(resolve11(dataDir2), "the Skills data directory", true);
14833
- checkDirectory(dirname12(executableCachePath(dataDir2)), "the agent-hooks directory", false);
14834
- check(join18(resolve11(dataDir2), "agent-policy.json"), "the managed policy", true);
15056
+ checkDirectory(resolve12(dataDir2), "the Skills data directory", true);
15057
+ checkDirectory(dirname13(executableCachePath(dataDir2)), "the agent-hooks directory", false);
15058
+ check(join18(resolve12(dataDir2), "agent-policy.json"), "the managed policy", true);
14835
15059
  check(executableCachePath(dataDir2), "the executable identity cache", false);
14836
15060
  }
14837
15061
  function runCodexNativePolicyHelper(request) {
@@ -15002,12 +15226,12 @@ function defaultProcessInspector() {
15002
15226
  return { platform: process.platform, arch: process.arch, pid: process.pid, parentOf: () => null, startTime: () => null, executablePath: () => null };
15003
15227
  }
15004
15228
  function codexNativePolicyReceiptPath(dataDir2) {
15005
- return join18(resolve11(dataDir2), "agent-hooks", "codex-native-policy-acceptance.json");
15229
+ return join18(resolve12(dataDir2), "agent-hooks", "codex-native-policy-acceptance.json");
15006
15230
  }
15007
15231
  function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
15008
- const path = codexNativePolicyReceiptPath(dataDir2), directory = dirname12(path);
15232
+ const path = codexNativePolicyReceiptPath(dataDir2), directory = dirname13(path);
15009
15233
  const acceptedCache = accepted.map((entry) => ({ path: entry.path, treeSha256: entry.treeSha256 })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
15010
- if (acceptedCache.some((entry) => !isAbsolute12(entry.path) || !HEX_DIGEST.test(entry.treeSha256)) || new Set(acceptedCache.map((entry) => entry.path)).size !== acceptedCache.length)
15234
+ if (acceptedCache.some((entry) => !isAbsolute13(entry.path) || !HEX_DIGEST.test(entry.treeSha256)) || new Set(acceptedCache.map((entry) => entry.path)).size !== acceptedCache.length)
15011
15235
  refuse3("NATIVE_SKILL_POLICY_RECEIPT_FAILED", "accepted cache entries must be unique absolute paths with tree digests");
15012
15236
  const value = {
15013
15237
  schema: CODEX_NATIVE_POLICY_RECEIPT_SCHEMA,
@@ -15053,11 +15277,11 @@ function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
15053
15277
  }
15054
15278
 
15055
15279
  // src/lib/codex-settings-witness-v1.ts
15056
- import { createHash as createHash14 } from "crypto";
15057
- var sha = (text2) => createHash14("sha256").update(text2).digest("hex");
15280
+ import { createHash as createHash15 } from "crypto";
15281
+ var sha = (text2) => createHash15("sha256").update(text2).digest("hex");
15058
15282
  var EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
15059
15283
  var VERBOSITY = new Set(["low", "medium", "high"]);
15060
- function need3(value) {
15284
+ function need4(value) {
15061
15285
  if (!value)
15062
15286
  throw new Error("Invalid Codex settings witness");
15063
15287
  }
@@ -15072,14 +15296,14 @@ function numericTokens(text2) {
15072
15296
  continue;
15073
15297
  }
15074
15298
  if (char === '"' || char === "'") {
15075
- const quote = char, triple = text2.slice(at, at + 3) === quote.repeat(3);
15299
+ const quote2 = char, triple = text2.slice(at, at + 3) === quote2.repeat(3);
15076
15300
  at += triple ? 3 : 1;
15077
15301
  while (at < text2.length) {
15078
- if (quote === '"' && text2[at] === "\\") {
15302
+ if (quote2 === '"' && text2[at] === "\\") {
15079
15303
  at += 2;
15080
15304
  continue;
15081
15305
  }
15082
- if (text2.slice(at, at + (triple ? 3 : 1)) === quote.repeat(triple ? 3 : 1)) {
15306
+ if (text2.slice(at, at + (triple ? 3 : 1)) === quote2.repeat(triple ? 3 : 1)) {
15083
15307
  at += triple ? 3 : 1;
15084
15308
  break;
15085
15309
  }
@@ -15100,8 +15324,8 @@ function numericTokens(text2) {
15100
15324
  }
15101
15325
  return result;
15102
15326
  }
15103
- function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
15104
- need3(depth <= 32 && --budget2.remaining >= 0);
15327
+ function canonical5(value, depth = 0, budget2 = { remaining: 65536 }) {
15328
+ need4(depth <= 32 && --budget2.remaining >= 0);
15105
15329
  if (value === null)
15106
15330
  return ["null"];
15107
15331
  if (typeof value === "string")
@@ -15109,20 +15333,20 @@ function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
15109
15333
  if (typeof value === "boolean")
15110
15334
  return ["boolean", value];
15111
15335
  if (typeof value === "number") {
15112
- need3(Number.isFinite(value));
15336
+ need4(Number.isFinite(value));
15113
15337
  return ["number", String(value)];
15114
15338
  }
15115
15339
  if (value instanceof Date) {
15116
- need3(Number.isFinite(value.getTime()));
15340
+ need4(Number.isFinite(value.getTime()));
15117
15341
  return ["date", value.toISOString()];
15118
15342
  }
15119
15343
  if (Array.isArray(value))
15120
- return ["array", value.map((child) => canonical4(child, depth + 1, budget2))];
15121
- need3(value && typeof value === "object");
15122
- return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1, budget2)])];
15344
+ return ["array", value.map((child2) => canonical5(child2, depth + 1, budget2))];
15345
+ need4(value && typeof value === "object");
15346
+ return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child2]) => [key, canonical5(child2, depth + 1, budget2)])];
15123
15347
  }
15124
15348
  function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
15125
- need3(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15349
+ need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15126
15350
  budget2.remaining -= Buffer.byteLength(text2);
15127
15351
  let config;
15128
15352
  try {
@@ -15130,17 +15354,17 @@ function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 *
15130
15354
  } catch {
15131
15355
  throw new Error("Invalid Codex settings witness");
15132
15356
  }
15133
- need3(config && typeof config === "object" && !Array.isArray(config));
15357
+ need4(config && typeof config === "object" && !Array.isArray(config));
15134
15358
  if (Object.hasOwn(config, "model")) {
15135
- need3(typeof config.model === "string" && /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/.test(config.model));
15359
+ need4(typeof config.model === "string" && /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/.test(config.model));
15136
15360
  delete config.model;
15137
15361
  }
15138
15362
  for (const [key, values] of [["model_reasoning_effort", EFFORT], ["model_verbosity", VERBOSITY]])
15139
15363
  if (Object.hasOwn(config, key)) {
15140
- need3(typeof config[key] === "string" && values.has(config[key]));
15364
+ need4(typeof config[key] === "string" && values.has(config[key]));
15141
15365
  delete config[key];
15142
15366
  }
15143
- return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical4(config), numericTokens(text2)])}`);
15367
+ return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical5(config), numericTokens(text2)])}`);
15144
15368
  }
15145
15369
  function captureCodexSettings(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
15146
15370
  const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
@@ -15211,15 +15435,15 @@ function projectRegularCodexMcp(config, source) {
15211
15435
  }
15212
15436
 
15213
15437
  // src/lib/codex-settings-witness.ts
15214
- import { createHash as createHash15 } from "crypto";
15438
+ import { createHash as createHash16 } from "crypto";
15215
15439
  import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
15216
- var sha2 = (text2) => createHash15("sha256").update(text2).digest("hex");
15440
+ var sha2 = (text2) => createHash16("sha256").update(text2).digest("hex");
15217
15441
  var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
15218
15442
  var VERBOSITY2 = new Set(["low", "medium", "high"]);
15219
15443
  var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
15220
15444
  var CODEX_DISCOVERY_PROJECTION_FIELDS = ["plugins", "marketplaces", "skills"];
15221
15445
  var INFERENCE = ["model", "model_reasoning_effort", "model_verbosity"];
15222
- function need4(value) {
15446
+ function need5(value) {
15223
15447
  if (!value)
15224
15448
  throw new Error("Invalid Codex settings witness");
15225
15449
  }
@@ -15240,14 +15464,14 @@ function numericTokens2(text2) {
15240
15464
  continue;
15241
15465
  }
15242
15466
  if (char === '"' || char === "'") {
15243
- const quote = char, triple = text2.slice(at, at + 3) === quote.repeat(3);
15467
+ const quote2 = char, triple = text2.slice(at, at + 3) === quote2.repeat(3);
15244
15468
  at += triple ? 3 : 1;
15245
15469
  while (at < text2.length) {
15246
- if (quote === '"' && text2[at] === "\\") {
15470
+ if (quote2 === '"' && text2[at] === "\\") {
15247
15471
  at += 2;
15248
15472
  continue;
15249
15473
  }
15250
- if (text2.slice(at, at + (triple ? 3 : 1)) === quote.repeat(triple ? 3 : 1)) {
15474
+ if (text2.slice(at, at + (triple ? 3 : 1)) === quote2.repeat(triple ? 3 : 1)) {
15251
15475
  at += triple ? 3 : 1;
15252
15476
  break;
15253
15477
  }
@@ -15268,8 +15492,8 @@ function numericTokens2(text2) {
15268
15492
  }
15269
15493
  return result.sort();
15270
15494
  }
15271
- function canonical5(value, depth = 0, budget2 = { remaining: 65536 }) {
15272
- need4(depth <= 32 && --budget2.remaining >= 0);
15495
+ function canonical6(value, depth = 0, budget2 = { remaining: 65536 }) {
15496
+ need5(depth <= 32 && --budget2.remaining >= 0);
15273
15497
  if (value === null)
15274
15498
  return ["null"];
15275
15499
  if (typeof value === "string")
@@ -15277,17 +15501,17 @@ function canonical5(value, depth = 0, budget2 = { remaining: 65536 }) {
15277
15501
  if (typeof value === "boolean")
15278
15502
  return ["boolean", value];
15279
15503
  if (typeof value === "number") {
15280
- need4(Number.isFinite(value));
15504
+ need5(Number.isFinite(value));
15281
15505
  return ["number", String(value)];
15282
15506
  }
15283
15507
  if (isDate(value)) {
15284
- need4(Number.isFinite(value.getTime()));
15508
+ need5(Number.isFinite(value.getTime()));
15285
15509
  return ["date", value.toISOString()];
15286
15510
  }
15287
15511
  if (Array.isArray(value))
15288
- return ["array", value.map((child) => canonical5(child, depth + 1, budget2))];
15289
- need4(isRecord3(value));
15290
- return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical5(child, depth + 1, budget2)])];
15512
+ return ["array", value.map((child2) => canonical6(child2, depth + 1, budget2))];
15513
+ need5(isRecord3(value));
15514
+ return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child2]) => [key, canonical6(child2, depth + 1, budget2)])];
15291
15515
  }
15292
15516
  function normalize3(config, version = 2) {
15293
15517
  if (isRecord3(config.hooks)) {
@@ -15329,15 +15553,15 @@ function inference(target, version) {
15329
15553
  if (Object.hasOwn(target, key)) {
15330
15554
  const value = target[key];
15331
15555
  if (key === "model")
15332
- need4(typeof value === "string" && MODEL.test(value));
15556
+ need5(typeof value === "string" && MODEL.test(value));
15333
15557
  else if (key === "personality")
15334
- need4(typeof value === "string" && ["none", "friendly", "pragmatic"].includes(value));
15558
+ need5(typeof value === "string" && ["none", "friendly", "pragmatic"].includes(value));
15335
15559
  else if (version >= 3 && key !== "model_verbosity") {
15336
- need4(typeof value === "string" && value.length <= 128 && !/[\x00-\x1f\x7f]/.test(value));
15560
+ need5(typeof value === "string" && value.length <= 128 && !/[\x00-\x1f\x7f]/.test(value));
15337
15561
  if (key !== "service_tier")
15338
- need4(value.length > 0);
15562
+ need5(value.length > 0);
15339
15563
  } else
15340
- need4(typeof value === "string" && (key === "model_reasoning_effort" ? EFFORT2.has(value) : VERBOSITY2.has(value)));
15564
+ need5(typeof value === "string" && (key === "model_reasoning_effort" ? EFFORT2.has(value) : VERBOSITY2.has(value)));
15341
15565
  delete target[key];
15342
15566
  }
15343
15567
  }
@@ -15370,14 +15594,14 @@ function projectModelAvailabilityNux(config, text2) {
15370
15594
  continue;
15371
15595
  }
15372
15596
  if (char === '"' || char === "'") {
15373
- const quote = char;
15597
+ const quote2 = char;
15374
15598
  at++;
15375
15599
  while (at < span.length) {
15376
- if (quote === '"' && span[at] === "\\") {
15600
+ if (quote2 === '"' && span[at] === "\\") {
15377
15601
  at += 2;
15378
15602
  continue;
15379
15603
  }
15380
- if (span[at++] === quote)
15604
+ if (span[at++] === quote2)
15381
15605
  break;
15382
15606
  }
15383
15607
  continue;
@@ -15457,14 +15681,14 @@ function settingsDigest2(text2, version = 2) {
15457
15681
  } catch {
15458
15682
  throw new Error("Invalid Codex settings witness");
15459
15683
  }
15460
- need4(isRecord3(config));
15684
+ need5(isRecord3(config));
15461
15685
  const mcp = version >= 3 ? projectRegularCodexMcp(config, text2) : { config, numericSource: text2 };
15462
15686
  const projected = version === 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
15463
15687
  normalize3(projected.config, version);
15464
- return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify([canonical5(projected.config), numericTokens2(projected.numericSource)])}`);
15688
+ return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify([canonical6(projected.config), numericTokens2(projected.numericSource)])}`);
15465
15689
  }
15466
15690
  function hashCodexSettingsReplacementV2(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
15467
- need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15691
+ need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15468
15692
  budget2.remaining -= Buffer.byteLength(text2);
15469
15693
  return settingsDigest2(text2);
15470
15694
  }
@@ -15476,17 +15700,17 @@ function readCodexSettingsPreimage(path, budget2 = { remaining: 2 * 1024 * 1024
15476
15700
  return readNativeSettingsWitnessFile(path, budget2, "config.toml");
15477
15701
  }
15478
15702
  function upgradeCodexSettingsWitness(previous, reviewedSettingsPath) {
15479
- need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "codex-settings-v1") && /^[a-f0-9]{64}$/.test(previous.sha256));
15703
+ need5(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "codex-settings-v1") && /^[a-f0-9]{64}$/.test(previous.sha256));
15480
15704
  const budget2 = { remaining: 2 * 1024 * 1024 };
15481
15705
  const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
15482
- need4((previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before)) === previous.sha256);
15706
+ need5((previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before)) === previous.sha256);
15483
15707
  const sha2562 = settingsDigest2(current);
15484
15708
  if (settingsDigest2(before) !== sha2562)
15485
15709
  throw new Error("Codex non-preference settings changed; explicit discovery review required");
15486
15710
  return { path: previous.path, hashMode: "codex-settings-v2", sha256: sha2562 };
15487
15711
  }
15488
15712
  function hashCodexSettingsReplacementV3(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
15489
- need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15713
+ need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15490
15714
  budget2.remaining -= Buffer.byteLength(text2);
15491
15715
  return settingsDigest2(text2, 3);
15492
15716
  }
@@ -15494,18 +15718,18 @@ function captureCodexSettingsV3(path, budget2 = { remaining: 256 * 1024 * 1024 }
15494
15718
  return { path, hashMode: "codex-settings-v3", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 3) };
15495
15719
  }
15496
15720
  function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
15497
- need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || ["bytes", "codex-settings-v1", "codex-settings-v2"].includes(previous.hashMode)) && /^[a-f0-9]{64}$/.test(previous.sha256));
15721
+ need5(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || ["bytes", "codex-settings-v1", "codex-settings-v2"].includes(previous.hashMode)) && /^[a-f0-9]{64}$/.test(previous.sha256));
15498
15722
  const budget2 = { remaining: 2 * 1024 * 1024 };
15499
15723
  const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
15500
15724
  const original = previous.hashMode === "codex-settings-v2" ? settingsDigest2(before) : previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before);
15501
- need4(original === previous.sha256);
15725
+ need5(original === previous.sha256);
15502
15726
  const sha2562 = settingsDigest2(current, 3);
15503
15727
  if (settingsDigest2(before, 3) !== sha2562)
15504
15728
  throw new Error("Codex settings outside the reviewed V3 contract changed; explicit discovery review required");
15505
15729
  return { path: previous.path, hashMode: "codex-settings-v3", sha256: sha2562 };
15506
15730
  }
15507
15731
  function hashCodexSettingsReplacementV4(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
15508
- need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15732
+ need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15509
15733
  budget2.remaining -= Buffer.byteLength(text2);
15510
15734
  return settingsDigest2(text2, 4);
15511
15735
  }
@@ -15513,11 +15737,11 @@ function captureCodexSettingsV4(path, budget2 = { remaining: 256 * 1024 * 1024 }
15513
15737
  return { path, hashMode: "codex-settings-v4", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 4) };
15514
15738
  }
15515
15739
  function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
15516
- need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || ["bytes", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3"].includes(previous.hashMode)) && /^[a-f0-9]{64}$/.test(previous.sha256));
15740
+ need5(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || ["bytes", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3"].includes(previous.hashMode)) && /^[a-f0-9]{64}$/.test(previous.sha256));
15517
15741
  const budget2 = { remaining: 2 * 1024 * 1024 };
15518
15742
  const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
15519
15743
  const original = previous.hashMode === "codex-settings-v3" ? settingsDigest2(before, 3) : previous.hashMode === "codex-settings-v2" ? settingsDigest2(before, 2) : previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before);
15520
- need4(original === previous.sha256);
15744
+ need5(original === previous.sha256);
15521
15745
  const sha2562 = settingsDigest2(current, 4);
15522
15746
  if (settingsDigest2(before, 4) !== sha2562)
15523
15747
  throw new Error("Codex settings outside the reviewed V4 contract changed; explicit discovery review required");
@@ -15525,17 +15749,17 @@ function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
15525
15749
  }
15526
15750
 
15527
15751
  // src/lib/agent-integration.ts
15528
- import { createHash as createHash19, randomUUID as randomUUID3 } from "crypto";
15752
+ import { createHash as createHash20, randomUUID as randomUUID3 } from "crypto";
15529
15753
  import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
15530
- import { existsSync as existsSync9, lstatSync as lstatSync20, statSync as statSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync15, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync6, realpathSync as realpathSync10, renameSync as renameSync3, rmdirSync as rmdirSync2, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4, chmodSync, openSync as openSync11, closeSync as closeSync11, fsyncSync as fsyncSync5, fstatSync as fstatSync11, readSync as readSync8, constants as constants11, linkSync as linkSync2 } from "fs";
15531
- import { dirname as dirname14, join as join23, resolve as resolve13, relative as relative6, isAbsolute as isAbsolute14, sep as sep7 } from "path";
15754
+ import { existsSync as existsSync9, lstatSync as lstatSync20, statSync as statSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync15, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync7, realpathSync as realpathSync10, renameSync as renameSync3, rmdirSync as rmdirSync2, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4, chmodSync, openSync as openSync11, closeSync as closeSync11, fsyncSync as fsyncSync5, fstatSync as fstatSync11, readSync as readSync8, constants as constants11, linkSync as linkSync2 } from "fs";
15755
+ import { dirname as dirname15, join as join23, resolve as resolve15, relative as relative6, isAbsolute as isAbsolute16, sep as sep7 } from "path";
15532
15756
  import { homedir as homedir2 } from "os";
15533
15757
 
15534
15758
  // src/lib/managed-policy.ts
15535
15759
  import { closeSync as closeSync8, constants as constants8, fstatSync as fstatSync8, lstatSync as lstatSync15, openSync as openSync8, readSync as readSync6 } from "fs";
15536
15760
  import { join as join19 } from "path";
15537
- function refusal(reason = "is unreadable") {
15538
- return new SkillSelectionError("INVALID_AGENT_POLICY", `The Skills agent loading policy ${reason}; refusing legacy fallback.`);
15761
+ function refusal(reason2 = "is unreadable") {
15762
+ return new SkillSelectionError("INVALID_AGENT_POLICY", `The Skills agent loading policy ${reason2}; refusing legacy fallback.`);
15539
15763
  }
15540
15764
  function parseManagedSkillPolicy(text2) {
15541
15765
  if (Buffer.byteLength(text2) > AGENT_POLICY_LIMITS.bytes)
@@ -15612,7 +15836,7 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
15612
15836
  }
15613
15837
 
15614
15838
  // src/lib/agent-bridge.ts
15615
- import { createHash as createHash16 } from "crypto";
15839
+ import { createHash as createHash17 } from "crypto";
15616
15840
  import { lstatSync as lstatSync16, readFileSync as readFileSync13, readdirSync as readdirSync7 } from "fs";
15617
15841
  import { join as join20 } from "path";
15618
15842
  var CLI_BRIDGE_NAME = "skills-cli";
@@ -15631,7 +15855,7 @@ Use the Skills CLI as the skill authority for this station.
15631
15855
 
15632
15856
  This bridge contains no skill payloads. Do not copy skill content into agent-native skill directories or load other native skill copies. If a Skills command refuses authority, selection, integrity, or native drift, report that refusal and use its repair guidance; do not substitute bundled or stale local content. Explicit local draft authoring remains available.
15633
15857
  `;
15634
- var CLI_BRIDGE_DIGEST = createHash16("sha256").update(markdown).digest("hex");
15858
+ var CLI_BRIDGE_DIGEST = createHash17("sha256").update(markdown).digest("hex");
15635
15859
  var CLI_BRIDGE_FILES = Object.freeze({
15636
15860
  "SKILL.md": markdown,
15637
15861
  ".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
@@ -15660,29 +15884,29 @@ var CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^
15660
15884
  var codexPathConfigRefusal = () => new Error("Codex skill path controls require ordinary [[skills.config]] TOML tables or a native single-line inline array; preserve and review unsupported inline, dotted, or malformed definitions before running skills hook install");
15661
15885
  function splitTopLevel(value, delimiter2) {
15662
15886
  const result = [];
15663
- let start = 0, square = 0, curly = 0, quote = null, escaped = false;
15887
+ let start = 0, square = 0, curly = 0, quote2 = null, escaped = false;
15664
15888
  for (let index = 0;index < value.length; index++) {
15665
15889
  const char = value[index];
15666
- if (quote === "double") {
15890
+ if (quote2 === "double") {
15667
15891
  if (escaped)
15668
15892
  escaped = false;
15669
15893
  else if (char === "\\")
15670
15894
  escaped = true;
15671
15895
  else if (char === '"')
15672
- quote = null;
15896
+ quote2 = null;
15673
15897
  continue;
15674
15898
  }
15675
- if (quote === "literal") {
15899
+ if (quote2 === "literal") {
15676
15900
  if (char === "'")
15677
- quote = null;
15901
+ quote2 = null;
15678
15902
  continue;
15679
15903
  }
15680
15904
  if (char === '"') {
15681
- quote = "double";
15905
+ quote2 = "double";
15682
15906
  continue;
15683
15907
  }
15684
15908
  if (char === "'") {
15685
- quote = "literal";
15909
+ quote2 = "literal";
15686
15910
  continue;
15687
15911
  }
15688
15912
  if (char === "[")
@@ -15700,7 +15924,7 @@ function splitTopLevel(value, delimiter2) {
15700
15924
  start = index + 1;
15701
15925
  }
15702
15926
  }
15703
- if (quote !== null || square !== 0 || curly !== 0)
15927
+ if (quote2 !== null || square !== 0 || curly !== 0)
15704
15928
  return null;
15705
15929
  result.push(value.slice(start));
15706
15930
  return result;
@@ -15708,29 +15932,29 @@ function splitTopLevel(value, delimiter2) {
15708
15932
  function inlineArrayEnd(value) {
15709
15933
  if (!value.startsWith("["))
15710
15934
  return null;
15711
- let square = 0, curly = 0, quote = null, escaped = false;
15935
+ let square = 0, curly = 0, quote2 = null, escaped = false;
15712
15936
  for (let index = 0;index < value.length; index++) {
15713
15937
  const char = value[index];
15714
- if (quote === "double") {
15938
+ if (quote2 === "double") {
15715
15939
  if (escaped)
15716
15940
  escaped = false;
15717
15941
  else if (char === "\\")
15718
15942
  escaped = true;
15719
15943
  else if (char === '"')
15720
- quote = null;
15944
+ quote2 = null;
15721
15945
  continue;
15722
15946
  }
15723
- if (quote === "literal") {
15947
+ if (quote2 === "literal") {
15724
15948
  if (char === "'")
15725
- quote = null;
15949
+ quote2 = null;
15726
15950
  continue;
15727
15951
  }
15728
15952
  if (char === '"') {
15729
- quote = "double";
15953
+ quote2 = "double";
15730
15954
  continue;
15731
15955
  }
15732
15956
  if (char === "'") {
15733
- quote = "literal";
15957
+ quote2 = "literal";
15734
15958
  continue;
15735
15959
  }
15736
15960
  if (char === "[")
@@ -15767,7 +15991,7 @@ function inlineRowsAsTables(value, newline) {
15767
15991
  const fields = splitTopLevel(row.slice(1, -1), ",");
15768
15992
  if (!fields)
15769
15993
  return null;
15770
- const lines = fields.map((field) => field.trim()).filter(Boolean);
15994
+ const lines = fields.map((field2) => field2.trim()).filter(Boolean);
15771
15995
  if (lines.some((line) => !/^(?:path|name|enabled)\s*=/.test(line)))
15772
15996
  return null;
15773
15997
  const keys = lines.map((line) => line.slice(0, line.indexOf("=")).trim());
@@ -15792,27 +16016,27 @@ function normalizeCodexInlinePathConfig(text2) {
15792
16016
  const matches = [];
15793
16017
  for (const assignment of text2.matchAll(/^([ \t]*config[ \t]*=[ \t]*)(.*?)(\r?)$/gm)) {
15794
16018
  const rawValue = assignment[2].trimEnd();
15795
- let quote = null, escaped = false, comment = "";
16019
+ let quote2 = null, escaped = false, comment = "";
15796
16020
  for (let index = 0;index < rawValue.length; index++) {
15797
16021
  const char = rawValue[index];
15798
- if (quote === "double") {
16022
+ if (quote2 === "double") {
15799
16023
  if (escaped)
15800
16024
  escaped = false;
15801
16025
  else if (char === "\\")
15802
16026
  escaped = true;
15803
16027
  else if (char === '"')
15804
- quote = null;
16028
+ quote2 = null;
15805
16029
  continue;
15806
16030
  }
15807
- if (quote === "literal") {
16031
+ if (quote2 === "literal") {
15808
16032
  if (char === "'")
15809
- quote = null;
16033
+ quote2 = null;
15810
16034
  continue;
15811
16035
  }
15812
16036
  if (char === '"')
15813
- quote = "double";
16037
+ quote2 = "double";
15814
16038
  else if (char === "'")
15815
- quote = "literal";
16039
+ quote2 = "literal";
15816
16040
  else if (char === "#") {
15817
16041
  comment = rawValue.slice(index);
15818
16042
  break;
@@ -15924,28 +16148,28 @@ function disableCodexBundledSkills(text2) {
15924
16148
 
15925
16149
  // src/lib/agent-sumi.ts
15926
16150
  import { existsSync as existsSync8, lstatSync as lstatSync17, realpathSync as realpathSync8 } from "fs";
15927
- import { join as join21, resolve as resolve12 } from "path";
16151
+ import { join as join21, resolve as resolve13 } from "path";
15928
16152
  function sumiConfigDirectory(home, env = process.env) {
15929
16153
  for (const key of ["SUMI_CONFIG", "SUMI_CONFIG_CONTENT"])
15930
16154
  if (env[key] !== undefined)
15931
16155
  throw new Error(`Sumi ${key} requires a dedicated discovery adapter; unset it before Skills integration`);
15932
16156
  const selected = (key) => env[key]?.trim() ? env[key] : undefined;
15933
- const expand = (value) => resolve12(value === "~" ? home : value.startsWith("~/") ? join21(home, value.slice(2)) : value);
16157
+ const expand = (value) => resolve13(value === "~" ? home : value.startsWith("~/") ? join21(home, value.slice(2)) : value);
15934
16158
  const explicit = selected("SUMI_CONFIG_DIR");
15935
16159
  const xdg = selected("XDG_CONFIG_HOME");
15936
16160
  const custom = selected("SUMI_HOME");
15937
- const canonical6 = explicit ? expand(explicit) : xdg ? join21(expand(xdg), "sumi") : join21(custom ? expand(custom) : join21(home, ".hasna-internal/sumi"), "config");
16161
+ const canonical7 = explicit ? expand(explicit) : xdg ? join21(expand(xdg), "sumi") : join21(custom ? expand(custom) : join21(home, ".hasna-internal/sumi"), "config");
15938
16162
  if (explicit || xdg || custom)
15939
- return canonical6;
16163
+ return canonical7;
15940
16164
  const legacy = join21(home, ".config/sumi");
15941
- const a = lstatSync17(canonical6, { throwIfNoEntry: false }), b = lstatSync17(legacy, { throwIfNoEntry: false });
15942
- if (a && b && realpathSync8(canonical6) !== realpathSync8(legacy))
16165
+ const a = lstatSync17(canonical7, { throwIfNoEntry: false }), b = lstatSync17(legacy, { throwIfNoEntry: false });
16166
+ if (a && b && realpathSync8(canonical7) !== realpathSync8(legacy))
15943
16167
  throw new Error("Sumi config roots conflict; complete native home adoption before Skills integration");
15944
- const path = a ? canonical6 : b ? legacy : canonical6;
16168
+ const path = a ? canonical7 : b ? legacy : canonical7;
15945
16169
  if (!existsSync8(path))
15946
16170
  return path;
15947
16171
  const physical = realpathSync8(path);
15948
- if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![canonical6, legacy].includes(physical))
16172
+ if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![canonical7, legacy].includes(physical))
15949
16173
  throw new Error("Sumi config alias must resolve to its canonical or legacy root");
15950
16174
  return physical;
15951
16175
  }
@@ -16045,33 +16269,49 @@ var SUMI_SKILL_PERMISSIONS = [
16045
16269
  ];
16046
16270
 
16047
16271
  // src/lib/codex-hook-trust-files.ts
16048
- import { closeSync as closeSync9, constants as constants9, fstatSync as fstatSync9, fsyncSync as fsyncSync3, lstatSync as lstatSync18, openSync as openSync9, readSync as readSync7, writeFileSync as writeFileSync5 } from "fs";
16049
- import { createHash as createHash17 } from "crypto";
16050
- import { dirname as dirname13 } from "path";
16051
- function need5(value, code) {
16272
+ import { closeSync as closeSync9, constants as constants9, fstatSync as fstatSync9, fsyncSync as fsyncSync3, lstatSync as lstatSync18, openSync as openSync9, readlinkSync as readlinkSync5, readSync as readSync7, writeFileSync as writeFileSync5 } from "fs";
16273
+ import { createHash as createHash18 } from "crypto";
16274
+ import { dirname as dirname14, isAbsolute as isAbsolute14, resolve as resolve14 } from "path";
16275
+ function need6(value, code) {
16052
16276
  if (!value)
16053
16277
  throw new Error(`CODEX_HOOK_TRUST_${code}`);
16054
16278
  }
16055
16279
  var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
16280
+ function trustedAncestorOwner(uid) {
16281
+ return BigInt(uid) === 0n || BigInt(uid) === BigInt(process.getuid());
16282
+ }
16056
16283
  function safeParents(file) {
16057
- for (let p = dirname13(file);; p = dirname13(p)) {
16284
+ for (let p = dirname14(file);; p = dirname14(p)) {
16058
16285
  const s = lstatSync18(p);
16059
- need5(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
16060
- need5((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
16061
- if (dirname13(p) === p)
16286
+ need6(s.isDirectory() && !s.isSymbolicLink() && trustedAncestorOwner(s.uid), "UNSAFE_PARENT");
16287
+ need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
16288
+ if (dirname14(p) === p)
16062
16289
  break;
16063
16290
  }
16064
16291
  }
16292
+ function directAliasTarget(link) {
16293
+ const text2 = readlinkSync5(link), parts = text2.split("/");
16294
+ let first = 0;
16295
+ if (isAbsolute14(text2))
16296
+ first = 1;
16297
+ else
16298
+ while (parts[first] === "..")
16299
+ first++;
16300
+ const names = parts.slice(first);
16301
+ if (!names.length || names.some((part) => part === "" || part === "." || part === ".."))
16302
+ return;
16303
+ return resolve14(dirname14(link), text2);
16304
+ }
16065
16305
  function snapshot(file, privateFile = false, options = {}) {
16066
16306
  const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
16067
16307
  safeParents(file);
16068
16308
  const initial = lstatSync18(file, { bigint: true });
16069
- need5(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
16070
- need5(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
16309
+ need6(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
16310
+ need6(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
16071
16311
  const fd = openSync9(file, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
16072
16312
  try {
16073
16313
  const before = fstatSync9(fd, { bigint: true });
16074
- need5(sameStat(initial, before), "INPUT_CHANGED");
16314
+ need6(sameStat(initial, before), "INPUT_CHANGED");
16075
16315
  const bytes = Buffer.alloc(Number(before.size) + 1);
16076
16316
  let length = 0;
16077
16317
  while (length < bytes.length) {
@@ -16080,22 +16320,22 @@ function snapshot(file, privateFile = false, options = {}) {
16080
16320
  break;
16081
16321
  length += n2;
16082
16322
  }
16083
- need5(length === Number(before.size) && sameStat(before, fstatSync9(fd, { bigint: true })) && sameStat(before, lstatSync18(file, { bigint: true })), "INPUT_CHANGED");
16323
+ need6(length === Number(before.size) && sameStat(before, fstatSync9(fd, { bigint: true })) && sameStat(before, lstatSync18(file, { bigint: true })), "INPUT_CHANGED");
16084
16324
  const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
16085
- return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash17("sha256").update(content).digest("hex") };
16325
+ return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash18("sha256").update(content).digest("hex") };
16086
16326
  } finally {
16087
16327
  closeSync9(fd);
16088
16328
  }
16089
16329
  }
16090
16330
  function unchanged2(before) {
16091
16331
  const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
16092
- need5(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
16332
+ need6(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
16093
16333
  }
16094
16334
 
16095
16335
  // src/cli/commands/runtime-launcher.ts
16096
- import { createHash as createHash18 } from "crypto";
16097
- import { closeSync as closeSync10, constants as constants10, fchmodSync, fstatSync as fstatSync10, fsyncSync as fsyncSync4, lstatSync as lstatSync19, openSync as openSync10, readFileSync as readFileSync14, readlinkSync as readlinkSync5, realpathSync as realpathSync9, symlinkSync, writeSync as writeSync2 } from "fs";
16098
- import { isAbsolute as isAbsolute13 } from "path";
16336
+ import { createHash as createHash19 } from "crypto";
16337
+ import { closeSync as closeSync10, constants as constants10, fchmodSync, fstatSync as fstatSync10, fsyncSync as fsyncSync4, lstatSync as lstatSync19, openSync as openSync10, readFileSync as readFileSync14, readlinkSync as readlinkSync6, realpathSync as realpathSync9, symlinkSync, writeSync as writeSync2 } from "fs";
16338
+ import { isAbsolute as isAbsolute15 } from "path";
16099
16339
  var PINNED_LAUNCHER_SCHEMA = "hasna-skills-pinned-launcher-v1";
16100
16340
  var LAUNCH_CWD_VARIABLE = "HASNA_SKILLS_LAUNCH_CWD";
16101
16341
  var PINNED_LAUNCHER_BUN_FLAGS = ["--config=/dev/null", "--no-env-file", "--no-macros", "--no-install"];
@@ -16136,10 +16376,10 @@ var PINNED_LAUNCHER_ENV_PREFIXES = ["HASNA", "SKILLS", "SKILL", "MCP", "XDG", "L
16136
16376
  var ENV_PREFIX = new RegExp(`^(${PINNED_LAUNCHER_ENV_PREFIXES.join("|")})_`);
16137
16377
  var ENV_EXACT = new Set(PINNED_LAUNCHER_ENV_NAMES);
16138
16378
  function sha2562(bytes) {
16139
- return createHash18("sha256").update(bytes).digest("hex");
16379
+ return createHash19("sha256").update(bytes).digest("hex");
16140
16380
  }
16141
16381
  function assertLauncherPath(value, code) {
16142
- if (typeof value !== "string" || !isAbsolute13(value) || /['\x00-\x1f\x7f]/.test(value))
16382
+ if (typeof value !== "string" || !isAbsolute15(value) || /['\x00-\x1f\x7f]/.test(value))
16143
16383
  throw new Error(code);
16144
16384
  }
16145
16385
  function renderPinnedLauncher(binding) {
@@ -16229,7 +16469,7 @@ function assertPinnedLauncherRuntimeSafe(runtime) {
16229
16469
  }
16230
16470
 
16231
16471
  // src/lib/agent-integration.ts
16232
- var sha3 = (data) => createHash19("sha256").update(data).digest("hex");
16472
+ var sha3 = (data) => createHash20("sha256").update(data).digest("hex");
16233
16473
  var ROOTS = NATIVE_SKILL_ROOTS;
16234
16474
  function hasTrustedDarwinAcl(fd, allowDenyOnly = false) {
16235
16475
  try {
@@ -16279,9 +16519,10 @@ function isSystemRootAlias(path) {
16279
16519
  return false;
16280
16520
  const target = `/private${path}`, link = `private${path}`;
16281
16521
  const identity5 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}`;
16522
+ const directoryIdentity = (stat) => `${stat.dev}:${stat.ino}:${stat.uid}:${stat.mode}`;
16282
16523
  try {
16283
16524
  const before = lstatSync20(path, { bigint: true });
16284
- if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync6(path) !== link)
16525
+ if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync7(path) !== link)
16285
16526
  return false;
16286
16527
  const directories = ["/", "/private", target].map((directory) => {
16287
16528
  const stat = lstatSync20(directory, { bigint: true });
@@ -16294,20 +16535,20 @@ function isSystemRootAlias(path) {
16294
16535
  throw new Error("Unprotected system temporary directory");
16295
16536
  const fd = openSync11(directory, constants11.O_RDONLY | constants11.O_DIRECTORY | constants11.O_NOFOLLOW);
16296
16537
  try {
16297
- if (identity5(fstatSync11(fd, { bigint: true })) !== identity5(stat) || !hasTrustedDarwinAcl(fd) || identity5(fstatSync11(fd, { bigint: true })) !== identity5(stat))
16538
+ if (directoryIdentity(fstatSync11(fd, { bigint: true })) !== directoryIdentity(stat) || !hasTrustedDarwinAcl(fd) || directoryIdentity(fstatSync11(fd, { bigint: true })) !== directoryIdentity(stat))
16298
16539
  throw new Error("Unverified system alias directory ACL");
16299
16540
  } finally {
16300
16541
  closeSync11(fd);
16301
16542
  }
16302
- return { directory, identity: identity5(stat) };
16543
+ return { directory, identity: directoryIdentity(stat) };
16303
16544
  });
16304
- return realpathSync10(path) === target && readlinkSync6(path) === link && identity5(lstatSync20(path, { bigint: true })) === identity5(before) && directories.every((item) => identity5(lstatSync20(item.directory, { bigint: true })) === item.identity);
16545
+ return realpathSync10(path) === target && readlinkSync7(path) === link && identity5(lstatSync20(path, { bigint: true })) === identity5(before) && directories.every((item) => directoryIdentity(lstatSync20(item.directory, { bigint: true })) === item.identity);
16305
16546
  } catch {
16306
16547
  return false;
16307
16548
  }
16308
16549
  }
16309
16550
  function canonicalSystemPath(path) {
16310
- const absolute = resolve13(path), root = `/${absolute.split(sep7)[1]}`;
16551
+ const absolute = resolve15(path), root = `/${absolute.split(sep7)[1]}`;
16311
16552
  if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync20(root, { throwIfNoEntry: false })?.isSymbolicLink())
16312
16553
  return absolute;
16313
16554
  if (!isSystemRootAlias(root))
@@ -16315,11 +16556,11 @@ function canonicalSystemPath(path) {
16315
16556
  return `/private${absolute}`;
16316
16557
  }
16317
16558
  function assertSafePath(path) {
16318
- let cursor = resolve13(path);
16559
+ let cursor = resolve15(path);
16319
16560
  while (true) {
16320
16561
  if (lstatSync20(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
16321
16562
  throw new Error(`Refusing symlink path: ${cursor}`);
16322
- const parent = dirname14(cursor);
16563
+ const parent = dirname15(cursor);
16323
16564
  if (parent === cursor)
16324
16565
  return;
16325
16566
  cursor = parent;
@@ -16331,13 +16572,13 @@ function readRootAlias(home, agent) {
16331
16572
  const stat = lstatSync20(alias, { throwIfNoEntry: false, bigint: true });
16332
16573
  if (!stat?.isSymbolicLink())
16333
16574
  throw new Error(`Agent root alias changed after planning: ${alias}`);
16334
- const link = readlinkSync6(alias), targetPath = resolve13(dirname14(alias), link);
16575
+ const link = readlinkSync7(alias), targetPath = resolve15(dirname15(alias), link);
16335
16576
  assertSafePath(targetPath);
16336
16577
  const targetStat = lstatSync20(targetPath, { throwIfNoEntry: false });
16337
16578
  if (!targetStat?.isDirectory())
16338
16579
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
16339
16580
  const target = realpathSync10(targetPath), inside = relative6(realHome, target);
16340
- if (!inside || inside === ".." || inside.startsWith(`..${sep7}`) || isAbsolute14(inside))
16581
+ if (!inside || inside === ".." || inside.startsWith(`..${sep7}`) || isAbsolute16(inside))
16341
16582
  throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
16342
16583
  return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
16343
16584
  }
@@ -16363,13 +16604,13 @@ function disabledCodexSkillPaths(config, aliases) {
16363
16604
  const rows = skills.config;
16364
16605
  if (!Array.isArray(rows))
16365
16606
  return [];
16366
- if (rows.some((entry) => !entry || typeof entry !== "object" || Array.isArray(entry) || typeof entry.enabled !== "boolean" || entry.path !== undefined === (entry.name !== undefined) || entry.path !== undefined && (typeof entry.path !== "string" || !isAbsolute14(entry.path)) || entry.name !== undefined && (typeof entry.name !== "string" || !entry.name.trim())))
16607
+ if (rows.some((entry) => !entry || typeof entry !== "object" || Array.isArray(entry) || typeof entry.enabled !== "boolean" || entry.path !== undefined === (entry.name !== undefined) || entry.path !== undefined && (typeof entry.path !== "string" || !isAbsolute16(entry.path)) || entry.name !== undefined && (typeof entry.name !== "string" || !entry.name.trim())))
16367
16608
  return [];
16368
16609
  if (rows.some((entry) => entry.name !== undefined && entry.enabled !== false))
16369
16610
  return [];
16370
16611
  const settings = new Map;
16371
16612
  for (const entry of rows) {
16372
- if (typeof entry.path !== "string" || !isAbsolute14(entry.path))
16613
+ if (typeof entry.path !== "string" || !isAbsolute16(entry.path))
16373
16614
  continue;
16374
16615
  let document2;
16375
16616
  try {
@@ -16381,7 +16622,7 @@ function disabledCodexSkillPaths(config, aliases) {
16381
16622
  continue;
16382
16623
  settings.set(document2, [...settings.get(document2) ?? [], entry.enabled === false]);
16383
16624
  }
16384
- return [...settings].filter(([, values]) => values.length === 1 && values[0] === true).map(([document2]) => dirname14(document2));
16625
+ return [...settings].filter(([, values]) => values.length === 1 && values[0] === true).map(([document2]) => dirname15(document2));
16385
16626
  }
16386
16627
  function recheckRootAliases(aliases) {
16387
16628
  for (const binding of aliases) {
@@ -16423,7 +16664,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
16423
16664
  }
16424
16665
  }
16425
16666
  function treeHash(root) {
16426
- const hash2 = createHash19("sha256");
16667
+ const hash2 = createHash20("sha256");
16427
16668
  let bytes = 0, files = 0;
16428
16669
  function visit2(path) {
16429
16670
  const stat = lstatSync20(path);
@@ -16455,20 +16696,20 @@ function selectNativeMigrationTargets(inventory, manifest) {
16455
16696
  const identities = new Set;
16456
16697
  const selected = [];
16457
16698
  for (const target of manifest.targets) {
16458
- if (!isAbsolute14(target.projectRoot) || resolve13(target.projectRoot) !== target.projectRoot)
16699
+ if (!isAbsolute16(target.projectRoot) || resolve15(target.projectRoot) !== target.projectRoot)
16459
16700
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
16460
- const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative6(projectRoot, resolve13(projectRoot, target.path));
16701
+ const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative6(projectRoot, resolve15(projectRoot, target.path));
16461
16702
  if (!existsSync9(projectRoot) || !lstatSync20(projectRoot).isDirectory())
16462
16703
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
16463
16704
  assertSafePath(projectRoot);
16464
- if (isAbsolute14(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep7}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
16705
+ if (isAbsolute16(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep7}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
16465
16706
  throw new Error(`Invalid native migration target: ${target.path}`);
16466
16707
  const identity5 = `${target.agent}\x00${canonicalSystemPath(projectRoot)}\x00${normalized}`;
16467
16708
  if (identities.has(identity5))
16468
16709
  throw new Error(`Duplicate native migration target: ${target.path}`);
16469
16710
  identities.add(identity5);
16470
- const expected = canonicalSystemPath(resolve13(target.projectRoot, target.path));
16471
- const matches = inventory.filter((entry2) => entry2.agent === target.agent && resolve13(entry2.path) === expected);
16711
+ const expected = canonicalSystemPath(resolve15(target.projectRoot, target.path));
16712
+ const matches = inventory.filter((entry2) => entry2.agent === target.agent && resolve15(entry2.path) === expected);
16472
16713
  if (matches.length !== 1)
16473
16714
  throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
16474
16715
  const entry = matches[0];
@@ -16487,7 +16728,7 @@ function projectAncestorDirectories(projects) {
16487
16728
  if (depth >= 100)
16488
16729
  throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
16489
16730
  directories.add(path);
16490
- const parent = dirname14(path);
16731
+ const parent = dirname15(path);
16491
16732
  if (parent === path)
16492
16733
  break;
16493
16734
  path = parent;
@@ -16496,7 +16737,7 @@ function projectAncestorDirectories(projects) {
16496
16737
  return [...directories];
16497
16738
  }
16498
16739
  function isCodexSkillDiscoveryInput(path) {
16499
- for (let cursor = canonicalSystemPath(path);cursor !== dirname14(cursor); cursor = dirname14(cursor)) {
16740
+ for (let cursor = canonicalSystemPath(path);cursor !== dirname15(cursor); cursor = dirname15(cursor)) {
16500
16741
  if (ROOTS.some(([agent, root]) => agent === "codex" && cursor.endsWith(join23(sep7, root))))
16501
16742
  return true;
16502
16743
  }
@@ -16504,9 +16745,9 @@ function isCodexSkillDiscoveryInput(path) {
16504
16745
  }
16505
16746
  function inventoryNativeSkills(home = homedir2(), options = {}) {
16506
16747
  home = canonicalSystemPath(home);
16507
- if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute14(options.reviewedCacheAlias) || resolve13(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
16748
+ if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute16(options.reviewedCacheAlias) || resolve15(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
16508
16749
  throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
16509
- if (options.disabledVendorPaths?.some((path) => !isAbsolute14(path) || resolve13(path) !== path))
16750
+ if (options.disabledVendorPaths?.some((path) => !isAbsolute16(path) || resolve15(path) !== path))
16510
16751
  throw new Error("Disabled vendor paths must be exact absolute paths");
16511
16752
  const reviewedCacheAlias = options.reviewedCacheAlias === undefined ? undefined : canonicalSystemPath(options.reviewedCacheAlias);
16512
16753
  const disabledVendorPaths = new Set((options.disabledVendorPaths ?? []).map(canonicalSystemPath));
@@ -16525,7 +16766,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16525
16766
  roots.push(["sumi", canonicalAgentPath(join23(project, directory, name), aliases)]);
16526
16767
  }
16527
16768
  const installationInputs = options.codexInstallationInputRoots ?? [];
16528
- if (installationInputs.some((input) => resolve13(input) !== input || realpathSync10(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep7) || input.startsWith(path + sep7)))))
16769
+ if (installationInputs.some((input) => resolve15(input) !== input || realpathSync10(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep7) || input.startsWith(path + sep7)))))
16529
16770
  throw new Error("Native installation input overlaps an unconditional skill-loading root");
16530
16771
  const entries = [], seen = new Set;
16531
16772
  const cacheScans = new Map, maxAliasProofEntries = 1e4;
@@ -16539,11 +16780,11 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16539
16780
  }
16540
16781
  let reviewedCacheAliasFound = false;
16541
16782
  function verifiedSiblingCacheAlias(agent, path, parent) {
16542
- const link = readlinkSync6(path), lexicalTarget = resolve13(parent, link);
16543
- if (isAbsolute14(link) ? link !== lexicalTarget : dirname14(link) !== ".")
16783
+ const link = readlinkSync7(path), lexicalTarget = resolve15(parent, link);
16784
+ if (isAbsolute16(link) ? link !== lexicalTarget : dirname15(link) !== ".")
16544
16785
  return false;
16545
16786
  const target = canonicalSystemPath(lexicalTarget);
16546
- if (dirname14(target) !== parent)
16787
+ if (dirname15(target) !== parent)
16547
16788
  return false;
16548
16789
  if (!lstatSync20(target, { throwIfNoEntry: false })?.isDirectory())
16549
16790
  return false;
@@ -16592,17 +16833,17 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16592
16833
  }
16593
16834
  const bridge = (!vendor || agent === "sumi") && isOwnedCliBridge(path, bridgePaths);
16594
16835
  const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep7));
16595
- entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" || isCodexSkillDiscoveryInput(path) ? { codexHome: process.env.CODEX_HOME ?? join23(home, ".codex") } : {}, ...agent === "codex" && path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep7) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve13(home) } : {}, ...rootAlias ? { rootAlias } : {} });
16836
+ entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" || isCodexSkillDiscoveryInput(path) ? { codexHome: process.env.CODEX_HOME ?? join23(home, ".codex") } : {}, ...agent === "codex" && path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep7) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve15(home) } : {}, ...rootAlias ? { rootAlias } : {} });
16596
16837
  return scan;
16597
16838
  }
16598
16839
  if (depth > (agent === "sumi" ? 32 : vendor ? 32 : 3))
16599
16840
  return { ...scan, complete: false };
16600
16841
  const children = [], directory = opendirSync3(path);
16601
16842
  try {
16602
- let child;
16603
- while ((child = directory.readSync()) !== null) {
16604
- admit(join23(path, child.name));
16605
- children.push(child);
16843
+ let child2;
16844
+ while ((child2 = directory.readSync()) !== null) {
16845
+ admit(join23(path, child2.name));
16846
+ children.push(child2);
16606
16847
  }
16607
16848
  } finally {
16608
16849
  directory.closeSync();
@@ -16618,15 +16859,15 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16618
16859
  const isVendor = vendor || name === ".system";
16619
16860
  if (isVendor && !options.includeVendor)
16620
16861
  continue;
16621
- const child = join23(path, name);
16622
- if (isVendor && lstatSync20(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
16623
- const target = statSync9(child, { throwIfNoEntry: false });
16624
- if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child, path)) {
16862
+ const child2 = join23(path, name);
16863
+ if (isVendor && lstatSync20(child2, { throwIfNoEntry: false })?.isSymbolicLink()) {
16864
+ const target = statSync9(child2, { throwIfNoEntry: false });
16865
+ if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child2, path)) {
16625
16866
  scan.entries++;
16626
16867
  continue;
16627
16868
  }
16628
16869
  }
16629
- const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true, configuredRoot);
16870
+ const childScan = visit2(agent, child2, isVendor, depth + 1, pluginCache, true, configuredRoot);
16630
16871
  scan.complete &&= childScan.complete;
16631
16872
  scan.hasSkills ||= childScan.hasSkills;
16632
16873
  scan.entries += childScan.entries;
@@ -16762,7 +17003,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
16762
17003
  function disableCodexSkills(text2, skills, aliases, bridgePath) {
16763
17004
  const bundled = disableCodexBundledSkills(text2);
16764
17005
  let result = normalizeCodexInlinePathConfig(bundled);
16765
- const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve13(bridgePath), resolve13(join23(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
17006
+ const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve15(bridgePath), resolve15(join23(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
16766
17007
  const previousConfig = Bun.TOML.parse(bundled).skills?.config;
16767
17008
  if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
16768
17009
  throw new Error("Codex skill controls require one path or name selector per [[skills.config]] entry; review ambiguous entries before running skills hook install");
@@ -16774,7 +17015,7 @@ function disableCodexSkills(text2, skills, aliases, bridgePath) {
16774
17015
  result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
16775
17016
  const parsed = Bun.TOML.parse(section);
16776
17017
  const declared = parsed.skills?.config?.[0];
16777
- if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve13(path), resolve13(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
17018
+ if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve15(path), resolve15(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
16778
17019
  return section;
16779
17020
  found = true;
16780
17021
  return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
@@ -16795,7 +17036,7 @@ enabled = false
16795
17036
  return result;
16796
17037
  }
16797
17038
  function assertCodexInstallationInputs(home, projectDirs, inputs) {
16798
- const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve13(directory, path)));
17039
+ const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve15(directory, path)));
16799
17040
  if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep7) || input.startsWith(root + sep7))))
16800
17041
  throw new Error("Native installation input overlaps an unconditional skill-loading root");
16801
17042
  }
@@ -16811,9 +17052,9 @@ function planAgentIntegration(options) {
16811
17052
  throw new Error("Invalid existing Skills bridge policy");
16812
17053
  if (policy.bridge?.agents !== undefined && (!Array.isArray(policy.bridge.agents) || policy.bridge.agents.some((agent) => !INTEGRATION_AGENTS.includes(agent))))
16813
17054
  throw new Error("Invalid existing bridge agent inventory");
16814
- for (const field of ["commands", "profiles"])
16815
- if (policy.bridge?.[field] !== undefined && (!policy.bridge[field] || typeof policy.bridge[field] !== "object" || Array.isArray(policy.bridge[field]) || Object.entries(policy.bridge[field]).some(([key, value]) => !INTEGRATION_AGENTS.includes(key) || typeof value !== "string" || !value || value.includes("\x00"))))
16816
- throw new Error(`Invalid existing bridge ${field} binding`);
17055
+ for (const field2 of ["commands", "profiles"])
17056
+ if (policy.bridge?.[field2] !== undefined && (!policy.bridge[field2] || typeof policy.bridge[field2] !== "object" || Array.isArray(policy.bridge[field2]) || Object.entries(policy.bridge[field2]).some(([key, value]) => !INTEGRATION_AGENTS.includes(key) || typeof value !== "string" || !value || value.includes("\x00"))))
17057
+ throw new Error(`Invalid existing bridge ${field2} binding`);
16817
17058
  const priorAgents = policy.bridge?.agents ?? [];
16818
17059
  const profileId = options.profileId ?? policy.profileId ?? "default";
16819
17060
  const validateProfile = (value) => {
@@ -16828,13 +17069,18 @@ function planAgentIntegration(options) {
16828
17069
  return [agent, { command, profileId: profileId2 }];
16829
17070
  }));
16830
17071
  const retainedAgents = [];
17072
+ const priorExecutables = policy.bridge?.discoveryExecutables ?? {};
17073
+ const executables = new Map;
16831
17074
  const discoveries = [...new Set(options.agents)].map((agent) => {
17075
+ const prior = priorExecutables[agent];
17076
+ const executable2 = discoveryExecutable(agent) ?? (prior ? recordedDiscoveryExecutable(agent, prior) : null);
17077
+ executables.set(agent, executable2);
16832
17078
  const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? { ...policy.bridge.discovery[agent], ...agent === "codex" ? { codexDisabledPluginSkills: policy.bridge.codexPluginSkills ?? [] } : {} } : undefined;
16833
17079
  if (retainedReview) {
16834
17080
  assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: options.projectDir ?? home });
16835
17081
  retainedAgents.push(agent);
16836
17082
  }
16837
- return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
17083
+ return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases), executable: executable2 });
16838
17084
  });
16839
17085
  const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases)) : null;
16840
17086
  const codexConfig = Bun.TOML.parse(codexConfigBefore ?? "");
@@ -16842,7 +17088,7 @@ function planAgentIntegration(options) {
16842
17088
  const codexPathConfig = options.agents.includes("codex") ? disableCodexSkills(codexConfigBefore ?? "", nativeSkills, aliases, canonicalAgentPath(join23(home, ".codex/skills", CLI_BRIDGE_NAME), aliases)) : undefined;
16843
17089
  const plannedCodexRules = codexPathConfig === undefined ? undefined : Bun.TOML.parse(codexPathConfig).skills?.config ?? [];
16844
17090
  const observedNativeSources = [];
16845
- const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join23(entry.path, "SKILL.md")), canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), resolve13(options.projectDir ?? home), (path) => {
17091
+ const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join23(entry.path, "SKILL.md")), canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), resolve15(options.projectDir ?? home), (path) => {
16846
17092
  const bytes = readNativeBytes(path, 1024 * 1024);
16847
17093
  observedNativeSources.push({ path, sha256: sha3(bytes) });
16848
17094
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
@@ -16963,7 +17209,7 @@ function planAgentIntegration(options) {
16963
17209
  const { codexInstallationInputs: previousInputs, ...current } = rebound;
16964
17210
  const inputs = codexPluginSourceInputs.map((input) => input.sourceRoot);
16965
17211
  const directories = options.codexNativeCatalog || !previousInputs ? captureDiscoveryDirectories((binding.directories ?? []).filter((directory) => inputs.some((root) => root.startsWith(directory.path + sep7))).map((directory) => directory.path), inputs) : previousInputs.directories;
16966
- const installed = codexPluginSourceInputs.length ? { ...current, codexInstallationInputs: { version: "codex-cli 0.160.0", catalogSha256: codexPluginSkillReview.catalogSha256, plugins: codexPluginSourceInputs, ...directories?.length ? { directories } : {} } } : current;
17212
+ const installed = codexPluginSourceInputs.length ? { ...current, codexInstallationInputs: { version: codexPluginSkillReview.version, catalogSha256: codexPluginSkillReview.catalogSha256, plugins: codexPluginSourceInputs, ...directories?.length ? { directories } : {} } } : current;
16967
17213
  return captureRetiredCodexDiscovery(installed, codexPluginSkills, Bun.TOML.parse(changes.find((change) => change.path === canonicalAgentPath(join23(home, ".codex/config.toml"), aliases))?.after ?? codexConfigBefore ?? "").skills?.config ?? []);
16968
17214
  });
16969
17215
  const nextPolicy = { ...policy, version: 1, loading: "cli", profileId, bridge: {
@@ -16971,7 +17217,7 @@ function planAgentIntegration(options) {
16971
17217
  version: CLI_BRIDGE_VERSION,
16972
17218
  digest: CLI_BRIDGE_DIGEST,
16973
17219
  agents: [...new Set([...priorAgents, ...options.agents])].sort(),
16974
- home: resolve13(home),
17220
+ home: resolve15(home),
16975
17221
  includeVendor: true,
16976
17222
  ...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join23(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha3(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
16977
17223
  commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
@@ -16981,19 +17227,30 @@ function planAgentIntegration(options) {
16981
17227
  ...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
16982
17228
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
16983
17229
  } };
17230
+ const discoveryExecutables = { ...priorExecutables };
17231
+ for (const [agent, executable2] of executables) {
17232
+ if (executable2)
17233
+ discoveryExecutables[agent] = executable2;
17234
+ else
17235
+ delete discoveryExecutables[agent];
17236
+ }
17237
+ if (Object.keys(discoveryExecutables).length)
17238
+ nextPolicy.bridge.discoveryExecutables = discoveryExecutables;
17239
+ else
17240
+ delete nextPolicy.bridge.discoveryExecutables;
16984
17241
  const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
16985
17242
  if (JSON.stringify(policy) !== JSON.stringify(nextPolicy))
16986
17243
  changes.push({ path: policyPath, before: previousPolicy, after: serializedPolicy });
16987
17244
  recheckRootAliases(aliases);
16988
17245
  return { dataDir: dataDir2, profileId, changes, nativeSkills, ...observedSettings ? { observedSettings } : {}, ...observedNativeSources.length ? { observedNativeSources } : {}, ...nextPolicy.bridge.codexPluginSkillReview ? { codexPluginSkillReview: nextPolicy.bridge.codexPluginSkillReview } : {}, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {}, ...retainedAgents.length ? { retainedReviewChecks: { home, projectDir: options.projectDir ?? home, agents: retainedAgents } } : {} };
16989
17246
  }
16990
- function hookProjectionRefusal(reason) {
16991
- throw new Error(`CLAUDE_MANAGED_HOOK_${reason}`);
17247
+ function hookProjectionRefusal(reason2) {
17248
+ throw new Error(`CLAUDE_MANAGED_HOOK_${reason2}`);
16992
17249
  }
16993
17250
  function projectedHookAcl(path, observed) {
16994
17251
  if (process.platform !== "darwin")
16995
17252
  return;
16996
- const identity5 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}:${stat.nlink}:${stat.size}:${stat.mtimeNs}`;
17253
+ const identity5 = observed.isDirectory() ? (stat) => `${stat.dev}:${stat.ino}:${stat.uid}:${stat.mode}` : (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}:${stat.nlink}:${stat.size}:${stat.mtimeNs}`;
16997
17254
  const fd = openSync11(path, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK | (observed.isDirectory() ? constants11.O_DIRECTORY : 0));
16998
17255
  try {
16999
17256
  if (identity5(fstatSync11(fd, { bigint: true })) !== identity5(observed) || !hasTrustedDarwinAcl(fd, true) || identity5(fstatSync11(fd, { bigint: true })) !== identity5(observed) || identity5(lstatSync20(path, { bigint: true })) !== identity5(observed))
@@ -17004,18 +17261,18 @@ function projectedHookAcl(path, observed) {
17004
17261
  }
17005
17262
  function projectedHookExecutable(command, expectedResolved) {
17006
17263
  try {
17007
- if (!isAbsolute14(command) || /[\0\r\n]/.test(command) || resolve13(command) !== command)
17264
+ if (!isAbsolute16(command) || /[\0\r\n]/.test(command) || resolve15(command) !== command)
17008
17265
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17009
17266
  command = canonicalSystemPath(command);
17010
17267
  const parents2 = (file = command) => {
17011
17268
  const result = [];
17012
- for (let path = dirname14(file);; path = dirname14(path)) {
17269
+ for (let path = dirname15(file);; path = dirname15(path)) {
17013
17270
  const stat = lstatSync20(path, { bigint: true });
17014
- if (!stat.isDirectory() || (stat.mode & 0o022n) !== 0n && !(stat.uid === 0n && (stat.mode & 0o1000n) !== 0n))
17271
+ if (!stat.isDirectory() || !trustedAncestorOwner(stat.uid) || (stat.mode & 0o022n) !== 0n && !(stat.uid === 0n && (stat.mode & 0o1000n) !== 0n))
17015
17272
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17016
17273
  projectedHookAcl(path, stat);
17017
17274
  result.push(`${path}:${stat.dev}:${stat.ino}:${stat.uid}:${stat.mode}`);
17018
- if (dirname14(path) === path)
17275
+ if (dirname15(path) === path)
17019
17276
  break;
17020
17277
  }
17021
17278
  return result;
@@ -17025,22 +17282,33 @@ function projectedHookExecutable(command, expectedResolved) {
17025
17282
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17026
17283
  const linkIdentity = (stat) => `${stat.dev}:${stat.ino}:${stat.mode}:${stat.uid}:${stat.ctimeNs}`;
17027
17284
  const target = resolveLauncherCommand(command), resolved = target.entry;
17285
+ const direct = () => link.isSymbolicLink() ? directAliasTarget(command) === target.physical : target.physical === command;
17286
+ if (!direct())
17287
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17028
17288
  if (expectedResolved !== undefined && resolved !== expectedResolved)
17029
17289
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17030
17290
  let launcher, launcherParentIdentity;
17291
+ let runtime;
17031
17292
  if (target.pinned) {
17032
17293
  launcherParentIdentity = parents2(target.physical);
17033
17294
  launcher = snapshot(target.physical, false);
17034
17295
  projectedHookAcl(launcher.file, launcher.stat);
17035
17296
  if (launcher.sha256 !== target.pinned.sha256 || (launcher.stat.mode & 0o100n) === 0n)
17036
17297
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17037
- assertPinnedLauncherRuntimeSafe(target.pinned.runtime);
17298
+ const path = target.pinned.runtime;
17299
+ assertPinnedLauncherRuntimeSafe(path);
17300
+ if (resolve15(path) !== path)
17301
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17302
+ runtime = { path, parents: parents2(path), stat: lstatSync20(path, { bigint: true }) };
17303
+ if (!runtime.stat.isFile())
17304
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17305
+ projectedHookAcl(path, runtime.stat);
17038
17306
  }
17039
17307
  const resolvedParentIdentity = parents2(resolved);
17040
- const manifest = snapshot(join23(dirname14(dirname14(resolved)), "package.json"), false, { readOnlyPackage: true });
17308
+ const manifest = snapshot(join23(dirname15(dirname15(resolved)), "package.json"), false, { readOnlyPackage: true });
17041
17309
  projectedHookAcl(manifest.file, manifest.stat);
17042
17310
  const pkg = JSON.parse(manifest.text);
17043
- if (pkg.name !== "@hasna/skills" || !/^\d+\.\d+\.\d+$/.test(pkg.version) || pkg.bin?.skills !== "bin/index.js" || resolved !== join23(dirname14(manifest.file), "bin/index.js"))
17311
+ if (pkg.name !== "@hasna/skills" || !/^\d+\.\d+\.\d+$/.test(pkg.version) || pkg.bin?.skills !== "bin/index.js" || resolved !== join23(dirname15(manifest.file), "bin/index.js"))
17044
17312
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17045
17313
  const cli = snapshot(resolved, false, { readOnlyPackage: true });
17046
17314
  projectedHookAcl(cli.file, cli.stat);
@@ -17049,7 +17317,7 @@ function projectedHookExecutable(command, expectedResolved) {
17049
17317
  const recheck = () => {
17050
17318
  try {
17051
17319
  const again = resolveLauncherCommand(command);
17052
- if (again.entry !== resolved || again.physical !== target.physical || linkIdentity(lstatSync20(command, { bigint: true })) !== linkIdentity(link) || !isDeepStrictEqual6(parents2(), parentIdentity) || !isDeepStrictEqual6(parents2(resolved), resolvedParentIdentity))
17320
+ if (again.entry !== resolved || again.physical !== target.physical || linkIdentity(lstatSync20(command, { bigint: true })) !== linkIdentity(link) || !direct() || !isDeepStrictEqual6(parents2(), parentIdentity) || !isDeepStrictEqual6(parents2(resolved), resolvedParentIdentity))
17053
17321
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17054
17322
  if (launcher) {
17055
17323
  if (!isDeepStrictEqual6(parents2(target.physical), launcherParentIdentity))
@@ -17057,6 +17325,12 @@ function projectedHookExecutable(command, expectedResolved) {
17057
17325
  projectedHookAcl(launcher.file, launcher.stat);
17058
17326
  unchanged2(launcher);
17059
17327
  }
17328
+ if (runtime) {
17329
+ assertPinnedLauncherRuntimeSafe(runtime.path);
17330
+ if (!isDeepStrictEqual6(parents2(runtime.path), runtime.parents))
17331
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17332
+ projectedHookAcl(runtime.path, runtime.stat);
17333
+ }
17060
17334
  projectedHookAcl(cli.file, cli.stat);
17061
17335
  projectedHookAcl(manifest.file, manifest.stat);
17062
17336
  unchanged2(cli);
@@ -17083,7 +17357,7 @@ function planClaudeManagedHookProjection(options) {
17083
17357
  } catch {
17084
17358
  hookProjectionRefusal("TARGET_INVALID");
17085
17359
  }
17086
- const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17360
+ const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17087
17361
  const policyPath = join23(dataDir2, "agent-policy.json");
17088
17362
  let policyBytes, settingsBytes, settingsPath, command, profile;
17089
17363
  try {
@@ -17175,29 +17449,28 @@ function planClaudePreToolUseHookUpdate(options) {
17175
17449
  return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
17176
17450
  }
17177
17451
  function claudeSettingsConsumers(home, path, discoveries, aliases) {
17178
- const canonical6 = (value) => canonicalAgentPath(value, aliases);
17452
+ const canonical7 = (value) => canonicalAgentPath(value, aliases);
17179
17453
  const containsSettings = (root) => {
17180
- const distance = relative6(canonical6(root), path);
17181
- return distance === "" || distance !== ".." && !distance.startsWith(`..${sep7}`) && !isAbsolute14(distance);
17454
+ const distance = relative6(canonical7(root), path);
17455
+ return distance === "" || distance !== ".." && !distance.startsWith(`..${sep7}`) && !isAbsolute16(distance);
17182
17456
  };
17183
- return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical6(join23(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical6(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
17457
+ return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical7(join23(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical7(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
17184
17458
  }
17185
- function verifyCoordinatedDiscovery(binding, home, aliases) {
17459
+ function verifyCoordinatedDiscovery(binding, home, aliases, executable2) {
17186
17460
  try {
17187
17461
  if (binding.method !== "automatic" && binding.method !== "reviewed")
17188
17462
  throw new Error("Invalid managed discovery method");
17189
17463
  verifyAgentDiscovery(binding);
17190
- if (binding.method === "automatic") {
17191
- const current = resolveAgentDiscovery({ home, agent: binding.agent, canonical: (path) => canonicalAgentPath(path, aliases) });
17192
- if (JSON.stringify(current) !== JSON.stringify(binding))
17193
- throw new Error("Configured native discovery roots changed during Claude update");
17194
- }
17464
+ if (binding.method === "automatic")
17465
+ verifyAutomaticDiscoveryClosure(binding, { home, canonical: (path) => canonicalAgentPath(path, aliases), change: "Configured native discovery roots changed during Claude update", executable: executable2 });
17195
17466
  } catch (error) {
17467
+ if (isDiscoveryRootUnresolved(error))
17468
+ throw error;
17196
17469
  throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
17197
17470
  }
17198
17471
  }
17199
17472
  function planClaudeHookEventsUpdate(options) {
17200
- const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17473
+ const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17201
17474
  const policyPath = join23(dataDir2, "agent-policy.json");
17202
17475
  assertSafePath(policyPath);
17203
17476
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
@@ -17223,8 +17496,9 @@ function planClaudeHookEventsUpdate(options) {
17223
17496
  });
17224
17497
  if (discoveries.length !== agents.length || !agents.includes("claude"))
17225
17498
  throw new Error("Missing managed Claude discovery coverage");
17499
+ const discoveryExecutables = bridge.discoveryExecutables;
17226
17500
  for (const binding of discoveries)
17227
- verifyCoordinatedDiscovery(binding, home, aliases);
17501
+ verifyCoordinatedDiscovery(binding, home, aliases, discoveryExecutables?.[binding.agent]);
17228
17502
  const consumers = claudeSettingsConsumers(home, path, discoveries, aliases);
17229
17503
  for (const agent of consumers)
17230
17504
  assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: home });
@@ -17259,13 +17533,14 @@ function planClaudeHookEventsUpdate(options) {
17259
17533
  observedPolicy: { path: policyPath, before: snapshot2.text },
17260
17534
  discoveryBefore: discoveries,
17261
17535
  discoveryAfter,
17536
+ ...discoveryExecutables ? { discoveryExecutables } : {},
17262
17537
  rootAliases: aliases,
17263
17538
  managedAgentChecks: { home, agents: consumers }
17264
17539
  };
17265
17540
  }
17266
17541
  function atomicWrite2(path, content) {
17267
17542
  assertSafePath(path);
17268
- mkdirSync6(dirname14(path), { recursive: true, mode: 448 });
17543
+ mkdirSync6(dirname15(path), { recursive: true, mode: 448 });
17269
17544
  const temporary = `${path}.skills-${randomUUID3()}`;
17270
17545
  try {
17271
17546
  writeFileSync6(temporary, content, { mode: 384, flag: "wx" });
@@ -17280,7 +17555,7 @@ function planAgentSettingsWitnessUpgrade(options) {
17280
17555
  throw new Error("Invalid Codex witness target version");
17281
17556
  if (options.targetClaudeVersion !== undefined && (options.agent !== "claude" || ![3, 4].includes(options.targetClaudeVersion)))
17282
17557
  throw new Error("Invalid Claude witness target version");
17283
- const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17558
+ const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17284
17559
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
17285
17560
  if (!snapshot2 || sha3(snapshot2.text) !== options.expectedPolicySha256)
17286
17561
  throw new Error("Managed policy preimage changed");
@@ -17340,7 +17615,7 @@ function planAgentSettingsWitnessUpgrade(options) {
17340
17615
  const replacement = { ...binding, sources: rebound };
17341
17616
  verifyAgentDiscovery(replacement);
17342
17617
  const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
17343
- return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
17618
+ return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], ...policy.bridge.discoveryExecutables ? { discoveryExecutables: policy.bridge.discoveryExecutables } : {}, rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
17344
17619
  }
17345
17620
  function applyAgentIntegration(plan, options = {}) {
17346
17621
  const roots = plan.changes.flatMap((change) => {
@@ -17358,10 +17633,10 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
17358
17633
  throw new Error("Native settings changed after witness planning");
17359
17634
  const policyPath = join23(plan.dataDir, "agent-policy.json");
17360
17635
  assertSafePath(policyPath);
17361
- const currentText = (path) => resolve13(path) === resolve13(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
17636
+ const currentText = (path) => resolve15(path) === resolve15(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
17362
17637
  readManagedSkillPolicySnapshot(plan.dataDir);
17363
17638
  for (const change of plan.changes)
17364
- if (resolve13(change.path) === resolve13(policyPath))
17639
+ if (resolve15(change.path) === resolve15(policyPath))
17365
17640
  parseManagedSkillPolicy(change.after);
17366
17641
  const aliases = plan.rootAliases ?? [];
17367
17642
  assertCurrent();
@@ -17382,7 +17657,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
17382
17657
  assertManagedAgentBridgeWithDiscovery(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home }, provenDiscovery?.agent === agent ? provenDiscovery : undefined);
17383
17658
  for (const binding of plan.discoveryBefore ?? []) {
17384
17659
  if (plan.managedAgentChecks)
17385
- verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
17660
+ verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases, plan.discoveryExecutables?.[binding.agent]);
17386
17661
  else
17387
17662
  verifyAgentDiscovery(binding);
17388
17663
  }
@@ -17414,7 +17689,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
17414
17689
  recheckRootAliases(aliases);
17415
17690
  if (currentText(change.path) !== change.before)
17416
17691
  throw new Error(`Configuration changed after planning: ${change.path}`);
17417
- for (let directory = dirname14(change.path);!existsSync9(directory); directory = dirname14(directory))
17692
+ for (let directory = dirname15(change.path);!existsSync9(directory); directory = dirname15(directory))
17418
17693
  createdDirectories.add(directory);
17419
17694
  const after = change.after;
17420
17695
  atomicWrite2(change.path, after);
@@ -17429,7 +17704,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
17429
17704
  throw new Error("Native settings changed during witness application");
17430
17705
  for (const binding of plan.discoveryAfter ?? []) {
17431
17706
  if (plan.managedAgentChecks)
17432
- verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
17707
+ verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases, plan.discoveryExecutables?.[binding.agent]);
17433
17708
  else
17434
17709
  verifyAgentDiscovery(binding);
17435
17710
  }
@@ -17486,7 +17761,7 @@ function writeArchiveJournal(path, value) {
17486
17761
  closeSync11(descriptor);
17487
17762
  descriptor = undefined;
17488
17763
  renameSync3(temporary, path);
17489
- syncArchiveDirectory(dirname14(path));
17764
+ syncArchiveDirectory(dirname15(path));
17490
17765
  } finally {
17491
17766
  if (descriptor !== undefined)
17492
17767
  closeSync11(descriptor);
@@ -17544,14 +17819,14 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
17544
17819
  };
17545
17820
  assertSafePath(archiveRoot);
17546
17821
  const created = [];
17547
- for (let path = archiveRoot;!existsSync9(path); path = dirname14(path))
17822
+ for (let path = archiveRoot;!existsSync9(path); path = dirname15(path))
17548
17823
  created.push(path);
17549
17824
  mkdirSync6(archiveRoot, { recursive: true, mode: 448 });
17550
17825
  try {
17551
17826
  for (const directory of created)
17552
17827
  syncArchiveDirectory(directory);
17553
17828
  if (created.length)
17554
- syncArchiveDirectory(dirname14(created.at(-1)));
17829
+ syncArchiveDirectory(dirname15(created.at(-1)));
17555
17830
  writeArchiveJournal(receiptPath, journal);
17556
17831
  for (const [index, entry] of selected.entries()) {
17557
17832
  assertCurrent();
@@ -17564,7 +17839,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
17564
17839
  writeArchiveJournal(receiptPath, journal);
17565
17840
  renameSync3(move.source, move.archive);
17566
17841
  moved.push(move);
17567
- syncArchiveDirectory(dirname14(move.source));
17842
+ syncArchiveDirectory(dirname15(move.source));
17568
17843
  syncArchiveDirectory(archiveRoot);
17569
17844
  verifyArchive(move);
17570
17845
  move.status = "archived";
@@ -17601,7 +17876,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
17601
17876
  }
17602
17877
  if (entry.discoveryOnly) {
17603
17878
  linkSync2(entry.archive, entry.source);
17604
- syncArchiveDirectory(dirname14(entry.source));
17879
+ syncArchiveDirectory(dirname15(entry.source));
17605
17880
  unlinkSync4(entry.archive);
17606
17881
  } else {
17607
17882
  if (lstatSync20(entry.source, { throwIfNoEntry: false })) {
@@ -17612,7 +17887,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
17612
17887
  renameSync3(entry.archive, entry.source);
17613
17888
  }
17614
17889
  entry.status = "restored";
17615
- syncArchiveDirectory(dirname14(entry.source));
17890
+ syncArchiveDirectory(dirname15(entry.source));
17616
17891
  syncArchiveDirectory(archiveRoot);
17617
17892
  } catch {
17618
17893
  entry.status = "recovery-required";
@@ -17637,7 +17912,7 @@ function isInertCodexPluginCacheCopy(entry, cache, allowedHostPaths, read2) {
17637
17912
  return false;
17638
17913
  const document2 = join23(entry.path, "SKILL.md");
17639
17914
  const inside = relative6(cache, entry.path);
17640
- if (!inside || isAbsolute14(inside) || inside.split(sep7).some((segment) => segment === ".."))
17915
+ if (!inside || isAbsolute16(inside) || inside.split(sep7).some((segment) => segment === ".."))
17641
17916
  return false;
17642
17917
  assertSafePath(entry.path);
17643
17918
  if (!lstatSync20(cache).isDirectory() || !lstatSync20(entry.path).isDirectory() || !lstatSync20(document2).isFile())
@@ -17657,7 +17932,7 @@ function assertManagedAgentBridge(agent, options = {}) {
17657
17932
  assertManagedAgentBridgeWithDiscovery(agent, options);
17658
17933
  }
17659
17934
  function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
17660
- const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17935
+ const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17661
17936
  assertSafePath(join23(dataDir2, "agent-policy.json"));
17662
17937
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
17663
17938
  if (!snapshot2)
@@ -17755,12 +18030,11 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
17755
18030
  verifyAgentDiscovery(agent === "codex" ? { ...discovery, codexDisabledPluginSkills: binding.codexPluginSkills ?? [] } : discovery, options.codexDiscoveryRecovery);
17756
18031
  if (agent === "sumi" && discovery.method === "reviewed")
17757
18032
  resolveAgentDiscovery({ home, agent, retainedReview: discovery, canonical: (path) => canonicalAgentPath(path, aliases) });
17758
- if (discovery.method === "automatic") {
17759
- const current = resolveAgentDiscovery({ home, agent, canonical: (path) => canonicalAgentPath(path, aliases) });
17760
- if (JSON.stringify(current) !== JSON.stringify(discovery))
17761
- throw new Error("Configured native discovery roots changed");
17762
- }
18033
+ if (discovery.method === "automatic")
18034
+ verifyAutomaticDiscoveryClosure(discovery, { home, canonical: (path) => canonicalAgentPath(path, aliases), change: "Configured native discovery roots changed", executable: binding.discoveryExecutables?.[agent] });
17763
18035
  } catch (error) {
18036
+ if (isDiscoveryRootUnresolved(error))
18037
+ throw error;
17764
18038
  throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
17765
18039
  }
17766
18040
  let installationInputRoots = [];
@@ -18053,9 +18327,9 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
18053
18327
  const action = dirExists ? "update" : "create";
18054
18328
  if (options.dryRun)
18055
18329
  return { action, path: skillMdPath };
18056
- const parentDir = dirname15(dir);
18330
+ const parentDir = dirname16(dir);
18057
18331
  mkdirSync7(parentDir, { recursive: true });
18058
- const transactionDir = mkdtempSync(join25(parentDir, `.hasna-skills-write-${basename7(dir)}-`));
18332
+ const transactionDir = mkdtempSync(join25(parentDir, `.hasna-skills-write-${basename8(dir)}-`));
18059
18333
  const candidateDir = join25(transactionDir, "candidate");
18060
18334
  const backupDir = join25(transactionDir, "backup");
18061
18335
  const candidateSkillMdPath = join25(candidateDir, "SKILL.md");
@@ -19047,7 +19321,7 @@ function exactProfileSelection(spec, profile) {
19047
19321
  }
19048
19322
 
19049
19323
  // src/lib/plugin-projection.ts
19050
- import { createHash as createHash20 } from "crypto";
19324
+ import { createHash as createHash21 } from "crypto";
19051
19325
  import { posix } from "path";
19052
19326
  var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
19053
19327
  var PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
@@ -19058,7 +19332,7 @@ function pluginNeed(value, message) {
19058
19332
  if (!value)
19059
19333
  pluginRefusal(message);
19060
19334
  }
19061
- var pluginHash = (bytes) => createHash20("sha256").update(bytes).digest("hex");
19335
+ var pluginHash = (bytes) => createHash21("sha256").update(bytes).digest("hex");
19062
19336
  function pluginObject(value) {
19063
19337
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
19064
19338
  }
@@ -19246,9 +19520,9 @@ function buildPluginProjection(entries) {
19246
19520
  }
19247
19521
 
19248
19522
  // src/lib/plugin-projection-store.ts
19249
- import { createHash as createHash21, randomUUID as randomUUID4 } from "crypto";
19523
+ import { createHash as createHash22, randomUUID as randomUUID4 } from "crypto";
19250
19524
  import { constants as constants12, closeSync as closeSync12, fchmodSync as fchmodSync2, fstatSync as fstatSync12, fsyncSync as fsyncSync6, linkSync as linkSync3, lstatSync as lstatSync22, mkdirSync as mkdirSync9, openSync as openSync12, opendirSync as opendirSync4, readSync as readSync9, renameSync as renameSync5, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync5, writeSync as writeSync3 } from "fs";
19251
- import { dirname as dirname16, isAbsolute as isAbsolute15, join as join29, parse as parse6, resolve as resolve14 } from "path";
19525
+ import { dirname as dirname17, isAbsolute as isAbsolute17, join as join29, parse as parse6, resolve as resolve16 } from "path";
19252
19526
  var READ_FLAGS = constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK;
19253
19527
  var WRITE_FLAGS = constants12.O_WRONLY | constants12.O_CREAT | constants12.O_EXCL | constants12.O_NOFOLLOW | constants12.O_NONBLOCK;
19254
19528
  var uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined;
@@ -19262,8 +19536,8 @@ function stat(path) {
19262
19536
  return pluginRefusal("Cannot inspect plugin filesystem entry");
19263
19537
  }
19264
19538
  }
19265
- function canonical6(path) {
19266
- pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute15(path) && resolve14(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
19539
+ function canonical7(path) {
19540
+ pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute17(path) && resolve16(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
19267
19541
  }
19268
19542
  function owner(stats, system = false) {
19269
19543
  const current = uid();
@@ -19273,8 +19547,8 @@ function same3(left, right, identityOnly = false) {
19273
19547
  pluginNeed(right && left.dev === right.dev && left.ino === right.ino && left.mode === right.mode && left.uid === right.uid && left.gid === right.gid && (identityOnly || left.size === right.size && left.nlink === right.nlink && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs), "Plugin filesystem entry changed during access");
19274
19548
  }
19275
19549
  function ancestors(path, create = false) {
19276
- canonical6(path);
19277
- const result = [], parent = dirname16(path), filesystemRoot = parse6(parent).root;
19550
+ canonical7(path);
19551
+ const result = [], parent = dirname17(path), filesystemRoot = parse6(parent).root;
19278
19552
  let current = filesystemRoot;
19279
19553
  for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
19280
19554
  if (segment)
@@ -19302,7 +19576,7 @@ function verifyAncestors(chain, identityOnly = true) {
19302
19576
  }
19303
19577
  function privateParent(path) {
19304
19578
  const chain = ancestors(path, true), parent = chain.at(-1);
19305
- pluginNeed(parent && parent.path === dirname16(path), "Plugin storage parent is unavailable");
19579
+ pluginNeed(parent && parent.path === dirname17(path), "Plugin storage parent is unavailable");
19306
19580
  owner(parent.stat);
19307
19581
  pluginNeed((parent.stat.mode & 0o7777n) === 0o700n, "Plugin storage parent must have mode 0700");
19308
19582
  return chain;
@@ -19588,7 +19862,7 @@ function pluginExecutableDigest(path) {
19588
19862
  const opened = fstatSync12(fd, { bigint: true });
19589
19863
  same3(before, opened);
19590
19864
  fileShape(opened, maximum, "executable");
19591
- const hash2 = createHash21("sha256"), buffer = new Uint8Array(64 * 1024);
19865
+ const hash2 = createHash22("sha256"), buffer = new Uint8Array(64 * 1024);
19592
19866
  let remaining = Number(opened.size);
19593
19867
  while (remaining > 0) {
19594
19868
  const count = readSync9(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
@@ -19646,7 +19920,7 @@ function validatePluginTarget(value) {
19646
19920
  for (const registration of value.registrations) {
19647
19921
  pluginKeys(registration, ["scope", "projectPath"]);
19648
19922
  pluginNeed(registration.scope === "user" || registration.scope === "project", "Unsupported plugin registration scope");
19649
- pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" && isAbsolute16(registration.projectPath) && resolve15(registration.projectPath) === registration.projectPath, "Plugin scope requires its exact project path");
19923
+ pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" && isAbsolute18(registration.projectPath) && resolve17(registration.projectPath) === registration.projectPath, "Plugin scope requires its exact project path");
19650
19924
  const key = JSON.stringify([registration.scope, registration.projectPath]);
19651
19925
  pluginNeed(!scopes.has(key), "Duplicate plugin registration scope");
19652
19926
  scopes.add(key);
@@ -19654,7 +19928,7 @@ function validatePluginTarget(value) {
19654
19928
  for (const key of ["native", "resolver"]) {
19655
19929
  pluginKeys(value[key], key === "native" ? ["version", "executable", "digest"] : ["executable", "digest"]);
19656
19930
  pluginText(value[key].executable);
19657
- pluginNeed(isAbsolute16(value[key].executable) && resolve15(value[key].executable) === value[key].executable, "Plugin executable must have a canonical absolute path");
19931
+ pluginNeed(isAbsolute18(value[key].executable) && resolve17(value[key].executable) === value[key].executable, "Plugin executable must have a canonical absolute path");
19658
19932
  pluginDigest(value[key].digest);
19659
19933
  }
19660
19934
  pluginNeed(["2.1.274", "2.1.276"].includes(value.native.version), "Plugin admission requires a certified Claude command-source runtime (2.1.274 or 2.1.276)");
@@ -19799,14 +20073,14 @@ function validatePlan(value) {
19799
20073
  pluginNeed(observedEvidenceDigest(plan.observation) === plan.evidenceDigest, "Plugin receipt observed evidence changed");
19800
20074
  pluginNeed(identityDigest(plan) === plan.planDigest, "Plugin receipt immutable identity changed");
19801
20075
  }
19802
- var quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
20076
+ var quote2 = (value) => `'${value.replaceAll("'", "'\\''")}'`;
19803
20077
  var resolverGuard = 'exec 9<"$1"; actual=$(/usr/bin/sha256sum /proc/self/fd/9); actual=${actual%% *}; [ "sha256:$actual" = "$2" ]; exec /proc/self/fd/9 integration plugin resolve --binding "$3"';
19804
20078
  function pluginResolverCommand(binding) {
19805
- return `${quote("/bin/sh")} -eu -c ${quote(resolverGuard)} plugin-resolver ${quote(binding.target.resolver.executable)} ${quote(binding.target.resolver.digest)} ${quote(bindingId(binding))}`;
20079
+ return `${quote2("/bin/sh")} -eu -c ${quote2(resolverGuard)} plugin-resolver ${quote2(binding.target.resolver.executable)} ${quote2(binding.target.resolver.digest)} ${quote2(bindingId(binding))}`;
19806
20080
  }
19807
20081
  function root(options) {
19808
20082
  const path = options.storeRoot ?? pluginAdmissionRoot();
19809
- pluginNeed(isAbsolute16(path) && resolve15(path) === path, "Invalid plugin admission store root");
20083
+ pluginNeed(isAbsolute18(path) && resolve17(path) === path, "Invalid plugin admission store root");
19810
20084
  return path;
19811
20085
  }
19812
20086
  function pluginBindingPath(storeRoot, id) {
@@ -20011,15 +20285,15 @@ function validateManagedPluginWitnesses(value) {
20011
20285
  pluginNeed(typeof item.bindingId === "string" && /^[a-f0-9]{64}$/.test(item.bindingId) && !seen.has(item.bindingId), "Invalid or duplicate managed plugin witness");
20012
20286
  seen.add(item.bindingId);
20013
20287
  pluginText(item.storeRoot);
20014
- pluginNeed(isAbsolute17(item.storeRoot) && resolve16(item.storeRoot) === item.storeRoot, "Invalid plugin receipt store path");
20288
+ pluginNeed(isAbsolute19(item.storeRoot) && resolve18(item.storeRoot) === item.storeRoot, "Invalid plugin receipt store path");
20015
20289
  }
20016
20290
  }
20017
20291
  function safe3(path) {
20018
20292
  pluginText(path);
20019
- pluginNeed(isAbsolute17(path) && resolve16(path) === path, "Invalid plugin registry path");
20020
- for (let at = path;; at = dirname17(at)) {
20293
+ pluginNeed(isAbsolute19(path) && resolve18(path) === path, "Invalid plugin registry path");
20294
+ for (let at = path;; at = dirname18(at)) {
20021
20295
  pluginNeed(!lstatSync23(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
20022
- if (at === dirname17(at))
20296
+ if (at === dirname18(at))
20023
20297
  break;
20024
20298
  }
20025
20299
  }
@@ -20069,7 +20343,7 @@ function hashManagedPluginRegistry(path, managed) {
20069
20343
  seen.add(target.pluginId);
20070
20344
  const rows = plugins[target.pluginId];
20071
20345
  pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
20072
- const [name, market] = target.pluginId.split("@"), cacheRoot = join31(dirname17(path), "cache", market, name);
20346
+ const [name, market] = target.pluginId.split("@"), cacheRoot = join31(dirname18(path), "cache", market, name);
20073
20347
  const scopes = new Set, approvals = new Map, currentFiles = new Map;
20074
20348
  plugins[target.pluginId] = rows.map((row) => {
20075
20349
  pluginNeed(pluginObject(row), "Invalid managed plugin registration");
@@ -20085,7 +20359,7 @@ function hashManagedPluginRegistry(path, managed) {
20085
20359
  pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join31(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
20086
20360
  const approval = (producer) => {
20087
20361
  pluginText(producer);
20088
- const digest = `sha256:${basename8(producer)}`;
20362
+ const digest = `sha256:${basename9(producer)}`;
20089
20363
  const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest);
20090
20364
  pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
20091
20365
  if (!verified.has(producer)) {
@@ -20147,39 +20421,39 @@ function captureManagedPluginRegistry(path, managedPlugins) {
20147
20421
  }
20148
20422
 
20149
20423
  // src/lib/claude-marketplace-registry.ts
20150
- import { createHash as createHash22 } from "crypto";
20424
+ import { createHash as createHash23 } from "crypto";
20151
20425
  import { closeSync as closeSync14, constants as constants14, fstatSync as fstatSync14, lstatSync as lstatSync24, openSync as openSync14, readSync as readSync11 } from "fs";
20152
- import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute18, resolve as resolve17 } from "path";
20426
+ import { basename as basename10, dirname as dirname19, isAbsolute as isAbsolute20, resolve as resolve19 } from "path";
20153
20427
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
20154
- function need6(value, reason) {
20428
+ function need7(value, reason2) {
20155
20429
  if (!value)
20156
- throw new Error(`Claude marketplace registry ${reason}`);
20430
+ throw new Error(`Claude marketplace registry ${reason2}`);
20157
20431
  }
20158
20432
  function absolutePath3(value) {
20159
- need6(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute18(value) && resolve17(value) === value, "requires a normalized absolute path");
20433
+ need7(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute20(value) && resolve19(value) === value, "requires a normalized absolute path");
20160
20434
  }
20161
20435
  var sameFile2 = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
20162
20436
  function parents2(path) {
20163
20437
  const result = [];
20164
- for (let at = dirname18(path);; at = dirname18(at)) {
20438
+ for (let at = dirname19(path);; at = dirname19(at)) {
20165
20439
  const stat2 = lstatSync24(at, { bigint: true });
20166
- need6(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
20440
+ need7(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
20167
20441
  result.push([at, stat2]);
20168
- if (at === dirname18(at))
20442
+ if (at === dirname19(at))
20169
20443
  return result;
20170
20444
  }
20171
20445
  }
20172
20446
  function readRegistry2(path, budget2) {
20173
20447
  absolutePath3(path);
20174
- need6(basename9(path) === "known_marketplaces.json", "requires known_marketplaces.json");
20175
- need6(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
20448
+ need7(basename10(path) === "known_marketplaces.json", "requires known_marketplaces.json");
20449
+ need7(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
20176
20450
  const ancestors2 = parents2(path), initial = lstatSync24(path, { bigint: true });
20177
- need6(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
20178
- need6(initial.size <= BigInt(budget2.remaining), "exceeds its aggregate byte limit");
20451
+ need7(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
20452
+ need7(initial.size <= BigInt(budget2.remaining), "exceeds its aggregate byte limit");
20179
20453
  const fd = openSync14(path, constants14.O_RDONLY | constants14.O_NOFOLLOW | constants14.O_NONBLOCK);
20180
20454
  try {
20181
20455
  const opened = fstatSync14(fd, { bigint: true });
20182
- need6(opened.isFile() && sameFile2(initial, opened), "changed during open");
20456
+ need7(opened.isFile() && sameFile2(initial, opened), "changed during open");
20183
20457
  const bytes = Buffer.alloc(Number(opened.size) + 1);
20184
20458
  let length = 0;
20185
20459
  while (length < bytes.length) {
@@ -20189,10 +20463,10 @@ function readRegistry2(path, budget2) {
20189
20463
  length += count;
20190
20464
  }
20191
20465
  const after = fstatSync14(fd, { bigint: true }), current = lstatSync24(path, { bigint: true });
20192
- need6(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
20466
+ need7(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
20193
20467
  for (const [ancestor, before] of ancestors2) {
20194
20468
  const now = lstatSync24(ancestor, { bigint: true });
20195
- need6(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
20469
+ need7(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
20196
20470
  }
20197
20471
  budget2.remaining -= length;
20198
20472
  try {
@@ -20212,7 +20486,7 @@ function parse7(text2) {
20212
20486
  at++;
20213
20487
  };
20214
20488
  function string() {
20215
- need6(text2[at] === '"', "contains invalid JSON");
20489
+ need7(text2[at] === '"', "contains invalid JSON");
20216
20490
  const start = at++;
20217
20491
  while (at < text2.length) {
20218
20492
  const character = text2[at++];
@@ -20223,7 +20497,7 @@ function parse7(text2) {
20223
20497
  } catch {
20224
20498
  throw new Error("Claude marketplace registry contains invalid JSON string");
20225
20499
  }
20226
- need6(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
20500
+ need7(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
20227
20501
  return value2;
20228
20502
  }
20229
20503
  if (character === "\\")
@@ -20232,7 +20506,7 @@ function parse7(text2) {
20232
20506
  throw new Error("Claude marketplace registry contains an unterminated JSON string");
20233
20507
  }
20234
20508
  function value(depth) {
20235
- need6(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
20509
+ need7(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
20236
20510
  space();
20237
20511
  const character = text2[at];
20238
20512
  if (character === '"')
@@ -20246,17 +20520,17 @@ function parse7(text2) {
20246
20520
  if (object6) {
20247
20521
  space();
20248
20522
  const key = string();
20249
- need6(!keys.has(key), "contains a duplicate JSON key");
20523
+ need7(!keys.has(key), "contains a duplicate JSON key");
20250
20524
  keys.add(key);
20251
20525
  space();
20252
- need6(text2[at++] === ":", "contains invalid JSON");
20526
+ need7(text2[at++] === ":", "contains invalid JSON");
20253
20527
  entries.push([key, value(depth + 1)]);
20254
20528
  } else
20255
20529
  items.push(value(depth + 1));
20256
20530
  space();
20257
20531
  if (text2[at] === end)
20258
20532
  break;
20259
- need6(text2[at++] === ",", "contains invalid JSON");
20533
+ need7(text2[at++] === ",", "contains invalid JSON");
20260
20534
  }
20261
20535
  at++;
20262
20536
  return object6 ? { kind: "object", entries } : { kind: "array", items };
@@ -20268,67 +20542,67 @@ function parse7(text2) {
20268
20542
  }
20269
20543
  numberPattern.lastIndex = at;
20270
20544
  const number = numberPattern.exec(text2);
20271
- need6(number, "contains invalid JSON");
20545
+ need7(number, "contains invalid JSON");
20272
20546
  at += number[0].length;
20273
20547
  return { kind: "number", value: number[0] };
20274
20548
  }
20275
20549
  const root2 = value(0);
20276
20550
  space();
20277
- need6(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
20278
- need6(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
20551
+ need7(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
20552
+ need7(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
20279
20553
  return root2;
20280
20554
  }
20281
20555
  function exactKeys(value, keys) {
20282
20556
  return value?.kind === "object" && value.entries.length === keys.length && value.entries.every(([key]) => keys.includes(key));
20283
20557
  }
20284
- var field = (object6, key) => object6.entries.find(([name]) => name === key)?.[1];
20558
+ var field2 = (object6, key) => object6.entries.find(([name]) => name === key)?.[1];
20285
20559
  function textField(value) {
20286
- need6(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
20560
+ need7(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
20287
20561
  return value.value;
20288
20562
  }
20289
20563
  function normalizeRow(row, version = 1) {
20290
20564
  if (!exactKeys(row, ["source", "installLocation", "lastUpdated"])) {
20291
20565
  if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
20292
20566
  return;
20293
- const autoUpdate = field(row, "autoUpdate");
20294
- need6(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
20567
+ const autoUpdate = field2(row, "autoUpdate");
20568
+ need7(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
20295
20569
  }
20296
- const source = field(row, "source");
20570
+ const source = field2(row, "source");
20297
20571
  if (source?.kind !== "object")
20298
20572
  return;
20299
- const type = field(source, "source");
20573
+ const type = field2(source, "source");
20300
20574
  if (type?.kind !== "string" || !["github", "directory"].includes(type.value))
20301
20575
  return;
20302
20576
  const location = type.value === "github" ? "repo" : "path";
20303
20577
  if (!exactKeys(source, ["source", location]))
20304
20578
  return;
20305
- const sourceLocation = textField(field(source, location));
20579
+ const sourceLocation = textField(field2(source, location));
20306
20580
  if (type.value === "directory")
20307
20581
  absolutePath3(sourceLocation);
20308
- absolutePath3(textField(field(row, "installLocation")));
20309
- const updated = textField(field(row, "lastUpdated"));
20310
- need6(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
20582
+ absolutePath3(textField(field2(row, "installLocation")));
20583
+ const updated = textField(field2(row, "lastUpdated"));
20584
+ need7(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
20311
20585
  const millis = Date.parse(updated);
20312
- need6(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
20586
+ need7(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
20313
20587
  row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
20314
20588
  }
20315
20589
  function captureClaudeMarketplaceRegistry(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
20316
20590
  const value = parse7(readRegistry2(path, budget2));
20317
20591
  for (const [, row] of value.entries)
20318
20592
  normalizeRow(row);
20319
- const sha2563 = createHash22("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
20593
+ const sha2563 = createHash23("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
20320
20594
  return { path, hashMode: "claude-marketplace-registry", sha256: sha2563 };
20321
20595
  }
20322
20596
  function captureClaudeMarketplaceRegistryV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
20323
20597
  const value = parse7(readRegistry2(path, budget2));
20324
20598
  for (const [, row] of value.entries)
20325
20599
  normalizeRow(row, 2);
20326
- const sha2563 = createHash22("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
20600
+ const sha2563 = createHash23("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
20327
20601
  return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2563 };
20328
20602
  }
20329
20603
 
20330
20604
  // src/lib/codex-hook-discovery-recovery.ts
20331
- import { createHash as createHash23 } from "crypto";
20605
+ import { createHash as createHash24 } from "crypto";
20332
20606
 
20333
20607
  // src/lib/codex-hook-additions-review.ts
20334
20608
  var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
@@ -20336,12 +20610,12 @@ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
20336
20610
  var proofs = new WeakMap;
20337
20611
 
20338
20612
  // src/lib/codex-hook-discovery-recovery.ts
20339
- var sha4 = (text2) => createHash23("sha256").update(text2).digest("hex");
20613
+ var sha4 = (text2) => createHash24("sha256").update(text2).digest("hex");
20340
20614
  var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
20341
20615
  var proofs2 = new WeakMap;
20342
20616
  function assertCodexHookDiscoveryRecovery(binding, witness) {
20343
20617
  const proof = proofs2.get(witness);
20344
- need5(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
20618
+ need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
20345
20619
  unchanged2(proof.before);
20346
20620
  unchanged2(proof.current);
20347
20621
  proof.recheckAdditions();
@@ -20350,12 +20624,43 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
20350
20624
  if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
20351
20625
  return false;
20352
20626
  assertCodexHookDiscoveryRecovery(binding, witness);
20353
- need5(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
20627
+ need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
20354
20628
  return true;
20355
20629
  }
20356
20630
 
20357
20631
  // src/lib/agent-discovery.ts
20358
- var digest = (text2) => createHash24("sha256").update(text2).digest("hex");
20632
+ var DISCOVERY_RUNTIME_COMMANDS = Object.freeze({ gemini: "gemini" });
20633
+ var DISCOVERY_ROOT_UNRESOLVED = "DISCOVERY_ROOT_UNRESOLVED";
20634
+ function discoveryRootUnresolved(agent, command, detail) {
20635
+ return new Error(`${DISCOVERY_ROOT_UNRESOLVED}: ${agent} discovery cannot resolve its "${command}" executable: ${detail}. The reviewed discovery is unchanged; this is an environment gap, not drift.`);
20636
+ }
20637
+ function isDiscoveryRootUnresolved(error) {
20638
+ return error instanceof Error && error.message.startsWith(`${DISCOVERY_ROOT_UNRESOLVED}: `);
20639
+ }
20640
+ var quotedPath = (path) => JSON.stringify(path).replace(/[^\x20-\x7e]/g, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
20641
+ var realpathOrNull = (path) => {
20642
+ try {
20643
+ return realpathSync11(path);
20644
+ } catch {
20645
+ return null;
20646
+ }
20647
+ };
20648
+ function discoveryExecutable(agent) {
20649
+ const command = DISCOVERY_RUNTIME_COMMANDS[agent];
20650
+ const found = command ? Bun.which(command) : null;
20651
+ if (!command || !found)
20652
+ return null;
20653
+ const path = resolve20(found), target = realpathOrNull(path);
20654
+ return target === null ? null : { command, path, target };
20655
+ }
20656
+ function recordedDiscoveryExecutable(agent, recorded) {
20657
+ const command = DISCOVERY_RUNTIME_COMMANDS[agent];
20658
+ if (!command || recorded.command !== command)
20659
+ throw new Error(`Recorded ${agent} discovery executable names another command`);
20660
+ const target = realpathOrNull(recorded.path);
20661
+ return target === null ? null : { command, path: recorded.path, target };
20662
+ }
20663
+ var digest = (text2) => createHash25("sha256").update(text2).digest("hex");
20359
20664
  function parseConfig(text2, path, toml = false) {
20360
20665
  try {
20361
20666
  const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
@@ -20367,12 +20672,12 @@ function parseConfig(text2, path, toml = false) {
20367
20672
  }
20368
20673
  }
20369
20674
  function safe4(path) {
20370
- if (!isAbsolute19(path) || path.includes("\x00"))
20675
+ if (!isAbsolute21(path) || path.includes("\x00"))
20371
20676
  throw new Error("Expected an absolute native discovery path");
20372
- for (let cursor = path;; cursor = dirname19(cursor)) {
20677
+ for (let cursor = path;; cursor = dirname20(cursor)) {
20373
20678
  if (lstatSync25(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
20374
20679
  throw new Error(`Refusing symlink native discovery input: ${cursor}`);
20375
- if (dirname19(cursor) === cursor)
20680
+ if (dirname20(cursor) === cursor)
20376
20681
  break;
20377
20682
  }
20378
20683
  }
@@ -20391,7 +20696,7 @@ function projectNativeDiscoveryFields(text2, format, fields2, path) {
20391
20696
  const object6 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
20392
20697
  if (!object6 || typeof object6 !== "object" || Array.isArray(object6))
20393
20698
  throw new Error("Expected native discovery configuration object");
20394
- return digest(JSON.stringify(Object.fromEntries(fields2.map((field2) => [field2, object6[field2] ?? null]))));
20699
+ return digest(JSON.stringify(Object.fromEntries(fields2.map((field3) => [field3, object6[field3] ?? null]))));
20395
20700
  }
20396
20701
  function isSupersededSettingsProjection(agent, configPath, source) {
20397
20702
  if (agent === "sumi")
@@ -20436,6 +20741,14 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
20436
20741
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
20437
20742
  return sha2563;
20438
20743
  }
20744
+ if (source.hashMode === "claude-marketplace-entry-v1") {
20745
+ if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256) || changes?.has(source.path) || !claudeMarketplaceEntrySourceValid(source))
20746
+ throw new Error("Claude marketplace entry witnesses require an exact reviewed entry without synthetic changes");
20747
+ const sha2563 = captureClaudeMarketplaceEntry(source.path, source.marketplace, source.plugin, budget2).sha256;
20748
+ if (sha2563 !== source.sha256)
20749
+ throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
20750
+ return sha2563;
20751
+ }
20439
20752
  if (source.hashMode === "claude-plugin-registry") {
20440
20753
  if (source.format !== undefined || source.fields !== undefined || !source.managedPlugins || changes?.has(source.path))
20441
20754
  throw new Error("Managed plugin registry witnesses require verified native registrations");
@@ -20473,7 +20786,7 @@ function absentCodexSkillRoots(binding) {
20473
20786
  throw new Error("Invalid disabled Codex discovery proof");
20474
20787
  if (binding.method !== "reviewed" || controls.every((control) => lstatSync25(control.pluginParent, { throwIfNoEntry: false })))
20475
20788
  return [];
20476
- const cache = dirname19(dirname19(controls[0].pluginParent)), config = join32(dirname19(dirname19(cache)), "config.toml");
20789
+ const cache = dirname20(dirname20(controls[0].pluginParent)), config = join32(dirname20(dirname20(cache)), "config.toml");
20477
20790
  const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
20478
20791
  if (!source || projected(source) !== source.sha256)
20479
20792
  throw new Error("Missing current disabled Codex configuration witness");
@@ -20481,7 +20794,7 @@ function absentCodexSkillRoots(binding) {
20481
20794
  return [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && absentDisabledCodexPluginParent(cache, parent, controls, rules));
20482
20795
  }
20483
20796
  function codexDirectHookSelects(cache, root2) {
20484
- const codex = dirname19(dirname19(cache)), config = Bun.TOML.parse(read2(join32(codex, "config.toml")) ?? "");
20797
+ const codex = dirname20(dirname20(cache)), config = Bun.TOML.parse(read2(join32(codex, "config.toml")) ?? "");
20485
20798
  const hooks = read2(join32(codex, "hooks.json"));
20486
20799
  if (hooks !== null)
20487
20800
  hashNativeJsonControls(hooks);
@@ -20494,7 +20807,7 @@ function retiredCodexRoots(binding) {
20494
20807
  const controls = binding.codexDisabledPluginSkills;
20495
20808
  if (binding.agent !== "codex" || binding.method !== "reviewed" || !Array.isArray(controls) || !controls.length || !Array.isArray(proof.roots) || proof.roots.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.roots).size !== proof.roots.length || !Array.isArray(proof.directories) || proof.parents !== undefined && (!Array.isArray(proof.parents) || proof.parents.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.parents).size !== proof.parents.length) || !proof.roots.length && !proof.parents?.length)
20496
20809
  throw new Error("Invalid retired Codex materialization proof");
20497
- const cache = dirname19(dirname19(controls[0].pluginParent)), config = join32(dirname19(dirname19(cache)), "config.toml");
20810
+ const cache = dirname20(dirname20(controls[0].pluginParent)), config = join32(dirname20(dirname20(cache)), "config.toml");
20498
20811
  const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
20499
20812
  if (!source || projected(source) !== source.sha256)
20500
20813
  throw new Error("Missing current retired Codex configuration witness");
@@ -20511,8 +20824,8 @@ function retiredCodexRoots(binding) {
20511
20824
  function captureRetiredCodexDiscovery(binding, controls, rules) {
20512
20825
  if (binding.agent !== "codex" || binding.method !== "reviewed" || !controls.length)
20513
20826
  return binding;
20514
- const cache = dirname19(dirname19(controls[0].pluginParent));
20515
- const candidates = binding.sources.filter((source) => source.path.endsWith(sep8 + ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined && [undefined, "bytes"].includes(source.hashMode)).map((source) => dirname19(dirname19(source.path)));
20827
+ const cache = dirname20(dirname20(controls[0].pluginParent));
20828
+ const candidates = binding.sources.filter((source) => source.path.endsWith(sep8 + ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined && [undefined, "bytes"].includes(source.hashMode)).map((source) => dirname20(dirname20(source.path)));
20516
20829
  const roots = [...new Set(candidates)].filter((root2) => !codexDirectHookSelects(cache, root2) && reviewedRetiredCodexRoot(cache, root2, controls, rules, (path) => read2(path), binding.codexRetiredMaterializations?.roots.includes(root2) ?? false)).sort();
20517
20830
  const parents3 = reviewedCodexPluginCapabilitiesUnchanged(cache, controls, (path) => read2(path), rules) ? [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && reviewedDisabledCodexPluginParent(cache, parent, controls, rules)).sort() : [];
20518
20831
  const { codexRetiredMaterializations: previous, ...current } = binding;
@@ -20541,7 +20854,7 @@ function absentRetiredClaudeRoots(binding) {
20541
20854
  safe4(root2);
20542
20855
  if (lstatSync25(root2, { throwIfNoEntry: false }))
20543
20856
  continue;
20544
- const claude = dirname19(dirname19(cache)), settings = join32(claude, "settings.json"), registry = join32(claude, "plugins/installed_plugins.json");
20857
+ const claude = dirname20(dirname20(cache)), settings = join32(claude, "settings.json"), registry = join32(claude, "plugins/installed_plugins.json");
20545
20858
  const full = (path) => binding.sources.find((item) => item.path === path && item.sha256 !== null && item.format === undefined && item.fields === undefined);
20546
20859
  const configSource = full(settings), registrySource = full(registry);
20547
20860
  if (!configSource || !registrySource || projected(configSource) !== configSource.sha256 || projected(registrySource) !== registrySource.sha256)
@@ -20558,7 +20871,7 @@ function absentRetiredClaudeRoots(binding) {
20558
20871
  if (!Array.isArray(rows) || rows.length !== 1 || rows[0]?.scope !== "user" || typeof rows[0]?.installPath !== "string")
20559
20872
  continue;
20560
20873
  const active = rows[0].installPath;
20561
- if (rows[0].version !== basename10(active) || resolve18(active) !== active || dirname19(active) !== dirname19(root2) || active === root2 || !lstatSync25(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync11(active) !== active)
20874
+ if (rows[0].version !== basename11(active) || resolve20(active) !== active || dirname20(active) !== dirname20(root2) || active === root2 || !lstatSync25(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync11(active) !== active)
20562
20875
  continue;
20563
20876
  const activeManifest = full(join32(active, ".claude-plugin/plugin.json"));
20564
20877
  if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
@@ -20609,7 +20922,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
20609
20922
  assertMarketplaceBinding(binding, source);
20610
20923
  if (source.hashMode === "claude-plugin-registry" && binding.agent !== "claude")
20611
20924
  throw new Error("Managed Claude registry witnesses cannot apply to another agent");
20612
- if (source.format !== undefined && (!["json", "toml", "yaml"].includes(source.format) || !Array.isArray(source.fields) || !source.fields.length || source.fields.length > 64 || source.fields.some((field2) => typeof field2 !== "string" || !field2)))
20925
+ if (source.format !== undefined && (!["json", "toml", "yaml"].includes(source.format) || !Array.isArray(source.fields) || !source.fields.length || source.fields.length > 64 || source.fields.some((field3) => typeof field3 !== "string" || !field3)))
20613
20926
  throw new Error("Invalid native discovery projection");
20614
20927
  if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
20615
20928
  throw new Error("Invalid native discovery digest");
@@ -20637,15 +20950,15 @@ function codexInstallationRoots(binding) {
20637
20950
  const proof = binding.codexInstallationInputs;
20638
20951
  if (proof === undefined)
20639
20952
  return [];
20640
- 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)
20953
+ if (binding.agent !== "codex" || binding.method !== "reviewed" || !supportsCodexNativeCapability(proof.version, "installed-plugin-review") || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
20641
20954
  throw new Error("Invalid native installation input proof");
20642
- const cache = dirname19(dirname19(proof.plugins[0].pluginParent));
20643
- const config = join32(dirname19(dirname19(cache)), "config.toml"), home = dirname19(dirname19(dirname19(cache)));
20955
+ const cache = dirname20(dirname20(proof.plugins[0].pluginParent));
20956
+ const config = join32(dirname20(dirname20(cache)), "config.toml"), home = dirname20(dirname20(dirname20(cache)));
20644
20957
  const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
20645
20958
  const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join32(home, path));
20646
- if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep8) || input.sourceRoot.startsWith(root2 + sep8)) || binding.sources.some((source) => basename10(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep8)))))
20959
+ if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep8) || input.sourceRoot.startsWith(root2 + sep8)) || binding.sources.some((source) => basename11(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep8)))))
20647
20960
  throw new Error("Native installation input overlaps configuration or loading roots");
20648
- if (proof.plugins.some((input) => dirname19(dirname19(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join32(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
20961
+ if (proof.plugins.some((input) => dirname20(dirname20(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join32(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
20649
20962
  throw new Error("Missing native installation input source witness");
20650
20963
  return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
20651
20964
  const text2 = read2(path);
@@ -20655,16 +20968,18 @@ function codexInstallationRoots(binding) {
20655
20968
  });
20656
20969
  }
20657
20970
  function assertMarketplaceBinding(binding, source) {
20658
- if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename10(source.path) !== "plugin.json" || !source.path.endsWith(join32(sep8, ".claude-plugin", "plugin.json"))))
20971
+ if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename11(source.path) !== "plugin.json" || !source.path.endsWith(join32(sep8, ".claude-plugin", "plugin.json"))))
20659
20972
  throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
20660
- if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename10(source.path) !== "sumi.json"))
20973
+ if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename11(source.path) !== "sumi.json"))
20661
20974
  throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
20662
- if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename10(source.path) !== "config.toml"))
20975
+ if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename11(source.path) !== "config.toml"))
20663
20976
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
20664
- if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename10(source.path) !== "settings.json"))
20977
+ if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename11(source.path) !== "settings.json"))
20665
20978
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
20666
20979
  if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
20667
20980
  throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
20981
+ if (source.hashMode === "claude-marketplace-entry-v1" && (binding.agent !== "claude" || binding.method !== "reviewed" || !claudeMarketplaceEntrySourceValid(source)))
20982
+ throw new Error("Claude marketplace entry witnesses require explicit reviewed Claude discovery of one named entry");
20668
20983
  }
20669
20984
  function rebindAgentDiscovery(binding, changes) {
20670
20985
  const budget2 = discoveryByteBudget();
@@ -20697,10 +21012,10 @@ function captureDiscoveryPathSources(paths) {
20697
21012
  const budget2 = discoveryByteBudget();
20698
21013
  return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget2) }));
20699
21014
  }
20700
- function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18, reviewed) {
20701
- const homeConfigPath = canonical7(agentDiscoveryConfigPath(home, agent));
21015
+ function assertProjectDiscovery(agent, directories, home, canonical8 = resolve20, reviewed) {
21016
+ const homeConfigPath = canonical8(agentDiscoveryConfigPath(home, agent));
20702
21017
  for (const directory of directories) {
20703
- const isHome = resolve18(directory) === resolve18(home);
21018
+ const isHome = resolve20(directory) === resolve20(home);
20704
21019
  if (agent === "hermes") {
20705
21020
  assertHermesEnvironment(home);
20706
21021
  continue;
@@ -20709,7 +21024,7 @@ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18
20709
21024
  continue;
20710
21025
  const paths = agent === "claude" ? [...isHome ? [] : [".claude/settings.json"], ".claude/settings.local.json"] : agent === "codex" ? [".codex/config.toml"] : agent === "gemini" ? [".gemini/settings.json"] : agent === "opencode" ? ["opencode.json", "opencode.jsonc", ".opencode/opencode.json", ".opencode/opencode.jsonc"] : agent === "sumi" ? ["sumi.json", "sumi.jsonc", ".sumi/sumi.json", ".sumi/sumi.jsonc"] : [".cursor/hooks.json"];
20711
21026
  for (const suffix of paths) {
20712
- const path = canonical7(join32(directory, suffix)), raw = read2(path);
21027
+ const path = canonical8(join32(directory, suffix)), raw = read2(path);
20713
21028
  if (raw === null)
20714
21029
  continue;
20715
21030
  const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
@@ -20725,7 +21040,7 @@ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18
20725
21040
  const managed = reviewed.sources.flatMap((source) => source.hashMode === "claude-plugin-registry" ? source.managedPlugins ?? [] : []);
20726
21041
  if (exact && projected(exact) === exact.sha256 && managed.length) {
20727
21042
  const bindings = managed.map((item) => readPluginBinding(item.storeRoot, item.bindingId));
20728
- admittedPlugins = Object.entries(config.enabledPlugins).every(([id, enabled]) => typeof enabled === "boolean" && bindings.some((binding) => binding.target.pluginId === id && binding.target.registrations.some((scope) => scope.scope === "project" && scope.projectPath === canonical7(directory))));
21043
+ admittedPlugins = Object.entries(config.enabledPlugins).every(([id, enabled]) => typeof enabled === "boolean" && bindings.some((binding) => binding.target.pluginId === id && binding.target.registrations.some((scope) => scope.scope === "project" && scope.projectPath === canonical8(directory))));
20729
21044
  }
20730
21045
  }
20731
21046
  if (keys.some((key) => config[key] !== undefined && !(key === "enabledPlugins" && admittedPlugins)) || config.disableAllHooks === true || config.disableBundledSkills === false || config.hooksConfig?.enabled === false || config.permission?.skill !== undefined || config.permissions?.deny?.some((rule) => typeof rule === "string" && /^Skill(?:\(|$)/.test(rule)))
@@ -20733,13 +21048,13 @@ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18
20733
21048
  }
20734
21049
  if (agent === "sumi")
20735
21050
  for (const name of ["plugin", "plugins"]) {
20736
- const path = canonical7(join32(directory, ".sumi", name));
21051
+ const path = canonical8(join32(directory, ".sumi", name));
20737
21052
  safe4(path);
20738
21053
  if (lstatSync25(path, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(path).length)
20739
21054
  throw new Error(`NATIVE_SKILL_DRIFT: project Sumi plugins require a dedicated discovery review: ${path}`);
20740
21055
  }
20741
21056
  if (agent === "claude") {
20742
- const commands = canonical7(join32(directory, ".claude/commands"));
21057
+ const commands = canonical8(join32(directory, ".claude/commands"));
20743
21058
  safe4(commands);
20744
21059
  if (lstatSync25(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
20745
21060
  throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
@@ -20751,11 +21066,37 @@ function agentDiscoveryConfigPath(home, agent) {
20751
21066
  return sumiConfigPath(home);
20752
21067
  return join32(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
20753
21068
  }
21069
+ function verifyAutomaticDiscoveryClosure(binding, options) {
21070
+ const command = DISCOVERY_RUNTIME_COMMANDS[binding.agent], recorded = options.executable;
21071
+ const onPath = discoveryExecutable(binding.agent);
21072
+ let runtime, gap;
21073
+ if (recorded) {
21074
+ runtime = recordedDiscoveryExecutable(binding.agent, recorded);
21075
+ if (runtime && runtime.target !== recorded.target)
21076
+ throw new Error(`${options.change}: the recorded ${binding.agent} executable now resolves to ${quotedPath(runtime.target)}, not the reviewed ${quotedPath(recorded.target)}`);
21077
+ if (onPath && onPath.target !== recorded.target)
21078
+ throw new Error(`${options.change}: the "${command}" on this process's PATH resolves to ${quotedPath(onPath.target)}, not the reviewed ${quotedPath(recorded.target)}`);
21079
+ if (!runtime)
21080
+ gap = `the recorded executable ${quotedPath(recorded.path)} no longer resolves. Restore it, or review the change with skills hook install`;
21081
+ } else {
21082
+ runtime = onPath;
21083
+ if (!runtime)
21084
+ gap = "it is not on this process's PATH, and the reviewed policy has no recorded executable path. Run with the PATH used for the review, or run skills hook install there to record it";
21085
+ }
21086
+ const current = resolveAgentDiscovery({ home: options.home, agent: binding.agent, canonical: options.canonical, executable: runtime });
21087
+ if (JSON.stringify(current) === JSON.stringify(binding))
21088
+ return;
21089
+ const reviewedWithRuntime = (binding.builtinNames?.length ?? 0) > 0 || binding.sources.some((source) => basename11(source.path) === "package.json");
21090
+ const otherwiseCurrent = current.method === binding.method && JSON.stringify(current.roots) === JSON.stringify(binding.roots) && JSON.stringify(current.directories) === JSON.stringify(binding.directories) && current.sources.every((source) => binding.sources.some((saved) => isDeepStrictEqual7(saved, source)));
21091
+ if (command && gap && runtime === null && reviewedWithRuntime && otherwiseCurrent)
21092
+ throw discoveryRootUnresolved(binding.agent, command, gap);
21093
+ throw new Error(options.change);
21094
+ }
20754
21095
  function resolveAgentDiscovery(options) {
20755
- const canonical7 = options.canonical ?? resolve18, home = resolve18(options.home), agent = options.agent;
21096
+ const canonical8 = options.canonical ?? resolve20, home = resolve20(options.home), agent = options.agent;
20756
21097
  const sources = [], roots = new Set, builtinNames = [];
20757
21098
  const witness = (path, format, fields2, hashMode) => {
20758
- const source = { path: canonical7(path), sha256: null, ...format ? { format, fields: fields2 } : {}, ...hashMode ? { hashMode } : {} };
21099
+ const source = { path: canonical8(path), sha256: null, ...format ? { format, fields: fields2 } : {}, ...hashMode ? { hashMode } : {} };
20759
21100
  source.sha256 = projected(source);
20760
21101
  sources.push(source);
20761
21102
  return read2(source.path);
@@ -20777,13 +21118,13 @@ function resolveAgentDiscovery(options) {
20777
21118
  unresolved("JSONC configuration requires a supported format adapter");
20778
21119
  for (const root2 of [directory, join32(home, ".claude"), join32(home, ".agents")])
20779
21120
  for (const name of ["skill", "skills"])
20780
- roots.add(canonical7(join32(root2, name)));
21121
+ roots.add(canonical8(join32(root2, name)));
20781
21122
  if (config.skills !== undefined && (!Array.isArray(config.skills) || config.skills.some((path) => typeof path !== "string")))
20782
21123
  unresolved("skills must be a string array");
20783
21124
  for (const value of config.skills ?? []) {
20784
- if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(isAbsolute19(value) || value.startsWith("~/")))
21125
+ if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(isAbsolute21(value) || value.startsWith("~/")))
20785
21126
  unresolved("remote or relative skill sources need a dedicated discovery adapter");
20786
- roots.add(canonical7(value.startsWith("~/") ? join32(home, value.slice(2)) : value));
21127
+ roots.add(canonical8(value.startsWith("~/") ? join32(home, value.slice(2)) : value));
20787
21128
  }
20788
21129
  for (const key of ["plugin", "plugins"])
20789
21130
  if (config[key] !== undefined && !Array.isArray(config[key]))
@@ -20822,23 +21163,23 @@ function resolveAgentDiscovery(options) {
20822
21163
  continue;
20823
21164
  if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
20824
21165
  unresolved("user-specific tilde expansion requires a separate discovery adapter");
20825
- const path = normalized === "~" ? home : normalized.startsWith("~/") ? join32(home, normalized.slice(2)) : resolve18(home, ".hermes", normalized);
21166
+ const path = normalized === "~" ? home : normalized.startsWith("~/") ? join32(home, normalized.slice(2)) : resolve20(home, ".hermes", normalized);
20826
21167
  safe4(path);
20827
21168
  roots.add(path);
20828
21169
  }
20829
21170
  }
20830
21171
  if (agent === "gemini") {
20831
- const executable2 = Bun.which("gemini");
21172
+ const executable2 = options.executable === undefined ? discoveryExecutable(agent) : options.executable;
20832
21173
  if (executable2) {
20833
21174
  let packageRoot;
20834
- for (let directory = dirname19(realpathSync11(executable2)), depth = 0;depth < 8; directory = dirname19(directory), depth++) {
21175
+ for (let directory = dirname20(executable2.target), depth = 0;depth < 8; directory = dirname20(directory), depth++) {
20835
21176
  const path = join32(directory, "package.json"), raw = read2(path);
20836
21177
  if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
20837
21178
  packageRoot = directory;
20838
21179
  witness(path);
20839
21180
  break;
20840
21181
  }
20841
- if (dirname19(directory) === directory)
21182
+ if (dirname20(directory) === directory)
20842
21183
  break;
20843
21184
  }
20844
21185
  if (!packageRoot)
@@ -20861,11 +21202,11 @@ function resolveAgentDiscovery(options) {
20861
21202
  if (retained.agent !== agent || retained.method !== "reviewed")
20862
21203
  unresolved("invalid retained review");
20863
21204
  verifyAgentDiscovery(retained);
20864
- if (!retained.sources.some((source) => source.path === canonical7(configPath) && source.format === undefined && source.fields === undefined))
21205
+ if (!retained.sources.some((source) => source.path === canonical8(configPath) && source.format === undefined && source.fields === undefined))
20865
21206
  unresolved("retained review is missing the full agent configuration source");
20866
- if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
21207
+ if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical8(configPath)))
20867
21208
  unresolved("retained settings witness names another configuration source");
20868
- const covered = supersedesSettingsProjection(agent, retained.sources, canonical7(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical7(configPath), source)) : sources;
21209
+ const covered = supersedesSettingsProjection(agent, retained.sources, canonical8(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical8(configPath), source)) : sources;
20869
21210
  if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual7(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
20870
21211
  unresolved("retained review is missing current runtime discovery coverage");
20871
21212
  return retained;
@@ -20877,15 +21218,15 @@ function resolveAgentDiscovery(options) {
20877
21218
  if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
20878
21219
  throw new Error("Explicit discovery reviews require full source-file hashes");
20879
21220
  verifyAgentDiscovery(supplied);
20880
- if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
21221
+ if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical8(configPath)))
20881
21222
  throw new Error("Semantic settings witnesses must name the configured agent configuration source");
20882
- if (!review.sources.some((source) => source.path === canonical7(configPath)))
21223
+ if (!review.sources.some((source) => source.path === canonical8(configPath)))
20883
21224
  throw new Error("Discovery review must include the agent configuration source");
20884
- const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical7(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical7(configPath), source)) : sources;
21225
+ const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical8(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical8(configPath), source)) : sources;
20885
21226
  const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
20886
- if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename10(source.path) !== "plugin.json" || !source.path.endsWith(join32(sep8, ".claude-plugin", "plugin.json")))
21227
+ if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename11(source.path) !== "plugin.json" || !source.path.endsWith(join32(sep8, ".claude-plugin", "plugin.json")))
20887
21228
  return source;
20888
- const pluginRoot = dirname19(dirname19(source.path)), hooksPath = join32(pluginRoot, "hooks/hooks.json");
21229
+ const pluginRoot = dirname20(dirname20(source.path)), hooksPath = join32(pluginRoot, "hooks/hooks.json");
20889
21230
  const hookStat = lstatSync25(hooksPath, { throwIfNoEntry: false });
20890
21231
  const text2 = read2(source.path);
20891
21232
  if (text2 === null)
@@ -20900,13 +21241,13 @@ function resolveAgentDiscovery(options) {
20900
21241
  if (manifest.hooks !== undefined) {
20901
21242
  if (typeof manifest.hooks !== "string")
20902
21243
  throw new Error("Reviewed Claude plugin manifest has an unsupported hooks target");
20903
- const declaredHookPath = resolve18(pluginRoot, manifest.hooks);
21244
+ const declaredHookPath = resolve20(pluginRoot, manifest.hooks);
20904
21245
  if (!declaredHookPath.startsWith(pluginRoot + sep8))
20905
21246
  throw new Error("Reviewed Claude plugin hooks target escapes its root");
20906
21247
  reviewedHookPaths.add(declaredHookPath);
20907
21248
  }
20908
21249
  for (const hooksFile of reviewedHookPaths)
20909
- if (!review.sources.some((hook) => hook.path === canonical7(hooksFile) && hook.sha256 !== null && hook.format === undefined && hook.fields === undefined && hook.managedPlugins === undefined && (hook.hashMode === undefined || hook.hashMode === "bytes")))
21250
+ if (!review.sources.some((hook) => hook.path === canonical8(hooksFile) && hook.sha256 !== null && hook.format === undefined && hook.fields === undefined && hook.managedPlugins === undefined && (hook.hashMode === undefined || hook.hashMode === "bytes")))
20910
21251
  throw new Error("Reviewed Claude plugin manifest requires separately reviewed exact hook file sources");
20911
21252
  return { path: source.path, sha256: manifestSha256, hashMode: "claude-plugin-manifest-v1" };
20912
21253
  });
@@ -20915,7 +21256,7 @@ function resolveAgentDiscovery(options) {
20915
21256
  return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
20916
21257
  }
20917
21258
  function plugin(root2) {
20918
- root2 = canonical7(root2);
21259
+ root2 = canonical8(root2);
20919
21260
  safe4(root2);
20920
21261
  const manifestPath = join32(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
20921
21262
  const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
@@ -20938,16 +21279,16 @@ function resolveAgentDiscovery(options) {
20938
21279
  if (agent === "claude")
20939
21280
  roots.add(join32(root2, "skills"));
20940
21281
  for (const value of declared) {
20941
- const path = resolve18(root2, value);
21282
+ const path = resolve20(root2, value);
20942
21283
  if (path !== root2 && !path.startsWith(root2 + sep8))
20943
21284
  unresolved("plugin skill path escapes its root");
20944
- roots.add(path.endsWith(`${sep8}SKILL.md`) ? dirname19(path) : path);
21285
+ roots.add(path.endsWith(`${sep8}SKILL.md`) ? dirname20(path) : path);
20945
21286
  }
20946
21287
  }
20947
21288
  if (agent === "claude") {
20948
21289
  const registrations = witness(join32(home, ".claude/plugins/installed_plugins.json"));
20949
21290
  const installed = registrations === null ? {} : parseConfig(registrations, join32(home, ".claude/plugins/installed_plugins.json")).plugins;
20950
- const commands = canonical7(join32(home, ".claude/commands"));
21291
+ const commands = canonical8(join32(home, ".claude/commands"));
20951
21292
  safe4(commands);
20952
21293
  if (lstatSync25(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
20953
21294
  unresolved("legacy home command discovery requires a dedicated format adapter");
@@ -20969,7 +21310,7 @@ function resolveAgentDiscovery(options) {
20969
21310
  unresolved("unsupported plugin enablement");
20970
21311
  const split = id.lastIndexOf("@"), name = id.slice(0, split), marketplace = id.slice(split + 1);
20971
21312
  const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
20972
- if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute19(market.source))
21313
+ if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute21(market.source))
20973
21314
  unresolved("enabled plugin marketplace is not an explicit local source");
20974
21315
  const catalogText = witness(join32(market.source, ".agents/plugins/marketplace.json"));
20975
21316
  if (catalogText === null)
@@ -20977,7 +21318,7 @@ function resolveAgentDiscovery(options) {
20977
21318
  const catalog = parseConfig(catalogText, join32(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
20978
21319
  if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
20979
21320
  unresolved("plugin catalog source is missing or ambiguous");
20980
- plugin(resolve18(market.source, matches[0].source.path));
21321
+ plugin(resolve20(market.source, matches[0].source.path));
20981
21322
  }
20982
21323
  } else if (agent === "opencode") {
20983
21324
  if (witness(join32(home, ".config/opencode/opencode.jsonc")) !== null || witness(join32(home, ".config/opencode/config.json")) !== null)
@@ -20985,7 +21326,7 @@ function resolveAgentDiscovery(options) {
20985
21326
  if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
20986
21327
  unresolved("external plugin or remote skill source");
20987
21328
  for (const path of config.skills?.paths ?? []) {
20988
- if (typeof path !== "string" || !isAbsolute19(path))
21329
+ if (typeof path !== "string" || !isAbsolute21(path))
20989
21330
  unresolved("relative or malformed added skill root");
20990
21331
  roots.add(path);
20991
21332
  }
@@ -20994,7 +21335,7 @@ function resolveAgentDiscovery(options) {
20994
21335
  if (lstatSync25(localPlugins, { throwIfNoEntry: false }) && readdirSync10(localPlugins).some((name) => name !== "skills-cli.js"))
20995
21336
  unresolved("additional local plugin hooks require review");
20996
21337
  } else if (agent === "sumi") {
20997
- if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical7(configPath) && source.path !== canonical7(join32(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
21338
+ if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical8(configPath) && source.path !== canonical8(join32(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
20998
21339
  unresolved("external or foreign local plugins require review");
20999
21340
  } else if (agent === "hermes") {
21000
21341
  for (const directory of [join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")]) {
@@ -21024,13 +21365,13 @@ function resolveAgentDiscovery(options) {
21024
21365
  roots.add(join32(root2, "skills"));
21025
21366
  }
21026
21367
  }
21027
- return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")].map((path) => canonical7(path))) } : {} };
21368
+ return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")].map((path) => canonical8(path))) } : {} };
21028
21369
  }
21029
21370
 
21030
21371
  // src/lib/authoring-path.ts
21031
21372
  var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
21032
21373
  function pathParts(path) {
21033
- return resolve19(path).split(sep9).filter(Boolean);
21374
+ return resolve21(path).split(sep9).filter(Boolean);
21034
21375
  }
21035
21376
  function nativeRootMatch(path) {
21036
21377
  const parts = pathParts(path);
@@ -21044,16 +21385,16 @@ function nativeRootMatch(path) {
21044
21385
  return;
21045
21386
  }
21046
21387
  function physicalPath(path) {
21047
- let cursor = resolve19(path);
21388
+ let cursor = resolve21(path);
21048
21389
  const suffix = [];
21049
21390
  while (!lstatSync26(cursor, { throwIfNoEntry: false })) {
21050
- suffix.unshift(basename11(cursor));
21051
- const parent = dirname20(cursor);
21391
+ suffix.unshift(basename12(cursor));
21392
+ const parent = dirname21(cursor);
21052
21393
  if (parent === cursor)
21053
- return resolve19(path);
21394
+ return resolve21(path);
21054
21395
  cursor = parent;
21055
21396
  }
21056
- return resolve19(realpathSync12(cursor), ...suffix);
21397
+ return resolve21(realpathSync12(cursor), ...suffix);
21057
21398
  }
21058
21399
  function contains(root2, path) {
21059
21400
  return path === root2 || path.startsWith(root2 + sep9);
@@ -21061,7 +21402,7 @@ function contains(root2, path) {
21061
21402
  function configuredRoots(home, explicitHome) {
21062
21403
  const homeData = join33(home, ".hasna", "skills");
21063
21404
  const homePolicy = readManagedSkillPolicySnapshot(homeData);
21064
- const configuredPolicy = !explicitHome && resolve19(getDataDirReadOnly()) !== resolve19(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
21405
+ const configuredPolicy = !explicitHome && resolve21(getDataDirReadOnly()) !== resolve21(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
21065
21406
  const policy = homePolicy ?? configuredPolicy;
21066
21407
  const bridge = policy?.value.bridge;
21067
21408
  if (bridge && (typeof bridge.home !== "string" || physicalPath(bridge.home) !== physicalPath(home))) {
@@ -21100,22 +21441,22 @@ function configuredRoots(home, explicitHome) {
21100
21441
  function assertPortableAuthoringPath(path, options = {}) {
21101
21442
  if (typeof path !== "string" || !path || path.includes("\x00"))
21102
21443
  throw new Error("Invalid portable authoring path");
21103
- const lexical = resolve19(path);
21444
+ const lexical = resolve21(path);
21104
21445
  const physical = physicalPath(lexical);
21105
21446
  const candidates = [lexical, physical];
21106
21447
  let native = candidates.map(nativeRootMatch).find(Boolean);
21107
- const home = resolve19(options.homeDir ?? homedir6());
21448
+ const home = resolve21(options.homeDir ?? homedir6());
21108
21449
  const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join33(home, ...parts));
21109
21450
  defaults.push(join33(home, ".codex/plugins/cache"), join33(home, ".claude/plugins/cache"), join33(home, ".claude/plugins/synced"), join33(home, ".gemini/extensions"));
21110
21451
  for (const root2 of defaults) {
21111
- if (candidates.some((candidate2) => contains(resolve19(root2), candidate2) || contains(physicalPath(root2), candidate2)))
21452
+ if (candidates.some((candidate2) => contains(resolve21(root2), candidate2) || contains(physicalPath(root2), candidate2)))
21112
21453
  native ??= root2;
21113
21454
  }
21114
21455
  if (native) {
21115
21456
  throw new Error(`Refusing portable Skills authoring below native discovery root '${native}': ${lexical}. Set HASNA_SKILLS_DIR to a private authoring corpus outside native roots.`);
21116
21457
  }
21117
21458
  for (const root2 of configuredRoots(home, options.homeDir !== undefined)) {
21118
- if (candidates.some((candidate2) => contains(resolve19(root2), candidate2) || contains(physicalPath(root2), candidate2))) {
21459
+ if (candidates.some((candidate2) => contains(resolve21(root2), candidate2) || contains(physicalPath(root2), candidate2))) {
21119
21460
  throw new Error(`Refusing portable Skills authoring below configured native discovery root: ${lexical}`);
21120
21461
  }
21121
21462
  }
@@ -21263,20 +21604,20 @@ function isSkillCandidate(dir) {
21263
21604
  return existsSync14(join34(dir, "SKILL.md")) || existsSync14(join34(dir, "skill.json")) || existsSync14(join34(dir, "package.json"));
21264
21605
  }
21265
21606
  function physicalImportPath(path) {
21266
- let ancestor = resolve20(path);
21607
+ let ancestor = resolve22(path);
21267
21608
  const suffix = [];
21268
21609
  while (!existsSync14(ancestor)) {
21269
- suffix.unshift(basename12(ancestor));
21270
- ancestor = dirname21(ancestor);
21610
+ suffix.unshift(basename13(ancestor));
21611
+ ancestor = dirname22(ancestor);
21271
21612
  }
21272
21613
  return join34(realpathSync13(ancestor), ...suffix);
21273
21614
  }
21274
21615
  function assertDisjointImportPaths(source, destination) {
21275
21616
  const sourcePath = realpathSync13(source);
21276
21617
  const destinationPath = physicalImportPath(destination);
21277
- const contains2 = (parent, child) => {
21278
- const path = relative7(parent, child);
21279
- return path === "" || !isAbsolute20(path) && path !== ".." && !path.startsWith(`..${sep10}`);
21618
+ const contains2 = (parent, child2) => {
21619
+ const path = relative7(parent, child2);
21620
+ return path === "" || !isAbsolute22(path) && path !== ".." && !path.startsWith(`..${sep10}`);
21280
21621
  };
21281
21622
  if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
21282
21623
  throw new Error("Source and destination skill directories must not overlap");
@@ -21291,8 +21632,8 @@ function portPortableSkill(sourcePath, options = {}) {
21291
21632
  const explicitName = options.name != null;
21292
21633
  const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
21293
21634
  if (isOfficialSkillName(skillName) && !options.allowShadow) {
21294
- const sourceSlug = safeNormalizeName(basename12(absoluteSource));
21295
- const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename12(absoluteSource)}')` : ""} matches a bundled official skill.`;
21635
+ const sourceSlug = safeNormalizeName(basename13(absoluteSource));
21636
+ const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename13(absoluteSource)}')` : ""} matches a bundled official skill.`;
21296
21637
  throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
21297
21638
  }
21298
21639
  const readOnlyDestination = join34(resolvePortableSkillsRoot(options, false), skillName);
@@ -21307,7 +21648,7 @@ function portPortableSkill(sourcePath, options = {}) {
21307
21648
  throw new Error(`Skill '${skillName}' already exists at ${destination}`);
21308
21649
  rmSync4(destination, { recursive: true, force: true });
21309
21650
  }
21310
- mkdirSync10(dirname21(destination), { recursive: true });
21651
+ mkdirSync10(dirname22(destination), { recursive: true });
21311
21652
  copySkillDirectory(absoluteSource, destination);
21312
21653
  const base = {
21313
21654
  ...inferred,
@@ -21557,7 +21898,7 @@ function safeIsDirectory(path) {
21557
21898
  }
21558
21899
  }
21559
21900
  function isSafeRelativePath2(value) {
21560
- if (!value.trim() || isAbsolute20(value))
21901
+ if (!value.trim() || isAbsolute22(value))
21561
21902
  return false;
21562
21903
  const normalized = normalize4(value).replace(/\\/g, "/");
21563
21904
  return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
@@ -21873,7 +22214,7 @@ function getAllTags(catalog) {
21873
22214
  return Array.from(tagSet).sort();
21874
22215
  }
21875
22216
  // src/lib/run-state.ts
21876
- import { createHash as createHash25, randomBytes } from "crypto";
22217
+ import { createHash as createHash26, randomBytes } from "crypto";
21877
22218
  import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync23, readdirSync as readdirSync13, statSync as statSync12, writeFileSync as writeFileSync10 } from "fs";
21878
22219
  import { extname, join as join36, relative as relative8 } from "path";
21879
22220
  function createSkillRun(params, targetDir = process.cwd()) {
@@ -22000,7 +22341,7 @@ function collectRunArtifacts(context) {
22000
22341
  artifacts.push({
22001
22342
  path: toProjectRelative(context.targetDir, path),
22002
22343
  mime: mimeForPath(path),
22003
- sha256: createHash25("sha256").update(bytes).digest("hex"),
22344
+ sha256: createHash26("sha256").update(bytes).digest("hex"),
22004
22345
  sizeBytes: stat2.size
22005
22346
  });
22006
22347
  }
@@ -25529,14 +25870,14 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
25529
25870
  const shape = zod.def.shape;
25530
25871
  const propValues = {};
25531
25872
  for (const key in shape) {
25532
- const field2 = shape[key]._zod;
25533
- if (field2.values) {
25873
+ const field3 = shape[key]._zod;
25874
+ if (field3.values) {
25534
25875
  if (!Object.prototype.hasOwnProperty.call(propValues, key)) {
25535
25876
  assignProp(propValues, key, new Set);
25536
25877
  }
25537
- for (const v of field2.values)
25878
+ for (const v of field3.values)
25538
25879
  propValues[key].add(v);
25539
- if (field2.optin !== undefined)
25880
+ if (field3.optin !== undefined)
25540
25881
  propValues[key].add(undefined);
25541
25882
  }
25542
25883
  }
@@ -26949,8 +27290,8 @@ function isRecursive(inst, stack) {
26949
27290
  return true;
26950
27291
  stack.add(inst);
26951
27292
  let result = false;
26952
- const check = (child) => {
26953
- if (!result && child?._zod && isRecursive(child, stack))
27293
+ const check = (child2) => {
27294
+ if (!result && child2?._zod && isRecursive(child2, stack))
26954
27295
  result = true;
26955
27296
  };
26956
27297
  const def = inst._zod.def;
@@ -37729,11 +38070,11 @@ var objectProcessor = (schema, ctx, _json, params) => {
37729
38070
  }
37730
38071
  const allKeys = new Set(Object.keys(shape));
37731
38072
  const requiredKeys = new Set([...allKeys].filter((key) => {
37732
- const field2 = def.shape[key];
38073
+ const field3 = def.shape[key];
37733
38074
  if (ctx.io === "input") {
37734
- return inputOptin(field2) === undefined;
38075
+ return inputOptin(field3) === undefined;
37735
38076
  } else {
37736
- return field2._zod.optout === undefined;
38077
+ return field3._zod.optout === undefined;
37737
38078
  }
37738
38079
  }));
37739
38080
  if (requiredKeys.size > 0) {
@@ -41419,7 +41760,7 @@ function clone2(value) {
41419
41760
  return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
41420
41761
  }
41421
41762
  // src/lib/canonical-json.ts
41422
- import { createHash as createHash26 } from "crypto";
41763
+ import { createHash as createHash27 } from "crypto";
41423
41764
  function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
41424
41765
  return canonicalJsonAtDepth(value, maximumBytes, 64);
41425
41766
  }
@@ -41463,7 +41804,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
41463
41804
  return visit3(value, 0);
41464
41805
  }
41465
41806
  function canonicalJsonSha256(value, maximumBytes) {
41466
- return createHash26("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
41807
+ return createHash27("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
41467
41808
  }
41468
41809
 
41469
41810
  // src/lib/remote-recurring-contract.ts
@@ -42287,17 +42628,17 @@ function runQuoteReceipt(value) {
42287
42628
  return value;
42288
42629
  }
42289
42630
  function parseRemoteRunQuote(value) {
42290
- const quote2 = object7(value);
42291
- runQuoteReceipt(quote2.quoteReceipt);
42292
- const pricing = object7(quote2.pricing);
42293
- if (typeof quote2.skill !== "string" || !/^[a-z0-9][a-z0-9-]*$/.test(quote2.skill))
42631
+ const quote3 = object7(value);
42632
+ runQuoteReceipt(quote3.quoteReceipt);
42633
+ const pricing = object7(quote3.pricing);
42634
+ if (typeof quote3.skill !== "string" || !/^[a-z0-9][a-z0-9-]*$/.test(quote3.skill))
42294
42635
  throw new Error("Invalid quoted skill");
42295
42636
  const costCents = creditCount(pricing.costCredits ?? pricing.costCents);
42296
42637
  if (pricing.costCredits !== undefined && pricing.costCents !== undefined && pricing.costCents !== costCents)
42297
42638
  throw new Error("Inconsistent quoted credit count");
42298
- if (quote2.availability && object7(quote2.availability).status !== "available")
42639
+ if (quote3.availability && object7(quote3.availability).status !== "available")
42299
42640
  throw new Error("This skill is unavailable for remote execution");
42300
- return { ...quote2, skill: quote2.skill, pricing: { ...pricing, costCredits: costCents, costCents, formattedCost: `${costCents} credits` } };
42641
+ return { ...quote3, skill: quote3.skill, pricing: { ...pricing, costCredits: costCents, costCents, formattedCost: `${costCents} credits` } };
42301
42642
  }
42302
42643
  function parseRemoteCreditPacks(value) {
42303
42644
  if (!Array.isArray(value))
@@ -42372,14 +42713,14 @@ function creditCheckoutFailure(value, status, key) {
42372
42713
  }
42373
42714
 
42374
42715
  // src/lib/remote-profile.ts
42375
- function customerNamePatch(input2, field2) {
42376
- if (!isRecord5(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field2))
42716
+ function customerNamePatch(input2, field3) {
42717
+ if (!isRecord5(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field3))
42377
42718
  throw new Error("Provide only the requested name field.");
42378
- const value = input2[field2];
42719
+ const value = input2[field3];
42379
42720
  if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
42380
42721
  throw new Error("Use a name of 1\u2013100 characters without control characters or newlines.");
42381
42722
  }
42382
- return { [field2]: value.trim() };
42723
+ return { [field3]: value.trim() };
42383
42724
  }
42384
42725
  function isRecord5(value) {
42385
42726
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -42424,10 +42765,10 @@ async function readQuoteUnavailableCode(response) {
42424
42765
  let timer;
42425
42766
  let deadlineExceeded = false;
42426
42767
  const expired = Symbol("quote body deadline");
42427
- const deadline = new Promise((resolve21) => {
42768
+ const deadline = new Promise((resolve23) => {
42428
42769
  timer = setTimeout(() => {
42429
42770
  deadlineExceeded = true;
42430
- resolve21(expired);
42771
+ resolve23(expired);
42431
42772
  reader.cancel().catch(() => {});
42432
42773
  }, 1500);
42433
42774
  });
@@ -42774,18 +43115,18 @@ class RemoteSkillsClient {
42774
43115
  const maximum = creditCount(approval.maxCredits ?? approval.maxCostCents ?? 0);
42775
43116
  if (approval.maxCostCents !== undefined && approval.maxCostCents !== maximum)
42776
43117
  throw new Error("Credit approval fields disagree");
42777
- const quote2 = approval.quoteReceipt === undefined ? await this.quoteRun(slug2, input2, args, approval.inputFiles?.length ? approval.inputFiles : undefined) : undefined;
42778
- if (quote2 && quote2.pricing.costCents > maximum)
42779
- throw new RemoteCreditApprovalError(quote2.pricing.costCents, maximum);
43118
+ const quote3 = approval.quoteReceipt === undefined ? await this.quoteRun(slug2, input2, args, approval.inputFiles?.length ? approval.inputFiles : undefined) : undefined;
43119
+ if (quote3 && quote3.pricing.costCents > maximum)
43120
+ throw new RemoteCreditApprovalError(quote3.pricing.costCents, maximum);
42780
43121
  const capabilities = await this.getCapabilities();
42781
43122
  if (!capabilities.capabilities.includes("runs.submit") || capabilities.billing?.boundedRunApproval !== true || capabilities.billing.unit !== "credits") {
42782
43123
  throw new Error("The configured server does not support bounded credit approval; refusing remote submission");
42783
43124
  }
42784
- return this.submitRun(quote2?.skill ?? slug2, input2, args, {
43125
+ return this.submitRun(quote3?.skill ?? slug2, input2, args, {
42785
43126
  ...approval,
42786
43127
  maxCredits: maximum,
42787
43128
  maxCostCents: maximum,
42788
- ...(quote2?.quoteReceipt ?? approval.quoteReceipt) === undefined ? {} : { quoteReceipt: quote2?.quoteReceipt ?? approval.quoteReceipt }
43129
+ ...(quote3?.quoteReceipt ?? approval.quoteReceipt) === undefined ? {} : { quoteReceipt: quote3?.quoteReceipt ?? approval.quoteReceipt }
42789
43130
  });
42790
43131
  }
42791
43132
  async getIdentity() {
@@ -43386,13 +43727,13 @@ class RemoteSkillsClient {
43386
43727
  return normalizeUpdatedSincePage(await response.json());
43387
43728
  }
43388
43729
  }
43389
- function requireOptionalString(record8, field2) {
43390
- if (record8[field2] === undefined)
43730
+ function requireOptionalString(record8, field3) {
43731
+ if (record8[field3] === undefined)
43391
43732
  return;
43392
- if (typeof record8[field2] !== "string") {
43393
- throw new Error(`Remote payload did not match the expected contract (${field2} must be a string when present)`);
43733
+ if (typeof record8[field3] !== "string") {
43734
+ throw new Error(`Remote payload did not match the expected contract (${field3} must be a string when present)`);
43394
43735
  }
43395
- return record8[field2];
43736
+ return record8[field3];
43396
43737
  }
43397
43738
  var INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
43398
43739
  function isVersionRecord(value) {
@@ -43730,7 +44071,7 @@ function recordScheduleRun(id, status, targetDir) {
43730
44071
  saveSchedules(data, targetDir);
43731
44072
  }
43732
44073
  // src/lib/revision.ts
43733
- import { createHash as createHash27 } from "crypto";
44074
+ import { createHash as createHash28 } from "crypto";
43734
44075
  var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
43735
44076
  function revisionIdOf(content) {
43736
44077
  const canonicalContent = {
@@ -43751,15 +44092,15 @@ function revisionIdOf(content) {
43751
44092
  canonicalContent.archiveReason = content.archiveReason ?? null;
43752
44093
  canonicalContent.replacementSlug = content.replacementSlug ?? null;
43753
44094
  }
43754
- const canonical7 = JSON.stringify(canonicalContent);
43755
- return createHash27("sha256").update(canonical7).digest("hex");
44095
+ const canonical8 = JSON.stringify(canonicalContent);
44096
+ return createHash28("sha256").update(canonical8).digest("hex");
43756
44097
  }
43757
44098
  function revisionIdOfRecord(record8) {
43758
44099
  return revisionIdOf(record8);
43759
44100
  }
43760
44101
  // src/lib/pull.ts
43761
44102
  import { existsSync as existsSync18, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync3, readFileSync as readFileSync26, renameSync as renameSync7, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "fs";
43762
- import { dirname as dirname22, join as join38 } from "path";
44103
+ import { dirname as dirname23, join as join38 } from "path";
43763
44104
 
43764
44105
  // src/lib/skill-bundles.ts
43765
44106
  import { createHmac, timingSafeEqual } from "crypto";
@@ -44101,7 +44442,7 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
44101
44442
  try {
44102
44443
  for (const entry of entries) {
44103
44444
  const destination = join38(staging, entry.path);
44104
- mkdirSync13(dirname22(destination), { recursive: true });
44445
+ mkdirSync13(dirname23(destination), { recursive: true });
44105
44446
  writeFileSync12(destination, entry.bytes, { mode: entry.mode });
44106
44447
  }
44107
44448
  writePullMarker(staging, {
@@ -44195,7 +44536,7 @@ function splitNameVersion(raw) {
44195
44536
  return version2 ? { name, version: version2 } : { name };
44196
44537
  }
44197
44538
  // src/lib/recurring-surface.ts
44198
- import { isAbsolute as isAbsolute21 } from "path";
44539
+ import { isAbsolute as isAbsolute23 } from "path";
44199
44540
 
44200
44541
  // src/lib/remote-invitation-recovery.ts
44201
44542
  class InvitationEmailInputError extends Error {
@@ -44293,7 +44634,7 @@ async function requestInvitationEmail(origin, action, input2) {
44293
44634
  }
44294
44635
 
44295
44636
  // src/lib/remote-private-publications.ts
44296
- import { createHash as createHash28 } from "crypto";
44637
+ import { createHash as createHash29 } from "crypto";
44297
44638
  var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
44298
44639
  var failures2 = {
44299
44640
  INVALID_REQUEST: [400, "The publication request is invalid."],
@@ -44340,7 +44681,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
44340
44681
  var record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
44341
44682
  var exact = (v, keys) => record9(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
44342
44683
  var date5 = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
44343
- var publicationSha256 = (bytes) => createHash28("sha256").update(bytes).digest("hex");
44684
+ var publicationSha256 = (bytes) => createHash29("sha256").update(bytes).digest("hex");
44344
44685
  function checkedPublicationDeclaration(value) {
44345
44686
  if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash3(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
44346
44687
  return bad();
@@ -44585,12 +44926,12 @@ class RemotePrivatePublicationsClient {
44585
44926
  previous = view;
44586
44927
  if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
44587
44928
  return view;
44588
- await new Promise((resolve21) => {
44929
+ await new Promise((resolve23) => {
44589
44930
  const timer = setTimeout(done, Math.min(1000, until - Date.now()));
44590
44931
  function done() {
44591
44932
  clearTimeout(timer);
44592
44933
  options.signal?.removeEventListener("abort", done);
44593
- resolve21();
44934
+ resolve23();
44594
44935
  }
44595
44936
  options.signal?.addEventListener("abort", done, { once: true });
44596
44937
  });
@@ -44854,7 +45195,7 @@ var stateSchema = exports_external.object({
44854
45195
  var uuid13 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
44855
45196
  var context = { userId: uuid13.optional(), membershipId: uuid13.optional() };
44856
45197
  var page2 = { limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() };
44857
- var directory = exports_external.string().max(4096).refine(isAbsolute21, "Use an absolute recovery directory");
45198
+ var directory = exports_external.string().max(4096).refine(isAbsolute23, "Use an absolute recovery directory");
44858
45199
  var email4 = exports_external.string().trim().toLowerCase().email().max(254);
44859
45200
  var code = exports_external.string().regex(/^\d{6}$/);
44860
45201
  var recurringSurfaceSchemas = {
@@ -45005,7 +45346,7 @@ function findSkillsParityForMcpTool(tool) {
45005
45346
  }
45006
45347
  // src/lib/registry-sync.ts
45007
45348
  import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync13 } from "fs";
45008
- import { dirname as dirname23, relative as relative9 } from "path";
45349
+ import { dirname as dirname24, relative as relative9 } from "path";
45009
45350
  function createRegistrySyncArtifact(options = {}) {
45010
45351
  const profile = options.profile ?? "all";
45011
45352
  const includeDocs = options.includeDocs ?? true;
@@ -45062,7 +45403,7 @@ function createRegistrySyncArtifact(options = {}) {
45062
45403
  };
45063
45404
  }
45064
45405
  function writeRegistrySyncArtifact(path, artifact) {
45065
- mkdirSync14(dirname23(path), { recursive: true });
45406
+ mkdirSync14(dirname24(path), { recursive: true });
45066
45407
  writeFileSync13(path, `${JSON.stringify(artifact, null, 2)}
45067
45408
  `);
45068
45409
  }
@@ -46147,14 +46488,14 @@ function clone3(value) {
46147
46488
  }
46148
46489
  // src/lib/feedback.ts
46149
46490
  import { appendFileSync, existsSync as existsSync19, mkdirSync as mkdirSync15 } from "fs";
46150
- import { dirname as dirname24, join as join39 } from "path";
46491
+ import { dirname as dirname25, join as join39 } from "path";
46151
46492
  import { Database } from "bun:sqlite";
46152
46493
  function getFeedbackDbPath() {
46153
46494
  return join39(getDataDir(), "skills.db");
46154
46495
  }
46155
46496
  function getFeedbackDb() {
46156
46497
  const dbPath = getFeedbackDbPath();
46157
- const dir = dirname24(dbPath);
46498
+ const dir = dirname25(dbPath);
46158
46499
  if (!existsSync19(dir))
46159
46500
  mkdirSync15(dir, { recursive: true });
46160
46501
  const db = new Database(dbPath);
@@ -46183,7 +46524,7 @@ function saveFeedback(input2) {
46183
46524
  const category = input2.category ?? "general";
46184
46525
  if (isApiMode()) {
46185
46526
  const path = join39(getDataDir(), "feedback.jsonl");
46186
- const dir = dirname24(path);
46527
+ const dir = dirname25(path);
46187
46528
  if (!existsSync19(dir))
46188
46529
  mkdirSync15(dir, { recursive: true });
46189
46530
  appendFileSync(path, JSON.stringify({ message, category, email: input2.email ?? null, agent: input2.agent ?? null, version: input2.version ?? null, createdAt: new Date().toISOString() }) + `
@@ -46206,7 +46547,7 @@ function isApiMode(env = process.env) {
46206
46547
  }
46207
46548
  }
46208
46549
  // src/lib/native-storage.ts
46209
- import { createHash as createHash29, createHmac as createHmac2 } from "crypto";
46550
+ import { createHash as createHash30, createHmac as createHmac2 } from "crypto";
46210
46551
  import {
46211
46552
  existsSync as existsSync20,
46212
46553
  mkdirSync as mkdirSync16,
@@ -46215,7 +46556,7 @@ import {
46215
46556
  statSync as statSync13,
46216
46557
  writeFileSync as writeFileSync14
46217
46558
  } from "fs";
46218
- import { dirname as dirname25, join as join40, normalize as normalize5, relative as relative10, sep as sep11 } from "path";
46559
+ import { dirname as dirname26, join as join40, normalize as normalize5, relative as relative10, sep as sep11 } from "path";
46219
46560
  var SKILLS_STORAGE_TABLES = [
46220
46561
  "skills_sync_records",
46221
46562
  "skills_sync_cursors"
@@ -46382,7 +46723,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
46382
46723
  files.push({
46383
46724
  path: relativePath,
46384
46725
  sizeBytes: bytes.byteLength,
46385
- sha256: createHash29("sha256").update(bytes).digest("hex"),
46726
+ sha256: createHash30("sha256").update(bytes).digest("hex"),
46386
46727
  ...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
46387
46728
  });
46388
46729
  }
@@ -46407,11 +46748,11 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
46407
46748
  continue;
46408
46749
  }
46409
46750
  const bytes = Buffer.from(file2.contentBase64, "base64");
46410
- const hash4 = createHash29("sha256").update(bytes).digest("hex");
46751
+ const hash4 = createHash30("sha256").update(bytes).digest("hex");
46411
46752
  if (hash4 !== file2.sha256) {
46412
46753
  throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
46413
46754
  }
46414
- mkdirSync16(dirname25(absolutePath4), { recursive: true });
46755
+ mkdirSync16(dirname26(absolutePath4), { recursive: true });
46415
46756
  writeFileSync14(absolutePath4, bytes);
46416
46757
  written += 1;
46417
46758
  }
@@ -46739,7 +47080,7 @@ function toIsoString(value) {
46739
47080
  return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
46740
47081
  }
46741
47082
  function sha256Hex2(value) {
46742
- return createHash29("sha256").update(value).digest("hex");
47083
+ return createHash30("sha256").update(value).digest("hex");
46743
47084
  }
46744
47085
  function normalizeHeaders(headers) {
46745
47086
  const result = {};
@@ -46791,7 +47132,7 @@ function toArrayBuffer(bytes) {
46791
47132
  return buffer;
46792
47133
  }
46793
47134
  // src/lib/station-snapshot.ts
46794
- import { createHash as createHash30 } from "crypto";
47135
+ import { createHash as createHash31 } from "crypto";
46795
47136
  import {
46796
47137
  copyFileSync,
46797
47138
  mkdirSync as mkdirSync17,
@@ -46799,7 +47140,7 @@ import {
46799
47140
  statSync as statSync15,
46800
47141
  writeFileSync as writeFileSync15
46801
47142
  } from "fs";
46802
- import { dirname as dirname26, isAbsolute as isAbsolute22, relative as relative11, resolve as resolve21, sep as sep13 } from "path";
47143
+ import { dirname as dirname27, isAbsolute as isAbsolute24, relative as relative11, resolve as resolve23, sep as sep13 } from "path";
46803
47144
 
46804
47145
  // src/lib/portable-snapshot-filter.ts
46805
47146
  import { readdirSync as readdirSync15, statSync as statSync14 } from "fs";
@@ -46966,7 +47307,7 @@ function validateStationId(stationId) {
46966
47307
  }
46967
47308
  }
46968
47309
  function sha256File(filePath) {
46969
- return createHash30("sha256").update(readFileSync28(filePath)).digest("hex");
47310
+ return createHash31("sha256").update(readFileSync28(filePath)).digest("hex");
46970
47311
  }
46971
47312
  function scanHome(definition, homesRoot) {
46972
47313
  const homePath = homePathFor(definition, homesRoot);
@@ -47037,7 +47378,7 @@ function humanHomes(scanned) {
47037
47378
  }));
47038
47379
  }
47039
47380
  function writeStationSnapshot(options) {
47040
- const repoRoot = resolve21(options.repoRoot ?? process.cwd());
47381
+ const repoRoot = resolve23(options.repoRoot ?? process.cwd());
47041
47382
  const { scanned, plans, totalBytes } = planStationSnapshot(options);
47042
47383
  const manifestFiles = plans.map((plan) => ({
47043
47384
  relativePath: plan.source.relativePath,
@@ -47063,9 +47404,9 @@ function writeStationSnapshot(options) {
47063
47404
  const conflicts = [];
47064
47405
  const untouched = [];
47065
47406
  for (const plan of plans) {
47066
- const destination = resolve21(repoRoot, plan.destination);
47407
+ const destination = resolve23(repoRoot, plan.destination);
47067
47408
  const destinationRelative = relative11(repoRoot, destination);
47068
- if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep13) || isAbsolute22(destinationRelative)) {
47409
+ if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep13) || isAbsolute24(destinationRelative)) {
47069
47410
  throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
47070
47411
  }
47071
47412
  let existingDigest = null;
@@ -47086,8 +47427,8 @@ function writeStationSnapshot(options) {
47086
47427
  }
47087
47428
  let written = 0;
47088
47429
  for (const plan of untouched) {
47089
- const destination = resolve21(repoRoot, plan.destination);
47090
- mkdirSync17(dirname26(destination), { recursive: true });
47430
+ const destination = resolve23(repoRoot, plan.destination);
47431
+ mkdirSync17(dirname27(destination), { recursive: true });
47091
47432
  copyFileSync(plan.source.fullPath, destination);
47092
47433
  written += 1;
47093
47434
  }
@@ -47105,8 +47446,8 @@ function writeStationSnapshot(options) {
47105
47446
  },
47106
47447
  files: manifestFiles
47107
47448
  };
47108
- const manifestPath = resolve21(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
47109
- mkdirSync17(dirname26(manifestPath), { recursive: true });
47449
+ const manifestPath = resolve23(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
47450
+ mkdirSync17(dirname27(manifestPath), { recursive: true });
47110
47451
  writeFileSync15(manifestPath, `${JSON.stringify(manifest, null, 2)}
47111
47452
  `);
47112
47453
  return {
@@ -47117,7 +47458,7 @@ function writeStationSnapshot(options) {
47117
47458
  };
47118
47459
  }
47119
47460
  // src/lib/station-hydrate.ts
47120
- import { createHash as createHash31 } from "crypto";
47461
+ import { createHash as createHash32 } from "crypto";
47121
47462
  import {
47122
47463
  copyFileSync as copyFileSync2,
47123
47464
  mkdirSync as mkdirSync18,
@@ -47126,7 +47467,7 @@ import {
47126
47467
  statSync as statSync16,
47127
47468
  writeFileSync as writeFileSync16
47128
47469
  } from "fs";
47129
- import { dirname as dirname27, join as join42, resolve as resolve22, sep as sep14 } from "path";
47470
+ import { dirname as dirname28, join as join42, resolve as resolve24, sep as sep14 } from "path";
47130
47471
  var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
47131
47472
  var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
47132
47473
  var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
@@ -47323,12 +47664,12 @@ function skillSha256(skill) {
47323
47664
  return sha256File(skill.files[0].winner.fullPath);
47324
47665
  }
47325
47666
  const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
47326
- return createHash31("sha256").update(joined.sort().join(`
47667
+ return createHash32("sha256").update(joined.sort().join(`
47327
47668
  `)).digest("hex");
47328
47669
  }
47329
47670
  function writeStationHydration(options) {
47330
- const repoRoot = resolve22(options.repoRoot ?? process.cwd());
47331
- const cacheRoot = resolve22(options.cacheRoot ?? resolveCorpusRoot());
47671
+ const repoRoot = resolve24(options.repoRoot ?? process.cwd());
47672
+ const cacheRoot = resolve24(options.cacheRoot ?? resolveCorpusRoot());
47332
47673
  const plan = planStationHydration(options.stationId, repoRoot);
47333
47674
  const resultSkills = plan.winners.map((skill) => ({
47334
47675
  ident: skill.ident,
@@ -47382,7 +47723,7 @@ function writeStationHydration(options) {
47382
47723
  }
47383
47724
  let written = 0;
47384
47725
  for (const entry of toWrite) {
47385
- mkdirSync18(dirname27(entry.destination), { recursive: true });
47726
+ mkdirSync18(dirname28(entry.destination), { recursive: true });
47386
47727
  copyFileSync2(entry.fullPath, entry.destination);
47387
47728
  written += 1;
47388
47729
  }
@@ -47403,8 +47744,8 @@ function writeStationHydration(options) {
47403
47744
  },
47404
47745
  skills: resultSkills
47405
47746
  };
47406
- const hydrationManifestPath = join42(dirname27(cacheRoot), `hydration-${options.stationId}.json`);
47407
- mkdirSync18(dirname27(hydrationManifestPath), { recursive: true });
47747
+ const hydrationManifestPath = join42(dirname28(cacheRoot), `hydration-${options.stationId}.json`);
47748
+ mkdirSync18(dirname28(hydrationManifestPath), { recursive: true });
47408
47749
  writeFileSync16(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
47409
47750
  `);
47410
47751
  return {
@@ -47416,19 +47757,19 @@ function writeStationHydration(options) {
47416
47757
  }
47417
47758
  // src/lib/private-publication-recovery.ts
47418
47759
  import { constants as constants15, closeSync as closeSync15, fsyncSync as fsyncSync7, fstatSync as fstatSync15, lstatSync as lstatSync27, mkdirSync as mkdirSync19, openSync as openSync15, readSync as readSync12, realpathSync as realpathSync14, renameSync as renameSync8, unlinkSync as unlinkSync6, writeFileSync as writeFileSync17 } from "fs";
47419
- import { dirname as dirname28, isAbsolute as isAbsolute23, join as join43, resolve as resolve23 } from "path";
47760
+ import { dirname as dirname29, isAbsolute as isAbsolute25, join as join43, resolve as resolve25 } from "path";
47420
47761
  import { randomUUID as randomUUID5 } from "crypto";
47421
47762
  var fail3 = () => {
47422
47763
  throw new PrivatePublicationError("PUBLICATION_RECOVERY_INVALID", "The recovery directory is invalid or changed. Preserve it and inspect the existing intent; do not start a replacement automatically.");
47423
47764
  };
47424
47765
  function safeDirectory(directory2) {
47425
- if (!isAbsolute23(directory2) || directory2 !== resolve23(directory2) || realpathSync14(directory2) !== directory2)
47766
+ if (!isAbsolute25(directory2) || directory2 !== resolve25(directory2) || realpathSync14(directory2) !== directory2)
47426
47767
  return fail3();
47427
- for (let path = directory2;; path = dirname28(path)) {
47768
+ for (let path = directory2;; path = dirname29(path)) {
47428
47769
  const stat2 = lstatSync27(path);
47429
47770
  if (!stat2.isDirectory() || stat2.isSymbolicLink())
47430
47771
  return fail3();
47431
- if (path === dirname28(path))
47772
+ if (path === dirname29(path))
47432
47773
  break;
47433
47774
  }
47434
47775
  const own2 = lstatSync27(directory2);
@@ -47560,11 +47901,11 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
47560
47901
  phase: "prepared",
47561
47902
  intent: null
47562
47903
  };
47563
- if (!isAbsolute23(recoveryDirectory) || resolve23(recoveryDirectory) !== recoveryDirectory || realpathSync14(dirname28(recoveryDirectory)) !== dirname28(recoveryDirectory))
47904
+ if (!isAbsolute25(recoveryDirectory) || resolve25(recoveryDirectory) !== recoveryDirectory || realpathSync14(dirname29(recoveryDirectory)) !== dirname29(recoveryDirectory))
47564
47905
  return fail3();
47565
47906
  mkdirSync19(recoveryDirectory, { mode: 448 });
47566
47907
  safeDirectory(recoveryDirectory);
47567
- const parent = openSync15(dirname28(recoveryDirectory), constants15.O_RDONLY | constants15.O_NOFOLLOW);
47908
+ const parent = openSync15(dirname29(recoveryDirectory), constants15.O_RDONLY | constants15.O_NOFOLLOW);
47568
47909
  try {
47569
47910
  fsyncSync7(parent);
47570
47911
  } finally {
@@ -47651,8 +47992,8 @@ async function inspectLocked(client2, directory2, cancel) {
47651
47992
  return privatePublicationResult(directory2, receipt);
47652
47993
  }
47653
47994
  // src/lib/skill-context.ts
47654
- import { createHash as createHash32 } from "crypto";
47655
- import { resolve as resolve24 } from "path";
47995
+ import { createHash as createHash33 } from "crypto";
47996
+ import { resolve as resolve26 } from "path";
47656
47997
  function words(value) {
47657
47998
  return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
47658
47999
  }
@@ -47691,7 +48032,7 @@ async function buildSkillContext(input2, options = {}) {
47691
48032
  const promptWords = words(prompt);
47692
48033
  const promptNames = new Set(prompt.toLowerCase().match(/[a-z0-9]+(?:-[a-z0-9]+)*/g) ?? []);
47693
48034
  const loaded = new Set(resolved.session?.loaded ?? []);
47694
- const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve24(input2.cwd)] : []];
48035
+ const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve26(input2.cwd)] : []];
47695
48036
  const candidates = profile.selections.map((selection) => {
47696
48037
  const key = selectionKey(selection);
47697
48038
  if (input2.restore && loaded.has(key))
@@ -47718,7 +48059,7 @@ async function buildSkillContext(input2, options = {}) {
47718
48059
  const omitted = [];
47719
48060
  let chars = 0;
47720
48061
  for (const candidate2 of candidates) {
47721
- const { selection, reason } = candidate2;
48062
+ const { selection, reason: reason2 } = candidate2;
47722
48063
  if (!input2.restore && !explicit.has(selectionKey(selection)) && loaded.has(selectionKey(selection)))
47723
48064
  continue;
47724
48065
  const loadCommand = `skills load ${selection.slug}@${selection.version} --selection-profile ${profile.profileId}`;
@@ -47728,7 +48069,7 @@ async function buildSkillContext(input2, options = {}) {
47728
48069
  }
47729
48070
  const entries = await readSelectedEntries(selection, resolved, resolverOptions);
47730
48071
  const { content } = readSelectedDocument(entries);
47731
- const section = `Skill ${selection.slug}@${selection.version} (${selection.bundleDigest}; ${reason})
48072
+ const section = `Skill ${selection.slug}@${selection.version} (${selection.bundleDigest}; ${reason2})
47732
48073
  ${content}`;
47733
48074
  if (maxChars !== undefined && chars + section.length + (sections.length ? 2 : 0) > maxChars) {
47734
48075
  omitted.push({ slug: selection.slug, version: selection.version, reason: "context-budget", loadCommand });
@@ -47736,7 +48077,7 @@ ${content}`;
47736
48077
  }
47737
48078
  chars += section.length + (sections.length ? 2 : 0);
47738
48079
  sections.push(section);
47739
- selections.push({ ...selection, reason });
48080
+ selections.push({ ...selection, reason: reason2 });
47740
48081
  loaded.add(selectionKey(selection));
47741
48082
  }
47742
48083
  const context2 = sections.join(`
@@ -47744,7 +48085,7 @@ ${content}`;
47744
48085
  `);
47745
48086
  const receipt = {
47746
48087
  schemaVersion: 1,
47747
- id: createHash32("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
48088
+ id: createHash33("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
47748
48089
  profileId: profile.profileId,
47749
48090
  profileRevision: profile.profileRevision,
47750
48091
  authority: profile.authority,
@@ -47760,9 +48101,9 @@ ${content}`;
47760
48101
  return { context: context2, selections, omitted, receipt };
47761
48102
  }
47762
48103
  // src/lib/session-reconciliation.ts
47763
- import { createHash as createHash33 } from "crypto";
48104
+ import { createHash as createHash34 } from "crypto";
47764
48105
  var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
47765
- var digest3 = (value) => createHash33("sha256").update(JSON.stringify(value)).digest("hex");
48106
+ var digest3 = (value) => createHash34("sha256").update(JSON.stringify(value)).digest("hex");
47766
48107
  function exactTimestamp(value) {
47767
48108
  const parsed = value === undefined ? Number.NaN : Date.parse(value);
47768
48109
  if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
@@ -47795,6 +48136,8 @@ function inspectSkillSession(sessionId, options = {}) {
47795
48136
  loadedCount: receipt.loaded.length
47796
48137
  };
47797
48138
  }
48139
+ var SESSION_RENEWAL_REFUSAL_TTL_MS = 5 * 60 * 1000;
48140
+ var SESSION_RENEWAL_REFUSAL_MAX_BYTES = 4 * 1024;
47798
48141
  async function reconcileSkillSession(input2, options = {}) {
47799
48142
  if (![input2.fromProfile, input2.fromRevision, input2.selectionProfile, input2.profileRevision].every((value) => typeof value === "string" && value.trim()) || !/^[a-f0-9]{64}$/.test(input2.receiptSha256)) {
47800
48143
  throw new SkillSelectionError("INVALID_SESSION_RECONCILIATION", "Name the exact old receipt SHA256, old profile/revision and intended target profile/revision.");
@@ -47982,6 +48325,7 @@ export {
47982
48325
  isPortableWithinSkill,
47983
48326
  isGatewayBackedSkill,
47984
48327
  isExcludedSkillFileName,
48328
+ isDiscoveryRootUnresolved,
47985
48329
  isCodexNativeSkillDisabled,
47986
48330
  isBasicSkillName,
47987
48331
  inventoryNativeSkills,
@@ -47994,6 +48338,7 @@ export {
47994
48338
  inspectPrivatePublication,
47995
48339
  importSkillsLocalSnapshot,
47996
48340
  homePathFor,
48341
+ hashClaudeMarketplaceEntry,
47997
48342
  getToolPrimitive,
47998
48343
  getStorageStatus,
47999
48344
  getStorageDatabaseUrl,
@@ -48078,6 +48423,7 @@ export {
48078
48423
  captureClaudeSettings,
48079
48424
  captureClaudeMarketplaceRegistryV2,
48080
48425
  captureClaudeMarketplaceRegistry,
48426
+ captureClaudeMarketplaceEntry,
48081
48427
  canonicalizeManifest,
48082
48428
  buildSkillsS3ObjectUrl,
48083
48429
  buildSkillsApiUrl,
@@ -48159,9 +48505,11 @@ export {
48159
48505
  InvitationEmailInputError,
48160
48506
  HttpProfileClient,
48161
48507
  HostedApiError,
48508
+ DISCOVERY_ROOT_UNRESOLVED,
48162
48509
  DEFAULT_EXPORT_DIR,
48163
48510
  ContentHashInputError,
48164
48511
  CONTENT_HASH_LIMITS,
48512
+ CLAUDE_MARKETPLACE_ENTRY_HASH_MODE,
48165
48513
  CLAUDE_COORDINATED_HOOK_EVENTS,
48166
48514
  CATEGORIES,
48167
48515
  BASIC_SKILL_NAMES,