@hasna/skills 0.10.51 → 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,7 @@ 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";
9383
9387
 
9384
9388
  // src/lib/codex-native-compatibility.ts
9385
9389
  var MEASURED_CODEX_CAPABILITIES = {
@@ -9401,6 +9405,199 @@ function supportsCodexNativeCapability(version, capability) {
9401
9405
  }
9402
9406
  var SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
9403
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
+
9404
9601
  // src/lib/agent-policy-limits.ts
9405
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 });
9406
9603
  function object(value) {
@@ -9455,7 +9652,7 @@ function assertAgentPolicyCollections(policy) {
9455
9652
  text(control.pluginId, 1024);
9456
9653
  text(control.namespace, 64);
9457
9654
  text(control.pluginParent);
9458
- 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));
9459
9656
  if (control.remotePluginId !== undefined)
9460
9657
  requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
9461
9658
  if (control.skillsOnly !== undefined)
@@ -9473,10 +9670,18 @@ function assertAgentPolicyCollections(policy) {
9473
9670
  text(control.pluginId, 1024);
9474
9671
  text(control.namespace, 64);
9475
9672
  text(control.pluginParent);
9476
- 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));
9477
9674
  ids.add(control.pluginId);
9478
9675
  }
9479
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
+ }
9480
9685
  if (bridge.disabledBuiltins !== undefined)
9481
9686
  for (const builtin of array(bridge.disabledBuiltins, AGENT_POLICY_LIMITS.builtinNames)) {
9482
9687
  requireBound(object(builtin));
@@ -9502,11 +9707,11 @@ function assertAgentPolicyCollections(policy) {
9502
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);
9503
9708
  for (const root of roots) {
9504
9709
  text(root);
9505
- requireBound(isAbsolute3(root) && resolve3(root) === root);
9710
+ requireBound(isAbsolute4(root) && resolve4(root) === root);
9506
9711
  }
9507
9712
  for (const parent of parents2) {
9508
9713
  text(parent);
9509
- requireBound(isAbsolute3(parent) && resolve3(parent) === parent);
9714
+ requireBound(isAbsolute4(parent) && resolve4(parent) === parent);
9510
9715
  }
9511
9716
  for (const directory of array(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
9512
9717
  requireBound(object(directory));
@@ -9526,7 +9731,7 @@ function assertAgentPolicyCollections(policy) {
9526
9731
  text(input.namespace, 64);
9527
9732
  text(input.pluginParent);
9528
9733
  text(input.sourceRoot);
9529
- 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));
9530
9735
  ids.add(input.pluginId);
9531
9736
  }
9532
9737
  if (proof.directories !== undefined)
@@ -9543,17 +9748,19 @@ function assertAgentPolicyCollections(policy) {
9543
9748
  text(source.path);
9544
9749
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
9545
9750
  if (source.hashMode !== undefined)
9546
- 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));
9547
9752
  if (source.hashMode === "claude-plugin-manifest-v1")
9548
- 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);
9549
9754
  if (source.hashMode === "sumi-settings-v1")
9550
- 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");
9551
9756
  if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
9552
- 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");
9553
9758
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
9554
- 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");
9555
9760
  if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
