@hasna/skills 0.10.64 → 0.10.66

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
@@ -7262,7 +7262,7 @@ import {
7262
7262
  statSync as statSync12,
7263
7263
  writeFileSync as writeFileSync9
7264
7264
  } from "fs";
7265
- import { basename as basename13, dirname as dirname25, isAbsolute as isAbsolute25, join as join37, normalize as normalize4, relative as relative10, resolve as resolve25, sep as sep13 } from "path";
7265
+ import { basename as basename15, dirname as dirname25, isAbsolute as isAbsolute25, join as join37, normalize as normalize4, relative as relative10, resolve as resolve25, sep as sep13 } 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 lstatSync29, realpathSync as realpathSync14 } from "fs";
8924
8924
  import { homedir as homedir6 } from "os";
8925
- import { basename as basename12, dirname as dirname24, join as join36, resolve as resolve24, sep as sep12 } from "path";
8925
+ import { basename as basename14, dirname as dirname24, join as join36, resolve as resolve24, sep as sep12 } from "path";
8926
8926
 
8927
8927
  // src/lib/agent-adapters.ts
8928
8928
  var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes", "sumi"];
@@ -9744,8 +9744,8 @@ function assertAgentPolicyCollections(policy) {
9744
9744
  }
9745
9745
  }
9746
9746
  if (value.codexSemanticCache !== undefined) {
9747
- const proof = record(value.codexSemanticCache, 8), witness = record(proof.witness, 6);
9748
- requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).length === 8 && proof.schema === "hasna.codex-semantic-cache-discovery.v1" && typeof proof.previousPolicySha256 === "string" && /^[a-f0-9]{64}$/.test(proof.previousPolicySha256) && Object.keys(witness).length === 6 && witness.schema === "hasna.codex-semantic-cache-witness.v1" && typeof witness.sha256 === "string" && /^[a-f0-9]{64}$/.test(witness.sha256));
9747
+ const proof = record(value.codexSemanticCache, 9), witness = record(proof.witness, 7);
9748
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && [8, 9].includes(Object.keys(proof).length) && Object.keys(proof).every((key) => ["schema", "configPath", "hookRootAlias", "witness", "appOnlyParents", "previousPolicySha256", "supersededSources", "supersededDirectories", "reviewedHookParents"].includes(key)) && proof.schema === "hasna.codex-semantic-cache-discovery.v1" && typeof proof.previousPolicySha256 === "string" && /^[a-f0-9]{64}$/.test(proof.previousPolicySha256) && [6, 7].includes(Object.keys(witness).length) && Object.keys(witness).every((key) => ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256", "hookParents"].includes(key)) && witness.schema === "hasna.codex-semantic-cache-witness.v1" && typeof witness.sha256 === "string" && /^[a-f0-9]{64}$/.test(witness.sha256));
9749
9749
  text(proof.configPath);
9750
9750
  requireBound(isAbsolute4(proof.configPath) && resolve4(proof.configPath) === proof.configPath && basename4(proof.configPath) === "config.toml");
9751
9751
  text(witness.cacheRoot);
@@ -9764,6 +9764,20 @@ function assertAgentPolicyCollections(policy) {
9764
9764
  text(parent[key]);
9765
9765
  requireBound(isAbsolute4(parent.pluginParent) && resolve4(parent.pluginParent) === parent.pluginParent);
9766
9766
  }
9767
+ if (proof.reviewedHookParents !== undefined)
9768
+ for (const parent of array(proof.reviewedHookParents, 4096)) {
9769
+ requireBound(object(parent) && parent.role === "reviewed-hook" && (parent.installation === "remote-receipt" && Object.keys(parent).length === 6 || parent.installation === "local-marketplace" && Object.keys(parent).length === 7));
9770
+ requireBound(parent.installation === "remote-receipt" ? Object.keys(parent).sort().join(",") === "installation,namespace,pluginId,pluginParent,remotePluginId,role" : Object.keys(parent).sort().join(",") === "installation,marketplaceSha256,marketplaceSourcePath,namespace,pluginId,pluginParent,role");
9771
+ for (const key of ["pluginId", "namespace", "pluginParent", "installation"])
9772
+ text(parent[key]);
9773
+ if (parent.installation === "remote-receipt")
9774
+ text(parent.remotePluginId);
9775
+ else {
9776
+ text(parent.marketplaceSourcePath);
9777
+ requireBound(typeof parent.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256) && isAbsolute4(parent.marketplaceSourcePath) && resolve4(parent.marketplaceSourcePath) === parent.marketplaceSourcePath && basename4(parent.marketplaceSourcePath) === "marketplace.json");
9778
+ }
9779
+ requireBound(isAbsolute4(parent.pluginParent) && resolve4(parent.pluginParent) === parent.pluginParent);
9780
+ }
9767
9781
  for (const row of [...array(witness.controls, 4096), ...array(witness.appOnlyParents, 4096)]) {
9768
9782
  requireBound(object(row));
9769
9783
  for (const key of ["pluginId", "namespace", "pluginParent"])
@@ -9772,6 +9786,26 @@ function assertAgentPolicyCollections(policy) {
9772
9786
  if (row[key] !== undefined)
9773
9787
  requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
9774
9788
  }
9789
+ if (witness.hookParents !== undefined)
9790
+ for (const row of array(witness.hookParents, 4096)) {
9791
+ requireBound(object(row) && row.role === "reviewed-hook" && (row.installation === "remote-receipt" && Object.keys(row).length === 9 || row.installation === "local-marketplace" && Object.keys(row).length === 9));
9792
+ requireBound(row.installation === "remote-receipt" ? Object.keys(row).sort().join(",") === "hooksSha256,installation,manifestSha256,namespace,pluginId,pluginParent,receiptSha256,remotePluginId,role" : Object.keys(row).sort().join(",") === "hooksSha256,installation,manifestSha256,marketplaceSha256,marketplaceSourcePath,namespace,pluginId,pluginParent,role");
9793
+ for (const key of ["pluginId", "namespace", "pluginParent", "installation"])
9794
+ text(row[key]);
9795
+ if (row.installation === "remote-receipt")
9796
+ text(row.remotePluginId);
9797
+ else {
9798
+ text(row.marketplaceSourcePath);
9799
+ requireBound(typeof row.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(row.marketplaceSha256) && isAbsolute4(row.marketplaceSourcePath) && resolve4(row.marketplaceSourcePath) === row.marketplaceSourcePath && basename4(row.marketplaceSourcePath) === "marketplace.json");
9800
+ }
9801
+ requireBound(isAbsolute4(row.pluginParent) && resolve4(row.pluginParent) === row.pluginParent);
9802
+ for (const key of ["manifestSha256", "hooksSha256"])
9803
+ requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
9804
+ if (row.installation === "remote-receipt")
9805
+ requireBound(typeof row.receiptSha256 === "string" && /^[a-f0-9]{64}$/.test(row.receiptSha256));
9806
+ else
9807
+ requireBound(row.receiptSha256 === undefined);
9808
+ }
9775
9809
  for (const skill of array(witness.skills, 4096)) {
9776
9810
  requireBound(object(skill));
9777
9811
  for (const key of ["name", "pluginId", "namespace", "pluginParent"])
@@ -9851,7 +9885,7 @@ function assertAgentPolicyCollections(policy) {
9851
9885
  // src/lib/agent-discovery.ts
9852
9886
  import { createHash as createHash28 } from "crypto";
9853
9887
  import { lstatSync as lstatSync28, readFileSync as readFileSync21, readdirSync as readdirSync11, realpathSync as realpathSync13 } from "fs";
9854
- import { basename as basename11, dirname as dirname23, isAbsolute as isAbsolute24, join as join35, relative as relative9, resolve as resolve23, sep as sep11 } from "path";
9888
+ import { basename as basename13, dirname as dirname23, isAbsolute as isAbsolute24, join as join35, relative as relative9, resolve as resolve23, sep as sep11 } from "path";
9855
9889
  import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
9856
9890
 
9857
9891
  // node_modules/yaml/dist/index.js
@@ -10542,7 +10576,7 @@ function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
10542
10576
 
10543
10577
  // src/lib/plugin-discovery.ts
10544
10578
  import { closeSync as closeSync16, constants as constants17, fstatSync as fstatSync16, lstatSync as lstatSync26, openSync as openSync16, opendirSync as opendirSync5, readSync as readSync12 } from "fs";
10545
- import { basename as basename9, dirname as dirname21, isAbsolute as isAbsolute22, join as join34, resolve as resolve21 } from "path";
10579
+ import { basename as basename11, dirname as dirname21, isAbsolute as isAbsolute22, join as join34, resolve as resolve21 } from "path";
10546
10580
 
10547
10581
  // src/lib/plugin-admission.ts
10548
10582
  import { homedir as homedir5 } from "os";
@@ -11888,7 +11922,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11888
11922
  });
11889
11923
  }