9556
- 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));
9557
9764
  if (source.hashMode === "claude-plugin-registry") {
9558
9765
  requireBound(agent === "claude");
9559
9766
  const managed = array(source.managedPlugins, 64);
@@ -9568,8 +9775,8 @@ function assertAgentPolicyCollections(policy) {
9568
9775
  if (source.format !== undefined)
9569
9776
  requireBound(["json", "toml", "yaml"].includes(source.format));
9570
9777
  if (source.fields !== undefined)
9571
- for (const field of array(source.fields, AGENT_POLICY_LIMITS.fields))
9572
- text(field, 256);
9778
+ for (const field2 of array(source.fields, AGENT_POLICY_LIMITS.fields))
9779
+ text(field2, 256);
9573
9780
  }
9574
9781
  if (value.directories !== undefined)
9575
9782
  for (const directory of array(value.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
@@ -9584,9 +9791,9 @@ function assertAgentPolicyCollections(policy) {
9584
9791
  }
9585
9792
 
9586
9793
  // src/lib/agent-discovery.ts
9587
- import { createHash as createHash24 } from "crypto";
9794
+ import { createHash as createHash25 } from "crypto";
9588
9795
  import { lstatSync as lstatSync25, readFileSync as readFileSync20, readdirSync as readdirSync10, realpathSync as realpathSync11 } from "fs";
9589
- 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";
9590
9797
  import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
9591
9798
 
9592
9799
  // node_modules/yaml/dist/index.js
@@ -9636,7 +9843,7 @@ var $visit = visit.visit;
9636
9843
  var $visitAsync = visit.visitAsync;
9637
9844
 
9638
9845
  // src/lib/agent-hermes.ts
9639
- 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";
9640
9847
  import { lstatSync as lstatSync4, readFileSync as readFileSync6, opendirSync } from "fs";
9641
9848
  var HERMES_OPT_OUT = ".no-bundled-skills";
9642
9849
  var EVENTS = ["pre_llm_call", "pre_tool_call"];
@@ -9674,7 +9881,7 @@ function quoted(value) {
9674
9881
  return `'${value.replace(/'/g, `'\\''`)}'`;
9675
9882
  }
9676
9883
  function hermesHookDefinitions(supervisor) {
9677
- if (!isAbsolute4(supervisor.runtime) || !isAbsolute4(supervisor.path))
9884
+ if (!isAbsolute5(supervisor.runtime) || !isAbsolute5(supervisor.path))
9678
9885
  throw new Error("Expected absolute Hermes supervisor paths");
9679
9886
  const entry = (event) => ({ command: `${quoted(supervisor.runtime)} ${quoted(supervisor.path)} --event ${event}`, timeout: 15, ...event === "pre_tool_call" ? { fail_closed: true } : {} });
9680
9887
  return { pre_llm_call: entry("pre_llm_call"), pre_tool_call: entry("pre_tool_call") };
@@ -9710,16 +9917,16 @@ function assertHermesEnvironment(home) {
9710
9917
  throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
9711
9918
  }
9712
9919
  const selected = process.env.HERMES_HOME?.trim();
9713
- if (selected && (!isAbsolute4(selected) || resolve4(selected) !== join8(resolve4(home), ".hermes")))
9920
+ if (selected && (!isAbsolute5(selected) || resolve5(selected) !== join8(resolve5(home), ".hermes")))
9714
9921
  throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
9715
9922
  if (process.env.HERMES_ENABLE_PROJECT_PLUGINS?.trim() && !["0", "false", "no", "off"].includes(process.env.HERMES_ENABLE_PROJECT_PLUGINS.trim().toLowerCase()))
9716
9923
  throw new Error("NATIVE_SKILL_DRIFT: Hermes project plugins require a dedicated discovery review");
9717
9924
  }
9718
9925
  function read(path) {
9719
- for (let current = path;; current = dirname4(current)) {
9926
+ for (let current = path;; current = dirname5(current)) {
9720
9927
  if (lstatSync4(current, { throwIfNoEntry: false })?.isSymbolicLink())
9721
9928
  throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes protection input");
9722
- if (dirname4(current) === current)
9929
+ if (dirname5(current) === current)
9723
9930
  break;
9724
9931
  }
9725
9932
  const stat = lstatSync4(path, { throwIfNoEntry: false });
@@ -9733,7 +9940,7 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
9733
9940
  assertHermesEnvironment(home);
9734
9941
  if (read(join8(home, ".hermes", HERMES_OPT_OUT)) === null)
9735
9942
  throw new Error("NATIVE_SKILL_DRIFT: Hermes bundled skill reseeding is not disabled; run skills hook install");
9736
- 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))
9737
9944
  throw new Error("NATIVE_SKILL_DRIFT: Hermes owned supervisor changed; run skills hook install");
9738
9945
  const raw = read(join8(home, ".hermes/shell-hooks-allowlist.json"));
9739
9946
  let trust;
@@ -9759,10 +9966,10 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
9759
9966
  }
9760
9967
  function assertNoHermesLegacyShadow(root) {
9761
9968
  let entries = 0;
9762
- for (let cursor = resolve4(root);; cursor = dirname4(cursor)) {
9969
+ for (let cursor = resolve5(root);; cursor = dirname5(cursor)) {
9763
9970
  if (lstatSync4(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
9764
9971
  throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes legacy discovery root");
9765
- if (dirname4(cursor) === cursor)
9972
+ if (dirname5(cursor) === cursor)
9766
9973
  break;
9767
9974
  }
9768
9975
  function scan(path, depth) {
@@ -9783,10 +9990,10 @@ function assertNoHermesLegacyShadow(root) {
9783
9990
  const hasDescriptor = Boolean(lstatSync4(join8(path, "SKILL.md"), { throwIfNoEntry: false }));
9784
9991
  const directory = opendirSync(path);
9785
9992
  try {
9786
- for (let child = directory.readSync();child; child = directory.readSync()) {
9787
- 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))
9788
9995
  continue;
9789
- scan(join8(path, child.name), depth + 1);
9996
+ scan(join8(path, child2.name), depth + 1);
9790
9997
  }
9791
9998
  } finally {
9792
9999
  directory.closeSync();
@@ -9843,18 +10050,18 @@ if (failed) process.exitCode = 2;
9843
10050
  }
9844
10051
 
9845
10052
  // src/lib/agent-discovery-directories.ts
9846
- import { createHash as createHash4 } from "crypto";
10053
+ import { createHash as createHash5 } from "crypto";
9847
10054
  import { lstatSync as lstatSync5, opendirSync as opendirSync2 } from "fs";
9848
- import { dirname as dirname5, isAbsolute as isAbsolute5, join as join9, resolve as resolve5 } from "path";
9849
- 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) {
9850
10057
  if (!value)
9851
10058
  throw new Error(message);
9852
10059
  }
9853
10060
  function safe(path) {
9854
- 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");
9855
- for (let cursor = path;; cursor = dirname5(cursor)) {
9856
- need2(!lstatSync5(cursor, { throwIfNoEntry: false })?.isSymbolicLink(), "Refusing symlink native discovery directory");
9857
- 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)
9858
10065
  break;
9859
10066
  }
9860
10067
  }
@@ -9868,45 +10075,45 @@ function capture(path, budget2, excludedRoots, omittedRoots = [], rows) {
9868
10075
  const root = lstatSync5(path, { throwIfNoEntry: false });
9869
10076
  if (!root)
9870
10077
  return { path, sha256: null };
9871
- need2(root.isDirectory(), "Unsupported native discovery directory input");
9872
- const hash = createHash4("sha256");
10078
+ need3(root.isDirectory(), "Unsupported native discovery directory input");
10079
+ const hash = createHash5("sha256");
9873
10080
  function walk(current, relative2, before, depth) {
9874
- need2(depth <= 64, "Native discovery directory depth limit exceeded");
10081
+ need3(depth <= 64, "Native discovery directory depth limit exceeded");
9875
10082
  const names = [], directory = opendirSync2(current);
9876
10083
  try {
9877
10084
  for (let entry = directory.readSync();entry; entry = directory.readSync()) {
9878
- 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");
9879
10086
  names.push(entry.name);
9880
10087
  }
9881
10088
  } finally {
9882
10089
  directory.closeSync();
9883
10090
  }
9884
- 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");
9885
10092
  for (const name of names.sort()) {
9886
- const child = join9(current, name), childRelative = relative2 ? `${relative2}/${name}` : name;
9887
- if (omittedRoots.includes(child))
10093
+ const child2 = join9(current, name), childRelative = relative2 ? `${relative2}/${name}` : name;
10094
+ if (omittedRoots.includes(child2))
9888
10095
  continue;
9889
- need2(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
9890
- const stat = lstatSync5(child);
9891
- 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");
9892
10099
  const row = JSON.stringify([childRelative, stat.isDirectory() ? "directory" : "file"]) + `
9893
10100
  `;
9894
10101
  budget2.bytes += Buffer.byteLength(row);
9895
- 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");
9896
10103
  hash.update(row);
9897
10104
  rows?.push(row);
9898
- if (stat.isDirectory() && !excludedRoots.includes(child))
9899
- walk(child, childRelative, stat, depth + 1);
10105
+ if (stat.isDirectory() && !excludedRoots.includes(child2))
10106
+ walk(child2, childRelative, stat, depth + 1);
9900
10107
  }
9901
- 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");
9902
10109
  }
9903
10110
  walk(path, "", root, 0);
9904
10111
  safe(path);
9905
10112
  return { path, sha256: hash.digest("hex") };
9906
10113
  }
9907
10114
  function captureDiscoveryDirectoryProjection(paths, omittedRoots) {
9908
- need2(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid projected discovery directory collection");
9909
- 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");
9910
10117
  for (const root of omittedRoots)
9911
10118
  safe(root);
9912
10119
  const budget2 = { entries: 0, bytes: 0 };
@@ -9916,62 +10123,62 @@ function captureDiscoveryDirectoryProjection(paths, omittedRoots) {
9916
10123
  });
9917
10124
  }
9918
10125
  function verifyDiscoveryDirectoryProjection(directories, omittedRoots) {
9919
- 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");
9920
10127
  let count = 0, bytes = 0;
9921
10128
  const expected = directories.map((directory) => {
9922
10129
  safe(directory.path);
9923
- need2(Array.isArray(directory.entries), "Missing reviewed directory membership inventory");
9924
- const hash = createHash4("sha256");
10130
+ need3(Array.isArray(directory.entries), "Missing reviewed directory membership inventory");
10131
+ const hash = createHash5("sha256");
9925
10132
  const kept = [];
9926
10133
  for (const row of directory.entries) {
9927
- 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");
9928
10135
  bytes += Buffer.byteLength(row);
9929
- 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");
9930
10137
  const value = JSON.parse(row);
9931
- 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) + `
9932
10139
  `, "Invalid reviewed directory member");
9933
10140
  hash.update(row);
9934
10141
  const path = join9(directory.path, value[0]);
9935
10142
  if (!omittedRoots.some((root) => path === root || path.startsWith(root + "/")))
9936
10143
  kept.push(row);
9937
10144
  }
9938
- 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");
9939
10146
  const absent = omittedRoots.some((root) => directory.path === root || directory.path.startsWith(root + "/"));
9940
- 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") };
9941
10148
  });
9942
10149
  const current = captureDiscoveryDirectoryProjection(directories.map((item) => item.path), omittedRoots);
9943
- 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");
9944
10151
  }
9945
10152
  function captureDiscoveryDirectories(paths, excludedRoots = []) {
9946
- 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");
9947
10154
  const budget2 = { entries: 0, bytes: 0 };
9948
10155
  for (const root of excludedRoots)
9949
10156
  safe(root);
9950
10157
  return paths.map((path) => capture(path, budget2, excludedRoots));
9951
10158
  }
9952
10159
  function verifyDiscoveryDirectories(directories, excludedRoots = []) {
9953
- 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");
9954
10161
  for (const directory of directories)
9955
- 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");
9956
10163
  const current = captureDiscoveryDirectories(directories.map((directory) => directory.path), excludedRoots);
9957
10164
  for (let i = 0;i < directories.length; i++)
9958
- 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}`);
9959
10166
  }
9960
10167
 
9961
10168
  // src/lib/agent-discovery-bytes.ts
9962
- import { createHash as createHash5 } from "crypto";
10169
+ import { createHash as createHash6 } from "crypto";
9963
10170
  import { closeSync as closeSync2, constants as constants2, fstatSync as fstatSync2, lstatSync as lstatSync6, openSync as openSync2, readSync as readSync2 } from "fs";
9964
- 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";
9965
10172
  function discoveryByteBudget() {
9966
10173
  return { remaining: AGENT_POLICY_LIMITS.discoveryRawTotalBytes };
9967
10174
  }
9968
10175
  function safe2(path) {
9969
- 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"))
9970
10177
  throw new Error("Expected a canonical absolute raw discovery path");
9971
- for (let cursor = path;; cursor = dirname6(cursor)) {
10178
+ for (let cursor = path;; cursor = dirname7(cursor)) {
9972
10179
  if (lstatSync6(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
9973
10180
  throw new Error("Refusing symlink raw discovery input");
9974
- if (dirname6(cursor) === cursor)
10181
+ if (dirname7(cursor) === cursor)
9975
10182
  break;
9976
10183
  }
9977
10184
  }
@@ -9990,7 +10197,7 @@ function hashRawDiscoveryFile(path, budget2, changes) {
9990
10197
  if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
9991
10198
  throw new Error("Raw discovery file byte limit exceeded");
9992
10199
  consume(budget2, bytes);
9993
- return createHash5("sha256").update(text2).digest("hex");
10200
+ return createHash6("sha256").update(text2).digest("hex");
9994
10201
  }
9995
10202
  const initial = lstatSync6(path, { throwIfNoEntry: false });
9996
10203
  if (!initial)
@@ -10004,7 +10211,7 @@ function hashRawDiscoveryFile(path, budget2, changes) {
10004
10211
  const opened = fstatSync2(fd);
10005
10212
  if (!opened.isFile() || !same2(initial, opened))
10006
10213
  throw new Error("Raw discovery input changed during open");
10007
- const hash = createHash5("sha256"), buffer = Buffer.alloc(64 * 1024);
10214
+ const hash = createHash6("sha256"), buffer = Buffer.alloc(64 * 1024);
10008
10215
  let bytes = 0;
10009
10216
  while (true) {
10010
10217
  const count = readSync2(fd, buffer, 0, Math.min(buffer.length, AGENT_POLICY_LIMITS.discoveryRawSourceBytes - bytes + 1), null);
@@ -10027,9 +10234,9 @@ function hashRawDiscoveryFile(path, budget2, changes) {
10027
10234
  }
10028
10235
 
10029
10236
  // src/lib/agent-discovery-path-bytes.ts
10030
- import { createHash as createHash6 } from "crypto";
10237
+ import { createHash as createHash7 } from "crypto";
10031
10238
  import { lstatSync as lstatSync7, readlinkSync } from "fs";
10032
- 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";
10033
10240
  function identity2(stat, mutable = false) {
10034
10241
  return {
10035
10242
  dev: String(stat.dev),
@@ -10040,12 +10247,12 @@ function identity2(stat, mutable = false) {
10040
10247
  ...mutable ? { size: String(stat.size), mtimeNs: String(stat.mtimeNs), ctimeNs: String(stat.ctimeNs) } : {}
10041
10248
  };
10042
10249
  }
10043
- function canonical2(path) {
10044
- 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)
10045
10252
  throw new Error("Expected a canonical absolute discovery path");
10046
10253
  }
10047
10254
  function trace(path) {
10048
- canonical2(path);
10255
+ canonical3(path);
10049
10256
  const rows = [], paths = [];
10050
10257
  let metadataBytes = 0, steps = 0, links = 0, cursor = parse2(path).root, pending = path.slice(cursor.length).split(sep3);
10051
10258
  function record2(path2, row) {
@@ -10070,7 +10277,7 @@ function trace(path) {
10070
10277
  if (!component || component === ".")
10071
10278
  continue;
10072
10279
  if (component === "..") {
10073
- cursor = dirname7(cursor);
10280
+ cursor = dirname8(cursor);
10074
10281
  continue;
10075
10282
  }
10076
10283
  const candidate = join10(cursor, component);
@@ -10090,11 +10297,11 @@ function trace(path) {
10090
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)))
10091
10298
  throw new Error("Discovery link changed during read");
10092
10299
  record2(candidate, { type: "link", target, ...identity2(stat, true) });
10093
- if (isAbsolute7(target)) {
10300
+ if (isAbsolute8(target)) {
10094
10301
  cursor = parse2(target).root;
10095
10302
  root();
10096
10303
  }
10097
- const remainder = isAbsolute7(target) ? target.slice(parse2(target).root.length) : target;
10304
+ const remainder = isAbsolute8(target) ? target.slice(parse2(target).root.length) : target;
10098
10305
  pending = [...remainder.split(sep3), ...pending];
10099
10306
  if (pending.length + steps > AGENT_POLICY_LIMITS.discoveryPathSteps)
10100
10307
  throw new Error("Discovery path step limit exceeded");
@@ -10137,15 +10344,15 @@ function hashDiscoveryPathFile(path, budget2, changes) {
10137
10344
  const after = trace(path);
10138
10345
  if (JSON.stringify(before.rows) !== JSON.stringify(after.rows) || before.file !== after.file)
10139
10346
  throw new Error("Discovery path input changed during read");
10140
- 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");
10141
10348
  }
10142
10349
 
10143
10350
  // src/lib/claude-plugin-manifest-witness.ts
10144
- import { createHash as createHash7 } from "crypto";
10351
+ import { createHash as createHash8 } from "crypto";
10145
10352
  var CLAUDE_PLUGIN_MANIFEST_LIMITS = Object.freeze({ bytes: 1024 * 1024, depth: 32, nodes: 65536, stringCharacters: 16384 });
10146
- function requireValue(value, reason) {
10353
+ function requireValue(value, reason2) {
10147
10354
  if (!value)
10148
- throw new Error("Claude plugin manifest " + reason);
10355
+ throw new Error("Claude plugin manifest " + reason2);
10149
10356
  }
10150
10357
  function parse3(text2) {
10151
10358
  requireValue(Buffer.byteLength(text2, "utf8") <= CLAUDE_PLUGIN_MANIFEST_LIMITS.bytes, "exceeds its byte limit");
@@ -10221,7 +10428,7 @@ function parse3(text2) {
10221
10428
  requireValue(at === text2.length && root.kind === "object", "requires a JSON object without trailing content");
10222
10429
  return root;
10223
10430
  }
10224
- function canonical3(value) {
10431
+ function canonical4(value) {
10225
10432
  if (value.kind === "string")
10226
10433
  return JSON.stringify(value.value);
10227
10434
  if (value.kind === "number")
@@ -10229,19 +10436,19 @@ function canonical3(value) {
10229
10436
  if (value.kind === "literal")
10230
10437
  return value.value;
10231
10438
  if (value.kind === "array")
10232
- return "[" + value.items.map(canonical3).join(",") + "]";
10233
- 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(",") + "}";
10234
10441
  }
10235
10442
  var DESCRIPTIVE_FIELDS = new Set(["description", "version", "author", "homepage", "repository", "license", "keywords"]);
10236
- function validateText(value, field) {
10237
- 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");
10238
10445
  }
10239
10446
  function validateDescriptiveFields(manifest) {
10240
10447
  const values = new Map(manifest.entries);
10241
- for (const field of ["description", "version", "homepage", "license"]) {
10242
- const value = values.get(field);
10448
+ for (const field2 of ["description", "version", "homepage", "license"]) {
10449
+ const value = values.get(field2);
10243
10450
  if (value !== undefined)
10244
- validateText(value, field);
10451
+ validateText(value, field2);
10245
10452
  }
10246
10453
  const author = values.get("author");
10247
10454
  if (author !== undefined) {
@@ -10266,10 +10473,10 @@ function hashClaudePluginManifest(text2) {
10266
10473
  const manifest = parse3(text2);
10267
10474
  validateDescriptiveFields(manifest);
10268
10475
  const discovery = { kind: "object", entries: manifest.entries.filter(([key]) => !DESCRIPTIVE_FIELDS.has(key)) };
10269
- return createHash7("sha256").update(canonical3(discovery)).digest("hex");
10476
+ return createHash8("sha256").update(canonical4(discovery)).digest("hex");
10270
10477
  }
10271
10478
  function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
10272
- 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) {
10273
10480
  throw new Error("Reviewed Claude plugin manifest changed before semantic projection");
10274
10481
  }
10275
10482
  return hashClaudePluginManifest(text2);
@@ -10277,22 +10484,22 @@ function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
10277
10484
 
10278
10485
  // src/lib/plugin-discovery.ts
10279
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";
10280
- 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";
10281
10488
 
10282
10489
  // src/lib/plugin-admission.ts
10283
10490
  import { homedir as homedir5 } from "os";
10284
- 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";
10285
10492
 
10286
10493
  // src/lib/selection-cache.ts
10287
- import { createHash as createHash9, randomUUID } from "crypto";
10494
+ import { createHash as createHash10, randomUUID } from "crypto";
10288
10495
  import { spawnSync } from "child_process";
10289
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";
10290
10497
  import { hostname } from "os";
10291
- 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";
10292
10499
  import { isDeepStrictEqual } from "util";
10293
10500
 
10294
10501
  // src/lib/skill-bundle.ts
10295
- import { createHash as createHash8 } from "crypto";
10502
+ import { createHash as createHash9 } from "crypto";
10296
10503
  import { readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
10297
10504
  import { join as join11, relative as relative2 } from "path";
10298
10505
  import { createGunzip } from "zlib";
@@ -10447,7 +10654,7 @@ function ownBytes(view) {
10447
10654
  return out;
10448
10655
  }
10449
10656
  function sha256Hex(bytes) {
10450
- return createHash8("sha256").update(bytes).digest("hex");
10657
+ return createHash9("sha256").update(bytes).digest("hex");
10451
10658
  }
10452
10659
  function collectSkillBundleEntries(dir) {
10453
10660
  const entries = [];
@@ -10643,12 +10850,12 @@ function inspectionLimits(options) {
10643
10850
  for (const key of Object.keys(options.limits ?? {})) {
10644
10851
  if (!Object.hasOwn(limits, key))
10645
10852
  throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Unknown bundle limit");
10646
- const field = key;
10647
- const value = options.limits[field];
10648
- 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]) {
10649
10856
  throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Bundle limits must be positive integers within the hard ceilings");
10650
10857
  }
10651
- limits[field] = value;
10858
+ limits[field2] = value;
10652
10859
  }
10653
10860
  return limits;
10654
10861
  }
@@ -10678,7 +10885,14 @@ async function inspectSkillBundle(bundle, options = {}) {
10678
10885
  decoder.destroy(terminalError);
10679
10886
  };
10680
10887
  const onAbort = () => stop("BUNDLE_ABORTED");
10681
- 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())));
10682
10896
  signal?.addEventListener("abort", onAbort, { once: true });
10683
10897
  let decompressedByteSize = 0;
10684
10898
  let bytesSinceYield = 0;
@@ -10693,7 +10907,7 @@ async function inspectSkillBundle(bundle, options = {}) {
10693
10907
  parser2.push(chunk);
10694
10908
  bytesSinceYield += chunk.byteLength;
10695
10909
  if (bytesSinceYield >= 256 * 1024) {
10696
- await new Promise((resolve8) => setTimeout(resolve8, 0));
10910
+ await new Promise((resolve9) => setTimeout(resolve9, 0));
10697
10911
  bytesSinceYield = 0;
10698
10912
  check();
10699
10913
  }
@@ -10830,8 +11044,8 @@ class BoundedTarReader {
10830
11044
  }
10831
11045
  }
10832
11046
  }
10833
- function tarOctal(field) {
10834
- const text2 = new TextDecoder().decode(field);
11047
+ function tarOctal(field2) {
11048
+ const text2 = new TextDecoder().decode(field2);
10835
11049
  if (!/^[0-7]+[\0 ]*$/.test(text2))
10836
11050
  invalidBundle("Invalid tar octal field");
10837
11051
  const value = Number.parseInt(text2, 8);
@@ -10922,8 +11136,8 @@ function profileSelectionSnapshot(selections) {
10922
11136
  // src/lib/selection-cache.ts
10923
11137
  class SkillSelectionError extends Error {
10924
11138
  code;
10925
- constructor(code, message) {
10926
- super(message);
11139
+ constructor(code, message, options) {
11140
+ super(message, options);
10927
11141
  this.code = code;
10928
11142
  this.name = "SkillSelectionError";
10929
11143
  }
@@ -10931,14 +11145,14 @@ class SkillSelectionError extends Error {
10931
11145
  var MAX_CACHED_PROFILE_AGE_MS = 24 * 60 * 60 * 1000;
10932
11146
  var SELECTION_LOCK_FILE = "selection.lock.json";
10933
11147
  function selectionCacheRoot(options = {}) {
10934
- return resolve8(options.cacheDir ?? join12(getDataDirReadOnly(), "selection-cache"));
11148
+ return resolve9(options.cacheDir ?? join12(getDataDirReadOnly(), "selection-cache"));
10935
11149
  }
10936
11150
  function selectionKey(selection) {
10937
11151
  validateSelection(selection);
10938
11152
  return hash([selection.authority, selection.workspaceId, selection.slug, selection.version, selection.bundleDigest]);
10939
11153
  }
10940
11154
  function hash(value) {
10941
- return createHash9("sha256").update(JSON.stringify(value)).digest("hex");
11155
+ return createHash10("sha256").update(JSON.stringify(value)).digest("hex");
10942
11156
  }
10943
11157
  function validateSelection(selection) {
10944
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()) {
@@ -10978,7 +11192,7 @@ function selectionBundlePath(selection, options = {}) {
10978
11192
  return join12(selectionCacheRoot(options), "objects", hash(selection.authority), hash(selection.workspaceId), `${selection.bundleDigest.slice(7)}.tar.gz`);
10979
11193
  }
10980
11194
  function assertRegularPath(path, createParents = false) {
10981
- const absolute = resolve8(path);
11195
+ const absolute = resolve9(path);
10982
11196
  const root = parse4(absolute).root;
10983
11197
  let cursor = root;
10984
11198
  const segments = absolute.slice(root.length).split("/");
@@ -11035,7 +11249,7 @@ function writeSelectionJson(path, value) {
11035
11249
  }
11036
11250
  function atomicWrite(path, bytes, mode, durable = false) {
11037
11251
  assertRegularPath(path, true);
11038
- const temporary = join12(dirname8(path), `.selection-${randomUUID()}.tmp`);
11252
+ const temporary = join12(dirname9(path), `.selection-${randomUUID()}.tmp`);
11039
11253
  try {
11040
11254
  const fd = openSync3(temporary, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, mode);
11041
11255
  try {
@@ -11047,7 +11261,7 @@ function atomicWrite(path, bytes, mode, durable = false) {
11047
11261
  }
11048
11262
  renameSync(temporary, path);
11049
11263
  if (durable)
11050
- syncDirectory(dirname8(path));
11264
+ syncDirectory(dirname9(path));
11051
11265
  } finally {
11052
11266
  try {
11053
11267
  unlinkSync(temporary);
@@ -11057,8 +11271,8 @@ function atomicWrite(path, bytes, mode, durable = false) {
11057
11271
  }
11058
11272
  }
11059
11273
  }
11060
- function readSelectionJson(path) {
11061
- 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));
11062
11276
  if (!bytes)
11063
11277
  return null;
11064
11278
  try {
@@ -11070,10 +11284,10 @@ function readSelectionJson(path) {
11070
11284
  async function verifiedEntries(selection, bytes, signal, deadline) {
11071
11285
  if (`sha256:${sha256Hex(bytes)}` !== selection.bundleDigest)
11072
11286
  throw new SkillSelectionError("BUNDLE_DIGEST_MISMATCH", "The skill bundle does not match its selected digest.");
11073
- const remaining = deadline === undefined ? undefined : Math.floor(deadline - performance.now());
11287
+ const remaining = deadline === undefined ? undefined : Math.ceil(deadline - performance.now());
11074
11288
  signal?.throwIfAborted();
11075
11289
  if (remaining !== undefined && remaining <= 0)
11076
- throw new SkillSelectionError("SKILLS_API_UNAVAILABLE", "Skills session authorization deadline exceeded.");
11290
+ throw new SkillBundleInspectionError("BUNDLE_TIMEOUT", "Bundle inspection deadline exceeded");
11077
11291
  const bundle = await inspectSkillBundle(bytes, { signal, ...remaining === undefined ? {} : { limits: { timeoutMs: remaining } } });
11078
11292
  return bundle.entries;
11079
11293
  }
@@ -11179,7 +11393,7 @@ function assertFreshCachedProfile(receipt, options = {}) {
11179
11393
  }
11180
11394
  }
11181
11395
  function projectSelectionLockPath(projectDir) {
11182
- return join12(resolve8(projectDir), ".skills", SELECTION_LOCK_FILE);
11396
+ return join12(resolve9(projectDir), ".skills", SELECTION_LOCK_FILE);
11183
11397
  }
11184
11398
  function readProjectSelection(projectDir) {
11185
11399
  const receipt = readSelectionJson(projectSelectionLockPath(projectDir));
@@ -11338,13 +11552,13 @@ function publishSessionLock(path, marker) {
11338
11552
  fsyncSync(fd);
11339
11553
  linkSync(stagingPath, path);
11340
11554
  published = true;
11341
- syncDirectory(dirname8(path));
11555
+ syncDirectory(dirname9(path));
11342
11556
  const staged = lstatSync8(stagingPath);
11343
11557
  if (!staged.isFile() || staged.dev !== stat.dev || staged.ino !== stat.ino) {
11344
11558
  throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "The initialized lock staging inode changed before publication completed.");
11345
11559
  }
11346
11560
  unlinkSync(stagingPath);
11347
- syncDirectory(dirname8(path));
11561
+ syncDirectory(dirname9(path));
11348
11562
  handedOff = true;
11349
11563
  return { path, stagingPath, fd, dev: stat.dev, ino: stat.ino };
11350
11564
  } finally {
@@ -11389,14 +11603,14 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
11389
11603
  writeFileSync3(guardFd, `${JSON.stringify({ schemaVersion: 1, pid: process.pid, operationId: randomUUID() })}
11390
11604
  `);
11391
11605
  fsyncSync(guardFd);
11392
- const cacheOptions = { cacheDir: dirname8(dirname8(lock.path)) };
11606
+ const cacheOptions = { cacheDir: dirname9(dirname9(lock.path)) };
11393
11607
  const current = readSessionLock(lock.marker.sessionId, cacheOptions);
11394
11608
  if (!current || current.dev !== lock.dev || current.ino !== lock.ino || current.sha256 !== lock.sha256 || current.mtimeMs !== lock.mtimeMs || !writerIsGone(current, explicitLocalHost))
11395
11609
  return null;
11396
11610
  const currentReceipt = readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions);
11397
11611
  if (currentReceipt?.sha256 !== receipt?.sha256 || currentReceipt?.generation !== receipt?.generation)
11398
11612
  return null;
11399
- const directory = join12(dirname8(dirname8(lock.path)), "session-lock-recoveries", randomUUID());
11613
+ const directory = join12(dirname9(dirname9(lock.path)), "session-lock-recoveries", randomUUID());
11400
11614
  const archivePath = join12(directory, "original.write-lock");
11401
11615
  const recordPath = join12(directory, "recovery.json");
11402
11616
  assertRegularPath(archivePath, true);
@@ -11413,7 +11627,7 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
11413
11627
  })}
11414
11628
  `));
11415
11629
  syncDirectory(directory);
11416
- syncDirectory(dirname8(directory));
11630
+ syncDirectory(dirname9(directory));
11417
11631
  const finalLock = readSessionLock(lock.marker.sessionId, cacheOptions);
11418
11632
  if (!finalLock || finalLock.dev !== lock.dev || finalLock.ino !== lock.ino || finalLock.sha256 !== lock.sha256 || readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions)?.sha256 !== receipt?.sha256)
11419
11633
  return null;
@@ -11428,7 +11642,7 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
11428
11642
  archivedPaths.push(publicationPath);
11429
11643
  }
11430
11644
  syncDirectory(directory);
11431
- syncDirectory(dirname8(lock.path));
11645
+ syncDirectory(dirname9(lock.path));
11432
11646
  for (const path of archivedPaths) {
11433
11647
  const before = lstatSync8(path, { throwIfNoEntry: false });
11434
11648
  const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
@@ -11598,7 +11812,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11598
11812
  archiveBytes(receiptPath, Buffer.from(`${JSON.stringify(receipt)}
11599
11813
  `));
11600
11814
  syncDirectory(directory);
11601
- syncDirectory(dirname8(directory));
11815
+ syncDirectory(dirname9(directory));
11602
11816
  syncDirectory(selectionCacheRoot(options));
11603
11817
  if (sha256Hex(readRegularFile(archivePath, MAX_PROFILE_DOCUMENT_BYTES)) !== before.sha256 || !sameSnapshot(readSkillSessionSnapshot(replacement.sessionId, options), expected)) {
11604
11818
  throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The session or its preservation archive changed before replacement; retained evidence must be reviewed.");
@@ -11618,7 +11832,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11618
11832
  // package.json
11619
11833
  var package_default = {
11620
11834
  name: "@hasna/skills",
11621
- version: "0.10.51",
11835
+ version: "0.10.52",
11622
11836
  description: "Skills library for AI coding agents",
11623
11837
  type: "module",
11624
11838
  bin: {
@@ -11755,12 +11969,12 @@ import { spawnSync as spawnSync2 } from "child_process";
11755
11969
  import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
11756
11970
  import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
11757
11971
  import { hostname as osHostname } from "os";
11758
- import { isAbsolute as isAbsolute8, join as join22 } from "path";
11972
+ import { isAbsolute as isAbsolute9, join as join22 } from "path";
11759
11973
  import { createRequire } from "module";
11760
11974
  import { fileURLToPath as fileURLToPath2 } from "url";
11761
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";
11762
11976
  import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
11763
- 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";
11764
11978
  import { fileURLToPath, pathToFileURL } from "url";
11765
11979
  function envToken(name) {
11766
11980
  return name.toUpperCase().replace(/-/g, "_");
@@ -11866,9 +12080,9 @@ function entryPoint(directory) {
11866
12080
  return pathToFileURL(file).href;
11867
12081
  }
11868
12082
  function resolveInstalledSecrets(parentUrl) {
11869
- let directory = dirname9(fileURLToPath(parentUrl));
12083
+ let directory = dirname10(fileURLToPath(parentUrl));
11870
12084
  for (;; ) {
11871
- if (basename4(directory) !== "node_modules") {
12085
+ if (basename5(directory) !== "node_modules") {
11872
12086
  const candidate = join13(directory, "node_modules", PACKAGE);
11873
12087
  let absent = false;
11874
12088
  try {
@@ -11881,7 +12095,7 @@ function resolveInstalledSecrets(parentUrl) {
11881
12095
  if (!absent)
11882
12096
  return entryPoint(candidate);
11883
12097
  }
11884
- const parent = dirname9(directory);
12098
+ const parent = dirname10(directory);
11885
12099
  if (parent === directory)
11886
12100
  throw new Error("Secrets SDK is not installed for this consumer");
11887
12101
  directory = parent;
@@ -11901,8 +12115,8 @@ class CredentialResolutionError extends Error {
11901
12115
 
11902
12116
  class CredentialFileUnsafeError extends Error {
11903
12117
  path;
11904
- constructor(path, reason) {
11905
- super(`Refusing unsafe credential/config file ${path}: ${reason}.`);
12118
+ constructor(path, reason2) {
12119
+ super(`Refusing unsafe credential/config file ${path}: ${reason2}.`);
11906
12120
  this.name = "CredentialFileUnsafeError";
11907
12121
  this.path = path;
11908
12122
  }
@@ -11928,7 +12142,7 @@ function homeDir(env) {
11928
12142
  }
11929
12143
  function absoluteOverride(env, key) {
11930
12144
  const value = env[key]?.trim();
11931
- return value && isAbsolute8(value) ? value : null;
12145
+ return value && isAbsolute9(value) ? value : null;
11932
12146
  }
11933
12147
  function hasnaHomeDir(env) {
11934
12148
  const override = absoluteOverride(env, HASNA_HOME_ENV_KEY);
@@ -11974,9 +12188,9 @@ function parseEnvFile(text2) {
11974
12188
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
11975
12189
  continue;
11976
12190
  let value = withoutExport.slice(equals + 1).trim();
11977
- const quote = value[0];
11978
- if (quote === '"' || quote === "'") {
11979
- if (value.length < 2 || !value.endsWith(quote)) {
12191
+ const quote2 = value[0];
12192
+ if (quote2 === '"' || quote2 === "'") {
12193
+ if (value.length < 2 || !value.endsWith(quote2)) {
11980
12194
  unusable.add(key);
11981
12195
  continue;
11982
12196
  }
@@ -12000,8 +12214,8 @@ function configFileReadsCoherent(before, after) {
12000
12214
  return before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs;
12001
12215
  }
12002
12216
  function readAppConfigFile(path) {
12003
- const unsafe = (reason) => {
12004
- throw new CredentialFileUnsafeError(path, reason);
12217
+ const unsafe = (reason2) => {
12218
+ throw new CredentialFileUnsafeError(path, reason2);
12005
12219
  };
12006
12220
  let fd = -1;
12007
12221
  try {
@@ -12197,8 +12411,8 @@ function readKeychainItem(name, env, kind, options) {
12197
12411
  try {
12198
12412
  result = run(["find-generic-password", "-a", account, "-s", service, "-w"]);
12199
12413
  } catch (error) {
12200
- const reason = keychainFailureHint(error instanceof Error ? error.message : String(error));
12201
- 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]);
12202
12416
  }
12203
12417
  if (result.status === KEYCHAIN_ITEM_NOT_FOUND_STATUS)
12204
12418
  return null;
@@ -13016,7 +13230,7 @@ class MissingSkillsFleetError extends Error {
13016
13230
  }
13017
13231
 
13018
13232
  // src/lib/remote-files.ts
13019
- import { createHash as createHash10 } from "crypto";
13233
+ import { createHash as createHash11 } from "crypto";
13020
13234
  var MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
13021
13235
  function describeRemoteFiles(files) {
13022
13236
  if (files.length > 10)
@@ -13034,7 +13248,7 @@ function describeRemoteFiles(files) {
13034
13248
  });
13035
13249
  }
13036
13250
  function sha256(bytes) {
13037
- return createHash10("sha256").update(bytes).digest("hex");
13251
+ return createHash11("sha256").update(bytes).digest("hex");
13038
13252
  }
13039
13253
  async function readBoundedResponse(response, maximum, signal) {
13040
13254
  if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
@@ -13092,6 +13306,10 @@ function identifier(value) {
13092
13306
  function invalid() {
13093
13307
  throw new Error("The Skills API returned an invalid profile response");
13094
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
+ }
13095
13313
  function validateSelection2(value) {
13096
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))
13097
13315
  invalid();
@@ -13139,8 +13357,8 @@ class HttpProfileClient {
13139
13357
  ...init.headers
13140
13358
  }
13141
13359
  });
13142
- } catch {
13143
- 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");
13144
13362
  }
13145
13363
  if (!response.ok) {
13146
13364
  response.body?.cancel().catch(() => {});
@@ -13157,7 +13375,9 @@ class HttpProfileClient {
13157
13375
  async read(response, limit = MAX_PROFILE_DOCUMENT_BYTES) {
13158
13376
  try {
13159
13377
  return JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, limit, this.signal)));
13160
- } catch {
13378
+ } catch (error) {
13379
+ if (this.signal?.aborted)
13380
+ throw unavailable(this.signal.reason ?? error, "The Skills API response was cut short");
13161
13381
  invalid();
13162
13382
  }
13163
13383
  }
@@ -13244,17 +13464,17 @@ import { constants as constants5, closeSync as closeSync6, fstatSync as fstatSyn
13244
13464
  import { join as join14 } from "path";
13245
13465
 
13246
13466
  // src/lib/preparation-process.ts
13247
- 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)) {
13248
13468
  let spawnedPid;
13249
13469
  try {
13250
- spawnedPid = child.pid;
13470
+ spawnedPid = child2.pid;
13251
13471
  } catch {}
13252
13472
  const groupId = typeof spawnedPid === "number" && Number.isSafeInteger(spawnedPid) && spawnedPid > 1 ? spawnedPid : undefined;
13253
13473
  const canSignalGroup = platform !== "win32" && groupId !== undefined;
13254
13474
  const killLiveChild = () => {
13255
- if (child.exitCode === null && child.signalCode === null) {
13475
+ if (child2.exitCode === null && child2.signalCode === null) {
13256
13476
  try {
13257
- child.kill("SIGKILL");
13477
+ child2.kill("SIGKILL");
13258
13478
  } catch {}
13259
13479
  }
13260
13480
  };
@@ -13453,20 +13673,20 @@ import { join as join28 } from "path";
13453
13673
  // src/lib/codex-corpus-write.ts
13454
13674
  import { KernelLock } from "@hasna/contracts/kernel-lock";
13455
13675
  import { execFileSync } from "child_process";
13456
- import { createHash as createHash11 } from "crypto";
13676
+ import { createHash as createHash12 } from "crypto";
13457
13677
  import { accessSync, constants as constants6, lstatSync as lstatSync12, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync7 } from "fs";
13458
- 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";
13459
13679
  var KEY = ".native-corpus-admission";
13460
13680
  var fail = () => {
13461
13681
  throw new Error("CODEX_CORPUS_ADMISSION_REQUIRED: verified native admission and its existing shared lock are required");
13462
13682
  };
13463
13683
  function physicalTarget(path) {
13464
13684
  const suffix = [];
13465
- for (let cursor = path;; cursor = dirname10(cursor)) {
13685
+ for (let cursor = path;; cursor = dirname11(cursor)) {
13466
13686
  try {
13467
13687
  return join15(realpathSync3(cursor), ...suffix);
13468
13688
  } catch (error) {
13469
- if (error.code !== "ENOENT" || cursor === dirname10(cursor))
13689
+ if (error.code !== "ENOENT" || cursor === dirname11(cursor))
13470
13690
  throw error;
13471
13691
  try {
13472
13692
  if (lstatSync12(cursor).isSymbolicLink())
@@ -13475,14 +13695,14 @@ function physicalTarget(path) {
13475
13695
  if (statError.code !== "ENOENT")
13476
13696
  throw statError;
13477
13697
  }
13478
- suffix.unshift(basename5(cursor));
13698
+ suffix.unshift(basename6(cursor));
13479
13699
  }
13480
13700
  }
13481
13701
  }
13482
13702
  function codexCorpusRootForPath(path) {
13483
- const target = resolve9(path), physical = physicalTarget(target);
13703
+ const target = resolve10(path), physical = physicalTarget(target);
13484
13704
  const selected = process.env.CODEX_HOME;
13485
- const selectedRoots = selected && isAbsolute9(selected) ? [resolve9(selected), physicalTarget(resolve9(selected))] : [];
13705
+ const selectedRoots = selected && isAbsolute10(selected) ? [resolve10(selected), physicalTarget(resolve10(selected))] : [];
13486
13706
  const classify = (candidate) => {
13487
13707
  const checked = (root2) => {
13488
13708
  if (candidate === root2 || candidate === join15(root2, `${KEY}.flock-v1`))
@@ -13491,10 +13711,10 @@ function codexCorpusRootForPath(path) {
13491
13711
  };
13492
13712
  for (const root2 of selectedRoots) {
13493
13713
  const distance = relative4(root2, candidate);
13494
- if (distance === "" || !distance.startsWith(`..${sep5}`) && distance !== ".." && !isAbsolute9(distance))
13714
+ if (distance === "" || !distance.startsWith(`..${sep5}`) && distance !== ".." && !isAbsolute10(distance))
13495
13715
  return checked(root2);
13496
13716
  }
13497
- for (let cursor = candidate;cursor !== dirname10(cursor); cursor = dirname10(cursor)) {
13717
+ for (let cursor = candidate;cursor !== dirname11(cursor); cursor = dirname11(cursor)) {
13498
13718
  if (cursor.endsWith(`${sep5}.codex`))
13499
13719
  return checked(cursor);
13500
13720
  }
@@ -13520,10 +13740,10 @@ function executable(options) {
13520
13740
  if (typeof command !== "string" || !command || command.includes("\x00") || options.codexSha256 !== undefined && !/^[a-f0-9]{64}$/.test(options.codexSha256))
13521
13741
  return fail();
13522
13742
  let found;
13523
- if (isAbsolute9(command))
13743
+ if (isAbsolute10(command))
13524
13744
  found = command;
13525
13745
  else if (!command.includes(sep5)) {
13526
- 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) => {
13527
13747
  try {
13528
13748
  accessSync(candidate, constants6.X_OK);
13529
13749
  return true;
@@ -13537,7 +13757,7 @@ function executable(options) {
13537
13757
  const path = realpathSync3(found), initial = statSync7(path, { bigint: true });
13538
13758
  if (!initial.isFile() || (initial.mode & 0o022n) !== 0n || (initial.mode & 0o111n) === 0n || initial.uid !== BigInt(process.getuid()) && initial.uid !== 0n)
13539
13759
  return fail();
13540
- const hash2 = () => createHash11("sha256").update(readFileSync11(path)).digest("hex");
13760
+ const hash2 = () => createHash12("sha256").update(readFileSync11(path)).digest("hex");
13541
13761
  const digest = hash2();
13542
13762
  if (options.codexSha256 && digest !== options.codexSha256)
13543
13763
  return fail();
@@ -13561,7 +13781,7 @@ function acquireCodexCorpusWrite(roots, options) {
13561
13781
  }
13562
13782
  };
13563
13783
  try {
13564
- 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")))
13565
13785
  fail();
13566
13786
  const inputs = [...new Set(roots)].map((input) => ({ input, root: realpathSync3(input) })).sort((a, b) => a.root.localeCompare(b.root));
13567
13787
  for (const { input, root } of inputs) {
@@ -13631,10 +13851,10 @@ import {
13631
13851
  writeFileSync as writeFileSync7
13632
13852
  } from "fs";
13633
13853
  import { homedir as homedir3 } from "os";
13634
- 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";
13635
13855
 
13636
13856
  // src/lib/codex-plugin-skill-controls.ts
13637
- import { createHash as createHash12 } from "crypto";
13857
+ import { createHash as createHash13 } from "crypto";
13638
13858
 
13639
13859
  // src/lib/codex-local-mcp-controls.ts
13640
13860
  function isCodexLocalStdioMcpServer(name, server) {
@@ -13703,24 +13923,24 @@ function isCodexLocalStdioMcpServer(name, server) {
13703
13923
  }
13704
13924
 
13705
13925
  // src/lib/codex-plugin-skill-controls.ts
13706
- 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";
13707
13927
  import { lstatSync as lstatSync13, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync6 } from "fs";
13708
13928
 
13709
13929
  // src/lib/codex-native-skill-catalog.ts
13710
13930
  import { realpathSync as realpathSync5 } from "fs";
13711
- import { isAbsolute as isAbsolute11, normalize as normalize2 } from "path";
13931
+ import { isAbsolute as isAbsolute12, normalize as normalize2 } from "path";
13712
13932
 
13713
13933
  // src/lib/codex-hook-rpc.ts
13714
13934
  import { join as join16 } from "path";
13715
13935
  import { execFile, spawn } from "child_process";
13716
13936
  import { realpathSync as realpathSync4, statSync as statSync8 } from "fs";
13717
- import { isAbsolute as isAbsolute10 } from "path";
13937
+ import { isAbsolute as isAbsolute11 } from "path";
13718
13938
  import { promisify } from "util";
13719
13939
  async function connectCodexHookRpc(options) {
13720
13940
  const timeoutMs = options.timeoutMs ?? 20000;
13721
13941
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 20000)
13722
13942
  throw new Error("CODEX_HOOK_TRUST_INVALID_TIMEOUT");
13723
- 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 });
13724
13944
  if (!found)
13725
13945
  throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE: install Codex or pass --codex-command");
13726
13946
  const binary = realpathSync4(found), stat = statSync8(binary);
@@ -13744,16 +13964,16 @@ async function connectCodexHookRpc(options) {
13744
13964
  else
13745
13965
  env.CODEX_HOME = options.codexHome;
13746
13966
  const corpusLease = acquireCodexCorpusWrite([options.codexHome ?? join16(options.home, ".codex")], { codexCommand: binary });
13747
- const child = spawn(binary, ["app-server", "--strict-config", "--stdio"], {
13967
+ const child2 = spawn(binary, ["app-server", "--strict-config", "--stdio"], {
13748
13968
  cwd: options.home,
13749
13969
  env,
13750
13970
  stdio: ["pipe", "pipe", "pipe"]
13751
13971
  });
13752
13972
  let childClosed = false;
13753
- const actualClose = new Promise((resolve10) => child.once("close", () => {
13973
+ const actualClose = new Promise((resolve11) => child2.once("close", () => {
13754
13974
  childClosed = true;
13755
13975
  corpusLease.close();
13756
- resolve10();
13976
+ resolve11();
13757
13977
  }));
13758
13978
  const pending = new Map;
13759
13979
  let nextId = 1, buffer = Buffer.alloc(0), stopped = false, diagnosticBytes = 0;
@@ -13765,20 +13985,20 @@ async function connectCodexHookRpc(options) {
13765
13985
  }
13766
13986
  pending.clear();
13767
13987
  };
13768
- child.on("error", fail2);
13769
- child.on("exit", fail2);
13770
- child.stdin.on("error", fail2);
13771
- 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) => {
13772
13992
  diagnosticBytes += bytes.length;
13773
13993
  if (diagnosticBytes > 2 * 1024 * 1024) {
13774
13994
  fail2();
13775
- child.kill();
13995
+ child2.kill();
13776
13996
  }
13777
13997
  });
13778
- child.stdout.on("data", (bytes) => {
13998
+ child2.stdout.on("data", (bytes) => {
13779
13999
  if (buffer.length + bytes.length > 8 * 1024 * 1024) {
13780
14000
  fail2();
13781
- child.kill();
14001
+ child2.kill();
13782
14002
  return;
13783
14003
  }
13784
14004
  buffer = Buffer.concat([buffer, bytes]);
@@ -13793,12 +14013,12 @@ async function connectCodexHookRpc(options) {
13793
14013
  value = JSON.parse(line.toString("utf8"));
13794
14014
  } catch {
13795
14015
  fail2();
13796
- child.kill();
14016
+ child2.kill();
13797
14017
  return;
13798
14018
  }
13799
14019
  if (!value || typeof value !== "object" || Array.isArray(value)) {
13800
14020
  fail2();
13801
- child.kill();
14021
+ child2.kill();
13802
14022
  return;
13803
14023
  }
13804
14024
  const waiting = pending.get(value.id);
@@ -13814,29 +14034,29 @@ async function connectCodexHookRpc(options) {
13814
14034
  });
13815
14035
  const rpc = {
13816
14036
  version,
13817
- processId: child.pid,
14037
+ processId: child2.pid,
13818
14038
  request(method, params) {
13819
14039
  if (stopped)
13820
14040
  return Promise.reject(new Error("CODEX_HOOK_TRUST_NATIVE_RPC_CLOSED"));
13821
- return new Promise((resolve10, reject) => {
14041
+ return new Promise((resolve11, reject) => {
13822
14042
  const id = nextId++;
13823
14043
  const timer = setTimeout(() => {
13824
14044
  fail2();
13825
- child.kill();
14045
+ child2.kill();
13826
14046
  }, timeoutMs);
13827
- pending.set(id, { resolve: resolve10, reject, timer });
13828
- 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 }) + `
13829
14049
  `);
13830
14050
  });
13831
14051
  },
13832
14052
  async close() {
13833
14053
  fail2();
13834
- child.stdin.end();
14054
+ child2.stdin.end();
13835
14055
  if (childClosed)
13836
14056
  return;
13837
- await new Promise((resolve10, reject) => {
14057
+ await new Promise((resolve11, reject) => {
13838
14058
  const terminate = setTimeout(() => {
13839
- child.kill();
14059
+ child2.kill();
13840
14060
  }, 1000);
13841
14061
  const deadline = setTimeout(() => {
13842
14062
  clearTimeout(terminate);
@@ -13845,7 +14065,7 @@ async function connectCodexHookRpc(options) {
13845
14065
  actualClose.then(() => {
13846
14066
  clearTimeout(terminate);
13847
14067
  clearTimeout(deadline);
13848
- resolve10();
14068
+ resolve11();
13849
14069
  });
13850
14070
  });
13851
14071
  }
@@ -13855,7 +14075,7 @@ async function connectCodexHookRpc(options) {
13855
14075
  const nativeVersion = version.slice("codex-cli ".length);
13856
14076
  if (typeof initialized?.userAgent !== "string" || !(initialized.userAgent === `skills-native-hook-enrollment/${nativeVersion}` || initialized.userAgent.startsWith(`skills-native-hook-enrollment/${nativeVersion} `)))
13857
14077
  throw new Error("CODEX_HOOK_TRUST_NATIVE_VERSION_MISMATCH");
13858
- child.stdin.write(`{"method":"initialized"}
14078
+ child2.stdin.write(`{"method":"initialized"}
13859
14079
  `);
13860
14080
  return rpc;
13861
14081
  } catch {
@@ -13879,7 +14099,7 @@ function refuse() {
13879
14099
  var object4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
13880
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);
13881
14101
  var remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value);
13882
- var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute11(value);
14102
+ var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute12(value);
13883
14103
  function projectPluginSource(source) {
13884
14104
  if (!object4(source))
13885
14105
  return {};
@@ -14078,7 +14298,7 @@ function mcpControls(path, read2) {
14078
14298
  function isBundledCleanupHooks(root, namespace, value) {
14079
14299
  const object5 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
14080
14300
  const exactKeys = (v, keys) => Object.keys(v).length === keys.length && keys.every((key) => Object.hasOwn(v, key));
14081
- if (basename6(dirname11(root)) !== namespace || basename6(dirname11(dirname11(root))) !== "openai-bundled")
14301
+ if (basename7(dirname12(root)) !== namespace || basename7(dirname12(dirname12(root))) !== "openai-bundled")
14082
14302
  return false;
14083
14303
  const server = namespace === "unified-computer-use" ? "cua_repl" : ["browser", "chrome", "chrome-dev", "chrome-internal", "computer-use"].includes(namespace) ? "node_repl" : undefined;
14084
14304
  if (!server || !object5(value) || !exactKeys(value, ["hooks"]) || !object5(value.hooks))
@@ -14112,7 +14332,7 @@ function rootControls(root, read2) {
14112
14332
  }
14113
14333
  if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
14114
14334
  refuse2();
14115
- 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;
14116
14336
  if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
14117
14337
  refuse2();
14118
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 }))
@@ -14157,7 +14377,7 @@ function documentIdentity(document2, cache, read2) {
14157
14377
  }
14158
14378
  if (!front || typeof front !== "object" || !identifier2(front.name) || typeof front.description !== "string" || !front.description.trim())
14159
14379
  refuse2();
14160
- 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++) {
14161
14381
  const path = join17(root, ".codex-plugin", "plugin.json");
14162
14382
  if (!lstatSync13(path, { throwIfNoEntry: false }))
14163
14383
  continue;
@@ -14175,9 +14395,9 @@ function documentIdentity(document2, cache, read2) {
14175
14395
  if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
14176
14396
  refuse2();
14177
14397
  const declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
14178
- 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))
14179
14399
  refuse2();
14180
- 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 };
14181
14401
  }
14182
14402
  refuse2();
14183
14403
  }
@@ -14206,13 +14426,13 @@ function classifyCodexPluginCacheDocument(document2, cache, read2) {
14206
14426
  function reviewedDisabledCodexPluginParent(cache, parent, controls, rules) {
14207
14427
  try {
14208
14428
  const parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
14209
- 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))
14210
14430
  return false;
14211
- for (let cursor = parent;; cursor = dirname11(cursor)) {
14431
+ for (let cursor = parent;; cursor = dirname12(cursor)) {
14212
14432
  const stat = lstatSync13(cursor, { throwIfNoEntry: false });
14213
14433
  if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
14214
14434
  return false;
14215
- if (dirname11(cursor) === cursor)
14435
+ if (dirname12(cursor) === cursor)
14216
14436
  break;
14217
14437
  }
14218
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));
@@ -14225,14 +14445,14 @@ function absentDisabledCodexPluginParent(cache, parent, controls, rules) {
14225
14445
  }
14226
14446
  function reviewedRetiredCodexRoot(cache, root, controls, rules, read2, allowAbsent = false) {
14227
14447
  try {
14228
- const parent = dirname11(root), parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
14229
- 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))
14230
14450
  return false;
14231
- for (let cursor = root;; cursor = dirname11(cursor)) {
14451
+ for (let cursor = root;; cursor = dirname12(cursor)) {
14232
14452
  const stat = lstatSync13(cursor, { throwIfNoEntry: false });
14233
14453
  if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
14234
14454
  return false;
14235
- if (dirname11(cursor) === cursor)
14455
+ if (dirname12(cursor) === cursor)
14236
14456
  break;
14237
14457
  }
14238
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)))
@@ -14279,8 +14499,8 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2, rules)
14279
14499
  for (const name of names) {
14280
14500
  let root = join17(parent, name), stat = lstatSync13(root);
14281
14501
  if (stat.isSymbolicLink()) {
14282
- const target = resolve10(parent, readlinkSync3(root));
14283
- 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)
14284
14504
  return false;
14285
14505
  root = target;
14286
14506
  stat = lstatSync13(root);
@@ -14332,7 +14552,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14332
14552
  if (listed.length > 1)
14333
14553
  refuse2();
14334
14554
  const installed = installedPlugins.filter((plugin) => plugin.id === expectedPluginId && plugin.name === pluginName);
14335
- if (installedPluginReview && 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])) {
14336
14556
  const plugin = installed[0], input = { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceRoot: plugin.sourcePath, sourceSha256: plugin.sourceSha256 };
14337
14557
  if (input.sourceRoot !== located.root && !input.sourceRoot.startsWith(cache + sep6) && (lstatSync13(input.sourceRoot, { throwIfNoEntry: false }) || documents.some((path) => path.startsWith(input.sourceRoot + sep6)))) {
14338
14558
  if (skills.some((skill) => skill.path === input.sourceRoot || skill.path.startsWith(input.sourceRoot + sep6)) || !sourceInputUnchanged(input, cache, read2))
@@ -14364,7 +14584,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14364
14584
  refuse2();
14365
14585
  const plugin = installed[0];
14366
14586
  if (plugin.localVersion !== null) {
14367
- if (plugin.localVersion !== basename6(dirname11(dirname11(dirname11(document2)))))
14587
+ if (plugin.localVersion !== basename7(dirname12(dirname12(dirname12(document2)))))
14368
14588
  continue;
14369
14589
  } else {
14370
14590
  if (plugin.sourceType !== "remote" || !plugin.remotePluginId || !remoteInstallationMatches(parsed.pluginParent, plugin.remotePluginId, read2) || !lstatSync13(document2).isFile() || realpathSync6(document2) !== document2)
@@ -14374,7 +14594,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14374
14594
  const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
14375
14595
  const nativeSkill = { name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true };
14376
14596
  const denial = denials.find((item) => item.path === document2);
14377
- const explicitDeny = denial !== undefined && remotePluginId !== undefined && installedPluginReview && 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);
14378
14598
  if (denial && !explicitDeny)
14379
14599
  refuse2();
14380
14600
  if (explicitDeny)
@@ -14395,7 +14615,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14395
14615
  function sourceInputUnchanged(input, cache, read2) {
14396
14616
  try {
14397
14617
  const parts = relative5(cache, input.pluginParent).split(sep6);
14398
- 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))
14399
14619
  return false;
14400
14620
  const path = join17(input.sourceRoot, ".codex-plugin/plugin.json"), stat = lstatSync13(path);
14401
14621
  if (!stat.isFile() || stat.size > 1024 * 1024 || realpathSync6(path) !== path)
@@ -14461,9 +14681,9 @@ enabled = false
14461
14681
  }
14462
14682
 
14463
14683
  // src/lib/codex-native-skill-policy.ts
14464
- import { createHash as createHash13, randomUUID as randomUUID2 } from "crypto";
14684
+ import { createHash as createHash14, randomUUID as randomUUID2 } from "crypto";
14465
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";
14466
- 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";
14467
14687
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
14468
14688
  var CODEX_NATIVE_POLICY_CAPABILITY = "host-path-allowlist-v1";
14469
14689
  var CODEX_NATIVE_POLICY_RECEIPT_SCHEMA = "skills.codex-native-policy-acceptance/v1";
@@ -14486,9 +14706,9 @@ function refuse3(code, detail) {
14486
14706
  throw new Error(`${code}: ${detail}`);
14487
14707
  }
14488
14708
  var object5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
14489
- function boundedId(value, field) {
14709
+ function boundedId(value, field2) {
14490
14710
  if (typeof value !== "string" || !value || value.length > MAX_ENVELOPE_ID_LENGTH || value.trim() !== value || /[\u0000-\u001f\u007f]/.test(value))
14491
- 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`);
14492
14712
  return value;
14493
14713
  }
14494
14714
  function parseCodexNativePolicyTrust(value) {
@@ -14511,9 +14731,9 @@ function parseCodexNativeHookEnvelope(envelope, bridgeDocument) {
14511
14731
  if (!object5(value))
14512
14732
  refuse3("NATIVE_SKILL_POLICY_INVALID", "native_skill_policy must be an object");
14513
14733
  const keys = Object.keys(value);
14514
- for (const field of POLICY_FIELDS)
14515
- if (!Object.hasOwn(value, field))
14516
- 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`);
14517
14737
  if (keys.length !== POLICY_FIELDS.length)
14518
14738
  refuse3("NATIVE_SKILL_POLICY_INVALID", "native_skill_policy carries unknown fields");
14519
14739
  if (value.capability !== CODEX_NATIVE_POLICY_CAPABILITY)
@@ -14577,19 +14797,19 @@ function readBounded(path, maximum, what) {
14577
14797
  }
14578
14798
  }
14579
14799
  function verifyCodexNativeBridgeDocument(path, expectedContent, expectedSha256) {
14580
- if (!isAbsolute12(path) || resolve11(path) !== path || !path.endsWith("/SKILL.md"))
14800
+ if (!isAbsolute13(path) || resolve12(path) !== path || !path.endsWith("/SKILL.md"))
14581
14801
  refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document path is not an exact absolute SKILL.md path");
14582
- for (let cursor = path;; cursor = dirname12(cursor)) {
14802
+ for (let cursor = path;; cursor = dirname13(cursor)) {
14583
14803
  const stat = lstatSync14(cursor, { throwIfNoEntry: false });
14584
14804
  if (!stat || stat.isSymbolicLink() || (cursor === path ? !stat.isFile() : !stat.isDirectory()))
14585
14805
  refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", `the bridge path has a missing, linked or unexpected component: ${cursor}`);
14586
- if (dirname12(cursor) === cursor)
14806
+ if (dirname13(cursor) === cursor)
14587
14807
  break;
14588
14808
  }
14589
14809
  if (realpathSync7.native(path) !== path)
14590
14810
  refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document real path differs from its lexical path");
14591
14811
  const bytes = readBounded(path, MAX_BRIDGE_DOCUMENT_BYTES, "the bridge document");
14592
- const sha2562 = createHash13("sha256").update(bytes).digest("hex");
14812
+ const sha2562 = createHash14("sha256").update(bytes).digest("hex");
14593
14813
  if (!bytes.equals(Buffer.from(expectedContent, "utf8")) || sha2562 !== expectedSha256)
14594
14814
  refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document bytes differ from the managed bridge");
14595
14815
  return { path, sha256: sha2562 };
@@ -14655,13 +14875,13 @@ function assertExecutableWitnessStat(stat, currentUid) {
14655
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) };
14656
14876
  }
14657
14877
  function executableWitness(path) {
14658
- if (!isAbsolute12(path) || resolve11(path) !== path)
14878
+ if (!isAbsolute13(path) || resolve12(path) !== path)
14659
14879
  refuse3("NATIVE_SKILL_POLICY_PROCESS_UNBOUND", "the consumer executable path is not an exact absolute path");
14660
- for (let cursor = dirname12(path);; cursor = dirname12(cursor)) {
14880
+ for (let cursor = dirname13(path);; cursor = dirname13(cursor)) {
14661
14881
  const stat2 = lstatSync14(cursor, { throwIfNoEntry: false });
14662
14882
  if (!stat2 || stat2.isSymbolicLink() || !stat2.isDirectory())
14663
14883
  refuse3("NATIVE_SKILL_POLICY_PROCESS_UNBOUND", `the consumer executable path has a missing, linked or unexpected component: ${cursor}`);
14664
- if (dirname12(cursor) === cursor)
14884
+ if (dirname13(cursor) === cursor)
14665
14885
  break;
14666
14886
  }
14667
14887
  const stat = lstatSync14(path, { throwIfNoEntry: false, bigint: true });
@@ -14672,7 +14892,7 @@ function executableWitness(path) {
14672
14892
  var witnessKey = (path, witness) => JSON.stringify([path, witness.dev, witness.ino, witness.size, witness.mtimeNs, witness.ctimeNs, witness.uid]);
14673
14893
  var executableDigestMemory = new Map;
14674
14894
  function executableCachePath(dataDir2) {
14675
- 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");
14676
14896
  }
14677
14897
  var currentUid = () => process.getuid?.() ?? -1;
14678
14898
  function readTrustedCacheBytes(path, ownerUid) {
@@ -14717,16 +14937,16 @@ function readExecutableCache(dataDir2, ownerUid = currentUid()) {
14717
14937
  if (!object5(value) || value.schema !== CODEX_NATIVE_POLICY_EXECUTABLE_CACHE_SCHEMA || !Array.isArray(value.entries) || value.entries.length > 16)
14718
14938
  return [];
14719
14939
  const fields = ["path", "sha256", "dev", "ino", "size", "mtimeNs", "ctimeNs", "uid"];
14720
- 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])));
14721
14941
  } catch {
14722
14942
  return [];
14723
14943
  }
14724
14944
  }
14725
14945
  function writeExecutableCache(dataDir2, entries) {
14726
- const path = executableCachePath(dataDir2), directory = dirname12(path), temporary = `${path}.skills-${randomUUID2()}`;
14946
+ const path = executableCachePath(dataDir2), directory = dirname13(path), temporary = `${path}.skills-${randomUUID2()}`;
14727
14947
  let descriptor;
14728
14948
  try {
14729
- const parent = lstatSync14(dirname12(directory), { throwIfNoEntry: false });
14949
+ const parent = lstatSync14(dirname13(directory), { throwIfNoEntry: false });
14730
14950
  if (!parent || parent.isSymbolicLink() || !parent.isDirectory())
14731
14951
  return;
14732
14952
  const existing = lstatSync14(directory, { throwIfNoEntry: false });
@@ -14794,16 +15014,16 @@ function verifyCodexNativeExecutable(inspector, processId, trust, cache = {}, ex
14794
15014
  }
14795
15015
  function assertOperatorTrustRoot(dataDir2) {
14796
15016
  const check = (path, what, required) => {
14797
- if (!isAbsolute12(path) || resolve11(path) !== path)
15017
+ if (!isAbsolute13(path) || resolve12(path) !== path)
14798
15018
  refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} path is not an exact absolute path`);
14799
- for (let cursor = dirname12(path);; cursor = dirname12(cursor)) {
15019
+ for (let cursor = dirname13(path);; cursor = dirname13(cursor)) {
14800
15020
  const stat2 = lstatSync14(cursor, { throwIfNoEntry: false });
14801
15021
  if (!stat2) {
14802
15022
  if (required)
14803
15023
  refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} has a missing path component: ${cursor}`);
14804
15024
  } else if (stat2.isSymbolicLink() || !stat2.isDirectory())
14805
15025
  refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} has a linked or unexpected path component: ${cursor}`);
14806
- if (dirname12(cursor) === cursor)
15026
+ if (dirname13(cursor) === cursor)
14807
15027
  break;
14808
15028
  }
14809
15029
  const stat = lstatSync14(path, { throwIfNoEntry: false, bigint: true });
@@ -14833,9 +15053,9 @@ function assertOperatorTrustRoot(dataDir2) {
14833
15053
  if ((stat.mode & 0o022n) !== 0n)
14834
15054
  refuse3("NATIVE_SKILL_POLICY_TRUST_INVALID", `${what} is group- or world-writable`);
14835
15055
  };
14836
- checkDirectory(resolve11(dataDir2), "the Skills data directory", true);
14837
- checkDirectory(dirname12(executableCachePath(dataDir2)), "the agent-hooks directory", false);
14838
- 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);
14839
15059
  check(executableCachePath(dataDir2), "the executable identity cache", false);
14840
15060
  }
14841
15061
  function runCodexNativePolicyHelper(request) {
@@ -15006,12 +15226,12 @@ function defaultProcessInspector() {
15006
15226
  return { platform: process.platform, arch: process.arch, pid: process.pid, parentOf: () => null, startTime: () => null, executablePath: () => null };
15007
15227
  }
15008
15228
  function codexNativePolicyReceiptPath(dataDir2) {
15009
- return join18(resolve11(dataDir2), "agent-hooks", "codex-native-policy-acceptance.json");
15229
+ return join18(resolve12(dataDir2), "agent-hooks", "codex-native-policy-acceptance.json");
15010
15230
  }
15011
15231
  function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
15012
- const path = codexNativePolicyReceiptPath(dataDir2), directory = dirname12(path);
15232
+ const path = codexNativePolicyReceiptPath(dataDir2), directory = dirname13(path);
15013
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);
15014
- 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)
15015
15235
  refuse3("NATIVE_SKILL_POLICY_RECEIPT_FAILED", "accepted cache entries must be unique absolute paths with tree digests");
15016
15236
  const value = {
15017
15237
  schema: CODEX_NATIVE_POLICY_RECEIPT_SCHEMA,
@@ -15057,11 +15277,11 @@ function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
15057
15277
  }
15058
15278
 
15059
15279
  // src/lib/codex-settings-witness-v1.ts
15060
- import { createHash as createHash14 } from "crypto";
15061
- 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");
15062
15282
  var EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
15063
15283
  var VERBOSITY = new Set(["low", "medium", "high"]);
15064
- function need3(value) {
15284
+ function need4(value) {
15065
15285
  if (!value)
15066
15286
  throw new Error("Invalid Codex settings witness");
15067
15287
  }
@@ -15076,14 +15296,14 @@ function numericTokens(text2) {
15076
15296
  continue;
15077
15297
  }
15078
15298
  if (char === '"' || char === "'") {
15079
- 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);
15080
15300
  at += triple ? 3 : 1;
15081
15301
  while (at < text2.length) {
15082
- if (quote === '"' && text2[at] === "\\") {
15302
+ if (quote2 === '"' && text2[at] === "\\") {
15083
15303
  at += 2;
15084
15304
  continue;
15085
15305
  }
15086
- 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)) {
15087
15307
  at += triple ? 3 : 1;
15088
15308
  break;
15089
15309
  }
@@ -15104,8 +15324,8 @@ function numericTokens(text2) {
15104
15324
  }
15105
15325
  return result;
15106
15326
  }
15107
- function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
15108
- need3(depth <= 32 && --budget2.remaining >= 0);
15327
+ function canonical5(value, depth = 0, budget2 = { remaining: 65536 }) {
15328
+ need4(depth <= 32 && --budget2.remaining >= 0);
15109
15329
  if (value === null)
15110
15330
  return ["null"];
15111
15331
  if (typeof value === "string")
@@ -15113,20 +15333,20 @@ function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
15113
15333
  if (typeof value === "boolean")
15114
15334
  return ["boolean", value];
15115
15335
  if (typeof value === "number") {
15116
- need3(Number.isFinite(value));
15336
+ need4(Number.isFinite(value));
15117
15337
  return ["number", String(value)];
15118
15338
  }
15119
15339
  if (value instanceof Date) {
15120
- need3(Number.isFinite(value.getTime()));
15340
+ need4(Number.isFinite(value.getTime()));
15121
15341
  return ["date", value.toISOString()];
15122
15342
  }
15123
15343
  if (Array.isArray(value))
15124
- return ["array", value.map((child) => canonical4(child, depth + 1, budget2))];
15125
- need3(value && typeof value === "object");
15126
- 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)])];
15127
15347
  }
15128
15348
  function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
15129
- need3(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15349
+ need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15130
15350
  budget2.remaining -= Buffer.byteLength(text2);
15131
15351
  let config;
15132
15352
  try {
@@ -15134,17 +15354,17 @@ function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 *
15134
15354
  } catch {
15135
15355
  throw new Error("Invalid Codex settings witness");
15136
15356
  }
15137
- need3(config && typeof config === "object" && !Array.isArray(config));
15357
+ need4(config && typeof config === "object" && !Array.isArray(config));
15138
15358
  if (Object.hasOwn(config, "model")) {
15139
- 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));
15140
15360
  delete config.model;
15141
15361
  }
15142
15362
  for (const [key, values] of [["model_reasoning_effort", EFFORT], ["model_verbosity", VERBOSITY]])
15143
15363
  if (Object.hasOwn(config, key)) {
15144
- need3(typeof config[key] === "string" && values.has(config[key]));
15364
+ need4(typeof config[key] === "string" && values.has(config[key]));
15145
15365
  delete config[key];
15146
15366
  }
15147
- 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)])}`);
15148
15368
  }
15149
15369
  function captureCodexSettings(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
15150
15370
  const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
@@ -15215,15 +15435,15 @@ function projectRegularCodexMcp(config, source) {
15215
15435
  }
15216
15436
 
15217
15437
  // src/lib/codex-settings-witness.ts
15218
- import { createHash as createHash15 } from "crypto";
15438
+ import { createHash as createHash16 } from "crypto";
15219
15439
  import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
15220
- var sha2 = (text2) => createHash15("sha256").update(text2).digest("hex");
15440
+ var sha2 = (text2) => createHash16("sha256").update(text2).digest("hex");
15221
15441
  var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
15222
15442
  var VERBOSITY2 = new Set(["low", "medium", "high"]);
15223
15443
  var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
15224
15444
  var CODEX_DISCOVERY_PROJECTION_FIELDS = ["plugins", "marketplaces", "skills"];
15225
15445
  var INFERENCE = ["model", "model_reasoning_effort", "model_verbosity"];
15226
- function need4(value) {
15446
+ function need5(value) {
15227
15447
  if (!value)
15228
15448
  throw new Error("Invalid Codex settings witness");
15229
15449
  }
@@ -15244,14 +15464,14 @@ function numericTokens2(text2) {
15244
15464
  continue;
15245
15465
  }
15246
15466
  if (char === '"' || char === "'") {
15247
- 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);
15248
15468
  at += triple ? 3 : 1;
15249
15469
  while (at < text2.length) {
15250
- if (quote === '"' && text2[at] === "\\") {
15470
+ if (quote2 === '"' && text2[at] === "\\") {
15251
15471
  at += 2;
15252
15472
  continue;
15253
15473
  }
15254
- 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)) {
15255
15475
  at += triple ? 3 : 1;
15256
15476
  break;
15257
15477
  }
@@ -15272,8 +15492,8 @@ function numericTokens2(text2) {
15272
15492
  }
15273
15493
  return result.sort();
15274
15494
  }
15275
- function canonical5(value, depth = 0, budget2 = { remaining: 65536 }) {
15276
- need4(depth <= 32 && --budget2.remaining >= 0);
15495
+ function canonical6(value, depth = 0, budget2 = { remaining: 65536 }) {
15496
+ need5(depth <= 32 && --budget2.remaining >= 0);
15277
15497
  if (value === null)
15278
15498
  return ["null"];
15279
15499
  if (typeof value === "string")
@@ -15281,17 +15501,17 @@ function canonical5(value, depth = 0, budget2 = { remaining: 65536 }) {
15281
15501
  if (typeof value === "boolean")
15282
15502
  return ["boolean", value];
15283
15503
  if (typeof value === "number") {
15284
- need4(Number.isFinite(value));
15504
+ need5(Number.isFinite(value));
15285
15505
  return ["number", String(value)];
15286
15506
  }
15287
15507
  if (isDate(value)) {
15288
- need4(Number.isFinite(value.getTime()));
15508
+ need5(Number.isFinite(value.getTime()));
15289
15509
  return ["date", value.toISOString()];
15290
15510
  }
15291
15511
  if (Array.isArray(value))
15292
- return ["array", value.map((child) => canonical5(child, depth + 1, budget2))];
15293
- need4(isRecord3(value));
15294
- 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)])];
15295
15515
  }
15296
15516
  function normalize3(config, version = 2) {
15297
15517
  if (isRecord3(config.hooks)) {
@@ -15333,15 +15553,15 @@ function inference(target, version) {
15333
15553
  if (Object.hasOwn(target, key)) {
15334
15554
  const value = target[key];
15335
15555
  if (key === "model")
15336
- need4(typeof value === "string" && MODEL.test(value));
15556
+ need5(typeof value === "string" && MODEL.test(value));
15337
15557
  else if (key === "personality")
15338
- need4(typeof value === "string" && ["none", "friendly", "pragmatic"].includes(value));
15558
+ need5(typeof value === "string" && ["none", "friendly", "pragmatic"].includes(value));
15339
15559
  else if (version >= 3 && key !== "model_verbosity") {
15340
- 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));
15341
15561
  if (key !== "service_tier")
15342
- need4(value.length > 0);
15562
+ need5(value.length > 0);
15343
15563
  } else
15344
- 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)));
15345
15565
  delete target[key];
15346
15566
  }
15347
15567
  }
@@ -15374,14 +15594,14 @@ function projectModelAvailabilityNux(config, text2) {
15374
15594
  continue;
15375
15595
  }
15376
15596
  if (char === '"' || char === "'") {
15377
- const quote = char;
15597
+ const quote2 = char;
15378
15598
  at++;
15379
15599
  while (at < span.length) {
15380
- if (quote === '"' && span[at] === "\\") {
15600
+ if (quote2 === '"' && span[at] === "\\") {
15381
15601
  at += 2;
15382
15602
  continue;
15383
15603
  }
15384
- if (span[at++] === quote)
15604
+ if (span[at++] === quote2)
15385
15605
  break;
15386
15606
  }
15387
15607
  continue;
@@ -15461,14 +15681,14 @@ function settingsDigest2(text2, version = 2) {
15461
15681
  } catch {
15462
15682
  throw new Error("Invalid Codex settings witness");
15463
15683
  }
15464
- need4(isRecord3(config));
15684
+ need5(isRecord3(config));
15465
15685
  const mcp = version >= 3 ? projectRegularCodexMcp(config, text2) : { config, numericSource: text2 };
15466
15686
  const projected = version === 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
15467
15687
  normalize3(projected.config, version);
15468
- 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)])}`);
15469
15689
  }
15470
15690
  function hashCodexSettingsReplacementV2(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
15471
- need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15691
+ need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15472
15692
  budget2.remaining -= Buffer.byteLength(text2);
15473
15693
  return settingsDigest2(text2);
15474
15694
  }
@@ -15480,17 +15700,17 @@ function readCodexSettingsPreimage(path, budget2 = { remaining: 2 * 1024 * 1024
15480
15700
  return readNativeSettingsWitnessFile(path, budget2, "config.toml");
15481
15701
  }
15482
15702
  function upgradeCodexSettingsWitness(previous, reviewedSettingsPath) {
15483
- 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));
15484
15704
  const budget2 = { remaining: 2 * 1024 * 1024 };
15485
15705
  const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
15486
- 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);
15487
15707
  const sha2562 = settingsDigest2(current);
15488
15708
  if (settingsDigest2(before) !== sha2562)
15489
15709
  throw new Error("Codex non-preference settings changed; explicit discovery review required");
15490
15710
  return { path: previous.path, hashMode: "codex-settings-v2", sha256: sha2562 };
15491
15711
  }
15492
15712
  function hashCodexSettingsReplacementV3(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
15493
- need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15713
+ need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15494
15714
  budget2.remaining -= Buffer.byteLength(text2);
15495
15715
  return settingsDigest2(text2, 3);
15496
15716
  }
@@ -15498,18 +15718,18 @@ function captureCodexSettingsV3(path, budget2 = { remaining: 256 * 1024 * 1024 }
15498
15718
  return { path, hashMode: "codex-settings-v3", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 3) };
15499
15719
  }
15500
15720
  function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
15501
- 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));
15502
15722
  const budget2 = { remaining: 2 * 1024 * 1024 };
15503
15723
  const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
15504
15724
  const original = previous.hashMode === "codex-settings-v2" ? settingsDigest2(before) : previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before);
15505
- need4(original === previous.sha256);
15725
+ need5(original === previous.sha256);
15506
15726
  const sha2562 = settingsDigest2(current, 3);
15507
15727
  if (settingsDigest2(before, 3) !== sha2562)
15508
15728
  throw new Error("Codex settings outside the reviewed V3 contract changed; explicit discovery review required");
15509
15729
  return { path: previous.path, hashMode: "codex-settings-v3", sha256: sha2562 };
15510
15730
  }
15511
15731
  function hashCodexSettingsReplacementV4(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
15512
- need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15732
+ need5(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
15513
15733
  budget2.remaining -= Buffer.byteLength(text2);
15514
15734
  return settingsDigest2(text2, 4);
15515
15735
  }
@@ -15517,11 +15737,11 @@ function captureCodexSettingsV4(path, budget2 = { remaining: 256 * 1024 * 1024 }
15517
15737
  return { path, hashMode: "codex-settings-v4", sha256: settingsDigest2(readNativeSettingsWitnessFile(path, budget2, "config.toml"), 4) };
15518
15738
  }
15519
15739
  function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
15520
- 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));
15521
15741
  const budget2 = { remaining: 2 * 1024 * 1024 };
15522
15742
  const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget2, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget2, "config.toml");
15523
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);
15524
- need4(original === previous.sha256);
15744
+ need5(original === previous.sha256);
15525
15745
  const sha2562 = settingsDigest2(current, 4);
15526
15746
  if (settingsDigest2(before, 4) !== sha2562)
15527
15747
  throw new Error("Codex settings outside the reviewed V4 contract changed; explicit discovery review required");
@@ -15529,17 +15749,17 @@ function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
15529
15749
  }
15530
15750
 
15531
15751
  // src/lib/agent-integration.ts
15532
- import { createHash as createHash19, randomUUID as randomUUID3 } from "crypto";
15752
+ import { createHash as createHash20, randomUUID as randomUUID3 } from "crypto";
15533
15753
  import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
15534
- 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";
15535
- 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";
15536
15756
  import { homedir as homedir2 } from "os";
15537
15757
 
15538
15758
  // src/lib/managed-policy.ts
15539
15759
  import { closeSync as closeSync8, constants as constants8, fstatSync as fstatSync8, lstatSync as lstatSync15, openSync as openSync8, readSync as readSync6 } from "fs";
15540
15760
  import { join as join19 } from "path";
15541
- function refusal(reason = "is unreadable") {
15542
- 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.`);
15543
15763
  }
15544
15764
  function parseManagedSkillPolicy(text2) {
15545
15765
  if (Buffer.byteLength(text2) > AGENT_POLICY_LIMITS.bytes)
@@ -15616,7 +15836,7 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
15616
15836
  }
15617
15837
 
15618
15838
  // src/lib/agent-bridge.ts
15619
- import { createHash as createHash16 } from "crypto";
15839
+ import { createHash as createHash17 } from "crypto";
15620
15840
  import { lstatSync as lstatSync16, readFileSync as readFileSync13, readdirSync as readdirSync7 } from "fs";
15621
15841
  import { join as join20 } from "path";
15622
15842
  var CLI_BRIDGE_NAME = "skills-cli";
@@ -15635,7 +15855,7 @@ Use the Skills CLI as the skill authority for this station.
15635
15855
 
15636
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.
15637
15857
  `;
15638
- var CLI_BRIDGE_DIGEST = createHash16("sha256").update(markdown).digest("hex");
15858
+ var CLI_BRIDGE_DIGEST = createHash17("sha256").update(markdown).digest("hex");
15639
15859
  var CLI_BRIDGE_FILES = Object.freeze({
15640
15860
  "SKILL.md": markdown,
15641
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)}
@@ -15664,29 +15884,29 @@ var CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^
15664
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");
15665
15885
  function splitTopLevel(value, delimiter2) {
15666
15886
  const result = [];
15667
- let start = 0, square = 0, curly = 0, quote = null, escaped = false;
15887
+ let start = 0, square = 0, curly = 0, quote2 = null, escaped = false;
15668
15888
  for (let index = 0;index < value.length; index++) {
15669
15889
  const char = value[index];
15670
- if (quote === "double") {
15890
+ if (quote2 === "double") {
15671
15891
  if (escaped)
15672
15892
  escaped = false;
15673
15893
  else if (char === "\\")
15674
15894
  escaped = true;
15675
15895
  else if (char === '"')
15676
- quote = null;
15896
+ quote2 = null;
15677
15897
  continue;
15678
15898
  }
15679
- if (quote === "literal") {
15899
+ if (quote2 === "literal") {
15680
15900
  if (char === "'")
15681
- quote = null;
15901
+ quote2 = null;
15682
15902
  continue;
15683
15903
  }
15684
15904
  if (char === '"') {
15685
- quote = "double";
15905
+ quote2 = "double";
15686
15906
  continue;
15687
15907
  }
15688
15908
  if (char === "'") {
15689
- quote = "literal";
15909
+ quote2 = "literal";
15690
15910
  continue;
15691
15911
  }
15692
15912
  if (char === "[")
@@ -15704,7 +15924,7 @@ function splitTopLevel(value, delimiter2) {
15704
15924
  start = index + 1;
15705
15925
  }
15706
15926
  }
15707
- if (quote !== null || square !== 0 || curly !== 0)
15927
+ if (quote2 !== null || square !== 0 || curly !== 0)
15708
15928
  return null;
15709
15929
  result.push(value.slice(start));
15710
15930
  return result;
@@ -15712,29 +15932,29 @@ function splitTopLevel(value, delimiter2) {
15712
15932
  function inlineArrayEnd(value) {
15713
15933
  if (!value.startsWith("["))
15714
15934
  return null;
15715
- let square = 0, curly = 0, quote = null, escaped = false;
15935
+ let square = 0, curly = 0, quote2 = null, escaped = false;
15716
15936
  for (let index = 0;index < value.length; index++) {
15717
15937
  const char = value[index];
15718
- if (quote === "double") {
15938
+ if (quote2 === "double") {
15719
15939
  if (escaped)
15720
15940
  escaped = false;
15721
15941
  else if (char === "\\")
15722
15942
  escaped = true;
15723
15943
  else if (char === '"')
15724
- quote = null;
15944
+ quote2 = null;
15725
15945
  continue;
15726
15946
  }
15727
- if (quote === "literal") {
15947
+ if (quote2 === "literal") {
15728
15948
  if (char === "'")
15729
- quote = null;
15949
+ quote2 = null;
15730
15950
  continue;
15731
15951
  }
15732
15952
  if (char === '"') {
15733
- quote = "double";
15953
+ quote2 = "double";
15734
15954
  continue;
15735
15955
  }
15736
15956
  if (char === "'") {
15737
- quote = "literal";
15957
+ quote2 = "literal";
15738
15958
  continue;
15739
15959
  }
15740
15960
  if (char === "[")
@@ -15771,7 +15991,7 @@ function inlineRowsAsTables(value, newline) {
15771
15991
  const fields = splitTopLevel(row.slice(1, -1), ",");
15772
15992
  if (!fields)
15773
15993
  return null;
15774
- const lines = fields.map((field) => field.trim()).filter(Boolean);
15994
+ const lines = fields.map((field2) => field2.trim()).filter(Boolean);
15775
15995
  if (lines.some((line) => !/^(?:path|name|enabled)\s*=/.test(line)))
15776
15996
  return null;
15777
15997
  const keys = lines.map((line) => line.slice(0, line.indexOf("=")).trim());
@@ -15796,27 +16016,27 @@ function normalizeCodexInlinePathConfig(text2) {
15796
16016
  const matches = [];
15797
16017
  for (const assignment of text2.matchAll(/^([ \t]*config[ \t]*=[ \t]*)(.*?)(\r?)$/gm)) {
15798
16018
  const rawValue = assignment[2].trimEnd();
15799
- let quote = null, escaped = false, comment = "";
16019
+ let quote2 = null, escaped = false, comment = "";
15800
16020
  for (let index = 0;index < rawValue.length; index++) {
15801
16021
  const char = rawValue[index];
15802
- if (quote === "double") {
16022
+ if (quote2 === "double") {
15803
16023
  if (escaped)
15804
16024
  escaped = false;
15805
16025
  else if (char === "\\")
15806
16026
  escaped = true;
15807
16027
  else if (char === '"')
15808
- quote = null;
16028
+ quote2 = null;
15809
16029
  continue;
15810
16030
  }
15811
- if (quote === "literal") {
16031
+ if (quote2 === "literal") {
15812
16032
  if (char === "'")
15813
- quote = null;
16033
+ quote2 = null;
15814
16034
  continue;
15815
16035
  }
15816
16036
  if (char === '"')
15817
- quote = "double";
16037
+ quote2 = "double";
15818
16038
  else if (char === "'")
15819
- quote = "literal";
16039
+ quote2 = "literal";
15820
16040
  else if (char === "#") {
15821
16041
  comment = rawValue.slice(index);
15822
16042
  break;
@@ -15928,28 +16148,28 @@ function disableCodexBundledSkills(text2) {
15928
16148
 
15929
16149
  // src/lib/agent-sumi.ts
15930
16150
  import { existsSync as existsSync8, lstatSync as lstatSync17, realpathSync as realpathSync8 } from "fs";
15931
- import { join as join21, resolve as resolve12 } from "path";
16151
+ import { join as join21, resolve as resolve13 } from "path";
15932
16152
  function sumiConfigDirectory(home, env = process.env) {
15933
16153
  for (const key of ["SUMI_CONFIG", "SUMI_CONFIG_CONTENT"])
15934
16154
  if (env[key] !== undefined)
15935
16155
  throw new Error(`Sumi ${key} requires a dedicated discovery adapter; unset it before Skills integration`);
15936
16156
  const selected = (key) => env[key]?.trim() ? env[key] : undefined;
15937
- 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);
15938
16158
  const explicit = selected("SUMI_CONFIG_DIR");
15939
16159
  const xdg = selected("XDG_CONFIG_HOME");
15940
16160
  const custom = selected("SUMI_HOME");
15941
- 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");
15942
16162
  if (explicit || xdg || custom)
15943
- return canonical6;
16163
+ return canonical7;
15944
16164
  const legacy = join21(home, ".config/sumi");
15945
- const a = lstatSync17(canonical6, { throwIfNoEntry: false }), b = lstatSync17(legacy, { throwIfNoEntry: false });
15946
- 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))
15947
16167
  throw new Error("Sumi config roots conflict; complete native home adoption before Skills integration");
15948
- const path = a ? canonical6 : b ? legacy : canonical6;
16168
+ const path = a ? canonical7 : b ? legacy : canonical7;
15949
16169
  if (!existsSync8(path))
15950
16170
  return path;
15951
16171
  const physical = realpathSync8(path);
15952
- if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![canonical6, legacy].includes(physical))
16172
+ if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![canonical7, legacy].includes(physical))
15953
16173
  throw new Error("Sumi config alias must resolve to its canonical or legacy root");
15954
16174
  return physical;
15955
16175
  }
@@ -16049,33 +16269,49 @@ var SUMI_SKILL_PERMISSIONS = [
16049
16269
  ];
16050
16270
 
16051
16271
  // src/lib/codex-hook-trust-files.ts
16052
- 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";
16053
- import { createHash as createHash17 } from "crypto";
16054
- import { dirname as dirname13 } from "path";
16055
- 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) {
16056
16276
  if (!value)
16057
16277
  throw new Error(`CODEX_HOOK_TRUST_${code}`);
16058
16278
  }
16059
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
+ }
16060
16283
  function safeParents(file) {
16061
- for (let p = dirname13(file);; p = dirname13(p)) {
16284
+ for (let p = dirname14(file);; p = dirname14(p)) {
16062
16285
  const s = lstatSync18(p);
16063
- need5(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
16064
- need5((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
16065
- 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)
16066
16289
  break;
16067
16290
  }
16068
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
+ }
16069
16305
  function snapshot(file, privateFile = false, options = {}) {
16070
16306
  const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
16071
16307
  safeParents(file);
16072
16308
  const initial = lstatSync18(file, { bigint: true });
16073
- 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");
16074
- 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");
16075
16311
  const fd = openSync9(file, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
16076
16312
  try {
16077
16313
  const before = fstatSync9(fd, { bigint: true });
16078
- need5(sameStat(initial, before), "INPUT_CHANGED");
16314
+ need6(sameStat(initial, before), "INPUT_CHANGED");
16079
16315
  const bytes = Buffer.alloc(Number(before.size) + 1);
16080
16316
  let length = 0;
16081
16317
  while (length < bytes.length) {
@@ -16084,22 +16320,22 @@ function snapshot(file, privateFile = false, options = {}) {
16084
16320
  break;
16085
16321
  length += n2;
16086
16322
  }
16087
- 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");
16088
16324
  const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
16089
- 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") };
16090
16326
  } finally {
16091
16327
  closeSync9(fd);
16092
16328
  }
16093
16329
  }
16094
16330
  function unchanged2(before) {
16095
16331
  const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
16096
- 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");
16097
16333
  }
16098
16334
 
16099
16335
  // src/cli/commands/runtime-launcher.ts
16100
- import { createHash as createHash18 } from "crypto";
16101
- 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";
16102
- 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";
16103
16339
  var PINNED_LAUNCHER_SCHEMA = "hasna-skills-pinned-launcher-v1";
16104
16340
  var LAUNCH_CWD_VARIABLE = "HASNA_SKILLS_LAUNCH_CWD";
16105
16341
  var PINNED_LAUNCHER_BUN_FLAGS = ["--config=/dev/null", "--no-env-file", "--no-macros", "--no-install"];
@@ -16140,10 +16376,10 @@ var PINNED_LAUNCHER_ENV_PREFIXES = ["HASNA", "SKILLS", "SKILL", "MCP", "XDG", "L
16140
16376
  var ENV_PREFIX = new RegExp(`^(${PINNED_LAUNCHER_ENV_PREFIXES.join("|")})_`);
16141
16377
  var ENV_EXACT = new Set(PINNED_LAUNCHER_ENV_NAMES);
16142
16378
  function sha2562(bytes) {
16143
- return createHash18("sha256").update(bytes).digest("hex");
16379
+ return createHash19("sha256").update(bytes).digest("hex");
16144
16380
  }
16145
16381
  function assertLauncherPath(value, code) {
16146
- if (typeof value !== "string" || !isAbsolute13(value) || /['\x00-\x1f\x7f]/.test(value))
16382
+ if (typeof value !== "string" || !isAbsolute15(value) || /['\x00-\x1f\x7f]/.test(value))
16147
16383
  throw new Error(code);
16148
16384
  }
16149
16385
  function renderPinnedLauncher(binding) {
@@ -16233,7 +16469,7 @@ function assertPinnedLauncherRuntimeSafe(runtime) {
16233
16469
  }
16234
16470
 
16235
16471
  // src/lib/agent-integration.ts
16236
- var sha3 = (data) => createHash19("sha256").update(data).digest("hex");
16472
+ var sha3 = (data) => createHash20("sha256").update(data).digest("hex");
16237
16473
  var ROOTS = NATIVE_SKILL_ROOTS;
16238
16474
  function hasTrustedDarwinAcl(fd, allowDenyOnly = false) {
16239
16475
  try {
@@ -16283,9 +16519,10 @@ function isSystemRootAlias(path) {
16283
16519
  return false;
16284
16520
  const target = `/private${path}`, link = `private${path}`;
16285
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}`;
16286
16523
  try {
16287
16524
  const before = lstatSync20(path, { bigint: true });
16288
- if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync6(path) !== link)
16525
+ if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync7(path) !== link)
16289
16526
  return false;