11890
11924
  // package.json
11891
- var version = "0.10.64";
11925
+ var version = "0.10.66";
11892
11926
  var package_default = {
11893
11927
  name: "@hasna/skills",
11894
11928
  version,
@@ -14246,7 +14280,7 @@ import {
14246
14280
  writeFileSync as writeFileSync7
14247
14281
  } from "fs";
14248
14282
  import { homedir as homedir3 } from "os";
14249
- import { basename as basename8, dirname as dirname19, join as join28 } from "path";
14283
+ import { basename as basename10, dirname as dirname19, join as join28 } from "path";
14250
14284
 
14251
14285
  // src/lib/codex-plugin-skill-controls.ts
14252
14286
  import { createHash as createHash14 } from "crypto";
@@ -16178,7 +16212,7 @@ function upgradeCodexSettingsWitnessV5(previous, reviewedSettingsPath) {
16178
16212
  // src/lib/codex-semantic-cache-witness.ts
16179
16213
  import { createHash as createHash18 } from "crypto";
16180
16214
  import { constants as constants9, closeSync as closeSync9, fstatSync as fstatSync9, lstatSync as lstatSync16, openSync as openSync9, readSync as readSync6, readdirSync as readdirSync7, realpathSync as realpathSync9 } from "fs";
16181
- import { dirname as dirname15, isAbsolute as isAbsolute14, join as join20, relative as relative7, resolve as resolve14, sep as sep8 } from "path";
16215
+ import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute14, join as join20, relative as relative7, resolve as resolve14, sep as sep8 } from "path";
16182
16216
  var CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA = "hasna.codex-semantic-cache-witness.v1";
16183
16217
  var identifier3 = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(value);
16184
16218
  var digest = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
@@ -16187,6 +16221,24 @@ var refuse4 = () => {
16187
16221
  };
16188
16222
  var MAX_DOCUMENT_BYTES = 1024 * 1024;
16189
16223
  var MAX_RECEIPT_BYTES = 16 * 1024;
16224
+ function codexLocalMarketplaceMapsPlugin(text2, marketplace, plugin) {
16225
+ try {
16226
+ hashNativeJsonControls(text2);
16227
+ const value = JSON.parse(text2);
16228
+ if (!plainObject(value) || !exactKeys(value, ["interface", "name", "plugins"]) || value.name !== marketplace || !(typeof value.interface === "string" || Number.isSafeInteger(value.interface)) || !Array.isArray(value.plugins) || value.plugins.length < 1 || value.plugins.length > 4096)
16229
+ return false;
16230
+ const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
16231
+ if (rows.length !== 1)
16232
+ return false;
16233
+ const row = rows[0];
16234
+ if (!exactKeys(row, ["name", "source"]) || !plainObject(row.source) || !exactKeys(row.source, ["source", "path"]) || row.source.source !== "local" || typeof row.source.path !== "string" || !row.source.path.startsWith("./") || row.source.path.includes("\\"))
16235
+ return false;
16236
+ const parts = row.source.path.split("/");
16237
+ return parts[0] === "." && parts.length > 1 && parts.slice(1).every((part) => /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(part)) && parts.at(-1) === plugin;
16238
+ } catch {
16239
+ return false;
16240
+ }
16241
+ }
16190
16242
  function defaultRead(path) {
16191
16243
  const before = lstatSync16(path, { throwIfNoEntry: false, bigint: true });
16192
16244
  if (before === undefined)
@@ -16248,7 +16300,7 @@ function validPluginParent(cacheRoot, pluginParent, namespace, pluginId) {
16248
16300
  return parts.length === 2 && parts.every(identifier3) && parts[1] === namespace && pluginId === `${namespace}@${parts[0]}`;
16249
16301
  }
16250
16302
  function assertWitnessShape(value) {
16251
- if (!plainObject(value) || !exactKeys(value, ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256"]) || value.schema !== CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA || typeof value.cacheRoot !== "string" || !isAbsolute14(value.cacheRoot) || resolve14(value.cacheRoot) !== value.cacheRoot || !digest(value.sha256) || !Array.isArray(value.controls) || value.controls.length === 0 || value.controls.length > 4096 || !Array.isArray(value.appOnlyParents) || value.appOnlyParents.length > 4096 || !Array.isArray(value.skills) || value.skills.length === 0 || value.skills.length > 4096)
16303
+ if (!plainObject(value) || !exactKeys(value, ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256"], ["hookParents"]) || value.schema !== CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA || typeof value.cacheRoot !== "string" || !isAbsolute14(value.cacheRoot) || resolve14(value.cacheRoot) !== value.cacheRoot || !digest(value.sha256) || !Array.isArray(value.controls) || value.controls.length === 0 || value.controls.length > 4096 || !Array.isArray(value.appOnlyParents) || value.appOnlyParents.length > 4096 || !Array.isArray(value.skills) || value.skills.length === 0 || value.skills.length > 4096)
16252
16304
  refuse4();
16253
16305
  const witness = value;
16254
16306
  const controlParents = new Set;
@@ -16269,6 +16321,20 @@ function assertWitnessShape(value) {
16269
16321
  refuse4();
16270
16322
  appOnlyParentPaths.add(row.pluginParent);
16271
16323
  }
16324
+ const hookParentPaths = new Set;
16325
+ const hookParents = witness.hookParents === undefined ? [] : witness.hookParents;
16326
+ if (!Array.isArray(hookParents) || hookParents.length > 4096)
16327
+ refuse4();
16328
+ for (const candidate of hookParents) {
16329
+ if (!plainObject(candidate))
16330
+ refuse4();
16331
+ const row = candidate;
16332
+ const remote = row.installation === "remote-receipt";
16333
+ const local = row.installation === "local-marketplace";
16334
+ if (!exactKeys(row, ["role", "pluginId", "namespace", "pluginParent", "installation", "manifestSha256", "hooksSha256"], remote ? ["remotePluginId", "receiptSha256"] : local ? ["marketplaceSourcePath", "marketplaceSha256"] : []) || row.role !== "reviewed-hook" || !identifier3(row.namespace) || typeof row.pluginId !== "string" || !/^[A-Za-z0-9_-]{1,64}@[A-Za-z0-9_-]{1,64}$/.test(row.pluginId) || typeof row.pluginParent !== "string" || !validPluginParent(String(witness.cacheRoot), row.pluginParent, row.namespace, row.pluginId) || remote && (typeof row.remotePluginId !== "string" || !/^[A-Za-z0-9_~-]{1,1024}$/.test(row.remotePluginId) || !digest(row.receiptSha256)) || local && (typeof row.marketplaceSourcePath !== "string" || !isAbsolute14(row.marketplaceSourcePath) || resolve14(row.marketplaceSourcePath) !== row.marketplaceSourcePath || basename8(row.marketplaceSourcePath) !== "marketplace.json" || !digest(row.marketplaceSha256)) || !remote && !local || !digest(row.manifestSha256) || !digest(row.hooksSha256) || hookParentPaths.has(row.pluginParent) || controlParents.has(row.pluginParent) || appOnlyParentPaths.has(row.pluginParent))
16335
+ refuse4();
16336
+ hookParentPaths.add(row.pluginParent);
16337
+ }
16272
16338
  const skillNames = new Set;
16273
16339
  const qualifiedNames = new Set;
16274
16340
  for (const candidate of witness.skills) {
@@ -16288,6 +16354,7 @@ function assertWitnessShape(value) {
16288
16354
  cacheRoot: witness.cacheRoot,
16289
16355
  controls: witness.controls,
16290
16356
  appOnlyParents: witness.appOnlyParents,
16357
+ ...witness.hookParents === undefined ? {} : { hookParents: witness.hookParents },
16291
16358
  skills: witness.skills
16292
16359
  };
16293
16360
  if (hash2(projection) !== witness.sha256)
@@ -16478,10 +16545,147 @@ function appOnlyParentProjection(cacheRoot, parents2, read2) {
16478
16545
  }
16479
16546
  return rows.sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.pluginParent.localeCompare(b.pluginParent));
16480
16547
  }
16481
- function assertParents(cacheRoot, controls, appOnlyParents, files, members) {
16548
+ var codexHookEvents = new Set([
16549
+ "SessionStart",
16550
+ "UserPromptSubmit",
16551
+ "PreToolUse",
16552
+ "PostToolUse",
16553
+ "PostToolUseFailure",
16554
+ "Stop",
16555
+ "SubagentStart",
16556
+ "SubagentStop",
16557
+ "PreCompact",
16558
+ "Notification",
16559
+ "PermissionRequest",
16560
+ "SessionEnd",
16561
+ "ConfigChange",
16562
+ "WorktreeCreate",
16563
+ "WorktreeRemove",
16564
+ "TeammateIdle",
16565
+ "TaskComplete"
16566
+ ]);
16567
+ function assertJsonValue(value, depth = 0, budget2 = { members: 0 }) {
16568
+ if (depth > 32 || ++budget2.members > 4096)
16569
+ refuse4();
16570
+ if (value === null || typeof value === "string" || typeof value === "boolean")
16571
+ return;
16572
+ if (typeof value === "number" && Number.isFinite(value))
16573
+ return;
16574
+ if (Array.isArray(value)) {
16575
+ for (const item of value)
16576
+ assertJsonValue(item, depth + 1, budget2);
16577
+ return;
16578
+ }
16579
+ if (!plainObject(value))
16580
+ refuse4();
16581
+ for (const [key, item] of Object.entries(value)) {
16582
+ if (!key || /[\x00-\x1f\x7f]/.test(key))
16583
+ refuse4();
16584
+ assertJsonValue(item, depth + 1, budget2);
16585
+ }
16586
+ }
16587
+ function reviewedHookControls(root, read2) {
16588
+ const manifestPath = join20(root, ".codex-plugin/plugin.json"), hooksPath = join20(root, "hooks/hooks.json");
16589
+ let manifest, manifestSha256, hooks, hooksSha256;
16590
+ try {
16591
+ const manifestText = read2(manifestPath);
16592
+ manifestSha256 = hashNativeJsonControls(manifestText, "version");
16593
+ manifest = JSON.parse(manifestText);
16594
+ const hooksText = read2(hooksPath);
16595
+ hashNativeJsonControls(hooksText);
16596
+ hooksSha256 = createHash18("sha256").update(hooksText).digest("hex");
16597
+ hooks = JSON.parse(hooksText);
16598
+ } catch {
16599
+ return refuse4();
16600
+ }
16601
+ if (!plainObject(manifest) || !exactKeys(manifest, ["name", "version", "hooks"], ["description"]) || !identifier3(manifest.name) || typeof manifest.version !== "string" || manifest.version !== basename8(root) || manifest.hooks !== "./hooks/hooks.json" || manifest.description !== undefined && (typeof manifest.description !== "string" || !manifest.description.trim() || manifest.description.length > 1024))
16602
+ refuse4();
16603
+ const manifestRecord = manifest;
16604
+ if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"]) || !plainObject(hooks.hooks))
16605
+ refuse4();
16606
+ const hookTable = hooks.hooks;
16607
+ const events = Object.entries(hookTable);
16608
+ if (!events.length || events.length > 32)
16609
+ refuse4();
16610
+ for (const [event, groups] of events) {
16611
+ if (!codexHookEvents.has(event) || !Array.isArray(groups) || !groups.length || groups.length > 32)
16612
+ refuse4();
16613
+ for (const group of groups) {
16614
+ if (!plainObject(group) || !exactKeys(group, ["hooks"]) || !Array.isArray(group.hooks) || !group.hooks.length || group.hooks.length > 32)
16615
+ refuse4();
16616
+ const handlers = group.hooks;
16617
+ for (const handler of handlers) {
16618
+ if (!plainObject(handler) || !exactKeys(handler, ["type", "server", "tool", "input"], ["timeout"]) || handler.type !== "mcp_tool" || !identifier3(handler.server) || !identifier3(handler.tool) || !plainObject(handler.input) || handler.timeout !== undefined && (!Number.isSafeInteger(handler.timeout) || Number(handler.timeout) < 1 || Number(handler.timeout) > 300))
16619
+ refuse4();
16620
+ assertJsonValue(handler.input);
16621
+ }
16622
+ }
16623
+ }
16624
+ return { namespace: manifestRecord.name, manifestSha256, hooksSha256 };
16625
+ }
16626
+ function reviewedHookParentProjection(cacheRoot, parents2, read2) {
16627
+ if (!Array.isArray(parents2) || parents2.length > 4096)
16628
+ refuse4();
16629
+ const seen = new Set, rows = [];
16630
+ for (const parent of parents2) {
16631
+ const remote = parent?.installation === "remote-receipt", local = parent?.installation === "local-marketplace";
16632
+ if (!parent || !exactKeys(parent, ["installation", "namespace", "pluginId", "pluginParent", "role"], remote ? ["remotePluginId"] : local ? ["marketplaceSourcePath", "marketplaceSha256"] : []) || parent.role !== "reviewed-hook" || !identifier3(parent.namespace) || !/^[A-Za-z0-9_-]{1,64}@[A-Za-z0-9_-]{1,64}$/.test(parent.pluginId) || typeof parent.pluginParent !== "string" || remote && (typeof parent.remotePluginId !== "string" || !/^[A-Za-z0-9_~-]{1,1024}$/.test(parent.remotePluginId)) || local && (typeof parent.marketplaceSourcePath !== "string" || !isAbsolute14(parent.marketplaceSourcePath) || resolve14(parent.marketplaceSourcePath) !== parent.marketplaceSourcePath || basename8(parent.marketplaceSourcePath) !== "marketplace.json" || !digest(parent.marketplaceSha256)) || !remote && !local)
16633
+ refuse4();
16634
+ const parts = relative7(cacheRoot, parent.pluginParent).split(sep8);
16635
+ if (!isAbsolute14(parent.pluginParent) || resolve14(parent.pluginParent) !== parent.pluginParent || parts.length !== 2 || parts.some((part) => !identifier3(part)) || parts[1] !== parent.namespace || parent.pluginId !== `${parent.namespace}@${parts[0]}` || seen.has(parent.pluginParent))
16636
+ refuse4();
16637
+ seen.add(parent.pluginParent);
16638
+ const parentStat = lstatSync16(parent.pluginParent, { throwIfNoEntry: false });
16639
+ if (!parentStat?.isDirectory() || parentStat.isSymbolicLink() || realpathSync9(parent.pluginParent) !== parent.pluginParent)
16640
+ refuse4();
16641
+ const receiptPath = join20(parent.pluginParent, ".codex-remote-plugin-install.json"), receiptStat = lstatSync16(receiptPath, { throwIfNoEntry: false });
16642
+ let receiptText;
16643
+ if (remote) {
16644
+ if (!receiptStat?.isFile() || receiptStat.isSymbolicLink() || receiptStat.size > MAX_RECEIPT_BYTES)
16645
+ refuse4();
16646
+ try {
16647
+ receiptText = read2(receiptPath);
16648
+ hashNativeJsonControls(receiptText);
16649
+ const receipt = JSON.parse(receiptText);
16650
+ if (!plainObject(receipt) || !exactKeys(receipt, ["schema_version", "remote_plugin_id"]) || receipt.schema_version !== 1 || receipt.remote_plugin_id !== parent.remotePluginId)
16651
+ refuse4();
16652
+ } catch {
16653
+ refuse4();
16654
+ }
16655
+ } else {
16656
+ const marketplaceSourcePath = parent.marketplaceSourcePath ?? refuse4();
16657
+ if (receiptStat !== undefined || !parent.marketplaceSha256)
16658
+ refuse4();
16659
+ let marketplaceText;
16660
+ try {
16661
+ marketplaceText = read2(marketplaceSourcePath);
16662
+ } catch {
16663
+ return refuse4();
16664
+ }
16665
+ if (createHash18("sha256").update(marketplaceText).digest("hex") !== parent.marketplaceSha256 || !codexLocalMarketplaceMapsPlugin(marketplaceText, parts[0], parent.namespace))
16666
+ refuse4();
16667
+ }
16668
+ const materializations = readdirSync7(parent.pluginParent).filter((name) => name !== ".codex-remote-plugin-install.json").map((version2) => {
16669
+ if (!/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2))
16670
+ refuse4();
16671
+ const root = join20(parent.pluginParent, version2), stat = lstatSync16(root, { throwIfNoEntry: false });
16672
+ if (!stat?.isDirectory() || stat.isSymbolicLink() || realpathSync9(root) !== root)
16673
+ refuse4();
16674
+ const controls = reviewedHookControls(root, read2);
16675
+ if (controls.namespace !== parent.namespace)
16676
+ refuse4();
16677
+ return { manifestSha256: controls.manifestSha256, hooksSha256: controls.hooksSha256 };
16678
+ });
16679
+ if (!materializations.length || materializations.some((item) => canonical7(item) !== canonical7(materializations[0])))
16680
+ refuse4();
16681
+ rows.push({ ...parent, ...receiptText === undefined ? {} : { receiptSha256: createHash18("sha256").update(receiptText).digest("hex") }, ...materializations[0] });
16682
+ }
16683
+ return rows.sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.pluginParent.localeCompare(b.pluginParent));
16684
+ }
16685
+ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files, members) {
16482
16686
  const expected = new Set;
16483
16687
  const expectedMarkets = new Set;
16484
- for (const control of [...controls, ...appOnlyParents]) {
16688
+ for (const control of [...controls, ...appOnlyParents, ...hookParents]) {
16485
16689
  const rel = relative7(cacheRoot, control.pluginParent), parts = rel.split(sep8);
16486
16690
  if (resolve14(control.pluginParent) !== control.pluginParent || parts.length !== 2 || parts.some((part) => !identifier3(part)) || control.pluginId !== `${control.namespace}@${parts[0]}` || parts[1] !== control.namespace)
16487
16691
  refuse4();
@@ -16522,7 +16726,9 @@ function assertParents(cacheRoot, controls, appOnlyParents, files, members) {
16522
16726
  refuse4();
16523
16727
  if ([".app.json", ".mcp.json"].includes(base) && (parts.length !== 4 || dirname15(member) !== root))
16524
16728
  refuse4();
16525
- if (parts.includes("hooks"))
16729
+ if ([".app.json", ".mcp.json"].includes(base) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
16730
+ refuse4();
16731
+ if (parts.includes("hooks") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base !== "hooks" : base !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join20(cacheRoot, parts[0], parts[1]))))
16526
16732
  refuse4();
16527
16733
  }
16528
16734
  }
@@ -16632,13 +16838,14 @@ function captureCodexSemanticCacheWitness(options) {
16632
16838
  const initialTree = treeSnapshot(options.cacheRoot);
16633
16839
  const controls = controlProjection(options.controls, read2);
16634
16840
  const appOnlyParents = appOnlyParentProjection(options.cacheRoot, options.appOnlyParents ?? [], read2);
16841
+ const hookParents = reviewedHookParentProjection(options.cacheRoot, options.reviewedHookParents ?? [], read2);
16635
16842
  const skillParents = new Set(controls.map((control) => control.pluginParent));
16636
- if (appOnlyParents.some((parent) => skillParents.has(parent.pluginParent)))
16843
+ if (appOnlyParents.some((parent) => skillParents.has(parent.pluginParent)) || hookParents.some((parent) => skillParents.has(parent.pluginParent) || appOnlyParents.some((app) => app.pluginParent === parent.pluginParent)))
16637
16844
  refuse4();
16638
16845
  if (!reviewedCodexPluginCapabilitiesUnchanged(options.cacheRoot, options.controls, read2, options.rules))
16639
16846
  refuse4();
16640
16847
  const files = initialTree.files;
16641
- assertParents(options.cacheRoot, controls, appOnlyParents, files, initialTree.state.members);
16848
+ assertParents(options.cacheRoot, controls, appOnlyParents, hookParents, files, initialTree.state.members);
16642
16849
  const expectedNamesByRoot = assertSkillMaterializations(options.cacheRoot, options.controls, files, read2);
16643
16850
  const docs = files.filter((path) => path.endsWith(`${sep8}SKILL.md`));
16644
16851
  const skillRows = [];
@@ -16647,6 +16854,8 @@ function captureCodexSemanticCacheWitness(options) {
16647
16854
  const rel = relative7(options.cacheRoot, document2).split(sep8);
16648
16855
  if (appOnlyParents.some((parent) => parent.pluginParent === join20(options.cacheRoot, rel[0], rel[1])))
16649
16856
  refuse4();
16857
+ if (hookParents.some((parent) => parent.pluginParent === join20(options.cacheRoot, rel[0], rel[1])))
16858
+ refuse4();
16650
16859
  const documentStat = lstatSync16(document2, { throwIfNoEntry: false });
16651
16860
  if (!documentStat?.isFile() || documentStat.isSymbolicLink() || documentStat.size > MAX_DOCUMENT_BYTES)
16652
16861
  refuse4();
@@ -16710,7 +16919,7 @@ function captureCodexSemanticCacheWitness(options) {
16710
16919
  tracked.verify();
16711
16920
  const finalTree = treeSnapshot(options.cacheRoot);
16712
16921
  assertSameTree(initialTree.state, finalTree.state, tracked.paths());
16713
- const completeProjection = { schema: CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA, cacheRoot: options.cacheRoot, controls, appOnlyParents, skills: uniqueSkills };
16922
+ const completeProjection = { schema: CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA, cacheRoot: options.cacheRoot, controls, appOnlyParents, hookParents, skills: uniqueSkills };
16714
16923
  return { ...completeProjection, sha256: hash2(completeProjection) };
16715
16924
  } catch (error) {
16716
16925
  if (error instanceof Error && error.message.startsWith("CODEX_SEMANTIC_CACHE_WITNESS_UNSUPPORTED"))
@@ -16726,6 +16935,7 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
16726
16935
  cacheRoot: previous.cacheRoot,
16727
16936
  controls: previous.controls,
16728
16937
  appOnlyParents: previous.appOnlyParents,
16938
+ ...previous.hookParents === undefined ? {} : { hookParents: previous.hookParents },
16729
16939
  skills: previous.skills
16730
16940
  };
16731
16941
  const currentProjection = {
@@ -16733,9 +16943,11 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
16733
16943
  cacheRoot: current.cacheRoot,
16734
16944
  controls: current.controls,
16735
16945
  appOnlyParents: current.appOnlyParents,
16946
+ ...current.hookParents === undefined ? {} : { hookParents: current.hookParents },
16736
16947
  skills: current.skills
16737
16948
  };
16738
- if (hash2(priorProjection) !== previous.sha256 || hash2(currentProjection) !== current.sha256 || canonical7(priorProjection) !== canonical7(currentProjection))
16949
+ const comparable = (projection) => ({ ...projection, hookParents: projection.hookParents ?? [] });
16950
+ if (hash2(priorProjection) !== previous.sha256 || hash2(currentProjection) !== current.sha256 || canonical7(comparable(priorProjection)) !== canonical7(comparable(currentProjection)))
16739
16951
  refuse4();
16740
16952
  }
16741
16953
 
@@ -16743,7 +16955,7 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
16743
16955
  import { createHash as createHash23, randomUUID as randomUUID3 } from "crypto";
16744
16956
  import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
16745
16957
  import { existsSync as existsSync9, lstatSync as lstatSync23, statSync as statSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync16, readdirSync as readdirSync9, opendirSync as opendirSync3, readlinkSync as readlinkSync7, realpathSync as realpathSync12, renameSync as renameSync3, rmdirSync as rmdirSync2, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4, chmodSync, openSync as openSync14, closeSync as closeSync14, fsyncSync as fsyncSync6, fstatSync as fstatSync14, readSync as readSync10, constants as constants15, linkSync as linkSync2 } from "fs";
16746
- import { dirname as dirname18, join as join27, resolve as resolve18, relative as relative8, isAbsolute as isAbsolute19, sep as sep10 } from "path";
16958
+ import { basename as basename9, dirname as dirname18, join as join27, resolve as resolve18, relative as relative8, isAbsolute as isAbsolute19, sep as sep10 } from "path";
16747
16959
  import { homedir as homedir2 } from "os";
16748
16960
 
16749
16961
  // src/lib/managed-policy.ts
@@ -17141,6 +17353,78 @@ function disableCodexBundledSkills(text2) {
17141
17353
  import { spawnSync as spawnSync4 } from "child_process";
17142
17354
  import { accessSync as accessSync2, constants as constants11, existsSync as existsSync8, lstatSync as lstatSync19, realpathSync as realpathSync10, statSync as statSync9 } from "fs";
17143
17355
  import { delimiter as delimiter2, isAbsolute as isAbsolute15, join as join25, resolve as resolve15 } from "path";
17356
+
17357
+ // src/lib/hook-diagnostics.ts
17358
+ var CONTEXT_ERROR_CODES = new Set([
17359
+ "SKILLS_API_CREDENTIAL_UNAVAILABLE",
17360
+ "SKILLS_API_UNAVAILABLE",
17361
+ "SKILLS_API_UNAUTHORIZED",
17362
+ "SKILLS_API_FORBIDDEN",
17363
+ "SKILLS_API_RESOURCE_UNAVAILABLE",
17364
+ "SESSION_WRITE_BUSY",
17365
+ "SKILLS_CONTEXT_FAILED",
17366
+ "SKILLS_STORAGE_FULL",
17367
+ "PROFILE_LOCK_MISMATCH",
17368
+ "PROFILE_IDENTITY_MISMATCH",
17369
+ "PROFILE_APPLIED_REPORT_FAILED",
17370
+ "CACHED_AUTHORITY_REQUIRED",
17371
+ "CACHED_PROFILE_EXPIRED",
17372
+ "CACHED_PROFILE_MISSING",
17373
+ "CACHED_BUNDLE_MISSING",
17374
+ "INVALID_CONTEXT_INPUT",
17375
+ "CONTEXT_INPUT_TOO_LARGE",
17376
+ "INVALID_CONTEXT_BUDGET",
17377
+ "INVALID_SELECTION",
17378
+ "INVALID_SELECTION_ALIASES",
17379
+ "INVALID_RECEIPT",
17380
+ "INVALID_CACHE_FILE",
17381
+ "UNSAFE_CACHE_PATH",
17382
+ "RECEIPT_TOO_LARGE",
17383
+ "BUNDLE_DIGEST_MISMATCH",
17384
+ "BUNDLE_IDENTITY_MISMATCH",
17385
+ "BUNDLE_UNAVAILABLE",
17386
+ "BUNDLE_TOO_LARGE",
17387
+ "SKILL_NOT_SELECTED",
17388
+ "INVALID_SESSION",
17389
+ "SESSION_PARENT_NOT_FOUND",
17390
+ "SESSION_PARENT_MISMATCH",
17391
+ "SESSION_NOT_FOUND",
17392
+ "SESSION_GENERATION_CHANGED",
17393
+ "SESSION_GENERATION_EXHAUSTED",
17394
+ "SESSION_PARENT_CHANGED",
17395
+ "SESSION_RECEIPT_CHANGED",
17396
+ "SESSION_RECONCILIATION_REQUIRED",
17397
+ "SESSION_RENEWAL_TIMEOUT",
17398
+ "SESSION_CONTEXT_CONFLICT",
17399
+ "SESSION_RECONCILIATION_INCOMPLETE",
17400
+ "SESSION_WRITE_LOCKED",
17401
+ "SESSION_WRITE_LOCK_CHANGED",
17402
+ "SESSION_LOCK_RECOVERY_INCOMPLETE"
17403
+ ]);
17404
+ var HOOK_REFUSAL_REASON_CODES = Object.freeze([
17405
+ ...CONTEXT_ERROR_CODES,
17406
+ "SKILLS_HOOK_FAILED",
17407
+ "SKILLS_HOOK_TIMEOUT",
17408
+ "SKILLS_HOOK_INVALID_RESPONSE"
17409
+ ]);
17410
+ var OPTIONAL_CONTEXT_FAILURES = new Set([
17411
+ "SKILLS_API_CREDENTIAL_UNAVAILABLE",
17412
+ "SKILLS_API_UNAVAILABLE",
17413
+ "SKILLS_API_UNAUTHORIZED",
17414
+ "SKILLS_API_FORBIDDEN",
17415
+ "SKILLS_API_RESOURCE_UNAVAILABLE",
17416
+ "CACHED_PROFILE_EXPIRED",
17417
+ "CACHED_PROFILE_MISSING",
17418
+ "CACHED_BUNDLE_MISSING",
17419
+ "SESSION_RECONCILIATION_REQUIRED",
17420
+ "SESSION_RENEWAL_TIMEOUT",
17421
+ "SESSION_WRITE_BUSY",
17422
+ "SESSION_PARENT_CHANGED",
17423
+ "SESSION_CONTEXT_CONFLICT",
17424
+ "SKILLS_HOOK_TIMEOUT"
17425
+ ]);
17426
+
17427
+ // src/lib/agent-sumi.ts
17144
17428
  var SUMI_PATH_SELECTORS = ["SUMI_HOME", "SUMI_CONFIG_DIR", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_CONFIG_HOME", "XDG_STATE_HOME"];
17145
17429
  var SUMI_PATH_KEYS = ["data", "cache", "config", "state"];
17146
17430
  var SUMI_PATH_OUTPUT_LIMIT = 128 * 1024;
@@ -17282,24 +17566,33 @@ function sumiConfigPath(home, cwd = process.cwd(), env = process.env) {
17282
17566
  return join25(sumiConfigDirectory(home, env, cwd), "sumi.json");
17283
17567
  }
17284
17568
  function renderSumiPlugin(command, profile) {
17285
- return renderSumiPluginVersion(command, profile, false);
17569
+ return renderSumiPluginVersion(command, profile, 3);
17286
17570
  }
17287
17571
  function isManagedSumiPlugin(text2, command, profile) {
17288
- return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, true);
17572
+ return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, 2) || text2 === renderSumiPluginVersion(command, profile, 1);
17289
17573
  }
17290
- function renderSumiPluginVersion(command, profile, legacy) {
17574
+ function renderSumiPluginVersion(command, profile, version2) {
17575
+ const legacy = version2 === 1;
17291
17576
  const failure = (message) => legacy ? `new Error(${JSON.stringify(message)})` : "refusal()";
17292
17577
  return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
17293
17578
  import { spawn } from "node:child_process";
17294
17579
  const command = ${JSON.stringify(command)};
17295
17580
  const profile = ${JSON.stringify(profile)};
17296
- ${legacy ? "" : `function refusal() {
17297
- const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry.");
17581
+ ${legacy ? "" : `function refusal(${version2 === 3 ? "reasonCode" : ""}) {
17582
+ const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry."${version2 === 3 ? ' + (reasonCode ? " [" + reasonCode + "]" : "")' : ""});
17298
17583
  error.name = "SkillsHookRefusal";
17299
- error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED" };
17584
+ error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED"${version2 === 3 ? ", ...(reasonCode ? { reasonCode } : {})" : ""} };
17300
17585
  return error;
17301
17586
  }
17302
- `}function context(input) {
17587
+ `}${version2 === 3 ? `const reasonCodes = new Set(${JSON.stringify(HOOK_REFUSAL_REASON_CODES)});
17588
+ function refusedResult(result) {
17589
+ const reason = result.reason ?? result.stopReason ?? result.systemMessage;
17590
+ if (typeof reason !== "string" || reason.length > 16384) return refusal();
17591
+ if (reason.startsWith("NATIVE_SKILL_DRIFT:")) return refusal("NATIVE_SKILL_DRIFT");
17592
+ const code = /^Skills context is unavailable \\[([A-Z_]+)\\]/.exec(reason)?.[1];
17593
+ return refusal(reasonCodes.has(code) ? code : undefined);
17594
+ }
17595
+ ` : ""}function context(input) {
17303
17596
  return new Promise((resolve, reject) => {
17304
17597
  const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
17305
17598
  const chunks = []; let bytes = 0, failure;
@@ -17314,12 +17607,12 @@ ${legacy ? "" : `function refusal() {
17314
17607
  try {
17315
17608
  const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
17316
17609
  if (!result || typeof result !== "object" || Array.isArray(result)) throw ${failure("Invalid Skills hook response")};
17317
- if (result.decision === "block" || result.continue === false) throw ${failure("Skills prompt hook refused")};
17610
+ if (result.decision === "block" || result.continue === false) throw ${version2 === 3 ? "refusedResult(result)" : failure("Skills prompt hook refused")};
17318
17611
  if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw ${failure("Invalid Skills hook response")};
17319
17612
  const text = result.hookSpecificOutput?.additionalContext ?? "";
17320
17613
  if (typeof text !== "string") throw ${failure("Invalid Skills prompt context")};
17321
17614
  resolve(text);
17322
- } catch (error) { reject(${legacy ? "error" : "refusal()"}); }
17615
+ } catch (error) { reject(${legacy ? "error" : version2 === 3 ? 'error instanceof Error && error.name === "SkillsHookRefusal" ? error : refusal()' : "refusal()"}); }
17323
17616
  });
17324
17617
  child.stdin.end(JSON.stringify(input));
17325
17618
  });
@@ -17352,9 +17645,13 @@ export default {
17352
17645
  sessions.add(event.sessionID);
17353
17646
  }));
17354
17647
  registrations.push(await ctx.session.hook("context", async event => {
17355
- const last = event.messages.findLast(message => message.role === "user");
17648
+ ${version2 === 3 ? ` // Model messages also contain checkpoints, attachments and synthetic
17649
+ // user-role history, with no owner-prompt provenance. Prompt admission
17650
+ // already matched and persisted Skills; restore that verified state only.
17651
+ const prompt = "";
17652
+ ` : ` const last = event.messages.findLast(message => message.role === "user");
17356
17653
  const prompt = (last?.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\\n");
17357
- // Recheck on every primary request, including resumed and synthetic
17654
+ `} // Recheck on every primary request, including resumed and synthetic
17358
17655
  // sessions. The CLI owns optional failures versus explicit refusals.
17359
17656
  const text = await context(await input(event.sessionID, prompt, !sessions.has(event.sessionID)));
17360
17657
  if (text) event.system.push({ type: "text", text });
@@ -19022,7 +19319,7 @@ function planAgentSettingsWitnessUpgrade(options) {
19022
19319
  return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join27(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join27(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 })) } };
19023
19320
  }
19024
19321
  function planCodexSemanticCacheWitnessUpgrade(options) {
19025
- if (options.agent !== "codex" || !/^[a-f0-9]{64}$/.test(options.expectedPolicySha256) || !/^[a-f0-9]{64}$/.test(options.expectedSettingsSha256) || options.expectedCacheWitnessSha256 !== undefined && !/^[a-f0-9]{64}$/.test(options.expectedCacheWitnessSha256) || !Array.isArray(options.appOnlyParents) || options.appOnlyParents.length > 4096 || new Set(options.appOnlyParents).size !== options.appOnlyParents.length)
19322
+ if (options.agent !== "codex" || !/^[a-f0-9]{64}$/.test(options.expectedPolicySha256) || !/^[a-f0-9]{64}$/.test(options.expectedSettingsSha256) || options.expectedCacheWitnessSha256 !== undefined && !/^[a-f0-9]{64}$/.test(options.expectedCacheWitnessSha256) || !Array.isArray(options.appOnlyParents) || options.appOnlyParents.length > 4096 || new Set(options.appOnlyParents).size !== options.appOnlyParents.length || options.reviewedHookParents !== undefined && (!Array.isArray(options.reviewedHookParents) || options.reviewedHookParents.length > 4096 || new Set(options.reviewedHookParents).size !== options.reviewedHookParents.length))
19026
19323
  throw new Error("Invalid Codex semantic cache upgrade request");
19027
19324
  const home = resolve18(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
19028
19325
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
@@ -19046,14 +19343,23 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
19046
19343
  throw new Error("Native settings preimage changed");
19047
19344
  const boundedRead = (path) => new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024));
19048
19345
  const unchangedFullSource = (path) => {
19049
- const source = binding.sources.find((source2) => source2.path === path && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && source2.reviewArtifact === undefined && (source2.hashMode === undefined || source2.hashMode === "bytes"));
19050
- if (!source)
19051
- throw new Error("Cache upgrade lacks prior full identity source");
19346
+ const sources = binding.sources.filter((source2) => source2.path === path && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && source2.reviewArtifact === undefined && (source2.hashMode === undefined || source2.hashMode === "bytes"));
19347
+ if (sources.length !== 1)
19348
+ throw new Error("Cache upgrade requires one unambiguous prior full identity source");
19349
+ const source = sources[0];
19052
19350
  const text2 = boundedRead(path);
19053
19351
  if (sha4(text2) !== source.sha256)
19054
19352
  throw new Error("Reviewed cache identity source changed");
19055
19353
  return text2;
19056
19354
  };
19355
+ const unchangedReviewedHookSource = (path) => {
19356
+ const sources = binding.sources.filter((source) => source.path === path);
19357
+ if (!sources.length)
19358
+ throw new Error("Cache upgrade lacks prior full identity source");
19359
+ if (sources.length !== 1)
19360
+ throw new Error("Reviewed hook identity source is ambiguous");
19361
+ return unchangedFullSource(path);
19362
+ };
19057
19363
  const appOnlyParents = options.appOnlyParents.map((parent) => {
19058
19364
  const parts = relative8(cacheRoot, parent).split(sep10);
19059
19365
  if (!isAbsolute19(parent) || resolve18(parent) !== parent || parts.length !== 2 || parts.some((part) => !/^[A-Za-z0-9_-]{1,64}$/.test(part)) || controls.some((control) => control.pluginParent === parent))
@@ -19068,11 +19374,60 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
19068
19374
  unchangedFullSource(source.path);
19069
19375
  return { role: "app-only", pluginId: `${parts[1]}@${parts[0]}`, namespace: parts[1], pluginParent: parent, remotePluginId: receipt.remote_plugin_id };
19070
19376
  }).sort((a, b) => a.pluginParent.localeCompare(b.pluginParent));
19071
- const parents2 = [...new Set([...controls.map((control) => control.pluginParent), ...appOnlyParents.map((parent) => parent.pluginParent)])];
19377
+ const reviewedHookParents = (options.reviewedHookParents ?? []).map((parent) => {
19378
+ const parts = relative8(cacheRoot, parent).split(sep10);
19379
+ if (!isAbsolute19(parent) || resolve18(parent) !== parent || parts.length !== 2 || parts.some((part) => !/^[A-Za-z0-9_-]{1,64}$/.test(part)) || controls.some((control) => control.pluginParent === parent) || appOnlyParents.some((role) => role.pluginParent === parent))
19380
+ throw new Error("Unsupported reviewed native-hook parent role");
19381
+ const receiptPath = join27(parent, ".codex-remote-plugin-install.json"), receiptStat = lstatSync23(receiptPath, { throwIfNoEntry: false });
19382
+ let identity5;
19383
+ if (receiptStat) {
19384
+ if (!receiptStat.isFile() || receiptStat.isSymbolicLink() || receiptStat.size > 16 * 1024)
19385
+ throw new Error("Invalid reviewed native-hook install receipt");
19386
+ const receiptText = unchangedReviewedHookSource(receiptPath);
19387
+ let receipt;
19388
+ try {
19389
+ hashNativeJsonControls(receiptText);
19390
+ receipt = JSON.parse(receiptText);
19391
+ } catch {
19392
+ throw new Error("Invalid reviewed native-hook install receipt");
19393
+ }
19394
+ if (!receipt || Array.isArray(receipt) || Object.keys(receipt).length !== 2 || receipt.schema_version !== 1 || typeof receipt.remote_plugin_id !== "string" || !/^[A-Za-z0-9_~-]{1,1024}$/.test(receipt.remote_plugin_id))
19395
+ throw new Error("Invalid reviewed native-hook install receipt");
19396
+ identity5 = { role: "reviewed-hook", pluginId: `${parts[1]}@${parts[0]}`, namespace: parts[1], pluginParent: parent, installation: "remote-receipt", remotePluginId: receipt.remote_plugin_id };
19397
+ } else {
19398
+ const pluginId = `${parts[1]}@${parts[0]}`;
19399
+ const marketplaceSources = binding.sources.filter((source2) => basename9(source2.path) === "marketplace.json" && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && source2.reviewArtifact === undefined && (source2.hashMode === undefined || source2.hashMode === "bytes"));
19400
+ const matches = marketplaceSources.flatMap((source2) => {
19401
+ const text2 = unchangedFullSource(source2.path);
19402
+ return codexLocalMarketplaceMapsPlugin(text2, parts[0], parts[1]) ? [{ source: source2, text: text2 }] : [];
19403
+ });
19404
+ if (matches.length !== 1)
19405
+ throw new Error("Local marketplace hook parent lacks one exact enrolled marketplace source");
19406
+ const { source } = matches[0];
19407
+ const config = Bun.TOML.parse(settings);
19408
+ if (config.plugins?.[pluginId]?.enabled !== true)
19409
+ throw new Error("Local marketplace hook parent is not enabled in the unchanged Codex settings");
19410
+ identity5 = { role: "reviewed-hook", pluginId, namespace: parts[1], pluginParent: parent, installation: "local-marketplace", marketplaceSourcePath: source.path, marketplaceSha256: source.sha256 };
19411
+ }
19412
+ const versions = readdirSync9(parent).filter((name) => name !== ".codex-remote-plugin-install.json");
19413
+ if (!versions.length || versions.length > 4096)
19414
+ throw new Error("Reviewed native-hook parent has no bounded materializations");
19415
+ for (const version2 of versions) {
19416
+ if (!/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2))
19417
+ throw new Error("Invalid reviewed native-hook version identity");
19418
+ const root = join27(parent, version2), stat = lstatSync23(root, { throwIfNoEntry: false });
19419
+ if (!stat?.isDirectory() || stat.isSymbolicLink() || realpathSync12(root) !== root)
19420
+ throw new Error("Invalid reviewed native-hook materialization");
19421
+ unchangedReviewedHookSource(join27(root, ".codex-plugin/plugin.json"));
19422
+ unchangedReviewedHookSource(join27(root, "hooks/hooks.json"));
19423
+ }
19424
+ return identity5;
19425
+ }).sort((a, b) => a.pluginParent.localeCompare(b.pluginParent));
19426
+ const parents2 = [...new Set([...controls.map((control) => control.pluginParent), ...appOnlyParents.map((parent) => parent.pluginParent), ...reviewedHookParents.map((parent) => parent.pluginParent)])];
19072
19427
  const reviewedAlias = aliases.find((alias) => alias.agent === "codex");
19073
19428
  const hookRootAlias = reviewedAlias ? { alias: reviewedAlias.alias, target: reviewedAlias.target } : null;
19074
19429
  assertCodexSemanticCacheHookSafety(cacheRoot, parents2, configPath, hookRootAlias);
19075
- const supersededSources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents2));
19430
+ const supersededSources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents2, reviewedHookParents.map((parent) => parent.pluginParent)));
19076
19431
  for (const source of supersededSources)
19077
19432
  if ([".app.json", ".mcp.json"].includes(source.path.split(sep10).at(-1)))
19078
19433
  unchangedFullSource(source.path);
@@ -19080,7 +19435,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
19080
19435
  if (!supersededDirectories.some((directory) => directory.path === cacheRoot) || (binding.directories ?? []).some((directory) => cacheRoot.startsWith(directory.path + sep10)))
19081
19436
  throw new Error("Cache upgrade requires exact cache directory coverage without an unproved ancestor projection");
19082
19437
  const rules = Bun.TOML.parse(settings).skills?.config ?? [];
19083
- const witness = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents, rules, read: boundedRead });
19438
+ const witness = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents, reviewedHookParents, rules, read: boundedRead });
19084
19439
  if (options.expectedCacheWitnessSha256 !== undefined && witness.sha256 !== options.expectedCacheWitnessSha256)
19085
19440
  throw new Error("Reviewed semantic cache preimage changed");
19086
19441
  const replacement = { ...binding, codexDisabledPluginSkills: controls, codexSemanticCache: {
@@ -19089,6 +19444,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
19089
19444
  hookRootAlias,
19090
19445
  witness,
19091
19446
  appOnlyParents,
19447
+ reviewedHookParents,
19092
19448
  previousPolicySha256: options.expectedPolicySha256,
19093
19449
  supersededSources,
19094
19450
  supersededDirectories
@@ -19107,7 +19463,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
19107
19463
  rootAliases: aliases,
19108
19464
  managedAgentChecks: { home, agents: ["codex"] },
19109
19465
  ...policy.bridge.discoveryExecutables ? { discoveryExecutables: policy.bridge.discoveryExecutables } : {},
19110
- cacheWitnessUpgrade: { ...options, home, dataDir: dataDir2, appOnlyParents: appOnlyParents.map((parent) => parent.pluginParent) }
19466
+ cacheWitnessUpgrade: { ...options, home, dataDir: dataDir2, appOnlyParents: appOnlyParents.map((parent) => parent.pluginParent), reviewedHookParents: reviewedHookParents.map((parent) => parent.pluginParent) }
19111
19467
  };
19112
19468
  }
19113
19469
  function planReviewedArtifactMigration(options) {
@@ -19885,7 +20241,7 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
19885
20241
  return { action, path: skillMdPath };
19886
20242
  const parentDir = dirname19(dir);
19887
20243
  mkdirSync8(parentDir, { recursive: true });
19888
- const transactionDir = mkdtempSync(join28(parentDir, `.hasna-skills-write-${basename8(dir)}-`));
20244
+ const transactionDir = mkdtempSync(join28(parentDir, `.hasna-skills-write-${basename10(dir)}-`));
19889
20245
  const candidateDir = join28(transactionDir, "candidate");
19890
20246
  const backupDir = join28(transactionDir, "backup");
19891
20247
  const candidateSkillMdPath = join28(candidateDir, "SKILL.md");
@@ -21915,7 +22271,7 @@ function hashManagedPluginRegistry(path, managed) {
21915
22271
  pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join34(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
21916
22272
  const approval = (producer) => {
21917
22273
  pluginText(producer);
21918
- const digest2 = `sha256:${basename9(producer)}`;
22274
+ const digest2 = `sha256:${basename11(producer)}`;
21919
22275
  const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest2);
21920
22276
  pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
21921
22277
  if (!verified.has(producer)) {
@@ -21979,7 +22335,7 @@ function captureManagedPluginRegistry(path, managedPlugins) {
21979
22335
  // src/lib/claude-marketplace-registry.ts
21980
22336
  import { createHash as createHash26 } from "crypto";
21981
22337
  import { closeSync as closeSync17, constants as constants18, fstatSync as fstatSync17, lstatSync as lstatSync27, openSync as openSync17, readSync as readSync13 } from "fs";
21982
- import { basename as basename10, dirname as dirname22, isAbsolute as isAbsolute23, resolve as resolve22 } from "path";
22338
+ import { basename as basename12, dirname as dirname22, isAbsolute as isAbsolute23, resolve as resolve22 } from "path";
21983
22339
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
21984
22340
  function need8(value, reason2) {
21985
22341
  if (!value)
@@ -22001,7 +22357,7 @@ function parents2(path) {
22001
22357
  }
22002
22358
  function readRegistry2(path, budget2) {
22003
22359
  absolutePath4(path);
22004
- need8(basename10(path) === "known_marketplaces.json", "requires known_marketplaces.json");
22360
+ need8(basename12(path) === "known_marketplaces.json", "requires known_marketplaces.json");
22005
22361
  need8(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
22006
22362
  const ancestors2 = parents2(path), initial = lstatSync27(path, { bigint: true });
22007
22363
  need8(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
@@ -22282,7 +22638,7 @@ function assertReviewedClaudeCacheClosures(home, config, review, canonical9) {
22282
22638
  throw new Error("Reviewed Claude enabled plugin registration requires an object with an installPath string");
22283
22639
  }
22284
22640
  if ([cacheAliasRoot, cacheRoot].some((root2) => row.installPath === root2 || row.installPath.startsWith(root2 + sep11))) {
22285
- if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename11(row.installPath) !== row.version)) {
22641
+ if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename13(row.installPath) !== row.version)) {
22286
22642
  throw new Error("Reviewed Claude discovery has a malformed active cache registration");
22287
22643
  }
22288
22644
  if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute24(row.projectPath) && resolve23(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
@@ -22560,7 +22916,7 @@ function absentRetiredClaudeRoots(binding) {
22560
22916
  if (!targetValid || !targetScopes.has("user\x00"))
22561
22917
  continue;
22562
22918
  const active = rows[0]?.installPath;
22563
- if (typeof active !== "string" || rows[0]?.version !== basename11(active) || dirname23(active) !== dirname23(root2) || active === root2 || !lstatSync28(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync13(active) !== active)
22919
+ if (typeof active !== "string" || rows[0]?.version !== basename13(active) || dirname23(active) !== dirname23(root2) || active === root2 || !lstatSync28(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync13(active) !== active)
22564
22920
  continue;
22565
22921
  const activeManifest = full(join35(active, ".claude-plugin/plugin.json"));
22566
22922
  if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
@@ -22569,14 +22925,15 @@ function absentRetiredClaudeRoots(binding) {
22569
22925
  }
22570
22926
  return [...result];
22571
22927
  }
22572
- function codexSemanticCacheSourceRole(source, cacheRoot, parents3) {
22928
+ function codexSemanticCacheSourceRole(source, cacheRoot, parents3, hookParents = []) {
22573
22929
  if (!isAbsolute24(source.path) || resolve23(source.path) !== source.path || /[\x00-\x1f\x7f]/.test(source.path) || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.reviewArtifact !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes")
22574
22930
  return false;
22575
22931
  const parts = relative9(cacheRoot, source.path).split(sep11);
22576
22932
  if (parts.length < 3 || !parents3.includes(join35(cacheRoot, parts[0], parts[1])))
22577
22933
  return false;
22578
22934
  const tail = parts.slice(3).join("/");
22579
- return parts.length === 5 && tail === ".codex-plugin/plugin.json" && source.sha256 !== null || parts.length === 4 && [".app.json", ".mcp.json"].includes(tail) && source.sha256 !== null || source.sha256 === null && (parts.length === 4 && tail === "SKILL.md" || parts.length === 5 && tail === "hooks/hooks.json");
22935
+ const parent = join35(cacheRoot, parts[0], parts[1]);
22936
+ return parts.length === 5 && tail === ".codex-plugin/plugin.json" && source.sha256 !== null || parts.length === 4 && [".app.json", ".mcp.json"].includes(tail) && source.sha256 !== null || parts.length === 5 && tail === "hooks/hooks.json" && hookParents.includes(parent) && source.sha256 !== null || source.sha256 === null && (parts.length === 4 && tail === "SKILL.md" || parts.length === 5 && tail === "hooks/hooks.json");
22580
22937
  }
22581
22938
  function codexSemanticCacheDirectoryRole(path, cacheRoot) {
22582
22939
  return isAbsolute24(path) && resolve23(path) === path && !/[\x00-\x1f\x7f]/.test(path) && (path === cacheRoot || path.startsWith(cacheRoot + sep11));
@@ -22608,20 +22965,28 @@ function verifyCodexSemanticCacheDiscovery(binding, reviewedRootAliases) {
22608
22965
  const cacheRoot = proof.witness.cacheRoot;
22609
22966
  if (controls.some((control) => dirname23(dirname23(control.pluginParent)) !== cacheRoot))
22610
22967
  throw new Error("Codex semantic cache names another enrolled cache");
22611
- const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent)])];
22612
- const sources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3));
22968
+ const reviewedHookParents = proof.reviewedHookParents ?? [];
22969
+ if (!Array.isArray(reviewedHookParents) || reviewedHookParents.length > 4096 || reviewedHookParents.some((parent) => !parent || !(parent.installation === "remote-receipt" && Object.keys(parent).sort().join(",") === "installation,namespace,pluginId,pluginParent,remotePluginId,role" && typeof parent.remotePluginId === "string" || parent.installation === "local-marketplace" && Object.keys(parent).sort().join(",") === "installation,marketplaceSha256,marketplaceSourcePath,namespace,pluginId,pluginParent,role" && typeof parent.marketplaceSourcePath === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256 ?? "")) || parent.role !== "reviewed-hook" || typeof parent.pluginId !== "string" || typeof parent.namespace !== "string" || typeof parent.pluginParent !== "string") || new Set(reviewedHookParents.map((parent) => parent.pluginParent)).size !== reviewedHookParents.length || !isDeepStrictEqual7((proof.witness.hookParents ?? []).map(({ manifestSha256: _manifest, hooksSha256: _hooks, receiptSha256: _receipt, ...parent }) => parent), reviewedHookParents)) {
22970
+ throw new Error("Invalid reviewed native-hook cache role");
22971
+ }
22972
+ for (const parent of reviewedHookParents)
22973
+ if (parent.installation === "local-marketplace" && !binding.sources.some((source) => source.path === parent.marketplaceSourcePath && source.sha256 === parent.marketplaceSha256 && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.reviewArtifact === undefined && (source.hashMode === undefined || source.hashMode === "bytes")))
22974
+ throw new Error("Local marketplace hook parent lost its enrolled source witness");
22975
+ const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent), ...reviewedHookParents.map((parent) => parent.pluginParent)])];
22976
+ const hookParentPaths = reviewedHookParents.map((parent) => parent.pluginParent);
22977
+ const sources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3, hookParentPaths));
22613
22978
  const directories = (binding.directories ?? []).filter((directory) => codexSemanticCacheDirectoryRole(directory.path, cacheRoot));
22614
22979
  if (!directories.length || !isDeepStrictEqual7(sources, proof.supersededSources) || !isDeepStrictEqual7(directories, proof.supersededDirectories))
22615
22980
  throw new Error("Codex semantic cache provenance changed");
22616
22981
  const configPath = proof.configPath;
22617
- if (!isAbsolute24(configPath) || resolve23(configPath) !== configPath || basename11(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
22982
+ if (!isAbsolute24(configPath) || resolve23(configPath) !== configPath || basename13(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
22618
22983
  throw new Error("Invalid semantic cache configuration source");
22619
22984
  const configSource = binding.sources.find((source) => source.path === configPath && source.sha256 !== null && source.format === undefined && source.fields === undefined);
22620
22985
  if (!configSource || projected(configSource) !== configSource.sha256)
22621
22986
  throw new Error("Codex semantic cache requires unchanged reviewed configuration");
22622
22987
  assertCodexSemanticCacheHookSafety(cacheRoot, parents3, configPath, proof.hookRootAlias);
22623
22988
  const rules = parseConfig(read2(configPath), configPath, true).skills?.config ?? [];
22624
- const current = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents: proof.appOnlyParents, rules });
22989
+ const current = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents: proof.appOnlyParents, reviewedHookParents, rules });
22625
22990
  assertCodexSemanticCacheWitnessUnchanged(proof.witness, current);
22626
22991
  return proof;
22627
22992
  }
@@ -22705,7 +23070,7 @@ function codexInstallationRoots(binding) {
22705
23070
  const config = join35(dirname23(dirname23(cache)), "config.toml"), home = dirname23(dirname23(dirname23(cache)));
22706
23071
  const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
22707
23072
  const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join35(home, path));
22708
- if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep11) || input.sourceRoot.startsWith(root2 + sep11)) || binding.sources.some((source) => basename11(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep11)))))
23073
+ if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep11) || input.sourceRoot.startsWith(root2 + sep11)) || binding.sources.some((source) => basename13(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep11)))))
22709
23074
  throw new Error("Native installation input overlaps configuration or loading roots");
22710
23075
  if (proof.plugins.some((input) => dirname23(dirname23(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join35(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
22711
23076
  throw new Error("Missing native installation input source witness");
@@ -22717,13 +23082,13 @@ function codexInstallationRoots(binding) {
22717
23082
  });
22718
23083
  }
22719
23084
  function assertMarketplaceBinding(binding, source) {
22720
- if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename11(source.path) !== "plugin.json" || !source.path.endsWith(join35(sep11, ".claude-plugin", "plugin.json"))))
23085
+ if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename13(source.path) !== "plugin.json" || !source.path.endsWith(join35(sep11, ".claude-plugin", "plugin.json"))))
22721
23086
  throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
22722
- if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename11(source.path) !== "sumi.json"))
23087
+ if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename13(source.path) !== "sumi.json"))
22723
23088
  throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
22724
- if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename11(source.path) !== "config.toml"))
23089
+ if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename13(source.path) !== "config.toml"))
22725
23090
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
22726
- 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"))
23091
+ 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" || basename13(source.path) !== "settings.json"))
22727
23092
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
22728
23093
  if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
22729
23094
  throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
@@ -22841,7 +23206,7 @@ function verifyAutomaticDiscoveryClosure(binding, options) {
22841
23206
  const current = resolveAgentDiscovery({ home: options.home, agent: binding.agent, canonical: options.canonical, executable: runtime });
22842
23207
  if (JSON.stringify(current) === JSON.stringify(binding))
22843
23208
  return;
22844
- const reviewedWithRuntime = (binding.builtinNames?.length ?? 0) > 0 || binding.sources.some((source) => basename11(source.path) === "package.json");
23209
+ const reviewedWithRuntime = (binding.builtinNames?.length ?? 0) > 0 || binding.sources.some((source) => basename13(source.path) === "package.json");
22845
23210
  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)));
22846
23211
  if (command && gap && runtime === null && reviewedWithRuntime && otherwiseCurrent)
22847
23212
  throw discoveryRootUnresolved(binding.agent, command, gap);
@@ -22984,7 +23349,7 @@ function resolveAgentDiscovery(options) {
22984
23349
  throw new Error("Discovery review must include the agent configuration source");
22985
23350
  const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical9(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical9(configPath), source)) : sources;
22986
23351
  const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
22987
- 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(join35(sep11, ".claude-plugin", "plugin.json")))
23352
+ if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename13(source.path) !== "plugin.json" || !source.path.endsWith(join35(sep11, ".claude-plugin", "plugin.json")))
22988
23353
  return source;
22989
23354
  const pluginRoot = dirname23(dirname23(source.path)), hooksPath = join35(pluginRoot, "hooks/hooks.json");
22990
23355
  const hookStat = lstatSync28(hooksPath, { throwIfNoEntry: false });
@@ -23150,7 +23515,7 @@ function physicalPath(path) {
23150
23515
  let cursor = resolve24(path);
23151
23516
  const suffix = [];
23152
23517
  while (!lstatSync29(cursor, { throwIfNoEntry: false })) {
23153
- suffix.unshift(basename12(cursor));
23518
+ suffix.unshift(basename14(cursor));
23154
23519
  const parent = dirname24(cursor);
23155
23520
  if (parent === cursor)
23156
23521
  return resolve24(path);
@@ -23369,7 +23734,7 @@ function physicalImportPath(path) {
23369
23734
  let ancestor = resolve25(path);
23370
23735
  const suffix = [];
23371
23736
  while (!existsSync14(ancestor)) {
23372
- suffix.unshift(basename13(ancestor));
23737
+ suffix.unshift(basename15(ancestor));
23373
23738
  ancestor = dirname25(ancestor);
23374
23739
  }
23375
23740
  return join37(realpathSync15(ancestor), ...suffix);
@@ -23394,8 +23759,8 @@ function portPortableSkill(sourcePath, options = {}) {
23394
23759
  const explicitName = options.name != null;
23395
23760
  const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
23396
23761
  if (isOfficialSkillName(skillName) && !options.allowShadow) {
23397
- const sourceSlug = safeNormalizeName(basename13(absoluteSource));
23398
- 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.`;
23762
+ const sourceSlug = safeNormalizeName(basename15(absoluteSource));
23763
+ const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename15(absoluteSource)}')` : ""} matches a bundled official skill.`;
23399
23764
  throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
23400
23765
  }
23401
23766
  const readOnlyDestination = join37(resolvePortableSkillsRoot(options, false), skillName);
@@ -49985,7 +50350,7 @@ async function reconcileSkillSession(input2, options = {}) {
49985
50350
  // src/lib/claude-prospective-review.ts
49986
50351
  import { createHash as createHash38 } from "crypto";
49987
50352
  import { lstatSync as lstatSync31, readFileSync as readFileSync31, readdirSync as readdirSync18, realpathSync as realpathSync17 } from "fs";
49988
- import { basename as basename14, dirname as dirname33, isAbsolute as isAbsolute29, join as join47, relative as relative15, resolve as resolve30, sep as sep18 } from "path";
50353
+ import { basename as basename16, dirname as dirname33, isAbsolute as isAbsolute29, join as join47, relative as relative15, resolve as resolve30, sep as sep18 } from "path";
49989
50354
  var MAX_CANDIDATE_FILES = 16384;
49990
50355
  var MAX_CANDIDATE_BYTES = 256 * 1024 * 1024;
49991
50356
  var sha7 = (data) => createHash38("sha256").update(data).digest("hex");