16290
16527
  const directories = ["/", "/private", target].map((directory) => {
16291
16528
  const stat = lstatSync20(directory, { bigint: true });
@@ -16298,20 +16535,20 @@ function isSystemRootAlias(path) {
16298
16535
  throw new Error("Unprotected system temporary directory");
16299
16536
  const fd = openSync11(directory, constants11.O_RDONLY | constants11.O_DIRECTORY | constants11.O_NOFOLLOW);
16300
16537
  try {
16301
- 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))
16302
16539
  throw new Error("Unverified system alias directory ACL");
16303
16540
  } finally {
16304
16541
  closeSync11(fd);
16305
16542
  }
16306
- return { directory, identity: identity5(stat) };
16543
+ return { directory, identity: directoryIdentity(stat) };
16307
16544
  });
16308
- 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);
16309
16546
  } catch {
16310
16547
  return false;
16311
16548
  }
16312
16549
  }
16313
16550
  function canonicalSystemPath(path) {
16314
- const absolute = resolve13(path), root = `/${absolute.split(sep7)[1]}`;
16551
+ const absolute = resolve15(path), root = `/${absolute.split(sep7)[1]}`;
16315
16552
  if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync20(root, { throwIfNoEntry: false })?.isSymbolicLink())
16316
16553
  return absolute;
16317
16554
  if (!isSystemRootAlias(root))
@@ -16319,11 +16556,11 @@ function canonicalSystemPath(path) {
16319
16556
  return `/private${absolute}`;
16320
16557
  }
16321
16558
  function assertSafePath(path) {
16322
- let cursor = resolve13(path);
16559
+ let cursor = resolve15(path);
16323
16560
  while (true) {
16324
16561
  if (lstatSync20(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
16325
16562
  throw new Error(`Refusing symlink path: ${cursor}`);
16326
- const parent = dirname14(cursor);
16563
+ const parent = dirname15(cursor);
16327
16564
  if (parent === cursor)
16328
16565
  return;
16329
16566
  cursor = parent;
@@ -16335,13 +16572,13 @@ function readRootAlias(home, agent) {
16335
16572
  const stat = lstatSync20(alias, { throwIfNoEntry: false, bigint: true });
16336
16573
  if (!stat?.isSymbolicLink())
16337
16574
  throw new Error(`Agent root alias changed after planning: ${alias}`);
16338
- const link = readlinkSync6(alias), targetPath = resolve13(dirname14(alias), link);
16575
+ const link = readlinkSync7(alias), targetPath = resolve15(dirname15(alias), link);
16339
16576
  assertSafePath(targetPath);
16340
16577
  const targetStat = lstatSync20(targetPath, { throwIfNoEntry: false });
16341
16578
  if (!targetStat?.isDirectory())
16342
16579
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
16343
16580
  const target = realpathSync10(targetPath), inside = relative6(realHome, target);
16344
- if (!inside || inside === ".." || inside.startsWith(`..${sep7}`) || isAbsolute14(inside))
16581
+ if (!inside || inside === ".." || inside.startsWith(`..${sep7}`) || isAbsolute16(inside))
16345
16582
  throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
16346
16583
  return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
16347
16584
  }
@@ -16367,13 +16604,13 @@ function disabledCodexSkillPaths(config, aliases) {
16367
16604
  const rows = skills.config;
16368
16605
  if (!Array.isArray(rows))
16369
16606
  return [];
16370
- 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())))
16371
16608
  return [];
16372
16609
  if (rows.some((entry) => entry.name !== undefined && entry.enabled !== false))
16373
16610
  return [];
16374
16611
  const settings = new Map;
16375
16612
  for (const entry of rows) {
16376
- if (typeof entry.path !== "string" || !isAbsolute14(entry.path))
16613
+ if (typeof entry.path !== "string" || !isAbsolute16(entry.path))
16377
16614
  continue;
16378
16615
  let document2;
16379
16616
  try {
@@ -16385,7 +16622,7 @@ function disabledCodexSkillPaths(config, aliases) {
16385
16622
  continue;
16386
16623
  settings.set(document2, [...settings.get(document2) ?? [], entry.enabled === false]);
16387
16624
  }
16388
- 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));
16389
16626
  }
16390
16627
  function recheckRootAliases(aliases) {
16391
16628
  for (const binding of aliases) {
@@ -16427,7 +16664,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
16427
16664
  }
16428
16665
  }
16429
16666
  function treeHash(root) {
16430
- const hash2 = createHash19("sha256");
16667
+ const hash2 = createHash20("sha256");
16431
16668
  let bytes = 0, files = 0;
16432
16669
  function visit2(path) {
16433
16670
  const stat = lstatSync20(path);
@@ -16459,20 +16696,20 @@ function selectNativeMigrationTargets(inventory, manifest) {
16459
16696
  const identities = new Set;
16460
16697
  const selected = [];
16461
16698
  for (const target of manifest.targets) {
16462
- if (!isAbsolute14(target.projectRoot) || resolve13(target.projectRoot) !== target.projectRoot)
16699
+ if (!isAbsolute16(target.projectRoot) || resolve15(target.projectRoot) !== target.projectRoot)
16463
16700
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
16464
- 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));
16465
16702
  if (!existsSync9(projectRoot) || !lstatSync20(projectRoot).isDirectory())
16466
16703
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
16467
16704
  assertSafePath(projectRoot);
16468
- 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))
16469
16706
  throw new Error(`Invalid native migration target: ${target.path}`);
16470
16707
  const identity5 = `${target.agent}\x00${canonicalSystemPath(projectRoot)}\x00${normalized}`;
16471
16708
  if (identities.has(identity5))
16472
16709
  throw new Error(`Duplicate native migration target: ${target.path}`);
16473
16710
  identities.add(identity5);
16474
- const expected = canonicalSystemPath(resolve13(target.projectRoot, target.path));
16475
- 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);
16476
16713
  if (matches.length !== 1)
16477
16714
  throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
16478
16715
  const entry = matches[0];
@@ -16491,7 +16728,7 @@ function projectAncestorDirectories(projects) {
16491
16728
  if (depth >= 100)
16492
16729
  throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
16493
16730
  directories.add(path);
16494
- const parent = dirname14(path);
16731
+ const parent = dirname15(path);
16495
16732
  if (parent === path)
16496
16733
  break;
16497
16734
  path = parent;
@@ -16500,7 +16737,7 @@ function projectAncestorDirectories(projects) {
16500
16737
  return [...directories];
16501
16738
  }
16502
16739
  function isCodexSkillDiscoveryInput(path) {
16503
- for (let cursor = canonicalSystemPath(path);cursor !== dirname14(cursor); cursor = dirname14(cursor)) {
16740
+ for (let cursor = canonicalSystemPath(path);cursor !== dirname15(cursor); cursor = dirname15(cursor)) {
16504
16741
  if (ROOTS.some(([agent, root]) => agent === "codex" && cursor.endsWith(join23(sep7, root))))
16505
16742
  return true;
16506
16743
  }
@@ -16508,9 +16745,9 @@ function isCodexSkillDiscoveryInput(path) {
16508
16745
  }
16509
16746
  function inventoryNativeSkills(home = homedir2(), options = {}) {
16510
16747
  home = canonicalSystemPath(home);
16511
- 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))
16512
16749
  throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
16513
- if (options.disabledVendorPaths?.some((path) => !isAbsolute14(path) || resolve13(path) !== path))
16750
+ if (options.disabledVendorPaths?.some((path) => !isAbsolute16(path) || resolve15(path) !== path))
16514
16751
  throw new Error("Disabled vendor paths must be exact absolute paths");
16515
16752
  const reviewedCacheAlias = options.reviewedCacheAlias === undefined ? undefined : canonicalSystemPath(options.reviewedCacheAlias);
16516
16753
  const disabledVendorPaths = new Set((options.disabledVendorPaths ?? []).map(canonicalSystemPath));
@@ -16529,7 +16766,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16529
16766
  roots.push(["sumi", canonicalAgentPath(join23(project, directory, name), aliases)]);
16530
16767
  }
16531
16768
  const installationInputs = options.codexInstallationInputRoots ?? [];
16532
- 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)))))
16533
16770
  throw new Error("Native installation input overlaps an unconditional skill-loading root");
16534
16771
  const entries = [], seen = new Set;
16535
16772
  const cacheScans = new Map, maxAliasProofEntries = 1e4;
@@ -16543,11 +16780,11 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16543
16780
  }
16544
16781
  let reviewedCacheAliasFound = false;
16545
16782
  function verifiedSiblingCacheAlias(agent, path, parent) {
16546
- const link = readlinkSync6(path), lexicalTarget = resolve13(parent, link);
16547
- if (isAbsolute14(link) ? link !== lexicalTarget : dirname14(link) !== ".")
16783
+ const link = readlinkSync7(path), lexicalTarget = resolve15(parent, link);
16784
+ if (isAbsolute16(link) ? link !== lexicalTarget : dirname15(link) !== ".")
16548
16785
  return false;
16549
16786
  const target = canonicalSystemPath(lexicalTarget);
16550
- if (dirname14(target) !== parent)
16787
+ if (dirname15(target) !== parent)
16551
16788
  return false;
16552
16789
  if (!lstatSync20(target, { throwIfNoEntry: false })?.isDirectory())
16553
16790
  return false;
@@ -16596,17 +16833,17 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16596
16833
  }
16597
16834
  const bridge = (!vendor || agent === "sumi") && isOwnedCliBridge(path, bridgePaths);
16598
16835
  const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep7));
16599
- 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 } : {} });
16600
16837
  return scan;
16601
16838
  }
16602
16839
  if (depth > (agent === "sumi" ? 32 : vendor ? 32 : 3))
16603
16840
  return { ...scan, complete: false };
16604
16841
  const children = [], directory = opendirSync3(path);
16605
16842
  try {
16606
- let child;
16607
- while ((child = directory.readSync()) !== null) {
16608
- admit(join23(path, child.name));
16609
- children.push(child);
16843
+ let child2;
16844
+ while ((child2 = directory.readSync()) !== null) {
16845
+ admit(join23(path, child2.name));
16846
+ children.push(child2);
16610
16847
  }
16611
16848
  } finally {
16612
16849
  directory.closeSync();
@@ -16622,15 +16859,15 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16622
16859
  const isVendor = vendor || name === ".system";
16623
16860
  if (isVendor && !options.includeVendor)
16624
16861
  continue;
16625
- const child = join23(path, name);
16626
- if (isVendor && lstatSync20(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
16627
- const target = statSync9(child, { throwIfNoEntry: false });
16628
- 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)) {
16629
16866
  scan.entries++;
16630
16867
  continue;
16631
16868
  }
16632
16869
  }
16633
- const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true, configuredRoot);
16870
+ const childScan = visit2(agent, child2, isVendor, depth + 1, pluginCache, true, configuredRoot);
16634
16871
  scan.complete &&= childScan.complete;
16635
16872
  scan.hasSkills ||= childScan.hasSkills;
16636
16873
  scan.entries += childScan.entries;
@@ -16766,7 +17003,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
16766
17003
  function disableCodexSkills(text2, skills, aliases, bridgePath) {
16767
17004
  const bundled = disableCodexBundledSkills(text2);
16768
17005
  let result = normalizeCodexInlinePathConfig(bundled);
16769
- 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));
16770
17007
  const previousConfig = Bun.TOML.parse(bundled).skills?.config;
16771
17008
  if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
16772
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");
@@ -16778,7 +17015,7 @@ function disableCodexSkills(text2, skills, aliases, bridgePath) {
16778
17015
  result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
16779
17016
  const parsed = Bun.TOML.parse(section);
16780
17017
  const declared = parsed.skills?.config?.[0];
16781
- 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))))
16782
17019
  return section;
16783
17020
  found = true;
16784
17021
  return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
@@ -16799,7 +17036,7 @@ enabled = false
16799
17036
  return result;
16800
17037
  }
16801
17038
  function assertCodexInstallationInputs(home, projectDirs, inputs) {
16802
- 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)));
16803
17040
  if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep7) || input.startsWith(root + sep7))))
16804
17041
  throw new Error("Native installation input overlaps an unconditional skill-loading root");
16805
17042
  }
@@ -16815,9 +17052,9 @@ function planAgentIntegration(options) {
16815
17052
  throw new Error("Invalid existing Skills bridge policy");
16816
17053
  if (policy.bridge?.agents !== undefined && (!Array.isArray(policy.bridge.agents) || policy.bridge.agents.some((agent) => !INTEGRATION_AGENTS.includes(agent))))
16817
17054
  throw new Error("Invalid existing bridge agent inventory");
16818
- for (const field of ["commands", "profiles"])
16819
- 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"))))
16820
- 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`);
16821
17058
  const priorAgents = policy.bridge?.agents ?? [];
16822
17059
  const profileId = options.profileId ?? policy.profileId ?? "default";
16823
17060
  const validateProfile = (value) => {
@@ -16832,13 +17069,18 @@ function planAgentIntegration(options) {
16832
17069
  return [agent, { command, profileId: profileId2 }];
16833
17070
  }));
16834
17071
  const retainedAgents = [];
17072
+ const priorExecutables = policy.bridge?.discoveryExecutables ?? {};
17073
+ const executables = new Map;
16835
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);
16836
17078
  const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? { ...policy.bridge.discovery[agent], ...agent === "codex" ? { codexDisabledPluginSkills: policy.bridge.codexPluginSkills ?? [] } : {} } : undefined;
16837
17079
  if (retainedReview) {
16838
17080
  assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: options.projectDir ?? home });
16839
17081
  retainedAgents.push(agent);
16840
17082
  }
16841
- 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 });
16842
17084
  });
16843
17085
  const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases)) : null;
16844
17086
  const codexConfig = Bun.TOML.parse(codexConfigBefore ?? "");
@@ -16846,7 +17088,7 @@ function planAgentIntegration(options) {
16846
17088
  const codexPathConfig = options.agents.includes("codex") ? disableCodexSkills(codexConfigBefore ?? "", nativeSkills, aliases, canonicalAgentPath(join23(home, ".codex/skills", CLI_BRIDGE_NAME), aliases)) : undefined;
16847
17089
  const plannedCodexRules = codexPathConfig === undefined ? undefined : Bun.TOML.parse(codexPathConfig).skills?.config ?? [];
16848
17090
  const observedNativeSources = [];
16849
- 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) => {
16850
17092
  const bytes = readNativeBytes(path, 1024 * 1024);
16851
17093
  observedNativeSources.push({ path, sha256: sha3(bytes) });
16852
17094
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
@@ -16975,7 +17217,7 @@ function planAgentIntegration(options) {
16975
17217
  version: CLI_BRIDGE_VERSION,
16976
17218
  digest: CLI_BRIDGE_DIGEST,
16977
17219
  agents: [...new Set([...priorAgents, ...options.agents])].sort(),
16978
- home: resolve13(home),
17220
+ home: resolve15(home),
16979
17221
  includeVendor: true,
16980
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)) } } } : {},
16981
17223
  commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
@@ -16985,19 +17227,30 @@ function planAgentIntegration(options) {
16985
17227
  ...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
16986
17228
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
16987
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;
16988
17241
  const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
16989
17242
  if (JSON.stringify(policy) !== JSON.stringify(nextPolicy))
16990
17243
  changes.push({ path: policyPath, before: previousPolicy, after: serializedPolicy });
16991
17244
  recheckRootAliases(aliases);
16992
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 } } : {} };
16993
17246
  }
16994
- function hookProjectionRefusal(reason) {
16995
- throw new Error(`CLAUDE_MANAGED_HOOK_${reason}`);
17247
+ function hookProjectionRefusal(reason2) {
17248
+ throw new Error(`CLAUDE_MANAGED_HOOK_${reason2}`);
16996
17249
  }
16997
17250
  function projectedHookAcl(path, observed) {
16998
17251
  if (process.platform !== "darwin")
16999
17252
  return;
17000
- 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}`;
17001
17254
  const fd = openSync11(path, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK | (observed.isDirectory() ? constants11.O_DIRECTORY : 0));
17002
17255
  try {
17003
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))
@@ -17008,18 +17261,18 @@ function projectedHookAcl(path, observed) {
17008
17261
  }
17009
17262
  function projectedHookExecutable(command, expectedResolved) {
17010
17263
  try {
17011
- if (!isAbsolute14(command) || /[\0\r\n]/.test(command) || resolve13(command) !== command)
17264
+ if (!isAbsolute16(command) || /[\0\r\n]/.test(command) || resolve15(command) !== command)
17012
17265
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17013
17266
  command = canonicalSystemPath(command);
17014
17267
  const parents2 = (file = command) => {
17015
17268
  const result = [];
17016
- for (let path = dirname14(file);; path = dirname14(path)) {
17269
+ for (let path = dirname15(file);; path = dirname15(path)) {
17017
17270
  const stat = lstatSync20(path, { bigint: true });
17018
- 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))
17019
17272
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17020
17273
  projectedHookAcl(path, stat);
17021
17274
  result.push(`${path}:${stat.dev}:${stat.ino}:${stat.uid}:${stat.mode}`);
17022
- if (dirname14(path) === path)
17275
+ if (dirname15(path) === path)
17023
17276
  break;
17024
17277
  }
17025
17278
  return result;
@@ -17029,22 +17282,33 @@ function projectedHookExecutable(command, expectedResolved) {
17029
17282
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17030
17283
  const linkIdentity = (stat) => `${stat.dev}:${stat.ino}:${stat.mode}:${stat.uid}:${stat.ctimeNs}`;
17031
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");
17032
17288
  if (expectedResolved !== undefined && resolved !== expectedResolved)
17033
17289
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17034
17290
  let launcher, launcherParentIdentity;
17291
+ let runtime;
17035
17292
  if (target.pinned) {
17036
17293
  launcherParentIdentity = parents2(target.physical);
17037
17294
  launcher = snapshot(target.physical, false);
17038
17295
  projectedHookAcl(launcher.file, launcher.stat);
17039
17296
  if (launcher.sha256 !== target.pinned.sha256 || (launcher.stat.mode & 0o100n) === 0n)
17040
17297
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17041
- 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);
17042
17306
  }
17043
17307
  const resolvedParentIdentity = parents2(resolved);
17044
- 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 });
17045
17309
  projectedHookAcl(manifest.file, manifest.stat);
17046
17310
  const pkg = JSON.parse(manifest.text);
17047
- 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"))
17048
17312
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17049
17313
  const cli = snapshot(resolved, false, { readOnlyPackage: true });
17050
17314
  projectedHookAcl(cli.file, cli.stat);
@@ -17053,7 +17317,7 @@ function projectedHookExecutable(command, expectedResolved) {
17053
17317
  const recheck = () => {
17054
17318
  try {
17055
17319
  const again = resolveLauncherCommand(command);
17056
- 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))
17057
17321
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
17058
17322
  if (launcher) {
17059
17323
  if (!isDeepStrictEqual6(parents2(target.physical), launcherParentIdentity))
@@ -17061,6 +17325,12 @@ function projectedHookExecutable(command, expectedResolved) {
17061
17325
  projectedHookAcl(launcher.file, launcher.stat);
17062
17326
  unchanged2(launcher);
17063
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
+ }
17064
17334
  projectedHookAcl(cli.file, cli.stat);
17065
17335
  projectedHookAcl(manifest.file, manifest.stat);
17066
17336
  unchanged2(cli);
@@ -17087,7 +17357,7 @@ function planClaudeManagedHookProjection(options) {
17087
17357
  } catch {
17088
17358
  hookProjectionRefusal("TARGET_INVALID");
17089
17359
  }
17090
- const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17360
+ const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17091
17361
  const policyPath = join23(dataDir2, "agent-policy.json");
17092
17362
  let policyBytes, settingsBytes, settingsPath, command, profile;
17093
17363
  try {
@@ -17179,29 +17449,28 @@ function planClaudePreToolUseHookUpdate(options) {
17179
17449
  return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
17180
17450
  }
17181
17451
  function claudeSettingsConsumers(home, path, discoveries, aliases) {
17182
- const canonical6 = (value) => canonicalAgentPath(value, aliases);
17452
+ const canonical7 = (value) => canonicalAgentPath(value, aliases);
17183
17453
  const containsSettings = (root) => {
17184
- const distance = relative6(canonical6(root), path);
17185
- return distance === "" || distance !== ".." && !distance.startsWith(`..${sep7}`) && !isAbsolute14(distance);
17454
+ const distance = relative6(canonical7(root), path);
17455
+ return distance === "" || distance !== ".." && !distance.startsWith(`..${sep7}`) && !isAbsolute16(distance);
17186
17456
  };
17187
- 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);
17188
17458
  }
17189
- function verifyCoordinatedDiscovery(binding, home, aliases) {
17459
+ function verifyCoordinatedDiscovery(binding, home, aliases, executable2) {
17190
17460
  try {
17191
17461
  if (binding.method !== "automatic" && binding.method !== "reviewed")
17192
17462
  throw new Error("Invalid managed discovery method");
17193
17463
  verifyAgentDiscovery(binding);
17194
- if (binding.method === "automatic") {
17195
- const current = resolveAgentDiscovery({ home, agent: binding.agent, canonical: (path) => canonicalAgentPath(path, aliases) });
17196
- if (JSON.stringify(current) !== JSON.stringify(binding))
17197
- throw new Error("Configured native discovery roots changed during Claude update");
17198
- }
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 });
17199
17466
  } catch (error) {
17467
+ if (isDiscoveryRootUnresolved(error))
17468
+ throw error;
17200
17469
  throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
17201
17470
  }
17202
17471
  }
17203
17472
  function planClaudeHookEventsUpdate(options) {
17204
- const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17473
+ const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17205
17474
  const policyPath = join23(dataDir2, "agent-policy.json");
17206
17475
  assertSafePath(policyPath);
17207
17476
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
@@ -17227,8 +17496,9 @@ function planClaudeHookEventsUpdate(options) {
17227
17496
  });
17228
17497
  if (discoveries.length !== agents.length || !agents.includes("claude"))
17229
17498
  throw new Error("Missing managed Claude discovery coverage");
17499
+ const discoveryExecutables = bridge.discoveryExecutables;
17230
17500
  for (const binding of discoveries)
17231
- verifyCoordinatedDiscovery(binding, home, aliases);
17501
+ verifyCoordinatedDiscovery(binding, home, aliases, discoveryExecutables?.[binding.agent]);
17232
17502
  const consumers = claudeSettingsConsumers(home, path, discoveries, aliases);
17233
17503
  for (const agent of consumers)
17234
17504
  assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: home });
@@ -17263,13 +17533,14 @@ function planClaudeHookEventsUpdate(options) {
17263
17533
  observedPolicy: { path: policyPath, before: snapshot2.text },
17264
17534
  discoveryBefore: discoveries,
17265
17535
  discoveryAfter,
17536
+ ...discoveryExecutables ? { discoveryExecutables } : {},
17266
17537
  rootAliases: aliases,
17267
17538
  managedAgentChecks: { home, agents: consumers }
17268
17539
  };
17269
17540
  }
17270
17541
  function atomicWrite2(path, content) {
17271
17542
  assertSafePath(path);
17272
- mkdirSync6(dirname14(path), { recursive: true, mode: 448 });
17543
+ mkdirSync6(dirname15(path), { recursive: true, mode: 448 });
17273
17544
  const temporary = `${path}.skills-${randomUUID3()}`;
17274
17545
  try {
17275
17546
  writeFileSync6(temporary, content, { mode: 384, flag: "wx" });
@@ -17284,7 +17555,7 @@ function planAgentSettingsWitnessUpgrade(options) {
17284
17555
  throw new Error("Invalid Codex witness target version");
17285
17556
  if (options.targetClaudeVersion !== undefined && (options.agent !== "claude" || ![3, 4].includes(options.targetClaudeVersion)))
17286
17557
  throw new Error("Invalid Claude witness target version");
17287
- const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17558
+ const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17288
17559
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
17289
17560
  if (!snapshot2 || sha3(snapshot2.text) !== options.expectedPolicySha256)
17290
17561
  throw new Error("Managed policy preimage changed");
@@ -17344,7 +17615,7 @@ function planAgentSettingsWitnessUpgrade(options) {
17344
17615
  const replacement = { ...binding, sources: rebound };
17345
17616
  verifyAgentDiscovery(replacement);
17346
17617
  const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
17347
- 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 })) } };
17348
17619
  }
17349
17620
  function applyAgentIntegration(plan, options = {}) {
17350
17621
  const roots = plan.changes.flatMap((change) => {
@@ -17362,10 +17633,10 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
17362
17633
  throw new Error("Native settings changed after witness planning");
17363
17634
  const policyPath = join23(plan.dataDir, "agent-policy.json");
17364
17635
  assertSafePath(policyPath);
17365
- 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);
17366
17637
  readManagedSkillPolicySnapshot(plan.dataDir);
17367
17638
  for (const change of plan.changes)
17368
- if (resolve13(change.path) === resolve13(policyPath))
17639
+ if (resolve15(change.path) === resolve15(policyPath))
17369
17640
  parseManagedSkillPolicy(change.after);
17370
17641
  const aliases = plan.rootAliases ?? [];
17371
17642
  assertCurrent();
@@ -17386,7 +17657,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
17386
17657
  assertManagedAgentBridgeWithDiscovery(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home }, provenDiscovery?.agent === agent ? provenDiscovery : undefined);
17387
17658
  for (const binding of plan.discoveryBefore ?? []) {
17388
17659
  if (plan.managedAgentChecks)
17389
- verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
17660
+ verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases, plan.discoveryExecutables?.[binding.agent]);
17390
17661
  else
17391
17662
  verifyAgentDiscovery(binding);
17392
17663
  }
@@ -17418,7 +17689,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
17418
17689
  recheckRootAliases(aliases);
17419
17690
  if (currentText(change.path) !== change.before)
17420
17691
  throw new Error(`Configuration changed after planning: ${change.path}`);
17421
- for (let directory = dirname14(change.path);!existsSync9(directory); directory = dirname14(directory))
17692
+ for (let directory = dirname15(change.path);!existsSync9(directory); directory = dirname15(directory))
17422
17693
  createdDirectories.add(directory);
17423
17694
  const after = change.after;
17424
17695
  atomicWrite2(change.path, after);
@@ -17433,7 +17704,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
17433
17704
  throw new Error("Native settings changed during witness application");
17434
17705
  for (const binding of plan.discoveryAfter ?? []) {
17435
17706
  if (plan.managedAgentChecks)
17436
- verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases);
17707
+ verifyCoordinatedDiscovery(binding, plan.managedAgentChecks.home, aliases, plan.discoveryExecutables?.[binding.agent]);
17437
17708
  else
17438
17709
  verifyAgentDiscovery(binding);
17439
17710
  }
@@ -17490,7 +17761,7 @@ function writeArchiveJournal(path, value) {
17490
17761
  closeSync11(descriptor);
17491
17762
  descriptor = undefined;
17492
17763
  renameSync3(temporary, path);
17493
- syncArchiveDirectory(dirname14(path));
17764
+ syncArchiveDirectory(dirname15(path));
17494
17765
  } finally {
17495
17766
  if (descriptor !== undefined)
17496
17767
  closeSync11(descriptor);
@@ -17548,14 +17819,14 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
17548
17819
  };
17549
17820
  assertSafePath(archiveRoot);
17550
17821
  const created = [];
17551
- for (let path = archiveRoot;!existsSync9(path); path = dirname14(path))
17822
+ for (let path = archiveRoot;!existsSync9(path); path = dirname15(path))
17552
17823
  created.push(path);
17553
17824
  mkdirSync6(archiveRoot, { recursive: true, mode: 448 });
17554
17825
  try {
17555
17826
  for (const directory of created)
17556
17827
  syncArchiveDirectory(directory);
17557
17828
  if (created.length)
17558
- syncArchiveDirectory(dirname14(created.at(-1)));
17829
+ syncArchiveDirectory(dirname15(created.at(-1)));
17559
17830
  writeArchiveJournal(receiptPath, journal);
17560
17831
  for (const [index, entry] of selected.entries()) {
17561
17832
  assertCurrent();
@@ -17568,7 +17839,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
17568
17839
  writeArchiveJournal(receiptPath, journal);
17569
17840
  renameSync3(move.source, move.archive);
17570
17841
  moved.push(move);
17571
- syncArchiveDirectory(dirname14(move.source));
17842
+ syncArchiveDirectory(dirname15(move.source));
17572
17843
  syncArchiveDirectory(archiveRoot);
17573
17844
  verifyArchive(move);
17574
17845
  move.status = "archived";
@@ -17605,7 +17876,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
17605
17876
  }
17606
17877
  if (entry.discoveryOnly) {
17607
17878
  linkSync2(entry.archive, entry.source);
17608
- syncArchiveDirectory(dirname14(entry.source));
17879
+ syncArchiveDirectory(dirname15(entry.source));
17609
17880
  unlinkSync4(entry.archive);
17610
17881
  } else {
17611
17882
  if (lstatSync20(entry.source, { throwIfNoEntry: false })) {
@@ -17616,7 +17887,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
17616
17887
  renameSync3(entry.archive, entry.source);
17617
17888
  }
17618
17889
  entry.status = "restored";
17619
- syncArchiveDirectory(dirname14(entry.source));
17890
+ syncArchiveDirectory(dirname15(entry.source));
17620
17891
  syncArchiveDirectory(archiveRoot);
17621
17892
  } catch {
17622
17893
  entry.status = "recovery-required";
@@ -17641,7 +17912,7 @@ function isInertCodexPluginCacheCopy(entry, cache, allowedHostPaths, read2) {
17641
17912
  return false;
17642
17913
  const document2 = join23(entry.path, "SKILL.md");
17643
17914
  const inside = relative6(cache, entry.path);
17644
- if (!inside || isAbsolute14(inside) || inside.split(sep7).some((segment) => segment === ".."))
17915
+ if (!inside || isAbsolute16(inside) || inside.split(sep7).some((segment) => segment === ".."))
17645
17916
  return false;
17646
17917
  assertSafePath(entry.path);
17647
17918
  if (!lstatSync20(cache).isDirectory() || !lstatSync20(entry.path).isDirectory() || !lstatSync20(document2).isFile())
@@ -17661,7 +17932,7 @@ function assertManagedAgentBridge(agent, options = {}) {
17661
17932
  assertManagedAgentBridgeWithDiscovery(agent, options);
17662
17933
  }
17663
17934
  function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
17664
- const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17935
+ const home = resolve15(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17665
17936
  assertSafePath(join23(dataDir2, "agent-policy.json"));
17666
17937
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
17667
17938
  if (!snapshot2)
@@ -17759,12 +18030,11 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
17759
18030
  verifyAgentDiscovery(agent === "codex" ? { ...discovery, codexDisabledPluginSkills: binding.codexPluginSkills ?? [] } : discovery, options.codexDiscoveryRecovery);
17760
18031
  if (agent === "sumi" && discovery.method === "reviewed")
17761
18032
  resolveAgentDiscovery({ home, agent, retainedReview: discovery, canonical: (path) => canonicalAgentPath(path, aliases) });
17762
- if (discovery.method === "automatic") {
17763
- const current = resolveAgentDiscovery({ home, agent, canonical: (path) => canonicalAgentPath(path, aliases) });
17764
- if (JSON.stringify(current) !== JSON.stringify(discovery))
17765
- throw new Error("Configured native discovery roots changed");
17766
- }
18033
+ if (discovery.method === "automatic")
18034
+ verifyAutomaticDiscoveryClosure(discovery, { home, canonical: (path) => canonicalAgentPath(path, aliases), change: "Configured native discovery roots changed", executable: binding.discoveryExecutables?.[agent] });
17767
18035
  } catch (error) {
18036
+ if (isDiscoveryRootUnresolved(error))
18037
+ throw error;
17768
18038
  throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
17769
18039
  }
17770
18040
  let installationInputRoots = [];
@@ -18057,9 +18327,9 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
18057
18327
  const action = dirExists ? "update" : "create";
18058
18328
  if (options.dryRun)
18059
18329
  return { action, path: skillMdPath };
18060
- const parentDir = dirname15(dir);
18330
+ const parentDir = dirname16(dir);
18061
18331
  mkdirSync7(parentDir, { recursive: true });
18062
- const transactionDir = mkdtempSync(join25(parentDir, `.hasna-skills-write-${basename7(dir)}-`));
18332
+ const transactionDir = mkdtempSync(join25(parentDir, `.hasna-skills-write-${basename8(dir)}-`));
18063
18333
  const candidateDir = join25(transactionDir, "candidate");
18064
18334
  const backupDir = join25(transactionDir, "backup");
18065
18335
  const candidateSkillMdPath = join25(candidateDir, "SKILL.md");
@@ -19051,7 +19321,7 @@ function exactProfileSelection(spec, profile) {
19051
19321
  }
19052
19322
 
19053
19323
  // src/lib/plugin-projection.ts
19054
- import { createHash as createHash20 } from "crypto";
19324
+ import { createHash as createHash21 } from "crypto";
19055
19325
  import { posix } from "path";
19056
19326
  var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
19057
19327
  var PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
@@ -19062,7 +19332,7 @@ function pluginNeed(value, message) {
19062
19332
  if (!value)
19063
19333
  pluginRefusal(message);
19064
19334
  }
19065
- var pluginHash = (bytes) => createHash20("sha256").update(bytes).digest("hex");
19335
+ var pluginHash = (bytes) => createHash21("sha256").update(bytes).digest("hex");
19066
19336
  function pluginObject(value) {
19067
19337
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
19068
19338
  }
@@ -19250,9 +19520,9 @@ function buildPluginProjection(entries) {
19250
19520
  }
19251
19521
 
19252
19522
  // src/lib/plugin-projection-store.ts
19253
- import { createHash as createHash21, randomUUID as randomUUID4 } from "crypto";
19523
+ import { createHash as createHash22, randomUUID as randomUUID4 } from "crypto";
19254
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";
19255
- 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";
19256
19526
  var READ_FLAGS = constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK;
19257
19527
  var WRITE_FLAGS = constants12.O_WRONLY | constants12.O_CREAT | constants12.O_EXCL | constants12.O_NOFOLLOW | constants12.O_NONBLOCK;
19258
19528
  var uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined;
@@ -19266,8 +19536,8 @@ function stat(path) {
19266
19536
  return pluginRefusal("Cannot inspect plugin filesystem entry");
19267
19537
  }
19268
19538
  }
19269
- function canonical6(path) {
19270
- 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");
19271
19541
  }
19272
19542
  function owner(stats, system = false) {
19273
19543
  const current = uid();
@@ -19277,8 +19547,8 @@ function same3(left, right, identityOnly = false) {
19277
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");
19278
19548
  }
19279
19549
  function ancestors(path, create = false) {
19280
- canonical6(path);
19281
- const result = [], parent = dirname16(path), filesystemRoot = parse6(parent).root;
19550
+ canonical7(path);
19551
+ const result = [], parent = dirname17(path), filesystemRoot = parse6(parent).root;
19282
19552
  let current = filesystemRoot;
19283
19553
  for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
19284
19554
  if (segment)
@@ -19306,7 +19576,7 @@ function verifyAncestors(chain, identityOnly = true) {
19306
19576
  }
19307
19577
  function privateParent(path) {
19308
19578
  const chain = ancestors(path, true), parent = chain.at(-1);
19309
- pluginNeed(parent && parent.path === dirname16(path), "Plugin storage parent is unavailable");
19579
+ pluginNeed(parent && parent.path === dirname17(path), "Plugin storage parent is unavailable");
19310
19580
  owner(parent.stat);
19311
19581
  pluginNeed((parent.stat.mode & 0o7777n) === 0o700n, "Plugin storage parent must have mode 0700");
19312
19582
  return chain;
@@ -19592,7 +19862,7 @@ function pluginExecutableDigest(path) {
19592
19862
  const opened = fstatSync12(fd, { bigint: true });
19593
19863
  same3(before, opened);
19594
19864
  fileShape(opened, maximum, "executable");
19595
- const hash2 = createHash21("sha256"), buffer = new Uint8Array(64 * 1024);
19865
+ const hash2 = createHash22("sha256"), buffer = new Uint8Array(64 * 1024);
19596
19866
  let remaining = Number(opened.size);
19597
19867
  while (remaining > 0) {
19598
19868
  const count = readSync9(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
@@ -19650,7 +19920,7 @@ function validatePluginTarget(value) {
19650
19920
  for (const registration of value.registrations) {
19651
19921
  pluginKeys(registration, ["scope", "projectPath"]);
19652
19922
  pluginNeed(registration.scope === "user" || registration.scope === "project", "Unsupported plugin registration scope");
19653
- 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");
19654
19924
  const key = JSON.stringify([registration.scope, registration.projectPath]);
19655
19925
  pluginNeed(!scopes.has(key), "Duplicate plugin registration scope");
19656
19926
  scopes.add(key);
@@ -19658,7 +19928,7 @@ function validatePluginTarget(value) {
19658
19928
  for (const key of ["native", "resolver"]) {
19659
19929
  pluginKeys(value[key], key === "native" ? ["version", "executable", "digest"] : ["executable", "digest"]);
19660
19930
  pluginText(value[key].executable);
19661
- 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");
19662
19932
  pluginDigest(value[key].digest);
19663
19933
  }
19664
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)");
@@ -19803,14 +20073,14 @@ function validatePlan(value) {
19803
20073
  pluginNeed(observedEvidenceDigest(plan.observation) === plan.evidenceDigest, "Plugin receipt observed evidence changed");
19804
20074
  pluginNeed(identityDigest(plan) === plan.planDigest, "Plugin receipt immutable identity changed");
19805
20075
  }
19806
- var quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
20076
+ var quote2 = (value) => `'${value.replaceAll("'", "'\\''")}'`;
19807
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"';
19808
20078
  function pluginResolverCommand(binding) {
19809
- 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))}`;
19810
20080
  }
19811
20081
  function root(options) {
19812
20082
  const path = options.storeRoot ?? pluginAdmissionRoot();
19813
- pluginNeed(isAbsolute16(path) && resolve15(path) === path, "Invalid plugin admission store root");
20083
+ pluginNeed(isAbsolute18(path) && resolve17(path) === path, "Invalid plugin admission store root");
19814
20084
  return path;
19815
20085
  }
19816
20086
  function pluginBindingPath(storeRoot, id) {
@@ -20015,15 +20285,15 @@ function validateManagedPluginWitnesses(value) {
20015
20285
  pluginNeed(typeof item.bindingId === "string" && /^[a-f0-9]{64}$/.test(item.bindingId) && !seen.has(item.bindingId), "Invalid or duplicate managed plugin witness");
20016
20286
  seen.add(item.bindingId);
20017
20287
  pluginText(item.storeRoot);
20018
- 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");
20019
20289
  }
20020
20290
  }
20021
20291
  function safe3(path) {
20022
20292
  pluginText(path);
20023
- pluginNeed(isAbsolute17(path) && resolve16(path) === path, "Invalid plugin registry path");
20024
- 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)) {
20025
20295
  pluginNeed(!lstatSync23(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
20026
- if (at === dirname17(at))
20296
+ if (at === dirname18(at))
20027
20297
  break;
20028
20298
  }
20029
20299
  }
@@ -20073,7 +20343,7 @@ function hashManagedPluginRegistry(path, managed) {
20073
20343
  seen.add(target.pluginId);
20074
20344
  const rows = plugins[target.pluginId];
20075
20345
  pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
20076
- 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);
20077
20347
  const scopes = new Set, approvals = new Map, currentFiles = new Map;
20078
20348
  plugins[target.pluginId] = rows.map((row) => {
20079
20349
  pluginNeed(pluginObject(row), "Invalid managed plugin registration");
@@ -20089,7 +20359,7 @@ function hashManagedPluginRegistry(path, managed) {
20089
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");
20090
20360
  const approval = (producer) => {
20091
20361
  pluginText(producer);
20092
- const digest = `sha256:${basename8(producer)}`;
20362
+ const digest = `sha256:${basename9(producer)}`;
20093
20363
  const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest);
20094
20364
  pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
20095
20365
  if (!verified.has(producer)) {
@@ -20151,39 +20421,39 @@ function captureManagedPluginRegistry(path, managedPlugins) {
20151
20421
  }
20152
20422
 
20153
20423
  // src/lib/claude-marketplace-registry.ts
20154
- import { createHash as createHash22 } from "crypto";
20424
+ import { createHash as createHash23 } from "crypto";
20155
20425
  import { closeSync as closeSync14, constants as constants14, fstatSync as fstatSync14, lstatSync as lstatSync24, openSync as openSync14, readSync as readSync11 } from "fs";
20156
- 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";
20157
20427
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
20158
- function need6(value, reason) {
20428
+ function need7(value, reason2) {
20159
20429
  if (!value)
20160
- throw new Error(`Claude marketplace registry ${reason}`);
20430
+ throw new Error(`Claude marketplace registry ${reason2}`);
20161
20431
  }
20162
20432
  function absolutePath3(value) {
20163
- 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");
20164
20434
  }
20165
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;
20166
20436
  function parents2(path) {
20167
20437
  const result = [];
20168
- for (let at = dirname18(path);; at = dirname18(at)) {
20438
+ for (let at = dirname19(path);; at = dirname19(at)) {
20169
20439
  const stat2 = lstatSync24(at, { bigint: true });
20170
- 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");
20171
20441
  result.push([at, stat2]);
20172
- if (at === dirname18(at))
20442
+ if (at === dirname19(at))
20173
20443
  return result;
20174
20444
  }
20175
20445
  }
20176
20446
  function readRegistry2(path, budget2) {
20177
20447
  absolutePath3(path);
20178
- need6(basename9(path) === "known_marketplaces.json", "requires known_marketplaces.json");
20179
- 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");
20180
20450
  const ancestors2 = parents2(path), initial = lstatSync24(path, { bigint: true });
20181
- need6(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
20182
- 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");
20183
20453
  const fd = openSync14(path, constants14.O_RDONLY | constants14.O_NOFOLLOW | constants14.O_NONBLOCK);
20184
20454
  try {
20185
20455
  const opened = fstatSync14(fd, { bigint: true });
20186
- need6(opened.isFile() && sameFile2(initial, opened), "changed during open");
20456
+ need7(opened.isFile() && sameFile2(initial, opened), "changed during open");
20187
20457
  const bytes = Buffer.alloc(Number(opened.size) + 1);
20188
20458
  let length = 0;
20189
20459
  while (length < bytes.length) {
@@ -20193,10 +20463,10 @@ function readRegistry2(path, budget2) {
20193
20463
  length += count;
20194
20464
  }
20195
20465
  const after = fstatSync14(fd, { bigint: true }), current = lstatSync24(path, { bigint: true });
20196
- 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");
20197
20467
  for (const [ancestor, before] of ancestors2) {
20198
20468
  const now = lstatSync24(ancestor, { bigint: true });
20199
- 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");
20200
20470
  }
20201
20471
  budget2.remaining -= length;
20202
20472
  try {
@@ -20216,7 +20486,7 @@ function parse7(text2) {
20216
20486
  at++;
20217
20487
  };
20218
20488
  function string() {
20219
- need6(text2[at] === '"', "contains invalid JSON");
20489
+ need7(text2[at] === '"', "contains invalid JSON");
20220
20490
  const start = at++;
20221
20491
  while (at < text2.length) {
20222
20492
  const character = text2[at++];
@@ -20227,7 +20497,7 @@ function parse7(text2) {
20227
20497
  } catch {
20228
20498
  throw new Error("Claude marketplace registry contains invalid JSON string");
20229
20499
  }
20230
- 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");
20231
20501
  return value2;
20232
20502
  }
20233
20503
  if (character === "\\")
@@ -20236,7 +20506,7 @@ function parse7(text2) {
20236
20506
  throw new Error("Claude marketplace registry contains an unterminated JSON string");
20237
20507
  }
20238
20508
  function value(depth) {
20239
- 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");
20240
20510
  space();
20241
20511
  const character = text2[at];
20242
20512
  if (character === '"')
@@ -20250,17 +20520,17 @@ function parse7(text2) {
20250
20520
  if (object6) {
20251
20521
  space();
20252
20522
  const key = string();
20253
- need6(!keys.has(key), "contains a duplicate JSON key");
20523
+ need7(!keys.has(key), "contains a duplicate JSON key");
20254
20524
  keys.add(key);
20255
20525
  space();
20256
- need6(text2[at++] === ":", "contains invalid JSON");
20526
+ need7(text2[at++] === ":", "contains invalid JSON");
20257
20527
  entries.push([key, value(depth + 1)]);
20258
20528
  } else
20259
20529
  items.push(value(depth + 1));
20260
20530
  space();
20261
20531
  if (text2[at] === end)
20262
20532
  break;
20263
- need6(text2[at++] === ",", "contains invalid JSON");
20533
+ need7(text2[at++] === ",", "contains invalid JSON");
20264
20534
  }
20265
20535
  at++;
20266
20536
  return object6 ? { kind: "object", entries } : { kind: "array", items };
@@ -20272,67 +20542,67 @@ function parse7(text2) {
20272
20542
  }
20273
20543
  numberPattern.lastIndex = at;
20274
20544
  const number = numberPattern.exec(text2);
20275
- need6(number, "contains invalid JSON");
20545
+ need7(number, "contains invalid JSON");
20276
20546
  at += number[0].length;
20277
20547
  return { kind: "number", value: number[0] };
20278
20548
  }
20279
20549
  const root2 = value(0);
20280
20550
  space();
20281
- need6(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
20282
- 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");
20283
20553
  return root2;
20284
20554
  }
20285
20555
  function exactKeys(value, keys) {
20286
20556
  return value?.kind === "object" && value.entries.length === keys.length && value.entries.every(([key]) => keys.includes(key));
20287
20557
  }
20288
- var field = (object6, key) => object6.entries.find(([name]) => name === key)?.[1];
20558
+ var field2 = (object6, key) => object6.entries.find(([name]) => name === key)?.[1];
20289
20559
  function textField(value) {
20290
- 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");
20291
20561
  return value.value;
20292
20562
  }
20293
20563
  function normalizeRow(row, version = 1) {
20294
20564
  if (!exactKeys(row, ["source", "installLocation", "lastUpdated"])) {
20295
20565
  if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
20296
20566
  return;
20297
- const autoUpdate = field(row, "autoUpdate");
20298
- 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");
20299
20569
  }
20300
- const source = field(row, "source");
20570
+ const source = field2(row, "source");
20301
20571
  if (source?.kind !== "object")
20302
20572
  return;
20303
- const type = field(source, "source");
20573
+ const type = field2(source, "source");
20304
20574
  if (type?.kind !== "string" || !["github", "directory"].includes(type.value))
20305
20575
  return;
20306
20576
  const location = type.value === "github" ? "repo" : "path";
20307
20577
  if (!exactKeys(source, ["source", location]))
20308
20578
  return;
20309
- const sourceLocation = textField(field(source, location));
20579
+ const sourceLocation = textField(field2(source, location));
20310
20580
  if (type.value === "directory")
20311
20581
  absolutePath3(sourceLocation);
20312
- absolutePath3(textField(field(row, "installLocation")));
20313
- const updated = textField(field(row, "lastUpdated"));
20314
- 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");
20315
20585
  const millis = Date.parse(updated);
20316
- 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");
20317
20587
  row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
20318
20588
  }
20319
20589
  function captureClaudeMarketplaceRegistry(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
20320
20590
  const value = parse7(readRegistry2(path, budget2));
20321
20591
  for (const [, row] of value.entries)
20322
20592
  normalizeRow(row);
20323
- 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");
20324
20594
  return { path, hashMode: "claude-marketplace-registry", sha256: sha2563 };
20325
20595
  }
20326
20596
  function captureClaudeMarketplaceRegistryV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
20327
20597
  const value = parse7(readRegistry2(path, budget2));
20328
20598
  for (const [, row] of value.entries)
20329
20599
  normalizeRow(row, 2);
20330
- 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");
20331
20601
  return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2563 };
20332
20602
  }
20333
20603
 
20334
20604
  // src/lib/codex-hook-discovery-recovery.ts
20335
- import { createHash as createHash23 } from "crypto";
20605
+ import { createHash as createHash24 } from "crypto";
20336
20606
 
20337
20607
  // src/lib/codex-hook-additions-review.ts
20338
20608
  var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
@@ -20340,12 +20610,12 @@ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
20340
20610
  var proofs = new WeakMap;
20341
20611
 
20342
20612
  // src/lib/codex-hook-discovery-recovery.ts
20343
- var sha4 = (text2) => createHash23("sha256").update(text2).digest("hex");
20613
+ var sha4 = (text2) => createHash24("sha256").update(text2).digest("hex");
20344
20614
  var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
20345
20615
  var proofs2 = new WeakMap;
20346
20616
  function assertCodexHookDiscoveryRecovery(binding, witness) {
20347
20617
  const proof = proofs2.get(witness);
20348
- 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");
20349
20619
  unchanged2(proof.before);
20350
20620
  unchanged2(proof.current);
20351
20621
  proof.recheckAdditions();
@@ -20354,12 +20624,43 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
20354
20624
  if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
20355
20625
  return false;
20356
20626
  assertCodexHookDiscoveryRecovery(binding, witness);
20357
- 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");
20358
20628
  return true;
20359
20629
  }
20360
20630
 
20361
20631
  // src/lib/agent-discovery.ts
20362
- 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");
20363
20664
  function parseConfig(text2, path, toml = false) {
20364
20665
  try {
20365
20666
  const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
@@ -20371,12 +20672,12 @@ function parseConfig(text2, path, toml = false) {
20371
20672
  }
20372
20673
  }
20373
20674
  function safe4(path) {
20374
- if (!isAbsolute19(path) || path.includes("\x00"))
20675
+ if (!isAbsolute21(path) || path.includes("\x00"))
20375
20676
  throw new Error("Expected an absolute native discovery path");
20376
- for (let cursor = path;; cursor = dirname19(cursor)) {
20677
+ for (let cursor = path;; cursor = dirname20(cursor)) {
20377
20678
  if (lstatSync25(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
20378
20679
  throw new Error(`Refusing symlink native discovery input: ${cursor}`);
20379
- if (dirname19(cursor) === cursor)
20680
+ if (dirname20(cursor) === cursor)
20380
20681
  break;
20381
20682
  }
20382
20683
  }
@@ -20395,7 +20696,7 @@ function projectNativeDiscoveryFields(text2, format, fields2, path) {
20395
20696
  const object6 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
20396
20697
  if (!object6 || typeof object6 !== "object" || Array.isArray(object6))
20397
20698
  throw new Error("Expected native discovery configuration object");
20398
- 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]))));
20399
20700
  }
20400
20701
  function isSupersededSettingsProjection(agent, configPath, source) {
20401
20702
  if (agent === "sumi")
@@ -20440,6 +20741,14 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
20440
20741
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
20441
20742
  return sha2563;
20442
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
+ }
20443
20752
  if (source.hashMode === "claude-plugin-registry") {
20444
20753
  if (source.format !== undefined || source.fields !== undefined || !source.managedPlugins || changes?.has(source.path))
20445
20754
  throw new Error("Managed plugin registry witnesses require verified native registrations");
@@ -20477,7 +20786,7 @@ function absentCodexSkillRoots(binding) {
20477
20786
  throw new Error("Invalid disabled Codex discovery proof");
20478
20787
  if (binding.method !== "reviewed" || controls.every((control) => lstatSync25(control.pluginParent, { throwIfNoEntry: false })))
20479
20788
  return [];
20480
- 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");
20481
20790
  const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
20482
20791
  if (!source || projected(source) !== source.sha256)
20483
20792
  throw new Error("Missing current disabled Codex configuration witness");
@@ -20485,7 +20794,7 @@ function absentCodexSkillRoots(binding) {
20485
20794
  return [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && absentDisabledCodexPluginParent(cache, parent, controls, rules));
20486
20795
  }
20487
20796
  function codexDirectHookSelects(cache, root2) {
20488
- 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")) ?? "");
20489
20798
  const hooks = read2(join32(codex, "hooks.json"));
20490
20799
  if (hooks !== null)
20491
20800
  hashNativeJsonControls(hooks);
@@ -20498,7 +20807,7 @@ function retiredCodexRoots(binding) {
20498
20807
  const controls = binding.codexDisabledPluginSkills;
20499
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)
20500
20809
  throw new Error("Invalid retired Codex materialization proof");
20501
- 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");
20502
20811
  const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
20503
20812
  if (!source || projected(source) !== source.sha256)
20504
20813
  throw new Error("Missing current retired Codex configuration witness");
@@ -20515,8 +20824,8 @@ function retiredCodexRoots(binding) {
20515
20824
  function captureRetiredCodexDiscovery(binding, controls, rules) {
20516
20825
  if (binding.agent !== "codex" || binding.method !== "reviewed" || !controls.length)
20517
20826
  return binding;
20518
- const cache = dirname19(dirname19(controls[0].pluginParent));
20519
- 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)));
20520
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();
20521
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() : [];
20522
20831
  const { codexRetiredMaterializations: previous, ...current } = binding;
@@ -20545,7 +20854,7 @@ function absentRetiredClaudeRoots(binding) {
20545
20854
  safe4(root2);
20546
20855
  if (lstatSync25(root2, { throwIfNoEntry: false }))
20547
20856
  continue;
20548
- 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");
20549
20858
  const full = (path) => binding.sources.find((item) => item.path === path && item.sha256 !== null && item.format === undefined && item.fields === undefined);
20550
20859
  const configSource = full(settings), registrySource = full(registry);
20551
20860
  if (!configSource || !registrySource || projected(configSource) !== configSource.sha256 || projected(registrySource) !== registrySource.sha256)
@@ -20562,7 +20871,7 @@ function absentRetiredClaudeRoots(binding) {
20562
20871
  if (!Array.isArray(rows) || rows.length !== 1 || rows[0]?.scope !== "user" || typeof rows[0]?.installPath !== "string")
20563
20872
  continue;
20564
20873
  const active = rows[0].installPath;
20565
- 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)
20566
20875
  continue;
20567
20876
  const activeManifest = full(join32(active, ".claude-plugin/plugin.json"));
20568
20877
  if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
@@ -20613,7 +20922,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
20613
20922
  assertMarketplaceBinding(binding, source);
20614
20923
  if (source.hashMode === "claude-plugin-registry" && binding.agent !== "claude")
20615
20924
  throw new Error("Managed Claude registry witnesses cannot apply to another agent");
20616
- 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)))
20617
20926
  throw new Error("Invalid native discovery projection");
20618
20927
  if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
20619
20928
  throw new Error("Invalid native discovery digest");
@@ -20643,13 +20952,13 @@ function codexInstallationRoots(binding) {
20643
20952
  return [];
20644
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)
20645
20954
  throw new Error("Invalid native installation input proof");
20646
- const cache = dirname19(dirname19(proof.plugins[0].pluginParent));
20647
- 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)));
20648
20957
  const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
20649
20958
  const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join32(home, path));
20650
- 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)))))
20651
20960
  throw new Error("Native installation input overlaps configuration or loading roots");
20652
- 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)))
20653
20962
  throw new Error("Missing native installation input source witness");
20654
20963
  return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
20655
20964
  const text2 = read2(path);
@@ -20659,16 +20968,18 @@ function codexInstallationRoots(binding) {
20659
20968
  });
20660
20969
  }
20661
20970
  function assertMarketplaceBinding(binding, source) {
20662
- 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"))))
20663
20972
  throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
20664
- 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"))
20665
20974
  throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
20666
- 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"))
20667
20976
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
20668
- 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"))
20669
20978
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
20670
20979
  if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
20671
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");
20672
20983
  }
20673
20984
  function rebindAgentDiscovery(binding, changes) {
20674
20985
  const budget2 = discoveryByteBudget();
@@ -20701,10 +21012,10 @@ function captureDiscoveryPathSources(paths) {
20701
21012
  const budget2 = discoveryByteBudget();
20702
21013
  return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget2) }));
20703
21014
  }
20704
- function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18, reviewed) {
20705
- const homeConfigPath = canonical7(agentDiscoveryConfigPath(home, agent));
21015
+ function assertProjectDiscovery(agent, directories, home, canonical8 = resolve20, reviewed) {
21016
+ const homeConfigPath = canonical8(agentDiscoveryConfigPath(home, agent));
20706
21017
  for (const directory of directories) {
20707
- const isHome = resolve18(directory) === resolve18(home);
21018
+ const isHome = resolve20(directory) === resolve20(home);
20708
21019
  if (agent === "hermes") {
20709
21020
  assertHermesEnvironment(home);
20710
21021
  continue;
@@ -20713,7 +21024,7 @@ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18
20713
21024
  continue;
20714
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"];
20715
21026
  for (const suffix of paths) {
20716
- const path = canonical7(join32(directory, suffix)), raw = read2(path);
21027
+ const path = canonical8(join32(directory, suffix)), raw = read2(path);
20717
21028
  if (raw === null)
20718
21029
  continue;
20719
21030
  const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
@@ -20729,7 +21040,7 @@ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18
20729
21040
  const managed = reviewed.sources.flatMap((source) => source.hashMode === "claude-plugin-registry" ? source.managedPlugins ?? [] : []);
20730
21041
  if (exact && projected(exact) === exact.sha256 && managed.length) {
20731
21042
  const bindings = managed.map((item) => readPluginBinding(item.storeRoot, item.bindingId));
20732
- 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))));
20733
21044
  }
20734
21045
  }
20735
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)))
@@ -20737,13 +21048,13 @@ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18
20737
21048
  }
20738
21049
  if (agent === "sumi")
20739
21050
  for (const name of ["plugin", "plugins"]) {
20740
- const path = canonical7(join32(directory, ".sumi", name));
21051
+ const path = canonical8(join32(directory, ".sumi", name));
20741
21052
  safe4(path);
20742
21053
  if (lstatSync25(path, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(path).length)
20743
21054
  throw new Error(`NATIVE_SKILL_DRIFT: project Sumi plugins require a dedicated discovery review: ${path}`);
20744
21055
  }
20745
21056
  if (agent === "claude") {
20746
- const commands = canonical7(join32(directory, ".claude/commands"));
21057
+ const commands = canonical8(join32(directory, ".claude/commands"));
20747
21058
  safe4(commands);
20748
21059
  if (lstatSync25(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
20749
21060
  throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
@@ -20755,11 +21066,37 @@ function agentDiscoveryConfigPath(home, agent) {
20755
21066
  return sumiConfigPath(home);
20756
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"}`);
20757
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
+ }
20758
21095
  function resolveAgentDiscovery(options) {
20759
- 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;
20760
21097
  const sources = [], roots = new Set, builtinNames = [];
20761
21098
  const witness = (path, format, fields2, hashMode) => {
20762
- 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 } : {} };
20763
21100
  source.sha256 = projected(source);
20764
21101
  sources.push(source);
20765
21102
  return read2(source.path);
@@ -20781,13 +21118,13 @@ function resolveAgentDiscovery(options) {
20781
21118
  unresolved("JSONC configuration requires a supported format adapter");
20782
21119
  for (const root2 of [directory, join32(home, ".claude"), join32(home, ".agents")])
20783
21120
  for (const name of ["skill", "skills"])
20784
- roots.add(canonical7(join32(root2, name)));
21121
+ roots.add(canonical8(join32(root2, name)));
20785
21122
  if (config.skills !== undefined && (!Array.isArray(config.skills) || config.skills.some((path) => typeof path !== "string")))
20786
21123
  unresolved("skills must be a string array");
20787
21124
  for (const value of config.skills ?? []) {
20788
- 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("~/")))
20789
21126
  unresolved("remote or relative skill sources need a dedicated discovery adapter");
20790
- roots.add(canonical7(value.startsWith("~/") ? join32(home, value.slice(2)) : value));
21127
+ roots.add(canonical8(value.startsWith("~/") ? join32(home, value.slice(2)) : value));
20791
21128
  }
20792
21129
  for (const key of ["plugin", "plugins"])
20793
21130
  if (config[key] !== undefined && !Array.isArray(config[key]))
@@ -20826,23 +21163,23 @@ function resolveAgentDiscovery(options) {
20826
21163
  continue;
20827
21164
  if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
20828
21165
  unresolved("user-specific tilde expansion requires a separate discovery adapter");
20829
- 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);
20830
21167
  safe4(path);
20831
21168
  roots.add(path);
20832
21169
  }
20833
21170
  }
20834
21171
  if (agent === "gemini") {
20835
- const executable2 = Bun.which("gemini");
21172
+ const executable2 = options.executable === undefined ? discoveryExecutable(agent) : options.executable;
20836
21173
  if (executable2) {
20837
21174
  let packageRoot;
20838
- 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++) {
20839
21176
  const path = join32(directory, "package.json"), raw = read2(path);
20840
21177
  if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
20841
21178
  packageRoot = directory;
20842
21179
  witness(path);
20843
21180
  break;
20844
21181
  }
20845
- if (dirname19(directory) === directory)
21182
+ if (dirname20(directory) === directory)
20846
21183
  break;
20847
21184
  }
20848
21185
  if (!packageRoot)
@@ -20865,11 +21202,11 @@ function resolveAgentDiscovery(options) {
20865
21202
  if (retained.agent !== agent || retained.method !== "reviewed")
20866
21203
  unresolved("invalid retained review");
20867
21204
  verifyAgentDiscovery(retained);
20868
- 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))
20869
21206
  unresolved("retained review is missing the full agent configuration source");
20870
- 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)))
20871
21208
  unresolved("retained settings witness names another configuration source");
20872
- 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;
20873
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))
20874
21211
  unresolved("retained review is missing current runtime discovery coverage");
20875
21212
  return retained;
@@ -20881,15 +21218,15 @@ function resolveAgentDiscovery(options) {
20881
21218
  if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
20882
21219
  throw new Error("Explicit discovery reviews require full source-file hashes");
20883
21220
  verifyAgentDiscovery(supplied);
20884
- 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)))
20885
21222
  throw new Error("Semantic settings witnesses must name the configured agent configuration source");
20886
- if (!review.sources.some((source) => source.path === canonical7(configPath)))
21223
+ if (!review.sources.some((source) => source.path === canonical8(configPath)))
20887
21224
  throw new Error("Discovery review must include the agent configuration source");
20888
- 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;
20889
21226
  const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
20890
- 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")))
20891
21228
  return source;
20892
- 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");
20893
21230
  const hookStat = lstatSync25(hooksPath, { throwIfNoEntry: false });
20894
21231
  const text2 = read2(source.path);
20895
21232
  if (text2 === null)
@@ -20904,13 +21241,13 @@ function resolveAgentDiscovery(options) {
20904
21241
  if (manifest.hooks !== undefined) {
20905
21242
  if (typeof manifest.hooks !== "string")
20906
21243
  throw new Error("Reviewed Claude plugin manifest has an unsupported hooks target");
20907
- const declaredHookPath = resolve18(pluginRoot, manifest.hooks);
21244
+ const declaredHookPath = resolve20(pluginRoot, manifest.hooks);
20908
21245
  if (!declaredHookPath.startsWith(pluginRoot + sep8))
20909
21246
  throw new Error("Reviewed Claude plugin hooks target escapes its root");
20910
21247
  reviewedHookPaths.add(declaredHookPath);
20911
21248
  }
20912
21249
  for (const hooksFile of reviewedHookPaths)
20913
- 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")))
20914
21251
  throw new Error("Reviewed Claude plugin manifest requires separately reviewed exact hook file sources");
20915
21252
  return { path: source.path, sha256: manifestSha256, hashMode: "claude-plugin-manifest-v1" };
20916
21253
  });
@@ -20919,7 +21256,7 @@ function resolveAgentDiscovery(options) {
20919
21256
  return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
20920
21257
  }
20921
21258
  function plugin(root2) {
20922
- root2 = canonical7(root2);
21259
+ root2 = canonical8(root2);
20923
21260
  safe4(root2);
20924
21261
  const manifestPath = join32(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
20925
21262
  const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
@@ -20942,16 +21279,16 @@ function resolveAgentDiscovery(options) {
20942
21279
  if (agent === "claude")
20943
21280
  roots.add(join32(root2, "skills"));
20944
21281
  for (const value of declared) {
20945
- const path = resolve18(root2, value);
21282
+ const path = resolve20(root2, value);
20946
21283
  if (path !== root2 && !path.startsWith(root2 + sep8))
20947
21284
  unresolved("plugin skill path escapes its root");
20948
- roots.add(path.endsWith(`${sep8}SKILL.md`) ? dirname19(path) : path);
21285
+ roots.add(path.endsWith(`${sep8}SKILL.md`) ? dirname20(path) : path);
20949
21286
  }
20950
21287
  }
20951
21288
  if (agent === "claude") {
20952
21289
  const registrations = witness(join32(home, ".claude/plugins/installed_plugins.json"));
20953
21290
  const installed = registrations === null ? {} : parseConfig(registrations, join32(home, ".claude/plugins/installed_plugins.json")).plugins;
20954
- const commands = canonical7(join32(home, ".claude/commands"));
21291
+ const commands = canonical8(join32(home, ".claude/commands"));
20955
21292
  safe4(commands);
20956
21293
  if (lstatSync25(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
20957
21294
  unresolved("legacy home command discovery requires a dedicated format adapter");
@@ -20973,7 +21310,7 @@ function resolveAgentDiscovery(options) {
20973
21310
  unresolved("unsupported plugin enablement");
20974
21311
  const split = id.lastIndexOf("@"), name = id.slice(0, split), marketplace = id.slice(split + 1);
20975
21312
  const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
20976
- 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))
20977
21314
  unresolved("enabled plugin marketplace is not an explicit local source");
20978
21315
  const catalogText = witness(join32(market.source, ".agents/plugins/marketplace.json"));
20979
21316
  if (catalogText === null)
@@ -20981,7 +21318,7 @@ function resolveAgentDiscovery(options) {
20981
21318
  const catalog = parseConfig(catalogText, join32(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
20982
21319
  if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
20983
21320
  unresolved("plugin catalog source is missing or ambiguous");
20984
- plugin(resolve18(market.source, matches[0].source.path));
21321
+ plugin(resolve20(market.source, matches[0].source.path));
20985
21322
  }
20986
21323
  } else if (agent === "opencode") {
20987
21324
  if (witness(join32(home, ".config/opencode/opencode.jsonc")) !== null || witness(join32(home, ".config/opencode/config.json")) !== null)
@@ -20989,7 +21326,7 @@ function resolveAgentDiscovery(options) {
20989
21326
  if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
20990
21327
  unresolved("external plugin or remote skill source");
20991
21328
  for (const path of config.skills?.paths ?? []) {
20992
- if (typeof path !== "string" || !isAbsolute19(path))
21329
+ if (typeof path !== "string" || !isAbsolute21(path))
20993
21330
  unresolved("relative or malformed added skill root");
20994
21331
  roots.add(path);
20995
21332
  }
@@ -20998,7 +21335,7 @@ function resolveAgentDiscovery(options) {
20998
21335
  if (lstatSync25(localPlugins, { throwIfNoEntry: false }) && readdirSync10(localPlugins).some((name) => name !== "skills-cli.js"))
20999
21336
  unresolved("additional local plugin hooks require review");
21000
21337
  } else if (agent === "sumi") {
21001
- 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))
21002
21339
  unresolved("external or foreign local plugins require review");
21003
21340
  } else if (agent === "hermes") {
21004
21341
  for (const directory of [join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")]) {
@@ -21028,13 +21365,13 @@ function resolveAgentDiscovery(options) {
21028
21365
  roots.add(join32(root2, "skills"));
21029
21366
  }
21030
21367
  }
21031
- 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))) } : {} };
21032
21369
  }
21033
21370
 
21034
21371
  // src/lib/authoring-path.ts
21035
21372
  var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
21036
21373
  function pathParts(path) {
21037
- return resolve19(path).split(sep9).filter(Boolean);
21374
+ return resolve21(path).split(sep9).filter(Boolean);
21038
21375
  }
21039
21376
  function nativeRootMatch(path) {
21040
21377
  const parts = pathParts(path);
@@ -21048,16 +21385,16 @@ function nativeRootMatch(path) {
21048
21385
  return;
21049
21386
  }
21050
21387
  function physicalPath(path) {
21051
- let cursor = resolve19(path);
21388
+ let cursor = resolve21(path);
21052
21389
  const suffix = [];
21053
21390
  while (!lstatSync26(cursor, { throwIfNoEntry: false })) {
21054
- suffix.unshift(basename11(cursor));
21055
- const parent = dirname20(cursor);
21391
+ suffix.unshift(basename12(cursor));
21392
+ const parent = dirname21(cursor);
21056
21393
  if (parent === cursor)
21057
- return resolve19(path);
21394
+ return resolve21(path);
21058
21395
  cursor = parent;
21059
21396
  }
21060
- return resolve19(realpathSync12(cursor), ...suffix);
21397
+ return resolve21(realpathSync12(cursor), ...suffix);
21061
21398
  }
21062
21399
  function contains(root2, path) {
21063
21400
  return path === root2 || path.startsWith(root2 + sep9);
@@ -21065,7 +21402,7 @@ function contains(root2, path) {
21065
21402
  function configuredRoots(home, explicitHome) {
21066
21403
  const homeData = join33(home, ".hasna", "skills");
21067
21404
  const homePolicy = readManagedSkillPolicySnapshot(homeData);
21068
- const configuredPolicy = !explicitHome && resolve19(getDataDirReadOnly()) !== resolve19(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
21405
+ const configuredPolicy = !explicitHome && resolve21(getDataDirReadOnly()) !== resolve21(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
21069
21406
  const policy = homePolicy ?? configuredPolicy;
21070
21407
  const bridge = policy?.value.bridge;
21071
21408
  if (bridge && (typeof bridge.home !== "string" || physicalPath(bridge.home) !== physicalPath(home))) {
@@ -21104,22 +21441,22 @@ function configuredRoots(home, explicitHome) {
21104
21441
  function assertPortableAuthoringPath(path, options = {}) {
21105
21442
  if (typeof path !== "string" || !path || path.includes("\x00"))
21106
21443
  throw new Error("Invalid portable authoring path");
21107
- const lexical = resolve19(path);
21444
+ const lexical = resolve21(path);
21108
21445
  const physical = physicalPath(lexical);
21109
21446
  const candidates = [lexical, physical];
21110
21447
  let native = candidates.map(nativeRootMatch).find(Boolean);
21111
- const home = resolve19(options.homeDir ?? homedir6());
21448
+ const home = resolve21(options.homeDir ?? homedir6());
21112
21449
  const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join33(home, ...parts));
21113
21450
  defaults.push(join33(home, ".codex/plugins/cache"), join33(home, ".claude/plugins/cache"), join33(home, ".claude/plugins/synced"), join33(home, ".gemini/extensions"));
21114
21451
  for (const root2 of defaults) {
21115
- 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)))
21116
21453
  native ??= root2;
21117
21454
  }
21118
21455
  if (native) {
21119
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.`);
21120
21457
  }
21121
21458
  for (const root2 of configuredRoots(home, options.homeDir !== undefined)) {
21122
- 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))) {
21123
21460
  throw new Error(`Refusing portable Skills authoring below configured native discovery root: ${lexical}`);
21124
21461
  }
21125
21462
  }
@@ -21267,20 +21604,20 @@ function isSkillCandidate(dir) {
21267
21604
  return existsSync14(join34(dir, "SKILL.md")) || existsSync14(join34(dir, "skill.json")) || existsSync14(join34(dir, "package.json"));
21268
21605
  }
21269
21606
  function physicalImportPath(path) {
21270
- let ancestor = resolve20(path);
21607
+ let ancestor = resolve22(path);
21271
21608
  const suffix = [];
21272
21609
  while (!existsSync14(ancestor)) {
21273
- suffix.unshift(basename12(ancestor));
21274
- ancestor = dirname21(ancestor);
21610
+ suffix.unshift(basename13(ancestor));
21611
+ ancestor = dirname22(ancestor);
21275
21612
  }
21276
21613
  return join34(realpathSync13(ancestor), ...suffix);
21277
21614
  }
21278
21615
  function assertDisjointImportPaths(source, destination) {
21279
21616
  const sourcePath = realpathSync13(source);
21280
21617
  const destinationPath = physicalImportPath(destination);
21281
- const contains2 = (parent, child) => {
21282
- const path = relative7(parent, child);
21283
- 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}`);
21284
21621
  };
21285
21622
  if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
21286
21623
  throw new Error("Source and destination skill directories must not overlap");
@@ -21295,8 +21632,8 @@ function portPortableSkill(sourcePath, options = {}) {
21295
21632
  const explicitName = options.name != null;
21296
21633
  const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
21297
21634
  if (isOfficialSkillName(skillName) && !options.allowShadow) {
21298
- const sourceSlug = safeNormalizeName(basename12(absoluteSource));
21299
- 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.`;
21300
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.`);
21301
21638
  }
21302
21639
  const readOnlyDestination = join34(resolvePortableSkillsRoot(options, false), skillName);
@@ -21311,7 +21648,7 @@ function portPortableSkill(sourcePath, options = {}) {
21311
21648
  throw new Error(`Skill '${skillName}' already exists at ${destination}`);
21312
21649
  rmSync4(destination, { recursive: true, force: true });
21313
21650
  }
21314
- mkdirSync10(dirname21(destination), { recursive: true });
21651
+ mkdirSync10(dirname22(destination), { recursive: true });
21315
21652
  copySkillDirectory(absoluteSource, destination);
21316
21653
  const base = {
21317
21654
  ...inferred,
@@ -21561,7 +21898,7 @@ function safeIsDirectory(path) {
21561
21898
  }
21562
21899
  }
21563
21900
  function isSafeRelativePath2(value) {
21564
- if (!value.trim() || isAbsolute20(value))
21901
+ if (!value.trim() || isAbsolute22(value))
21565
21902
  return false;
21566
21903
  const normalized = normalize4(value).replace(/\\/g, "/");
21567
21904
  return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
@@ -21877,7 +22214,7 @@ function getAllTags(catalog) {
21877
22214
  return Array.from(tagSet).sort();
21878
22215
  }
21879
22216
  // src/lib/run-state.ts
21880
- import { createHash as createHash25, randomBytes } from "crypto";
22217
+ import { createHash as createHash26, randomBytes } from "crypto";
21881
22218
  import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync23, readdirSync as readdirSync13, statSync as statSync12, writeFileSync as writeFileSync10 } from "fs";
21882
22219
  import { extname, join as join36, relative as relative8 } from "path";
21883
22220
  function createSkillRun(params, targetDir = process.cwd()) {
@@ -22004,7 +22341,7 @@ function collectRunArtifacts(context) {
22004
22341
  artifacts.push({
22005
22342
  path: toProjectRelative(context.targetDir, path),
22006
22343
  mime: mimeForPath(path),
22007
- sha256: createHash25("sha256").update(bytes).digest("hex"),
22344
+ sha256: createHash26("sha256").update(bytes).digest("hex"),
22008
22345
  sizeBytes: stat2.size
22009
22346
  });
22010
22347
  }
@@ -25533,14 +25870,14 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
25533
25870
  const shape = zod.def.shape;
25534
25871
  const propValues = {};
25535
25872
  for (const key in shape) {
25536
- const field2 = shape[key]._zod;
25537
- if (field2.values) {
25873
+ const field3 = shape[key]._zod;
25874
+ if (field3.values) {
25538
25875
  if (!Object.prototype.hasOwnProperty.call(propValues, key)) {
25539
25876
  assignProp(propValues, key, new Set);
25540
25877
  }
25541
- for (const v of field2.values)
25878
+ for (const v of field3.values)
25542
25879
  propValues[key].add(v);
25543
- if (field2.optin !== undefined)
25880
+ if (field3.optin !== undefined)
25544
25881
  propValues[key].add(undefined);
25545
25882
  }
25546
25883
  }
@@ -26953,8 +27290,8 @@ function isRecursive(inst, stack) {
26953
27290
  return true;
26954
27291
  stack.add(inst);
26955
27292
  let result = false;
26956
- const check = (child) => {
26957
- if (!result && child?._zod && isRecursive(child, stack))
27293
+ const check = (child2) => {
27294
+ if (!result && child2?._zod && isRecursive(child2, stack))
26958
27295
  result = true;
26959
27296
  };
26960
27297
  const def = inst._zod.def;
@@ -37733,11 +38070,11 @@ var objectProcessor = (schema, ctx, _json, params) => {
37733
38070
  }
37734
38071
  const allKeys = new Set(Object.keys(shape));
37735
38072
  const requiredKeys = new Set([...allKeys].filter((key) => {
37736
- const field2 = def.shape[key];
38073
+ const field3 = def.shape[key];
37737
38074
  if (ctx.io === "input") {
37738
- return inputOptin(field2) === undefined;
38075
+ return inputOptin(field3) === undefined;
37739
38076
  } else {
37740
- return field2._zod.optout === undefined;
38077
+ return field3._zod.optout === undefined;
37741
38078
  }
37742
38079
  }));
37743
38080
  if (requiredKeys.size > 0) {
@@ -41423,7 +41760,7 @@ function clone2(value) {
41423
41760
  return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
41424
41761
  }
41425
41762
  // src/lib/canonical-json.ts
41426
- import { createHash as createHash26 } from "crypto";
41763
+ import { createHash as createHash27 } from "crypto";
41427
41764
  function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
41428
41765
  return canonicalJsonAtDepth(value, maximumBytes, 64);
41429
41766
  }
@@ -41467,7 +41804,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
41467
41804
  return visit3(value, 0);
41468
41805
  }
41469
41806
  function canonicalJsonSha256(value, maximumBytes) {
41470
- return createHash26("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
41807
+ return createHash27("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
41471
41808
  }
41472
41809
 
41473
41810
  // src/lib/remote-recurring-contract.ts
@@ -42291,17 +42628,17 @@ function runQuoteReceipt(value) {
42291
42628
  return value;
42292
42629
  }
42293
42630
  function parseRemoteRunQuote(value) {
42294
- const quote2 = object7(value);
42295
- runQuoteReceipt(quote2.quoteReceipt);
42296
- const pricing = object7(quote2.pricing);
42297
- 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))
42298
42635
  throw new Error("Invalid quoted skill");
42299
42636
  const costCents = creditCount(pricing.costCredits ?? pricing.costCents);
42300
42637
  if (pricing.costCredits !== undefined && pricing.costCents !== undefined && pricing.costCents !== costCents)
42301
42638
  throw new Error("Inconsistent quoted credit count");
42302
- if (quote2.availability && object7(quote2.availability).status !== "available")
42639
+ if (quote3.availability && object7(quote3.availability).status !== "available")
42303
42640
  throw new Error("This skill is unavailable for remote execution");
42304
- 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` } };
42305
42642
  }
42306
42643
  function parseRemoteCreditPacks(value) {
42307
42644
  if (!Array.isArray(value))
@@ -42376,14 +42713,14 @@ function creditCheckoutFailure(value, status, key) {
42376
42713
  }
42377
42714
 
42378
42715
  // src/lib/remote-profile.ts
42379
- function customerNamePatch(input2, field2) {
42380
- 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))
42381
42718
  throw new Error("Provide only the requested name field.");
42382
- const value = input2[field2];
42719
+ const value = input2[field3];
42383
42720
  if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
42384
42721
  throw new Error("Use a name of 1\u2013100 characters without control characters or newlines.");
42385
42722
  }
42386
- return { [field2]: value.trim() };
42723
+ return { [field3]: value.trim() };
42387
42724
  }
42388
42725
  function isRecord5(value) {
42389
42726
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -42428,10 +42765,10 @@ async function readQuoteUnavailableCode(response) {
42428
42765
  let timer;
42429
42766
  let deadlineExceeded = false;
42430
42767
  const expired = Symbol("quote body deadline");
42431
- const deadline = new Promise((resolve21) => {
42768
+ const deadline = new Promise((resolve23) => {
42432
42769
  timer = setTimeout(() => {
42433
42770
  deadlineExceeded = true;
42434
- resolve21(expired);
42771
+ resolve23(expired);
42435
42772
  reader.cancel().catch(() => {});
42436
42773
  }, 1500);
42437
42774
  });
@@ -42778,18 +43115,18 @@ class RemoteSkillsClient {
42778
43115
  const maximum = creditCount(approval.maxCredits ?? approval.maxCostCents ?? 0);
42779
43116
  if (approval.maxCostCents !== undefined && approval.maxCostCents !== maximum)
42780
43117
  throw new Error("Credit approval fields disagree");
42781
- const quote2 = approval.quoteReceipt === undefined ? await this.quoteRun(slug2, input2, args, approval.inputFiles?.length ? approval.inputFiles : undefined) : undefined;
42782
- if (quote2 && quote2.pricing.costCents > maximum)
42783
- 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);
42784
43121
  const capabilities = await this.getCapabilities();
42785
43122
  if (!capabilities.capabilities.includes("runs.submit") || capabilities.billing?.boundedRunApproval !== true || capabilities.billing.unit !== "credits") {
42786
43123
  throw new Error("The configured server does not support bounded credit approval; refusing remote submission");
42787
43124
  }
42788
- return this.submitRun(quote2?.skill ?? slug2, input2, args, {
43125
+ return this.submitRun(quote3?.skill ?? slug2, input2, args, {
42789
43126
  ...approval,
42790
43127
  maxCredits: maximum,
42791
43128
  maxCostCents: maximum,
42792
- ...(quote2?.quoteReceipt ?? approval.quoteReceipt) === undefined ? {} : { quoteReceipt: quote2?.quoteReceipt ?? approval.quoteReceipt }
43129
+ ...(quote3?.quoteReceipt ?? approval.quoteReceipt) === undefined ? {} : { quoteReceipt: quote3?.quoteReceipt ?? approval.quoteReceipt }
42793
43130
  });
42794
43131
  }
42795
43132
  async getIdentity() {
@@ -43390,13 +43727,13 @@ class RemoteSkillsClient {
43390
43727
  return normalizeUpdatedSincePage(await response.json());
43391
43728
  }
43392
43729
  }
43393
- function requireOptionalString(record8, field2) {
43394
- if (record8[field2] === undefined)
43730
+ function requireOptionalString(record8, field3) {
43731
+ if (record8[field3] === undefined)
43395
43732
  return;
43396
- if (typeof record8[field2] !== "string") {
43397
- 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)`);
43398
43735
  }
43399
- return record8[field2];
43736
+ return record8[field3];
43400
43737
  }
43401
43738
  var INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
43402
43739
  function isVersionRecord(value) {
@@ -43734,7 +44071,7 @@ function recordScheduleRun(id, status, targetDir) {
43734
44071
  saveSchedules(data, targetDir);
43735
44072
  }
43736
44073
  // src/lib/revision.ts
43737
- import { createHash as createHash27 } from "crypto";
44074
+ import { createHash as createHash28 } from "crypto";
43738
44075
  var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
43739
44076
  function revisionIdOf(content) {
43740
44077
  const canonicalContent = {
@@ -43755,15 +44092,15 @@ function revisionIdOf(content) {
43755
44092
  canonicalContent.archiveReason = content.archiveReason ?? null;
43756
44093
  canonicalContent.replacementSlug = content.replacementSlug ?? null;
43757
44094
  }
43758
- const canonical7 = JSON.stringify(canonicalContent);
43759
- return createHash27("sha256").update(canonical7).digest("hex");
44095
+ const canonical8 = JSON.stringify(canonicalContent);
44096
+ return createHash28("sha256").update(canonical8).digest("hex");
43760
44097
  }
43761
44098
  function revisionIdOfRecord(record8) {
43762
44099
  return revisionIdOf(record8);
43763
44100
  }
43764
44101
  // src/lib/pull.ts
43765
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";
43766
- import { dirname as dirname22, join as join38 } from "path";
44103
+ import { dirname as dirname23, join as join38 } from "path";
43767
44104
 
43768
44105
  // src/lib/skill-bundles.ts
43769
44106
  import { createHmac, timingSafeEqual } from "crypto";
@@ -44105,7 +44442,7 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
44105
44442
  try {
44106
44443
  for (const entry of entries) {
44107
44444
  const destination = join38(staging, entry.path);
44108
- mkdirSync13(dirname22(destination), { recursive: true });
44445
+ mkdirSync13(dirname23(destination), { recursive: true });
44109
44446
  writeFileSync12(destination, entry.bytes, { mode: entry.mode });
44110
44447
  }
44111
44448
  writePullMarker(staging, {
@@ -44199,7 +44536,7 @@ function splitNameVersion(raw) {
44199
44536
  return version2 ? { name, version: version2 } : { name };
44200
44537
  }
44201
44538
  // src/lib/recurring-surface.ts
44202
- import { isAbsolute as isAbsolute21 } from "path";
44539
+ import { isAbsolute as isAbsolute23 } from "path";
44203
44540
 
44204
44541
  // src/lib/remote-invitation-recovery.ts
44205
44542
  class InvitationEmailInputError extends Error {
@@ -44297,7 +44634,7 @@ async function requestInvitationEmail(origin, action, input2) {
44297
44634
  }
44298
44635
 
44299
44636
  // src/lib/remote-private-publications.ts
44300
- import { createHash as createHash28 } from "crypto";
44637
+ import { createHash as createHash29 } from "crypto";
44301
44638
  var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
44302
44639
  var failures2 = {
44303
44640
  INVALID_REQUEST: [400, "The publication request is invalid."],
@@ -44344,7 +44681,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
44344
44681
  var record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
44345
44682
  var exact = (v, keys) => record9(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
44346
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));
44347
- var publicationSha256 = (bytes) => createHash28("sha256").update(bytes).digest("hex");
44684
+ var publicationSha256 = (bytes) => createHash29("sha256").update(bytes).digest("hex");
44348
44685
  function checkedPublicationDeclaration(value) {
44349
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)
44350
44687
  return bad();
@@ -44589,12 +44926,12 @@ class RemotePrivatePublicationsClient {
44589
44926
  previous = view;
44590
44927
  if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
44591
44928
  return view;
44592
- await new Promise((resolve21) => {
44929
+ await new Promise((resolve23) => {
44593
44930
  const timer = setTimeout(done, Math.min(1000, until - Date.now()));
44594
44931
  function done() {
44595
44932
  clearTimeout(timer);
44596
44933
  options.signal?.removeEventListener("abort", done);
44597
- resolve21();
44934
+ resolve23();
44598
44935
  }
44599
44936
  options.signal?.addEventListener("abort", done, { once: true });
44600
44937
  });
@@ -44858,7 +45195,7 @@ var stateSchema = exports_external.object({
44858
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}$/);
44859
45196
  var context = { userId: uuid13.optional(), membershipId: uuid13.optional() };
44860
45197
  var page2 = { limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() };
44861
- 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");
44862
45199
  var email4 = exports_external.string().trim().toLowerCase().email().max(254);
44863
45200
  var code = exports_external.string().regex(/^\d{6}$/);
44864
45201
  var recurringSurfaceSchemas = {
@@ -45009,7 +45346,7 @@ function findSkillsParityForMcpTool(tool) {
45009
45346
  }
45010
45347
  // src/lib/registry-sync.ts
45011
45348
  import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync13 } from "fs";
45012
- import { dirname as dirname23, relative as relative9 } from "path";
45349
+ import { dirname as dirname24, relative as relative9 } from "path";
45013
45350
  function createRegistrySyncArtifact(options = {}) {
45014
45351
  const profile = options.profile ?? "all";
45015
45352
  const includeDocs = options.includeDocs ?? true;
@@ -45066,7 +45403,7 @@ function createRegistrySyncArtifact(options = {}) {
45066
45403
  };
45067
45404
  }
45068
45405
  function writeRegistrySyncArtifact(path, artifact) {
45069
- mkdirSync14(dirname23(path), { recursive: true });
45406
+ mkdirSync14(dirname24(path), { recursive: true });
45070
45407
  writeFileSync13(path, `${JSON.stringify(artifact, null, 2)}
45071
45408
  `);
45072
45409
  }
@@ -46151,14 +46488,14 @@ function clone3(value) {
46151
46488
  }
46152
46489
  // src/lib/feedback.ts
46153
46490
  import { appendFileSync, existsSync as existsSync19, mkdirSync as mkdirSync15 } from "fs";
46154
- import { dirname as dirname24, join as join39 } from "path";
46491
+ import { dirname as dirname25, join as join39 } from "path";
46155
46492
  import { Database } from "bun:sqlite";
46156
46493
  function getFeedbackDbPath() {
46157
46494
  return join39(getDataDir(), "skills.db");
46158
46495
  }
46159
46496
  function getFeedbackDb() {
46160
46497
  const dbPath = getFeedbackDbPath();
46161
- const dir = dirname24(dbPath);
46498
+ const dir = dirname25(dbPath);
46162
46499
  if (!existsSync19(dir))
46163
46500
  mkdirSync15(dir, { recursive: true });
46164
46501
  const db = new Database(dbPath);
@@ -46187,7 +46524,7 @@ function saveFeedback(input2) {
46187
46524
  const category = input2.category ?? "general";
46188
46525
  if (isApiMode()) {
46189
46526
  const path = join39(getDataDir(), "feedback.jsonl");
46190
- const dir = dirname24(path);
46527
+ const dir = dirname25(path);
46191
46528
  if (!existsSync19(dir))
46192
46529
  mkdirSync15(dir, { recursive: true });
46193
46530
  appendFileSync(path, JSON.stringify({ message, category, email: input2.email ?? null, agent: input2.agent ?? null, version: input2.version ?? null, createdAt: new Date().toISOString() }) + `
@@ -46210,7 +46547,7 @@ function isApiMode(env = process.env) {
46210
46547
  }
46211
46548
  }
46212
46549
  // src/lib/native-storage.ts
46213
- import { createHash as createHash29, createHmac as createHmac2 } from "crypto";
46550
+ import { createHash as createHash30, createHmac as createHmac2 } from "crypto";
46214
46551
  import {
46215
46552
  existsSync as existsSync20,
46216
46553
  mkdirSync as mkdirSync16,
@@ -46219,7 +46556,7 @@ import {
46219
46556
  statSync as statSync13,
46220
46557
  writeFileSync as writeFileSync14
46221
46558
  } from "fs";
46222
- 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";
46223
46560
  var SKILLS_STORAGE_TABLES = [
46224
46561
  "skills_sync_records",
46225
46562
  "skills_sync_cursors"
@@ -46386,7 +46723,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
46386
46723
  files.push({
46387
46724
  path: relativePath,
46388
46725
  sizeBytes: bytes.byteLength,
46389
- sha256: createHash29("sha256").update(bytes).digest("hex"),
46726
+ sha256: createHash30("sha256").update(bytes).digest("hex"),
46390
46727
  ...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
46391
46728
  });
46392
46729
  }
@@ -46411,11 +46748,11 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
46411
46748
  continue;
46412
46749
  }
46413
46750
  const bytes = Buffer.from(file2.contentBase64, "base64");
46414
- const hash4 = createHash29("sha256").update(bytes).digest("hex");
46751
+ const hash4 = createHash30("sha256").update(bytes).digest("hex");
46415
46752
  if (hash4 !== file2.sha256) {
46416
46753
  throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
46417
46754
  }
46418
- mkdirSync16(dirname25(absolutePath4), { recursive: true });
46755
+ mkdirSync16(dirname26(absolutePath4), { recursive: true });
46419
46756
  writeFileSync14(absolutePath4, bytes);
46420
46757
  written += 1;
46421
46758
  }
@@ -46743,7 +47080,7 @@ function toIsoString(value) {
46743
47080
  return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
46744
47081
  }
46745
47082
  function sha256Hex2(value) {
46746
- return createHash29("sha256").update(value).digest("hex");
47083
+ return createHash30("sha256").update(value).digest("hex");
46747
47084
  }
46748
47085
  function normalizeHeaders(headers) {
46749
47086
  const result = {};
@@ -46795,7 +47132,7 @@ function toArrayBuffer(bytes) {
46795
47132
  return buffer;
46796
47133
  }
46797
47134
  // src/lib/station-snapshot.ts
46798
- import { createHash as createHash30 } from "crypto";
47135
+ import { createHash as createHash31 } from "crypto";
46799
47136
  import {
46800
47137
  copyFileSync,
46801
47138
  mkdirSync as mkdirSync17,
@@ -46803,7 +47140,7 @@ import {
46803
47140
  statSync as statSync15,
46804
47141
  writeFileSync as writeFileSync15
46805
47142
  } from "fs";
46806
- 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";
46807
47144
 
46808
47145
  // src/lib/portable-snapshot-filter.ts
46809
47146
  import { readdirSync as readdirSync15, statSync as statSync14 } from "fs";
@@ -46970,7 +47307,7 @@ function validateStationId(stationId) {
46970
47307
  }
46971
47308
  }
46972
47309
  function sha256File(filePath) {
46973
- return createHash30("sha256").update(readFileSync28(filePath)).digest("hex");
47310
+ return createHash31("sha256").update(readFileSync28(filePath)).digest("hex");
46974
47311
  }
46975
47312
  function scanHome(definition, homesRoot) {
46976
47313
  const homePath = homePathFor(definition, homesRoot);
@@ -47041,7 +47378,7 @@ function humanHomes(scanned) {
47041
47378
  }));
47042
47379
  }
47043
47380
  function writeStationSnapshot(options) {
47044
- const repoRoot = resolve21(options.repoRoot ?? process.cwd());
47381
+ const repoRoot = resolve23(options.repoRoot ?? process.cwd());
47045
47382
  const { scanned, plans, totalBytes } = planStationSnapshot(options);
47046
47383
  const manifestFiles = plans.map((plan) => ({
47047
47384
  relativePath: plan.source.relativePath,
@@ -47067,9 +47404,9 @@ function writeStationSnapshot(options) {
47067
47404
  const conflicts = [];
47068
47405
  const untouched = [];
47069
47406
  for (const plan of plans) {
47070
- const destination = resolve21(repoRoot, plan.destination);
47407
+ const destination = resolve23(repoRoot, plan.destination);
47071
47408
  const destinationRelative = relative11(repoRoot, destination);
47072
- if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep13) || isAbsolute22(destinationRelative)) {
47409
+ if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep13) || isAbsolute24(destinationRelative)) {
47073
47410
  throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
47074
47411
  }
47075
47412
  let existingDigest = null;
@@ -47090,8 +47427,8 @@ function writeStationSnapshot(options) {
47090
47427
  }
47091
47428
  let written = 0;
47092
47429
  for (const plan of untouched) {
47093
- const destination = resolve21(repoRoot, plan.destination);
47094
- mkdirSync17(dirname26(destination), { recursive: true });
47430
+ const destination = resolve23(repoRoot, plan.destination);
47431
+ mkdirSync17(dirname27(destination), { recursive: true });
47095
47432
  copyFileSync(plan.source.fullPath, destination);
47096
47433
  written += 1;
47097
47434
  }
@@ -47109,8 +47446,8 @@ function writeStationSnapshot(options) {
47109
47446
  },
47110
47447
  files: manifestFiles
47111
47448
  };
47112
- const manifestPath = resolve21(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
47113
- mkdirSync17(dirname26(manifestPath), { recursive: true });
47449
+ const manifestPath = resolve23(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
47450
+ mkdirSync17(dirname27(manifestPath), { recursive: true });
47114
47451
  writeFileSync15(manifestPath, `${JSON.stringify(manifest, null, 2)}
47115
47452
  `);
47116
47453
  return {
@@ -47121,7 +47458,7 @@ function writeStationSnapshot(options) {
47121
47458
  };
47122
47459
  }
47123
47460
  // src/lib/station-hydrate.ts
47124
- import { createHash as createHash31 } from "crypto";
47461
+ import { createHash as createHash32 } from "crypto";
47125
47462
  import {
47126
47463
  copyFileSync as copyFileSync2,
47127
47464
  mkdirSync as mkdirSync18,
@@ -47130,7 +47467,7 @@ import {
47130
47467
  statSync as statSync16,
47131
47468
  writeFileSync as writeFileSync16
47132
47469
  } from "fs";
47133
- 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";
47134
47471
  var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
47135
47472
  var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
47136
47473
  var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
@@ -47327,12 +47664,12 @@ function skillSha256(skill) {
47327
47664
  return sha256File(skill.files[0].winner.fullPath);
47328
47665
  }
47329
47666
  const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
47330
- return createHash31("sha256").update(joined.sort().join(`
47667
+ return createHash32("sha256").update(joined.sort().join(`
47331
47668
  `)).digest("hex");
47332
47669
  }
47333
47670
  function writeStationHydration(options) {
47334
- const repoRoot = resolve22(options.repoRoot ?? process.cwd());
47335
- const cacheRoot = resolve22(options.cacheRoot ?? resolveCorpusRoot());
47671
+ const repoRoot = resolve24(options.repoRoot ?? process.cwd());
47672
+ const cacheRoot = resolve24(options.cacheRoot ?? resolveCorpusRoot());
47336
47673
  const plan = planStationHydration(options.stationId, repoRoot);
47337
47674
  const resultSkills = plan.winners.map((skill) => ({
47338
47675
  ident: skill.ident,
@@ -47386,7 +47723,7 @@ function writeStationHydration(options) {
47386
47723
  }
47387
47724
  let written = 0;
47388
47725
  for (const entry of toWrite) {
47389
- mkdirSync18(dirname27(entry.destination), { recursive: true });
47726
+ mkdirSync18(dirname28(entry.destination), { recursive: true });
47390
47727
  copyFileSync2(entry.fullPath, entry.destination);
47391
47728
  written += 1;
47392
47729
  }
@@ -47407,8 +47744,8 @@ function writeStationHydration(options) {
47407
47744
  },
47408
47745
  skills: resultSkills
47409
47746
  };
47410
- const hydrationManifestPath = join42(dirname27(cacheRoot), `hydration-${options.stationId}.json`);
47411
- mkdirSync18(dirname27(hydrationManifestPath), { recursive: true });
47747
+ const hydrationManifestPath = join42(dirname28(cacheRoot), `hydration-${options.stationId}.json`);
47748
+ mkdirSync18(dirname28(hydrationManifestPath), { recursive: true });
47412
47749
  writeFileSync16(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
47413
47750
  `);
47414
47751
  return {
@@ -47420,19 +47757,19 @@ function writeStationHydration(options) {
47420
47757
  }
47421
47758
  // src/lib/private-publication-recovery.ts
47422
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";
47423
- 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";
47424
47761
  import { randomUUID as randomUUID5 } from "crypto";
47425
47762
  var fail3 = () => {
47426
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.");
47427
47764
  };
47428
47765
  function safeDirectory(directory2) {
47429
- if (!isAbsolute23(directory2) || directory2 !== resolve23(directory2) || realpathSync14(directory2) !== directory2)
47766
+ if (!isAbsolute25(directory2) || directory2 !== resolve25(directory2) || realpathSync14(directory2) !== directory2)
47430
47767
  return fail3();
47431
- for (let path = directory2;; path = dirname28(path)) {
47768
+ for (let path = directory2;; path = dirname29(path)) {
47432
47769
  const stat2 = lstatSync27(path);
47433
47770
  if (!stat2.isDirectory() || stat2.isSymbolicLink())
47434
47771
  return fail3();
47435
- if (path === dirname28(path))
47772
+ if (path === dirname29(path))
47436
47773
  break;
47437
47774
  }
47438
47775
  const own2 = lstatSync27(directory2);
@@ -47564,11 +47901,11 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
47564
47901
  phase: "prepared",
47565
47902
  intent: null
47566
47903
  };
47567
- if (!isAbsolute23(recoveryDirectory) || resolve23(recoveryDirectory) !== recoveryDirectory || realpathSync14(dirname28(recoveryDirectory)) !== dirname28(recoveryDirectory))
47904
+ if (!isAbsolute25(recoveryDirectory) || resolve25(recoveryDirectory) !== recoveryDirectory || realpathSync14(dirname29(recoveryDirectory)) !== dirname29(recoveryDirectory))
47568
47905
  return fail3();
47569
47906
  mkdirSync19(recoveryDirectory, { mode: 448 });
47570
47907
  safeDirectory(recoveryDirectory);
47571
- const parent = openSync15(dirname28(recoveryDirectory), constants15.O_RDONLY | constants15.O_NOFOLLOW);
47908
+ const parent = openSync15(dirname29(recoveryDirectory), constants15.O_RDONLY | constants15.O_NOFOLLOW);
47572
47909
  try {
47573
47910
  fsyncSync7(parent);
47574
47911
  } finally {
@@ -47655,8 +47992,8 @@ async function inspectLocked(client2, directory2, cancel) {
47655
47992
  return privatePublicationResult(directory2, receipt);
47656
47993
  }
47657
47994
  // src/lib/skill-context.ts
47658
- import { createHash as createHash32 } from "crypto";
47659
- import { resolve as resolve24 } from "path";
47995
+ import { createHash as createHash33 } from "crypto";
47996
+ import { resolve as resolve26 } from "path";
47660
47997
  function words(value) {
47661
47998
  return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
47662
47999
  }
@@ -47695,7 +48032,7 @@ async function buildSkillContext(input2, options = {}) {
47695
48032
  const promptWords = words(prompt);
47696
48033
  const promptNames = new Set(prompt.toLowerCase().match(/[a-z0-9]+(?:-[a-z0-9]+)*/g) ?? []);
47697
48034
  const loaded = new Set(resolved.session?.loaded ?? []);
47698
- const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve24(input2.cwd)] : []];
48035
+ const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve26(input2.cwd)] : []];
47699
48036
  const candidates = profile.selections.map((selection) => {
47700
48037
  const key = selectionKey(selection);
47701
48038
  if (input2.restore && loaded.has(key))
@@ -47722,7 +48059,7 @@ async function buildSkillContext(input2, options = {}) {
47722
48059
  const omitted = [];
47723
48060
  let chars = 0;
47724
48061
  for (const candidate2 of candidates) {
47725
- const { selection, reason } = candidate2;
48062
+ const { selection, reason: reason2 } = candidate2;
47726
48063
  if (!input2.restore && !explicit.has(selectionKey(selection)) && loaded.has(selectionKey(selection)))
47727
48064
  continue;
47728
48065
  const loadCommand = `skills load ${selection.slug}@${selection.version} --selection-profile ${profile.profileId}`;
@@ -47732,7 +48069,7 @@ async function buildSkillContext(input2, options = {}) {
47732
48069
  }
47733
48070
  const entries = await readSelectedEntries(selection, resolved, resolverOptions);
47734
48071
  const { content } = readSelectedDocument(entries);
47735
- const section = `Skill ${selection.slug}@${selection.version} (${selection.bundleDigest}; ${reason})
48072
+ const section = `Skill ${selection.slug}@${selection.version} (${selection.bundleDigest}; ${reason2})
47736
48073
  ${content}`;
47737
48074
  if (maxChars !== undefined && chars + section.length + (sections.length ? 2 : 0) > maxChars) {
47738
48075
  omitted.push({ slug: selection.slug, version: selection.version, reason: "context-budget", loadCommand });
@@ -47740,7 +48077,7 @@ ${content}`;
47740
48077
  }
47741
48078
  chars += section.length + (sections.length ? 2 : 0);
47742
48079
  sections.push(section);
47743
- selections.push({ ...selection, reason });
48080
+ selections.push({ ...selection, reason: reason2 });
47744
48081
  loaded.add(selectionKey(selection));
47745
48082
  }
47746
48083
  const context2 = sections.join(`
@@ -47748,7 +48085,7 @@ ${content}`;
47748
48085
  `);
47749
48086
  const receipt = {
47750
48087
  schemaVersion: 1,
47751
- 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"),
47752
48089
  profileId: profile.profileId,
47753
48090
  profileRevision: profile.profileRevision,
47754
48091
  authority: profile.authority,
@@ -47764,9 +48101,9 @@ ${content}`;
47764
48101
  return { context: context2, selections, omitted, receipt };
47765
48102
  }
47766
48103
  // src/lib/session-reconciliation.ts
47767
- import { createHash as createHash33 } from "crypto";
48104
+ import { createHash as createHash34 } from "crypto";
47768
48105
  var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
47769
- var digest3 = (value) => createHash33("sha256").update(JSON.stringify(value)).digest("hex");
48106
+ var digest3 = (value) => createHash34("sha256").update(JSON.stringify(value)).digest("hex");
47770
48107
  function exactTimestamp(value) {
47771
48108
  const parsed = value === undefined ? Number.NaN : Date.parse(value);
47772
48109
  if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
@@ -47799,6 +48136,8 @@ function inspectSkillSession(sessionId, options = {}) {
47799
48136
  loadedCount: receipt.loaded.length
47800
48137
  };
47801
48138
  }
48139
+ var SESSION_RENEWAL_REFUSAL_TTL_MS = 5 * 60 * 1000;
48140
+ var SESSION_RENEWAL_REFUSAL_MAX_BYTES = 4 * 1024;
47802
48141
  async function reconcileSkillSession(input2, options = {}) {
47803
48142
  if (![input2.fromProfile, input2.fromRevision, input2.selectionProfile, input2.profileRevision].every((value) => typeof value === "string" && value.trim()) || !/^[a-f0-9]{64}$/.test(input2.receiptSha256)) {
47804
48143
  throw new SkillSelectionError("INVALID_SESSION_RECONCILIATION", "Name the exact old receipt SHA256, old profile/revision and intended target profile/revision.");
@@ -47986,6 +48325,7 @@ export {
47986
48325
  isPortableWithinSkill,
47987
48326
  isGatewayBackedSkill,
47988
48327
  isExcludedSkillFileName,
48328
+ isDiscoveryRootUnresolved,
47989
48329
  isCodexNativeSkillDisabled,
47990
48330
  isBasicSkillName,
47991
48331
  inventoryNativeSkills,
@@ -47998,6 +48338,7 @@ export {
47998
48338
  inspectPrivatePublication,
47999
48339
  importSkillsLocalSnapshot,
48000
48340
  homePathFor,
48341
+ hashClaudeMarketplaceEntry,
48001
48342
  getToolPrimitive,
48002
48343
  getStorageStatus,
48003
48344
  getStorageDatabaseUrl,
@@ -48082,6 +48423,7 @@ export {
48082
48423
  captureClaudeSettings,
48083
48424
  captureClaudeMarketplaceRegistryV2,
48084
48425
  captureClaudeMarketplaceRegistry,
48426
+ captureClaudeMarketplaceEntry,
48085
48427
  canonicalizeManifest,
48086
48428
  buildSkillsS3ObjectUrl,
48087
48429
  buildSkillsApiUrl,
@@ -48163,9 +48505,11 @@ export {
48163
48505
  InvitationEmailInputError,
48164
48506
  HttpProfileClient,
48165
48507
  HostedApiError,
48508
+ DISCOVERY_ROOT_UNRESOLVED,
48166
48509
  DEFAULT_EXPORT_DIR,
48167
48510
  ContentHashInputError,
48168
48511
  CONTENT_HASH_LIMITS,
48512
+ CLAUDE_MARKETPLACE_ENTRY_HASH_MODE,
48169
48513
  CLAUDE_COORDINATED_HOOK_EVENTS,
48170
48514
  CATEGORIES,
48171
48515
  BASIC_SKILL_NAMES,