@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/bin/index.js CHANGED
@@ -36888,7 +36888,7 @@ var package_default;
36888
36888
  var init_package = __esm(() => {
36889
36889
  package_default = {
36890
36890
  name: "@hasna/skills",
36891
- version: "0.10.64",
36891
+ version: "0.10.66",
36892
36892
  description: "Skills library for AI coding agents",
36893
36893
  type: "module",
36894
36894
  bin: {
@@ -40633,8 +40633,8 @@ function assertAgentPolicyCollections(policy) {
40633
40633
  }
40634
40634
  }
40635
40635
  if (value.codexSemanticCache !== undefined) {
40636
- const proof = record(value.codexSemanticCache, 8), witness = record(proof.witness, 6);
40637
- 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));
40636
+ const proof = record(value.codexSemanticCache, 9), witness = record(proof.witness, 7);
40637
+ 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));
40638
40638
  text(proof.configPath);
40639
40639
  requireBound(isAbsolute4(proof.configPath) && resolve5(proof.configPath) === proof.configPath && basename4(proof.configPath) === "config.toml");
40640
40640
  text(witness.cacheRoot);
@@ -40653,6 +40653,20 @@ function assertAgentPolicyCollections(policy) {
40653
40653
  text(parent[key]);
40654
40654
  requireBound(isAbsolute4(parent.pluginParent) && resolve5(parent.pluginParent) === parent.pluginParent);
40655
40655
  }
40656
+ if (proof.reviewedHookParents !== undefined)
40657
+ for (const parent of array(proof.reviewedHookParents, 4096)) {
40658
+ 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));
40659
+ 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");
40660
+ for (const key of ["pluginId", "namespace", "pluginParent", "installation"])
40661
+ text(parent[key]);
40662
+ if (parent.installation === "remote-receipt")
40663
+ text(parent.remotePluginId);
40664
+ else {
40665
+ text(parent.marketplaceSourcePath);
40666
+ requireBound(typeof parent.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256) && isAbsolute4(parent.marketplaceSourcePath) && resolve5(parent.marketplaceSourcePath) === parent.marketplaceSourcePath && basename4(parent.marketplaceSourcePath) === "marketplace.json");
40667
+ }
40668
+ requireBound(isAbsolute4(parent.pluginParent) && resolve5(parent.pluginParent) === parent.pluginParent);
40669
+ }
40656
40670
  for (const row of [...array(witness.controls, 4096), ...array(witness.appOnlyParents, 4096)]) {
40657
40671
  requireBound(object(row));
40658
40672
  for (const key of ["pluginId", "namespace", "pluginParent"])
@@ -40661,6 +40675,26 @@ function assertAgentPolicyCollections(policy) {
40661
40675
  if (row[key] !== undefined)
40662
40676
  requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
40663
40677
  }
40678
+ if (witness.hookParents !== undefined)
40679
+ for (const row of array(witness.hookParents, 4096)) {
40680
+ 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));
40681
+ 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");
40682
+ for (const key of ["pluginId", "namespace", "pluginParent", "installation"])
40683
+ text(row[key]);
40684
+ if (row.installation === "remote-receipt")
40685
+ text(row.remotePluginId);
40686
+ else {
40687
+ text(row.marketplaceSourcePath);
40688
+ requireBound(typeof row.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(row.marketplaceSha256) && isAbsolute4(row.marketplaceSourcePath) && resolve5(row.marketplaceSourcePath) === row.marketplaceSourcePath && basename4(row.marketplaceSourcePath) === "marketplace.json");
40689
+ }
40690
+ requireBound(isAbsolute4(row.pluginParent) && resolve5(row.pluginParent) === row.pluginParent);
40691
+ for (const key of ["manifestSha256", "hooksSha256"])
40692
+ requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
40693
+ if (row.installation === "remote-receipt")
40694
+ requireBound(typeof row.receiptSha256 === "string" && /^[a-f0-9]{64}$/.test(row.receiptSha256));
40695
+ else
40696
+ requireBound(row.receiptSha256 === undefined);
40697
+ }
40664
40698
  for (const skill of array(witness.skills, 4096)) {
40665
40699
  requireBound(object(skill));
40666
40700
  for (const key of ["name", "pluginId", "namespace", "pluginParent"])
@@ -53789,7 +53823,25 @@ var init_codex_settings_witness = __esm(() => {
53789
53823
  // src/lib/codex-semantic-cache-witness.ts
53790
53824
  import { createHash as createHash17 } from "crypto";
53791
53825
  import { constants as constants10, closeSync as closeSync9, fstatSync as fstatSync9, lstatSync as lstatSync16, openSync as openSync9, readSync as readSync7, readdirSync as readdirSync7, realpathSync as realpathSync8 } from "fs";
53792
- import { dirname as dirname15, isAbsolute as isAbsolute15, join as join25, relative as relative6, resolve as resolve15, sep as sep7 } from "path";
53826
+ import { basename as basename9, dirname as dirname15, isAbsolute as isAbsolute15, join as join25, relative as relative6, resolve as resolve15, sep as sep7 } from "path";
53827
+ function codexLocalMarketplaceMapsPlugin(text2, marketplace, plugin) {
53828
+ try {
53829
+ hashNativeJsonControls(text2);
53830
+ const value = JSON.parse(text2);
53831
+ 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)
53832
+ return false;
53833
+ const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
53834
+ if (rows.length !== 1)
53835
+ return false;
53836
+ const row = rows[0];
53837
+ 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("\\"))
53838
+ return false;
53839
+ const parts = row.source.path.split("/");
53840
+ 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;
53841
+ } catch {
53842
+ return false;
53843
+ }
53844
+ }
53793
53845
  function defaultRead(path) {
53794
53846
  const before2 = lstatSync16(path, { throwIfNoEntry: false, bigint: true });
53795
53847
  if (before2 === undefined)
@@ -53851,7 +53903,7 @@ function validPluginParent(cacheRoot, pluginParent, namespace, pluginId) {
53851
53903
  return parts.length === 2 && parts.every(identifier3) && parts[1] === namespace && pluginId === `${namespace}@${parts[0]}`;
53852
53904
  }
53853
53905
  function assertWitnessShape(value) {
53854
- if (!plainObject(value) || !exactKeys(value, ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256"]) || value.schema !== CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA || typeof value.cacheRoot !== "string" || !isAbsolute15(value.cacheRoot) || resolve15(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)
53906
+ if (!plainObject(value) || !exactKeys(value, ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256"], ["hookParents"]) || value.schema !== CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA || typeof value.cacheRoot !== "string" || !isAbsolute15(value.cacheRoot) || resolve15(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)
53855
53907
  refuse4();
53856
53908
  const witness = value;
53857
53909
  const controlParents = new Set;
@@ -53872,6 +53924,20 @@ function assertWitnessShape(value) {
53872
53924
  refuse4();
53873
53925
  appOnlyParentPaths.add(row.pluginParent);
53874
53926
  }
53927
+ const hookParentPaths = new Set;
53928
+ const hookParents = witness.hookParents === undefined ? [] : witness.hookParents;
53929
+ if (!Array.isArray(hookParents) || hookParents.length > 4096)
53930
+ refuse4();
53931
+ for (const candidate of hookParents) {
53932
+ if (!plainObject(candidate))
53933
+ refuse4();
53934
+ const row = candidate;
53935
+ const remote = row.installation === "remote-receipt";
53936
+ const local = row.installation === "local-marketplace";
53937
+ 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" || !isAbsolute15(row.marketplaceSourcePath) || resolve15(row.marketplaceSourcePath) !== row.marketplaceSourcePath || basename9(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))
53938
+ refuse4();
53939
+ hookParentPaths.add(row.pluginParent);
53940
+ }
53875
53941
  const skillNames = new Set;
53876
53942
  const qualifiedNames = new Set;
53877
53943
  for (const candidate of witness.skills) {
@@ -53891,6 +53957,7 @@ function assertWitnessShape(value) {
53891
53957
  cacheRoot: witness.cacheRoot,
53892
53958
  controls: witness.controls,
53893
53959
  appOnlyParents: witness.appOnlyParents,
53960
+ ...witness.hookParents === undefined ? {} : { hookParents: witness.hookParents },
53894
53961
  skills: witness.skills
53895
53962
  };
53896
53963
  if (hash2(projection) !== witness.sha256)
@@ -54081,10 +54148,128 @@ function appOnlyParentProjection(cacheRoot, parents2, read2) {
54081
54148
  }
54082
54149
  return rows.sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.pluginParent.localeCompare(b.pluginParent));
54083
54150
  }
54084
- function assertParents(cacheRoot, controls, appOnlyParents, files, members) {
54151
+ function assertJsonValue(value, depth = 0, budget2 = { members: 0 }) {
54152
+ if (depth > 32 || ++budget2.members > 4096)
54153
+ refuse4();
54154
+ if (value === null || typeof value === "string" || typeof value === "boolean")
54155
+ return;
54156
+ if (typeof value === "number" && Number.isFinite(value))
54157
+ return;
54158
+ if (Array.isArray(value)) {
54159
+ for (const item of value)
54160
+ assertJsonValue(item, depth + 1, budget2);
54161
+ return;
54162
+ }
54163
+ if (!plainObject(value))
54164
+ refuse4();
54165
+ for (const [key, item] of Object.entries(value)) {
54166
+ if (!key || /[\x00-\x1f\x7f]/.test(key))
54167
+ refuse4();
54168
+ assertJsonValue(item, depth + 1, budget2);
54169
+ }
54170
+ }
54171
+ function reviewedHookControls(root, read2) {
54172
+ const manifestPath = join25(root, ".codex-plugin/plugin.json"), hooksPath = join25(root, "hooks/hooks.json");
54173
+ let manifest, manifestSha256, hooks, hooksSha256;
54174
+ try {
54175
+ const manifestText = read2(manifestPath);
54176
+ manifestSha256 = hashNativeJsonControls(manifestText, "version");
54177
+ manifest = JSON.parse(manifestText);
54178
+ const hooksText = read2(hooksPath);
54179
+ hashNativeJsonControls(hooksText);
54180
+ hooksSha256 = createHash17("sha256").update(hooksText).digest("hex");
54181
+ hooks = JSON.parse(hooksText);
54182
+ } catch {
54183
+ return refuse4();
54184
+ }
54185
+ if (!plainObject(manifest) || !exactKeys(manifest, ["name", "version", "hooks"], ["description"]) || !identifier3(manifest.name) || typeof manifest.version !== "string" || manifest.version !== basename9(root) || manifest.hooks !== "./hooks/hooks.json" || manifest.description !== undefined && (typeof manifest.description !== "string" || !manifest.description.trim() || manifest.description.length > 1024))
54186
+ refuse4();
54187
+ const manifestRecord = manifest;
54188
+ if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"]) || !plainObject(hooks.hooks))
54189
+ refuse4();
54190
+ const hookTable = hooks.hooks;
54191
+ const events = Object.entries(hookTable);
54192
+ if (!events.length || events.length > 32)
54193
+ refuse4();
54194
+ for (const [event, groups] of events) {
54195
+ if (!codexHookEvents.has(event) || !Array.isArray(groups) || !groups.length || groups.length > 32)
54196
+ refuse4();
54197
+ for (const group of groups) {
54198
+ if (!plainObject(group) || !exactKeys(group, ["hooks"]) || !Array.isArray(group.hooks) || !group.hooks.length || group.hooks.length > 32)
54199
+ refuse4();
54200
+ const handlers = group.hooks;
54201
+ for (const handler of handlers) {
54202
+ 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))
54203
+ refuse4();
54204
+ assertJsonValue(handler.input);
54205
+ }
54206
+ }
54207
+ }
54208
+ return { namespace: manifestRecord.name, manifestSha256, hooksSha256 };
54209
+ }
54210
+ function reviewedHookParentProjection(cacheRoot, parents2, read2) {
54211
+ if (!Array.isArray(parents2) || parents2.length > 4096)
54212
+ refuse4();
54213
+ const seen = new Set, rows = [];
54214
+ for (const parent of parents2) {
54215
+ const remote = parent?.installation === "remote-receipt", local = parent?.installation === "local-marketplace";
54216
+ 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" || !isAbsolute15(parent.marketplaceSourcePath) || resolve15(parent.marketplaceSourcePath) !== parent.marketplaceSourcePath || basename9(parent.marketplaceSourcePath) !== "marketplace.json" || !digest(parent.marketplaceSha256)) || !remote && !local)
54217
+ refuse4();
54218
+ const parts = relative6(cacheRoot, parent.pluginParent).split(sep7);
54219
+ if (!isAbsolute15(parent.pluginParent) || resolve15(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))
54220
+ refuse4();
54221
+ seen.add(parent.pluginParent);
54222
+ const parentStat = lstatSync16(parent.pluginParent, { throwIfNoEntry: false });
54223
+ if (!parentStat?.isDirectory() || parentStat.isSymbolicLink() || realpathSync8(parent.pluginParent) !== parent.pluginParent)
54224
+ refuse4();
54225
+ const receiptPath = join25(parent.pluginParent, ".codex-remote-plugin-install.json"), receiptStat = lstatSync16(receiptPath, { throwIfNoEntry: false });
54226
+ let receiptText;
54227
+ if (remote) {
54228
+ if (!receiptStat?.isFile() || receiptStat.isSymbolicLink() || receiptStat.size > MAX_RECEIPT_BYTES)
54229
+ refuse4();
54230
+ try {
54231
+ receiptText = read2(receiptPath);
54232
+ hashNativeJsonControls(receiptText);
54233
+ const receipt = JSON.parse(receiptText);
54234
+ if (!plainObject(receipt) || !exactKeys(receipt, ["schema_version", "remote_plugin_id"]) || receipt.schema_version !== 1 || receipt.remote_plugin_id !== parent.remotePluginId)
54235
+ refuse4();
54236
+ } catch {
54237
+ refuse4();
54238
+ }
54239
+ } else {
54240
+ const marketplaceSourcePath = parent.marketplaceSourcePath ?? refuse4();
54241
+ if (receiptStat !== undefined || !parent.marketplaceSha256)
54242
+ refuse4();
54243
+ let marketplaceText;
54244
+ try {
54245
+ marketplaceText = read2(marketplaceSourcePath);
54246
+ } catch {
54247
+ return refuse4();
54248
+ }
54249
+ if (createHash17("sha256").update(marketplaceText).digest("hex") !== parent.marketplaceSha256 || !codexLocalMarketplaceMapsPlugin(marketplaceText, parts[0], parent.namespace))
54250
+ refuse4();
54251
+ }
54252
+ const materializations = readdirSync7(parent.pluginParent).filter((name) => name !== ".codex-remote-plugin-install.json").map((version) => {
54253
+ if (!/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version))
54254
+ refuse4();
54255
+ const root = join25(parent.pluginParent, version), stat = lstatSync16(root, { throwIfNoEntry: false });
54256
+ if (!stat?.isDirectory() || stat.isSymbolicLink() || realpathSync8(root) !== root)
54257
+ refuse4();
54258
+ const controls = reviewedHookControls(root, read2);
54259
+ if (controls.namespace !== parent.namespace)
54260
+ refuse4();
54261
+ return { manifestSha256: controls.manifestSha256, hooksSha256: controls.hooksSha256 };
54262
+ });
54263
+ if (!materializations.length || materializations.some((item) => canonical7(item) !== canonical7(materializations[0])))
54264
+ refuse4();
54265
+ rows.push({ ...parent, ...receiptText === undefined ? {} : { receiptSha256: createHash17("sha256").update(receiptText).digest("hex") }, ...materializations[0] });
54266
+ }
54267
+ return rows.sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.pluginParent.localeCompare(b.pluginParent));
54268
+ }
54269
+ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files, members) {
54085
54270
  const expected = new Set;
54086
54271
  const expectedMarkets = new Set;
54087
- for (const control of [...controls, ...appOnlyParents]) {
54272
+ for (const control of [...controls, ...appOnlyParents, ...hookParents]) {
54088
54273
  const rel = relative6(cacheRoot, control.pluginParent), parts = rel.split(sep7);
54089
54274
  if (resolve15(control.pluginParent) !== control.pluginParent || parts.length !== 2 || parts.some((part) => !identifier3(part)) || control.pluginId !== `${control.namespace}@${parts[0]}` || parts[1] !== control.namespace)
54090
54275
  refuse4();
@@ -54125,7 +54310,9 @@ function assertParents(cacheRoot, controls, appOnlyParents, files, members) {
54125
54310
  refuse4();
54126
54311
  if ([".app.json", ".mcp.json"].includes(base2) && (parts.length !== 4 || dirname15(member) !== root))
54127
54312
  refuse4();
54128
- if (parts.includes("hooks"))
54313
+ if ([".app.json", ".mcp.json"].includes(base2) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
54314
+ refuse4();
54315
+ if (parts.includes("hooks") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base2 !== "hooks" : base2 !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join25(cacheRoot, parts[0], parts[1]))))
54129
54316
  refuse4();
54130
54317
  }
54131
54318
  }
@@ -54235,13 +54422,14 @@ function captureCodexSemanticCacheWitness(options) {
54235
54422
  const initialTree = treeSnapshot(options.cacheRoot);
54236
54423
  const controls = controlProjection(options.controls, read2);
54237
54424
  const appOnlyParents = appOnlyParentProjection(options.cacheRoot, options.appOnlyParents ?? [], read2);
54425
+ const hookParents = reviewedHookParentProjection(options.cacheRoot, options.reviewedHookParents ?? [], read2);
54238
54426
  const skillParents = new Set(controls.map((control) => control.pluginParent));
54239
- if (appOnlyParents.some((parent) => skillParents.has(parent.pluginParent)))
54427
+ if (appOnlyParents.some((parent) => skillParents.has(parent.pluginParent)) || hookParents.some((parent) => skillParents.has(parent.pluginParent) || appOnlyParents.some((app) => app.pluginParent === parent.pluginParent)))
54240
54428
  refuse4();
54241
54429
  if (!reviewedCodexPluginCapabilitiesUnchanged(options.cacheRoot, options.controls, read2, options.rules))
54242
54430
  refuse4();
54243
54431
  const files = initialTree.files;
54244
- assertParents(options.cacheRoot, controls, appOnlyParents, files, initialTree.state.members);
54432
+ assertParents(options.cacheRoot, controls, appOnlyParents, hookParents, files, initialTree.state.members);
54245
54433
  const expectedNamesByRoot = assertSkillMaterializations(options.cacheRoot, options.controls, files, read2);
54246
54434
  const docs = files.filter((path) => path.endsWith(`${sep7}SKILL.md`));
54247
54435
  const skillRows = [];
@@ -54250,6 +54438,8 @@ function captureCodexSemanticCacheWitness(options) {
54250
54438
  const rel = relative6(options.cacheRoot, document3).split(sep7);
54251
54439
  if (appOnlyParents.some((parent) => parent.pluginParent === join25(options.cacheRoot, rel[0], rel[1])))
54252
54440
  refuse4();
54441
+ if (hookParents.some((parent) => parent.pluginParent === join25(options.cacheRoot, rel[0], rel[1])))
54442
+ refuse4();
54253
54443
  const documentStat = lstatSync16(document3, { throwIfNoEntry: false });
54254
54444
  if (!documentStat?.isFile() || documentStat.isSymbolicLink() || documentStat.size > MAX_DOCUMENT_BYTES)
54255
54445
  refuse4();
@@ -54313,7 +54503,7 @@ function captureCodexSemanticCacheWitness(options) {
54313
54503
  tracked.verify();
54314
54504
  const finalTree = treeSnapshot(options.cacheRoot);
54315
54505
  assertSameTree(initialTree.state, finalTree.state, tracked.paths());
54316
- const completeProjection = { schema: CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA, cacheRoot: options.cacheRoot, controls, appOnlyParents, skills: uniqueSkills };
54506
+ const completeProjection = { schema: CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA, cacheRoot: options.cacheRoot, controls, appOnlyParents, hookParents, skills: uniqueSkills };
54317
54507
  return { ...completeProjection, sha256: hash2(completeProjection) };
54318
54508
  } catch (error) {
54319
54509
  if (error instanceof Error && error.message.startsWith("CODEX_SEMANTIC_CACHE_WITNESS_UNSUPPORTED"))
@@ -54329,6 +54519,7 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
54329
54519
  cacheRoot: previous.cacheRoot,
54330
54520
  controls: previous.controls,
54331
54521
  appOnlyParents: previous.appOnlyParents,
54522
+ ...previous.hookParents === undefined ? {} : { hookParents: previous.hookParents },
54332
54523
  skills: previous.skills
54333
54524
  };
54334
54525
  const currentProjection = {
@@ -54336,20 +54527,41 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
54336
54527
  cacheRoot: current.cacheRoot,
54337
54528
  controls: current.controls,
54338
54529
  appOnlyParents: current.appOnlyParents,
54530
+ ...current.hookParents === undefined ? {} : { hookParents: current.hookParents },
54339
54531
  skills: current.skills
54340
54532
  };
54341
- if (hash2(priorProjection) !== previous.sha256 || hash2(currentProjection) !== current.sha256 || canonical7(priorProjection) !== canonical7(currentProjection))
54533
+ const comparable = (projection) => ({ ...projection, hookParents: projection.hookParents ?? [] });
54534
+ if (hash2(priorProjection) !== previous.sha256 || hash2(currentProjection) !== current.sha256 || canonical7(comparable(priorProjection)) !== canonical7(comparable(currentProjection)))
54342
54535
  refuse4();
54343
54536
  }
54344
54537
  var CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA = "hasna.codex-semantic-cache-witness.v1", identifier3 = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(value), digest = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value), refuse4 = () => {
54345
54538
  throw new Error("CODEX_SEMANTIC_CACHE_WITNESS_UNSUPPORTED: review Codex plugin cache identity and controls");
54346
- }, MAX_DOCUMENT_BYTES, MAX_RECEIPT_BYTES;
54539
+ }, MAX_DOCUMENT_BYTES, MAX_RECEIPT_BYTES, codexHookEvents;
54347
54540
  var init_codex_semantic_cache_witness = __esm(() => {
54348
54541
  init_claude_settings_witness();
54349
54542
  init_codex_plugin_skill_controls();
54350
54543
  init_codex_native_skill_catalog();
54351
54544
  MAX_DOCUMENT_BYTES = 1024 * 1024;
54352
54545
  MAX_RECEIPT_BYTES = 16 * 1024;
54546
+ codexHookEvents = new Set([
54547
+ "SessionStart",
54548
+ "UserPromptSubmit",
54549
+ "PreToolUse",
54550
+ "PostToolUse",
54551
+ "PostToolUseFailure",
54552
+ "Stop",
54553
+ "SubagentStart",
54554
+ "SubagentStop",
54555
+ "PreCompact",
54556
+ "Notification",
54557
+ "PermissionRequest",
54558
+ "SessionEnd",
54559
+ "ConfigChange",
54560
+ "WorktreeCreate",
54561
+ "WorktreeRemove",
54562
+ "TeammateIdle",
54563
+ "TaskComplete"
54564
+ ]);
54353
54565
  });
54354
54566
 
54355
54567
  // src/lib/managed-policy.ts
@@ -54750,6 +54962,130 @@ var init_agent_codex = __esm(() => {
54750
54962
  CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm;
54751
54963
  });
54752
54964
 
54965
+ // src/lib/hook-diagnostics.ts
54966
+ function hookChildError(stdout, stage) {
54967
+ let code;
54968
+ if (stdout.length <= 16 * 1024) {
54969
+ try {
54970
+ code = JSON.parse(stdout)?.error?.code;
54971
+ } catch {}
54972
+ }
54973
+ return new HookDiagnosticError(typeof code === "string" && CONTEXT_ERROR_CODES.has(code) ? code : "SKILLS_CONTEXT_FAILED", stage);
54974
+ }
54975
+ function hookFailureReason(error, profileId) {
54976
+ const owned = error instanceof HookDiagnosticError;
54977
+ const code = owned && (CONTEXT_ERROR_CODES.has(error.code) || ["SKILLS_HOOK_TIMEOUT", "SKILLS_HOOK_INVALID_RESPONSE"].includes(error.code)) ? error.code : "SKILLS_HOOK_FAILED";
54978
+ const profile = typeof profileId === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(profileId) && !profileId.includes("..") ? profileId : undefined;
54979
+ const identity6 = [profile ? `profile=${profile}` : undefined, owned ? `stage=${error.stage}` : undefined].filter(Boolean).join(", ");
54980
+ const prefix = `Skills context is unavailable [${code}]${identity6 ? ` (${identity6})` : ""}.`;
54981
+ if (code === "SKILLS_STORAGE_FULL") {
54982
+ return `${prefix} The filesystem is full or its quota is exhausted. Free space through your approved recoverable cleanup process, then retry the same session. Do not delete session receipts or change credentials to bypass this failure.`;
54983
+ }
54984
+ if (code === "PROFILE_LOCK_MISMATCH") {
54985
+ return `${prefix} This session or project is pinned to a different profile. Inspect the session with skills sessions show <session-id> --json; review skills sessions reconcile for an intentional session change, or explicit project sync for a project change. Sync alone does not change that pin.`;
54986
+ }
54987
+ if (code === "SESSION_RECONCILIATION_REQUIRED") {
54988
+ return `${prefix} A loaded skill changed or was removed. Inspect this session with skills sessions show <session-id> --json, then review skills sessions reconcile for an intentional change. Sync alone does not change this pin. A refusal is re-checked with the authority within five minutes, or sooner after the receipt or the synced profile revision changes.`;
54989
+ }
54990
+ if (code === "SESSION_RENEWAL_TIMEOUT") {
54991
+ return `${prefix} Renewing this session's expired authorization did not finish within the hook's renewal window; no request was refused. Sync and diagnosis do not change this window. The existing pin is unchanged and the next prompt retries the renewal. If it keeps recurring, inspect the session with skills sessions show <session-id> --json and review skills sessions reconcile.`;
54992
+ }
54993
+ if (code.startsWith("SESSION_")) {
54994
+ return `${prefix} Inspect the session with skills sessions show <session-id> --json and review its parent or concurrent writer before retrying. Do not remove or rewrite its receipt to bypass this refusal.`;
54995
+ }
54996
+ if (["PROFILE_IDENTITY_MISMATCH", "BUNDLE_DIGEST_MISMATCH", "BUNDLE_IDENTITY_MISMATCH", "INVALID_RECEIPT", "INVALID_CACHE_FILE", "UNSAFE_CACHE_PATH"].includes(code)) {
54997
+ return `${prefix} Verify the selected authority and receipt integrity before repairing the profile; do not bypass verification or substitute another cache.`;
54998
+ }
54999
+ const selected = profile ?? "<id>";
55000
+ return `${prefix} Run skills sync --selection-profile ${selected} --json, then diagnose with skills context --stdin --json --cached --selection-profile ${selected} using the same hook session and working-directory fields. Do not include credentials in diagnostic input.`;
55001
+ }
55002
+ function isOptionalHookContextFailure(error) {
55003
+ return error instanceof HookDiagnosticError && OPTIONAL_CONTEXT_FAILURES.has(error.code);
55004
+ }
55005
+ function hookUnavailableContext(error, profileId) {
55006
+ return `${hookFailureReason(error, profileId)} No Skills instructions were delivered by this hook. Ordinary work may continue. Do not perform actions that depend on unavailable Skills instructions, including previously loaded instructions. Explicit skills load, context and run still require verification. Do not substitute native, unmanaged or stale payloads.`;
55007
+ }
55008
+ var CONTEXT_ERROR_CODES, HOOK_REFUSAL_REASON_CODES, HookDiagnosticError, OPTIONAL_CONTEXT_FAILURES;
55009
+ var init_hook_diagnostics = __esm(() => {
55010
+ CONTEXT_ERROR_CODES = new Set([
55011
+ "SKILLS_API_CREDENTIAL_UNAVAILABLE",
55012
+ "SKILLS_API_UNAVAILABLE",
55013
+ "SKILLS_API_UNAUTHORIZED",
55014
+ "SKILLS_API_FORBIDDEN",
55015
+ "SKILLS_API_RESOURCE_UNAVAILABLE",
55016
+ "SESSION_WRITE_BUSY",
55017
+ "SKILLS_CONTEXT_FAILED",
55018
+ "SKILLS_STORAGE_FULL",
55019
+ "PROFILE_LOCK_MISMATCH",
55020
+ "PROFILE_IDENTITY_MISMATCH",
55021
+ "PROFILE_APPLIED_REPORT_FAILED",
55022
+ "CACHED_AUTHORITY_REQUIRED",
55023
+ "CACHED_PROFILE_EXPIRED",
55024
+ "CACHED_PROFILE_MISSING",
55025
+ "CACHED_BUNDLE_MISSING",
55026
+ "INVALID_CONTEXT_INPUT",
55027
+ "CONTEXT_INPUT_TOO_LARGE",
55028
+ "INVALID_CONTEXT_BUDGET",
55029
+ "INVALID_SELECTION",
55030
+ "INVALID_SELECTION_ALIASES",
55031
+ "INVALID_RECEIPT",
55032
+ "INVALID_CACHE_FILE",
55033
+ "UNSAFE_CACHE_PATH",
55034
+ "RECEIPT_TOO_LARGE",
55035
+ "BUNDLE_DIGEST_MISMATCH",
55036
+ "BUNDLE_IDENTITY_MISMATCH",
55037
+ "BUNDLE_UNAVAILABLE",
55038
+ "BUNDLE_TOO_LARGE",
55039
+ "SKILL_NOT_SELECTED",
55040
+ "INVALID_SESSION",
55041
+ "SESSION_PARENT_NOT_FOUND",
55042
+ "SESSION_PARENT_MISMATCH",
55043
+ "SESSION_NOT_FOUND",
55044
+ "SESSION_GENERATION_CHANGED",
55045
+ "SESSION_GENERATION_EXHAUSTED",
55046
+ "SESSION_PARENT_CHANGED",
55047
+ "SESSION_RECEIPT_CHANGED",
55048
+ "SESSION_RECONCILIATION_REQUIRED",
55049
+ "SESSION_RENEWAL_TIMEOUT",
55050
+ "SESSION_CONTEXT_CONFLICT",
55051
+ "SESSION_RECONCILIATION_INCOMPLETE",
55052
+ "SESSION_WRITE_LOCKED",
55053
+ "SESSION_WRITE_LOCK_CHANGED",
55054
+ "SESSION_LOCK_RECOVERY_INCOMPLETE"
55055
+ ]);
55056
+ HOOK_REFUSAL_REASON_CODES = Object.freeze([
55057
+ ...CONTEXT_ERROR_CODES,
55058
+ "SKILLS_HOOK_FAILED",
55059
+ "SKILLS_HOOK_TIMEOUT",
55060
+ "SKILLS_HOOK_INVALID_RESPONSE"
55061
+ ]);
55062
+ HookDiagnosticError = class HookDiagnosticError extends Error {
55063
+ code;
55064
+ stage;
55065
+ constructor(code, stage) {
55066
+ super("Skills hook operation failed");
55067
+ this.code = code;
55068
+ this.stage = stage;
55069
+ }
55070
+ };
55071
+ OPTIONAL_CONTEXT_FAILURES = new Set([
55072
+ "SKILLS_API_CREDENTIAL_UNAVAILABLE",
55073
+ "SKILLS_API_UNAVAILABLE",
55074
+ "SKILLS_API_UNAUTHORIZED",
55075
+ "SKILLS_API_FORBIDDEN",
55076
+ "SKILLS_API_RESOURCE_UNAVAILABLE",
55077
+ "CACHED_PROFILE_EXPIRED",
55078
+ "CACHED_PROFILE_MISSING",
55079
+ "CACHED_BUNDLE_MISSING",
55080
+ "SESSION_RECONCILIATION_REQUIRED",
55081
+ "SESSION_RENEWAL_TIMEOUT",
55082
+ "SESSION_WRITE_BUSY",
55083
+ "SESSION_PARENT_CHANGED",
55084
+ "SESSION_CONTEXT_CONFLICT",
55085
+ "SKILLS_HOOK_TIMEOUT"
55086
+ ]);
55087
+ });
55088
+
54753
55089
  // src/lib/agent-sumi.ts
54754
55090
  import { spawnSync as spawnSync3 } from "child_process";
54755
55091
  import { accessSync as accessSync2, constants as constants12, existsSync as existsSync12, lstatSync as lstatSync19, realpathSync as realpathSync9, statSync as statSync9 } from "fs";
@@ -54883,24 +55219,33 @@ function sumiConfigPath(home, cwd2 = process.cwd(), env3 = process.env) {
54883
55219
  return join28(sumiConfigDirectory(home, env3, cwd2), "sumi.json");
54884
55220
  }
54885
55221
  function renderSumiPlugin(command, profile) {
54886
- return renderSumiPluginVersion(command, profile, false);
55222
+ return renderSumiPluginVersion(command, profile, 3);
54887
55223
  }
54888
55224
  function isManagedSumiPlugin(text2, command, profile) {
54889
- return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, true);
55225
+ return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, 2) || text2 === renderSumiPluginVersion(command, profile, 1);
54890
55226
  }
54891
- function renderSumiPluginVersion(command, profile, legacy) {
55227
+ function renderSumiPluginVersion(command, profile, version) {
55228
+ const legacy = version === 1;
54892
55229
  const failure = (message) => legacy ? `new Error(${JSON.stringify(message)})` : "refusal()";
54893
55230
  return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
54894
55231
  import { spawn } from "node:child_process";
54895
55232
  const command = ${JSON.stringify(command)};
54896
55233
  const profile = ${JSON.stringify(profile)};
54897
- ${legacy ? "" : `function refusal() {
54898
- const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry.");
55234
+ ${legacy ? "" : `function refusal(${version === 3 ? "reasonCode" : ""}) {
55235
+ const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry."${version === 3 ? ' + (reasonCode ? " [" + reasonCode + "]" : "")' : ""});
54899
55236
  error.name = "SkillsHookRefusal";
54900
- error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED" };
55237
+ error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED"${version === 3 ? ", ...(reasonCode ? { reasonCode } : {})" : ""} };
54901
55238
  return error;
54902
55239
  }
54903
- `}function context(input) {
55240
+ `}${version === 3 ? `const reasonCodes = new Set(${JSON.stringify(HOOK_REFUSAL_REASON_CODES)});
55241
+ function refusedResult(result) {
55242
+ const reason = result.reason ?? result.stopReason ?? result.systemMessage;
55243
+ if (typeof reason !== "string" || reason.length > 16384) return refusal();
55244
+ if (reason.startsWith("NATIVE_SKILL_DRIFT:")) return refusal("NATIVE_SKILL_DRIFT");
55245
+ const code = /^Skills context is unavailable \\[([A-Z_]+)\\]/.exec(reason)?.[1];
55246
+ return refusal(reasonCodes.has(code) ? code : undefined);
55247
+ }
55248
+ ` : ""}function context(input) {
54904
55249
  return new Promise((resolve, reject) => {
54905
55250
  const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
54906
55251
  const chunks = []; let bytes = 0, failure;
@@ -54915,12 +55260,12 @@ ${legacy ? "" : `function refusal() {
54915
55260
  try {
54916
55261
  const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
54917
55262
  if (!result || typeof result !== "object" || Array.isArray(result)) throw ${failure("Invalid Skills hook response")};
54918
- if (result.decision === "block" || result.continue === false) throw ${failure("Skills prompt hook refused")};
55263
+ if (result.decision === "block" || result.continue === false) throw ${version === 3 ? "refusedResult(result)" : failure("Skills prompt hook refused")};
54919
55264
  if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw ${failure("Invalid Skills hook response")};
54920
55265
  const text = result.hookSpecificOutput?.additionalContext ?? "";
54921
55266
  if (typeof text !== "string") throw ${failure("Invalid Skills prompt context")};
54922
55267
  resolve(text);
54923
- } catch (error) { reject(${legacy ? "error" : "refusal()"}); }
55268
+ } catch (error) { reject(${legacy ? "error" : version === 3 ? 'error instanceof Error && error.name === "SkillsHookRefusal" ? error : refusal()' : "refusal()"}); }
54924
55269
  });
54925
55270
  child.stdin.end(JSON.stringify(input));
54926
55271
  });
@@ -54953,9 +55298,13 @@ export default {
54953
55298
  sessions.add(event.sessionID);
54954
55299
  }));
54955
55300
  registrations.push(await ctx.session.hook("context", async event => {
54956
- const last = event.messages.findLast(message => message.role === "user");
55301
+ ${version === 3 ? ` // Model messages also contain checkpoints, attachments and synthetic
55302
+ // user-role history, with no owner-prompt provenance. Prompt admission
55303
+ // already matched and persisted Skills; restore that verified state only.
55304
+ const prompt = "";
55305
+ ` : ` const last = event.messages.findLast(message => message.role === "user");
54957
55306
  const prompt = (last?.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\\n");
54958
- // Recheck on every primary request, including resumed and synthetic
55307
+ `} // Recheck on every primary request, including resumed and synthetic
54959
55308
  // sessions. The CLI owns optional failures versus explicit refusals.
54960
55309
  const text = await context(await input(event.sessionID, prompt, !sessions.has(event.sessionID)));
54961
55310
  if (text) event.system.push({ type: "text", text });
@@ -54968,6 +55317,7 @@ export default {
54968
55317
  }
54969
55318
  var SUMI_PATH_SELECTORS, SUMI_PATH_KEYS, SUMI_PATH_OUTPUT_LIMIT, SumiPathResolverError, SUMI_SKILL_PERMISSIONS;
54970
55319
  var init_agent_sumi = __esm(() => {
55320
+ init_hook_diagnostics();
54971
55321
  SUMI_PATH_SELECTORS = ["SUMI_HOME", "SUMI_CONFIG_DIR", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_CONFIG_HOME", "XDG_STATE_HOME"];
54972
55322
  SUMI_PATH_KEYS = ["data", "cache", "config", "state"];
54973
55323
  SUMI_PATH_OUTPUT_LIMIT = 128 * 1024;
@@ -55607,7 +55957,7 @@ var init_retained_review_artifacts = __esm(() => {
55607
55957
  import { createHash as createHash22, randomUUID as randomUUID6 } from "crypto";
55608
55958
  import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
55609
55959
  import { existsSync as existsSync13, lstatSync as lstatSync23, statSync as statSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync16, readdirSync as readdirSync9, opendirSync as opendirSync3, readlinkSync as readlinkSync7, realpathSync as realpathSync11, 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 readSync11, constants as constants16, linkSync as linkSync2 } from "fs";
55610
- import { dirname as dirname18, join as join30, resolve as resolve19, relative as relative7, isAbsolute as isAbsolute20, sep as sep9 } from "path";
55960
+ import { basename as basename10, dirname as dirname18, join as join30, resolve as resolve19, relative as relative7, isAbsolute as isAbsolute20, sep as sep9 } from "path";
55611
55961
  import { homedir as homedir3 } from "os";
55612
55962
  function nativeAgentRoot(home, agent, cwd2 = process.cwd()) {
55613
55963
  if (agent === "sumi")
@@ -56758,7 +57108,7 @@ function planAgentSettingsWitnessUpgrade(options) {
56758
57108
  return { dataDir: dataDir3, profileId: policy.profileId, changes: [{ path: join30(dataDir3, "agent-policy.json"), before: snapshot2.text, after: after2 }], nativeSkills: [], observedPolicy: { path: join30(dataDir3, "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 })) } };
56759
57109
  }
56760
57110
  function planCodexSemanticCacheWitnessUpgrade(options) {
56761
- 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)
57111
+ 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))
56762
57112
  throw new Error("Invalid Codex semantic cache upgrade request");
56763
57113
  const home = resolve19(options.home ?? homedir3()), dataDir3 = options.dataDir ?? getDataDirReadOnly();
56764
57114
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir3);
@@ -56782,14 +57132,23 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
56782
57132
  throw new Error("Native settings preimage changed");
56783
57133
  const boundedRead = (path) => new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024));
56784
57134
  const unchangedFullSource = (path) => {
56785
- 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"));
56786
- if (!source)
56787
- throw new Error("Cache upgrade lacks prior full identity source");
57135
+ 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"));
57136
+ if (sources.length !== 1)
57137
+ throw new Error("Cache upgrade requires one unambiguous prior full identity source");
57138
+ const source = sources[0];
56788
57139
  const text2 = boundedRead(path);
56789
57140
  if (sha4(text2) !== source.sha256)
56790
57141
  throw new Error("Reviewed cache identity source changed");
56791
57142
  return text2;
56792
57143
  };
57144
+ const unchangedReviewedHookSource = (path) => {
57145
+ const sources = binding.sources.filter((source) => source.path === path);
57146
+ if (!sources.length)
57147
+ throw new Error("Cache upgrade lacks prior full identity source");
57148
+ if (sources.length !== 1)
57149
+ throw new Error("Reviewed hook identity source is ambiguous");
57150
+ return unchangedFullSource(path);
57151
+ };
56793
57152
  const appOnlyParents = options.appOnlyParents.map((parent) => {
56794
57153
  const parts = relative7(cacheRoot, parent).split(sep9);
56795
57154
  if (!isAbsolute20(parent) || resolve19(parent) !== parent || parts.length !== 2 || parts.some((part) => !/^[A-Za-z0-9_-]{1,64}$/.test(part)) || controls.some((control) => control.pluginParent === parent))
@@ -56804,11 +57163,60 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
56804
57163
  unchangedFullSource(source.path);
56805
57164
  return { role: "app-only", pluginId: `${parts[1]}@${parts[0]}`, namespace: parts[1], pluginParent: parent, remotePluginId: receipt.remote_plugin_id };
56806
57165
  }).sort((a, b) => a.pluginParent.localeCompare(b.pluginParent));
56807
- const parents2 = [...new Set([...controls.map((control) => control.pluginParent), ...appOnlyParents.map((parent) => parent.pluginParent)])];
57166
+ const reviewedHookParents = (options.reviewedHookParents ?? []).map((parent) => {
57167
+ const parts = relative7(cacheRoot, parent).split(sep9);
57168
+ if (!isAbsolute20(parent) || resolve19(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))
57169
+ throw new Error("Unsupported reviewed native-hook parent role");
57170
+ const receiptPath = join30(parent, ".codex-remote-plugin-install.json"), receiptStat = lstatSync23(receiptPath, { throwIfNoEntry: false });
57171
+ let identity6;
57172
+ if (receiptStat) {
57173
+ if (!receiptStat.isFile() || receiptStat.isSymbolicLink() || receiptStat.size > 16 * 1024)
57174
+ throw new Error("Invalid reviewed native-hook install receipt");
57175
+ const receiptText = unchangedReviewedHookSource(receiptPath);
57176
+ let receipt;
57177
+ try {
57178
+ hashNativeJsonControls(receiptText);
57179
+ receipt = JSON.parse(receiptText);
57180
+ } catch {
57181
+ throw new Error("Invalid reviewed native-hook install receipt");
57182
+ }
57183
+ 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))
57184
+ throw new Error("Invalid reviewed native-hook install receipt");
57185
+ identity6 = { role: "reviewed-hook", pluginId: `${parts[1]}@${parts[0]}`, namespace: parts[1], pluginParent: parent, installation: "remote-receipt", remotePluginId: receipt.remote_plugin_id };
57186
+ } else {
57187
+ const pluginId = `${parts[1]}@${parts[0]}`;
57188
+ const marketplaceSources = binding.sources.filter((source2) => basename10(source2.path) === "marketplace.json" && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && source2.reviewArtifact === undefined && (source2.hashMode === undefined || source2.hashMode === "bytes"));
57189
+ const matches2 = marketplaceSources.flatMap((source2) => {
57190
+ const text2 = unchangedFullSource(source2.path);
57191
+ return codexLocalMarketplaceMapsPlugin(text2, parts[0], parts[1]) ? [{ source: source2, text: text2 }] : [];
57192
+ });
57193
+ if (matches2.length !== 1)
57194
+ throw new Error("Local marketplace hook parent lacks one exact enrolled marketplace source");
57195
+ const { source } = matches2[0];
57196
+ const config = Bun.TOML.parse(settings);
57197
+ if (config.plugins?.[pluginId]?.enabled !== true)
57198
+ throw new Error("Local marketplace hook parent is not enabled in the unchanged Codex settings");
57199
+ identity6 = { role: "reviewed-hook", pluginId, namespace: parts[1], pluginParent: parent, installation: "local-marketplace", marketplaceSourcePath: source.path, marketplaceSha256: source.sha256 };
57200
+ }
57201
+ const versions = readdirSync9(parent).filter((name) => name !== ".codex-remote-plugin-install.json");
57202
+ if (!versions.length || versions.length > 4096)
57203
+ throw new Error("Reviewed native-hook parent has no bounded materializations");
57204
+ for (const version of versions) {
57205
+ if (!/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version))
57206
+ throw new Error("Invalid reviewed native-hook version identity");
57207
+ const root = join30(parent, version), stat = lstatSync23(root, { throwIfNoEntry: false });
57208
+ if (!stat?.isDirectory() || stat.isSymbolicLink() || realpathSync11(root) !== root)
57209
+ throw new Error("Invalid reviewed native-hook materialization");
57210
+ unchangedReviewedHookSource(join30(root, ".codex-plugin/plugin.json"));
57211
+ unchangedReviewedHookSource(join30(root, "hooks/hooks.json"));
57212
+ }
57213
+ return identity6;
57214
+ }).sort((a, b) => a.pluginParent.localeCompare(b.pluginParent));
57215
+ const parents2 = [...new Set([...controls.map((control) => control.pluginParent), ...appOnlyParents.map((parent) => parent.pluginParent), ...reviewedHookParents.map((parent) => parent.pluginParent)])];
56808
57216
  const reviewedAlias = aliases.find((alias) => alias.agent === "codex");
56809
57217
  const hookRootAlias = reviewedAlias ? { alias: reviewedAlias.alias, target: reviewedAlias.target } : null;
56810
57218
  assertCodexSemanticCacheHookSafety(cacheRoot, parents2, configPath, hookRootAlias);
56811
- const supersededSources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents2));
57219
+ const supersededSources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents2, reviewedHookParents.map((parent) => parent.pluginParent)));
56812
57220
  for (const source of supersededSources)
56813
57221
  if ([".app.json", ".mcp.json"].includes(source.path.split(sep9).at(-1)))
56814
57222
  unchangedFullSource(source.path);
@@ -56816,7 +57224,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
56816
57224
  if (!supersededDirectories.some((directory) => directory.path === cacheRoot) || (binding.directories ?? []).some((directory) => cacheRoot.startsWith(directory.path + sep9)))
56817
57225
  throw new Error("Cache upgrade requires exact cache directory coverage without an unproved ancestor projection");
56818
57226
  const rules = Bun.TOML.parse(settings).skills?.config ?? [];
56819
- const witness = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents, rules, read: boundedRead });
57227
+ const witness = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents, reviewedHookParents, rules, read: boundedRead });
56820
57228
  if (options.expectedCacheWitnessSha256 !== undefined && witness.sha256 !== options.expectedCacheWitnessSha256)
56821
57229
  throw new Error("Reviewed semantic cache preimage changed");
56822
57230
  const replacement = { ...binding, codexDisabledPluginSkills: controls, codexSemanticCache: {
@@ -56825,6 +57233,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
56825
57233
  hookRootAlias,
56826
57234
  witness,
56827
57235
  appOnlyParents,
57236
+ reviewedHookParents,
56828
57237
  previousPolicySha256: options.expectedPolicySha256,
56829
57238
  supersededSources,
56830
57239
  supersededDirectories
@@ -56843,7 +57252,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
56843
57252
  rootAliases: aliases,
56844
57253
  managedAgentChecks: { home, agents: ["codex"] },
56845
57254
  ...policy.bridge.discoveryExecutables ? { discoveryExecutables: policy.bridge.discoveryExecutables } : {},
56846
- cacheWitnessUpgrade: { ...options, home, dataDir: dataDir3, appOnlyParents: appOnlyParents.map((parent) => parent.pluginParent) }
57255
+ cacheWitnessUpgrade: { ...options, home, dataDir: dataDir3, appOnlyParents: appOnlyParents.map((parent) => parent.pluginParent), reviewedHookParents: reviewedHookParents.map((parent) => parent.pluginParent) }
56847
57256
  };
56848
57257
  }
56849
57258
  function planReviewedArtifactMigration(options) {
@@ -57578,7 +57987,7 @@ import {
57578
57987
  writeFileSync as writeFileSync8
57579
57988
  } from "fs";
57580
57989
  import { homedir as homedir4 } from "os";
57581
- import { basename as basename9, dirname as dirname19, join as join32 } from "path";
57990
+ import { basename as basename11, dirname as dirname19, join as join32 } from "path";
57582
57991
  function isSkillsOwnershipMarker(marker) {
57583
57992
  return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
57584
57993
  }
@@ -57820,7 +58229,7 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
57820
58229
  return { action, path: skillMdPath };
57821
58230
  const parentDir = dirname19(dir);
57822
58231
  mkdirSync9(parentDir, { recursive: true });
57823
- const transactionDir = mkdtempSync(join32(parentDir, `.hasna-skills-write-${basename9(dir)}-`));
58232
+ const transactionDir = mkdtempSync(join32(parentDir, `.hasna-skills-write-${basename11(dir)}-`));
57824
58233
  const candidateDir = join32(transactionDir, "candidate");
57825
58234
  const backupDir = join32(transactionDir, "backup");
57826
58235
  const candidateSkillMdPath = join32(candidateDir, "SKILL.md");
@@ -59994,7 +60403,7 @@ var init_plugin_admission = __esm(() => {
59994
60403
 
59995
60404
  // src/lib/plugin-discovery.ts
59996
60405
  import { closeSync as closeSync16, constants as constants18, fstatSync as fstatSync16, lstatSync as lstatSync26, openSync as openSync16, opendirSync as opendirSync5, readSync as readSync13 } from "fs";
59997
- import { basename as basename10, dirname as dirname21, isAbsolute as isAbsolute23, join as join38, resolve as resolve22 } from "path";
60406
+ import { basename as basename12, dirname as dirname21, isAbsolute as isAbsolute23, join as join38, resolve as resolve22 } from "path";
59998
60407
  function validateManagedPluginWitnesses(value) {
59999
60408
  pluginNeed(Array.isArray(value) && value.length > 0 && value.length <= PLUGIN_PROJECTION_LIMITS.registrations, "Invalid managed plugin witness collection");
60000
60409
  const seen = new Set;
@@ -60077,7 +60486,7 @@ function hashManagedPluginRegistry(path, managed) {
60077
60486
  pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join38(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
60078
60487
  const approval = (producer) => {
60079
60488
  pluginText(producer);
60080
- const digest2 = `sha256:${basename10(producer)}`;
60489
+ const digest2 = `sha256:${basename12(producer)}`;
60081
60490
  const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest2);
60082
60491
  pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
60083
60492
  if (!verified.has(producer)) {
@@ -60146,7 +60555,7 @@ var init_plugin_discovery = __esm(() => {
60146
60555
  // src/lib/claude-marketplace-registry.ts
60147
60556
  import { createHash as createHash25 } from "crypto";
60148
60557
  import { closeSync as closeSync17, constants as constants19, fstatSync as fstatSync17, lstatSync as lstatSync27, openSync as openSync17, readSync as readSync14 } from "fs";
60149
- import { basename as basename11, dirname as dirname22, isAbsolute as isAbsolute24, resolve as resolve23 } from "path";
60558
+ import { basename as basename13, dirname as dirname22, isAbsolute as isAbsolute24, resolve as resolve23 } from "path";
60150
60559
  function need8(value, reason2) {
60151
60560
  if (!value)
60152
60561
  throw new Error(`Claude marketplace registry ${reason2}`);
@@ -60166,7 +60575,7 @@ function parents2(path) {
60166
60575
  }
60167
60576
  function readRegistry2(path, budget2) {
60168
60577
  absolutePath4(path);
60169
- need8(basename11(path) === "known_marketplaces.json", "requires known_marketplaces.json");
60578
+ need8(basename13(path) === "known_marketplaces.json", "requires known_marketplaces.json");
60170
60579
  need8(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
60171
60580
  const ancestors2 = parents2(path), initial2 = lstatSync27(path, { bigint: true });
60172
60581
  need8(initial2.isFile() && !initial2.isSymbolicLink() && initial2.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
@@ -60613,7 +61022,7 @@ var init_codex_hook_discovery_recovery = __esm(() => {
60613
61022
  // src/lib/agent-discovery.ts
60614
61023
  import { createHash as createHash27 } from "crypto";
60615
61024
  import { lstatSync as lstatSync28, readFileSync as readFileSync21, readdirSync as readdirSync12, realpathSync as realpathSync12 } from "fs";
60616
- import { basename as basename12, dirname as dirname23, isAbsolute as isAbsolute26, join as join39, relative as relative8, resolve as resolve24, sep as sep10 } from "path";
61025
+ import { basename as basename14, dirname as dirname23, isAbsolute as isAbsolute26, join as join39, relative as relative8, resolve as resolve24, sep as sep10 } from "path";
60617
61026
  import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
60618
61027
  function discoveryRootUnresolved(agent, command, detail) {
60619
61028
  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.`);
@@ -60693,7 +61102,7 @@ function assertReviewedClaudeCacheClosures(home, config, review, canonical9) {
60693
61102
  throw new Error("Reviewed Claude enabled plugin registration requires an object with an installPath string");
60694
61103
  }
60695
61104
  if ([cacheAliasRoot, cacheRoot].some((root2) => row.installPath === root2 || row.installPath.startsWith(root2 + sep10))) {
60696
- if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename12(row.installPath) !== row.version)) {
61105
+ if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename14(row.installPath) !== row.version)) {
60697
61106
  throw new Error("Reviewed Claude discovery has a malformed active cache registration");
60698
61107
  }
60699
61108
  if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute26(row.projectPath) && resolve24(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
@@ -60971,7 +61380,7 @@ function absentRetiredClaudeRoots(binding) {
60971
61380
  if (!targetValid || !targetScopes.has("user\x00"))
60972
61381
  continue;
60973
61382
  const active = rows[0]?.installPath;
60974
- if (typeof active !== "string" || rows[0]?.version !== basename12(active) || dirname23(active) !== dirname23(root2) || active === root2 || !lstatSync28(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync12(active) !== active)
61383
+ if (typeof active !== "string" || rows[0]?.version !== basename14(active) || dirname23(active) !== dirname23(root2) || active === root2 || !lstatSync28(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync12(active) !== active)
60975
61384
  continue;
60976
61385
  const activeManifest = full(join39(active, ".claude-plugin/plugin.json"));
60977
61386
  if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
@@ -60980,14 +61389,15 @@ function absentRetiredClaudeRoots(binding) {
60980
61389
  }
60981
61390
  return [...result2];
60982
61391
  }
60983
- function codexSemanticCacheSourceRole(source, cacheRoot, parents3) {
61392
+ function codexSemanticCacheSourceRole(source, cacheRoot, parents3, hookParents = []) {
60984
61393
  if (!isAbsolute26(source.path) || resolve24(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")
60985
61394
  return false;
60986
61395
  const parts = relative8(cacheRoot, source.path).split(sep10);
60987
61396
  if (parts.length < 3 || !parents3.includes(join39(cacheRoot, parts[0], parts[1])))
60988
61397
  return false;
60989
61398
  const tail2 = parts.slice(3).join("/");
60990
- return parts.length === 5 && tail2 === ".codex-plugin/plugin.json" && source.sha256 !== null || parts.length === 4 && [".app.json", ".mcp.json"].includes(tail2) && source.sha256 !== null || source.sha256 === null && (parts.length === 4 && tail2 === "SKILL.md" || parts.length === 5 && tail2 === "hooks/hooks.json");
61399
+ const parent = join39(cacheRoot, parts[0], parts[1]);
61400
+ return parts.length === 5 && tail2 === ".codex-plugin/plugin.json" && source.sha256 !== null || parts.length === 4 && [".app.json", ".mcp.json"].includes(tail2) && source.sha256 !== null || parts.length === 5 && tail2 === "hooks/hooks.json" && hookParents.includes(parent) && source.sha256 !== null || source.sha256 === null && (parts.length === 4 && tail2 === "SKILL.md" || parts.length === 5 && tail2 === "hooks/hooks.json");
60991
61401
  }
60992
61402
  function codexSemanticCacheDirectoryRole(path, cacheRoot) {
60993
61403
  return isAbsolute26(path) && resolve24(path) === path && !/[\x00-\x1f\x7f]/.test(path) && (path === cacheRoot || path.startsWith(cacheRoot + sep10));
@@ -61019,20 +61429,28 @@ function verifyCodexSemanticCacheDiscovery(binding, reviewedRootAliases) {
61019
61429
  const cacheRoot = proof.witness.cacheRoot;
61020
61430
  if (controls.some((control) => dirname23(dirname23(control.pluginParent)) !== cacheRoot))
61021
61431
  throw new Error("Codex semantic cache names another enrolled cache");
61022
- const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent)])];
61023
- const sources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3));
61432
+ const reviewedHookParents = proof.reviewedHookParents ?? [];
61433
+ 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 || !isDeepStrictEqual11((proof.witness.hookParents ?? []).map(({ manifestSha256: _manifest, hooksSha256: _hooks, receiptSha256: _receipt, ...parent }) => parent), reviewedHookParents)) {
61434
+ throw new Error("Invalid reviewed native-hook cache role");
61435
+ }
61436
+ for (const parent of reviewedHookParents)
61437
+ 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")))
61438
+ throw new Error("Local marketplace hook parent lost its enrolled source witness");
61439
+ const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent), ...reviewedHookParents.map((parent) => parent.pluginParent)])];
61440
+ const hookParentPaths = reviewedHookParents.map((parent) => parent.pluginParent);
61441
+ const sources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3, hookParentPaths));
61024
61442
  const directories = (binding.directories ?? []).filter((directory) => codexSemanticCacheDirectoryRole(directory.path, cacheRoot));
61025
61443
  if (!directories.length || !isDeepStrictEqual11(sources, proof.supersededSources) || !isDeepStrictEqual11(directories, proof.supersededDirectories))
61026
61444
  throw new Error("Codex semantic cache provenance changed");
61027
61445
  const configPath = proof.configPath;
61028
- if (!isAbsolute26(configPath) || resolve24(configPath) !== configPath || basename12(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
61446
+ if (!isAbsolute26(configPath) || resolve24(configPath) !== configPath || basename14(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
61029
61447
  throw new Error("Invalid semantic cache configuration source");
61030
61448
  const configSource = binding.sources.find((source) => source.path === configPath && source.sha256 !== null && source.format === undefined && source.fields === undefined);
61031
61449
  if (!configSource || projected(configSource) !== configSource.sha256)
61032
61450
  throw new Error("Codex semantic cache requires unchanged reviewed configuration");
61033
61451
  assertCodexSemanticCacheHookSafety(cacheRoot, parents3, configPath, proof.hookRootAlias);
61034
61452
  const rules = parseConfig(read2(configPath), configPath, true).skills?.config ?? [];
61035
- const current = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents: proof.appOnlyParents, rules });
61453
+ const current = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents: proof.appOnlyParents, reviewedHookParents, rules });
61036
61454
  assertCodexSemanticCacheWitnessUnchanged(proof.witness, current);
61037
61455
  return proof;
61038
61456
  }
@@ -61116,7 +61534,7 @@ function codexInstallationRoots(binding) {
61116
61534
  const config = join39(dirname23(dirname23(cache3)), "config.toml"), home = dirname23(dirname23(dirname23(cache3)));
61117
61535
  const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
61118
61536
  const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join39(home, path));
61119
- if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep10) || input.sourceRoot.startsWith(root2 + sep10)) || binding.sources.some((source) => basename12(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep10)))))
61537
+ if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep10) || input.sourceRoot.startsWith(root2 + sep10)) || binding.sources.some((source) => basename14(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep10)))))
61120
61538
  throw new Error("Native installation input overlaps configuration or loading roots");
61121
61539
  if (proof.plugins.some((input) => dirname23(dirname23(input.pluginParent)) !== cache3 || !binding.sources.some((source) => source.path === join39(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
61122
61540
  throw new Error("Missing native installation input source witness");
@@ -61128,13 +61546,13 @@ function codexInstallationRoots(binding) {
61128
61546
  });
61129
61547
  }
61130
61548
  function assertMarketplaceBinding(binding, source) {
61131
- if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename12(source.path) !== "plugin.json" || !source.path.endsWith(join39(sep10, ".claude-plugin", "plugin.json"))))
61549
+ if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename14(source.path) !== "plugin.json" || !source.path.endsWith(join39(sep10, ".claude-plugin", "plugin.json"))))
61132
61550
  throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
61133
- if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename12(source.path) !== "sumi.json"))
61551
+ if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename14(source.path) !== "sumi.json"))
61134
61552
  throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
61135
- 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" || basename12(source.path) !== "config.toml"))
61553
+ 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" || basename14(source.path) !== "config.toml"))
61136
61554
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
61137
- 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" || basename12(source.path) !== "settings.json"))
61555
+ 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" || basename14(source.path) !== "settings.json"))
61138
61556
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
61139
61557
  if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
61140
61558
  throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
@@ -61246,7 +61664,7 @@ function verifyAutomaticDiscoveryClosure(binding, options) {
61246
61664
  const current = resolveAgentDiscovery({ home: options.home, agent: binding.agent, canonical: options.canonical, executable: runtime });
61247
61665
  if (JSON.stringify(current) === JSON.stringify(binding))
61248
61666
  return;
61249
- const reviewedWithRuntime = (binding.builtinNames?.length ?? 0) > 0 || binding.sources.some((source) => basename12(source.path) === "package.json");
61667
+ const reviewedWithRuntime = (binding.builtinNames?.length ?? 0) > 0 || binding.sources.some((source) => basename14(source.path) === "package.json");
61250
61668
  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) => isDeepStrictEqual11(saved, source)));
61251
61669
  if (command && gap && runtime === null && reviewedWithRuntime && otherwiseCurrent)
61252
61670
  throw discoveryRootUnresolved(binding.agent, command, gap);
@@ -61389,7 +61807,7 @@ function resolveAgentDiscovery(options) {
61389
61807
  throw new Error("Discovery review must include the agent configuration source");
61390
61808
  const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical9(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical9(configPath), source)) : sources;
61391
61809
  const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
61392
- if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename12(source.path) !== "plugin.json" || !source.path.endsWith(join39(sep10, ".claude-plugin", "plugin.json")))
61810
+ if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename14(source.path) !== "plugin.json" || !source.path.endsWith(join39(sep10, ".claude-plugin", "plugin.json")))
61393
61811
  return source;
61394
61812
  const pluginRoot = dirname23(dirname23(source.path)), hooksPath = join39(pluginRoot, "hooks/hooks.json");
61395
61813
  const hookStat = lstatSync28(hooksPath, { throwIfNoEntry: false });
@@ -61569,7 +61987,7 @@ var init_agent_discovery = __esm(() => {
61569
61987
  // src/lib/authoring-path.ts
61570
61988
  import { lstatSync as lstatSync29, realpathSync as realpathSync13 } from "fs";
61571
61989
  import { homedir as homedir7 } from "os";
61572
- import { basename as basename13, dirname as dirname24, join as join40, resolve as resolve25, sep as sep11 } from "path";
61990
+ import { basename as basename15, dirname as dirname24, join as join40, resolve as resolve25, sep as sep11 } from "path";
61573
61991
  function pathParts(path) {
61574
61992
  return resolve25(path).split(sep11).filter(Boolean);
61575
61993
  }
@@ -61588,7 +62006,7 @@ function physicalPath(path) {
61588
62006
  let cursor = resolve25(path);
61589
62007
  const suffix = [];
61590
62008
  while (!lstatSync29(cursor, { throwIfNoEntry: false })) {
61591
- suffix.unshift(basename13(cursor));
62009
+ suffix.unshift(basename15(cursor));
61592
62010
  const parent = dirname24(cursor);
61593
62011
  if (parent === cursor)
61594
62012
  return resolve25(path);
@@ -61683,7 +62101,7 @@ import {
61683
62101
  statSync as statSync13,
61684
62102
  writeFileSync as writeFileSync10
61685
62103
  } from "fs";
61686
- import { basename as basename14, dirname as dirname25, isAbsolute as isAbsolute27, join as join41, normalize as normalize4, relative as relative9, resolve as resolve26, sep as sep12 } from "path";
62104
+ import { basename as basename16, dirname as dirname25, isAbsolute as isAbsolute27, join as join41, normalize as normalize4, relative as relative9, resolve as resolve26, sep as sep12 } from "path";
61687
62105
  function getPortableSkillsRoot(options = {}) {
61688
62106
  return resolvePortableSkillsRoot(options, true);
61689
62107
  }
@@ -61831,7 +62249,7 @@ function physicalImportPath(path) {
61831
62249
  let ancestor = resolve26(path);
61832
62250
  const suffix = [];
61833
62251
  while (!existsSync19(ancestor)) {
61834
- suffix.unshift(basename14(ancestor));
62252
+ suffix.unshift(basename16(ancestor));
61835
62253
  ancestor = dirname25(ancestor);
61836
62254
  }
61837
62255
  return join41(realpathSync14(ancestor), ...suffix);
@@ -61856,8 +62274,8 @@ function portPortableSkill(sourcePath, options = {}) {
61856
62274
  const explicitName = options.name != null;
61857
62275
  const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
61858
62276
  if (isOfficialSkillName(skillName) && !options.allowShadow) {
61859
- const sourceSlug = safeNormalizeName(basename14(absoluteSource));
61860
- const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename14(absoluteSource)}')` : ""} matches a bundled official skill.`;
62277
+ const sourceSlug = safeNormalizeName(basename16(absoluteSource));
62278
+ const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename16(absoluteSource)}')` : ""} matches a bundled official skill.`;
61861
62279
  throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
61862
62280
  }
61863
62281
  const readOnlyDestination = join41(resolvePortableSkillsRoot(options, false), skillName);
@@ -64066,13 +64484,13 @@ var require_cli_spinners = __commonJS((exports, module) => {
64066
64484
 
64067
64485
  // src/lib/auth-store.ts
64068
64486
  import { chmodSync as chmodSync2, existsSync as existsSync21, mkdirSync as mkdirSync13, readFileSync as readFileSync25, renameSync as renameSync8, statSync as statSync14, unlinkSync as unlinkSync6, writeFileSync as writeFileSync11 } from "fs";
64069
- import { basename as basename15, dirname as dirname26, join as join43 } from "path";
64487
+ import { basename as basename17, dirname as dirname26, join as join43 } from "path";
64070
64488
  function getAuthFilePath(env3 = process.env) {
64071
64489
  return skillsCredentialFilePath(env3);
64072
64490
  }
64073
64491
  function getIdentityFilePath(env3 = process.env) {
64074
64492
  const file = skillsCredentialFilePath(env3);
64075
- return join43(dirname26(file), basename15(file).replace(/^credentials/, "identity") + ".json");
64493
+ return join43(dirname26(file), basename17(file).replace(/^credentials/, "identity") + ".json");
64076
64494
  }
64077
64495
  function getAuthIdentity(env3 = process.env) {
64078
64496
  return readIdentity(env3);
@@ -86096,7 +86514,7 @@ var init_remote_auth = __esm(() => {
86096
86514
  // src/lib/sign-in.ts
86097
86515
  import { createHash as createHash30 } from "crypto";
86098
86516
  import { chmodSync as chmodSync3, mkdirSync as mkdirSync14, readFileSync as readFileSync26, readdirSync as readdirSync15, renameSync as renameSync9, unlinkSync as unlinkSync7, writeFileSync as writeFileSync12 } from "fs";
86099
- import { basename as basename16, dirname as dirname27, join as join44 } from "path";
86517
+ import { basename as basename18, dirname as dirname27, join as join44 } from "path";
86100
86518
  function isRecord7(value) {
86101
86519
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
86102
86520
  }
@@ -86241,7 +86659,7 @@ async function pollDeviceAuthorization(input2) {
86241
86659
  }
86242
86660
  function pendingDeviceSignInStem(env3) {
86243
86661
  const file2 = skillsCredentialFilePath(env3);
86244
- return { dir: dirname27(file2), stem: basename16(file2).replace(/^credentials/, "device-login") };
86662
+ return { dir: dirname27(file2), stem: basename18(file2).replace(/^credentials/, "device-login") };
86245
86663
  }
86246
86664
  function pendingDeviceSignInPath(origin, env3 = process.env) {
86247
86665
  const { dir, stem } = pendingDeviceSignInStem(env3);
@@ -113635,7 +114053,7 @@ import {
113635
114053
  renameSync as renameSync14,
113636
114054
  writeFileSync as writeFileSync24
113637
114055
  } from "fs";
113638
- import { basename as basename17, delimiter as delimiter3, dirname as dirname36, isAbsolute as isAbsolute33, join as join63, relative as relative12, resolve as resolve31, sep as sep14 } from "path";
114056
+ import { basename as basename19, delimiter as delimiter3, dirname as dirname36, isAbsolute as isAbsolute33, join as join63, relative as relative12, resolve as resolve31, sep as sep14 } from "path";
113639
114057
  function oldLauncherState(item) {
113640
114058
  if (item.oldShape === "pinned")
113641
114059
  return { shape: "pinned", runtime: item.oldRuntime, cwd: item.oldCwd, target: item.oldTarget, sha256: item.oldSha256, format: item.oldFormat, profile: item.oldProfile };
@@ -114467,7 +114885,7 @@ function inspectAliasPackageChain(home, aliasPath, oldLinkTarget, oldTarget, bin
114467
114885
  function validateAliasSource(home, name, actual) {
114468
114886
  const packageRoot = dirname36(dirname36(actual));
114469
114887
  const roots = [join63(home, ".hasna", "skills", "runtime"), join63(home, ".bun", "install"), join63(home, ".local", "lib", "node_modules")];
114470
- if (basename17(packageRoot) !== "skills" || basename17(dirname36(packageRoot)) !== "@hasna" || !roots.some((root2) => isWithin(root2, packageRoot))) {
114888
+ if (basename19(packageRoot) !== "skills" || basename19(dirname36(packageRoot)) !== "@hasna" || !roots.some((root2) => isWithin(root2, packageRoot))) {
114471
114889
  throw new Error("ALIAS_SOURCE_ROOT_UNSUPPORTED");
114472
114890
  }
114473
114891
  assertOwnedSafePath(packageRoot, home);
@@ -114562,8 +114980,8 @@ function adoptCopyfileAliases(options = {}) {
114562
114980
  for (const item of aliases) {
114563
114981
  if (!launcherIs(item.path, oldLauncherState(item)))
114564
114982
  throw new Error("ALIAS_CHANGED_BEFORE_SWITCH");
114565
- inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename17(item.path)], item.chain, true);
114566
- const source = validateAliasSource(home, basename17(item.path), item.oldTarget);
114983
+ inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename19(item.path)], item.chain, true);
114984
+ const source = validateAliasSource(home, basename19(item.path), item.oldTarget);
114567
114985
  if (source.version !== item.oldVersion || source.binarySha256 !== item.oldBinarySha256)
114568
114986
  throw new Error("ALIAS_SOURCE_DRIFT");
114569
114987
  receipt.state = "switching";
@@ -114572,11 +114990,11 @@ function adoptCopyfileAliases(options = {}) {
114572
114990
  replaceLauncher(item);
114573
114991
  if (!launcherIs(item.path, newLauncherState(item)) || !launcherIs(item.backupPath, oldLauncherState(item)))
114574
114992
  throw new Error("ALIAS_SWITCH_READBACK_MISMATCH");
114575
- inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename17(item.path)], item.chain, true);
114993
+ inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename19(item.path)], item.chain, true);
114576
114994
  options.onAliasSwitched?.(item.path);
114577
114995
  }
114578
114996
  for (const item of aliases) {
114579
- inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename17(item.path)], item.chain, true);
114997
+ inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename19(item.path)], item.chain, true);
114580
114998
  if (!launcherIs(item.backupPath, oldLauncherState(item)))
114581
114999
  throw new Error("ALIAS_BACKUP_DRIFT");
114582
115000
  }
@@ -114589,16 +115007,16 @@ function adoptCopyfileAliases(options = {}) {
114589
115007
  for (const item of [...aliases].reverse()) {
114590
115008
  try {
114591
115009
  if (!entryExists(item.backupPath)) {
114592
- inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename17(item.path)], item.chain, true);
114593
- const source2 = validateAliasSource(home, basename17(item.path), item.oldTarget);
115010
+ inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename19(item.path)], item.chain, true);
115011
+ const source2 = validateAliasSource(home, basename19(item.path), item.oldTarget);
114594
115012
  if (!entryExists(item.path) || !launcherIs(item.path, oldLauncherState(item)) || source2.version !== item.oldVersion || source2.binarySha256 !== item.oldBinarySha256)
114595
115013
  failed = true;
114596
115014
  continue;
114597
115015
  }
114598
115016
  if (!launcherIs(item.backupPath, oldLauncherState(item)))
114599
115017
  throw new Error("ALIAS_BACKUP_DRIFT");
114600
- inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename17(item.path)], item.chain, true);
114601
- const source = validateAliasSource(home, basename17(item.path), item.oldTarget);
115018
+ inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename19(item.path)], item.chain, true);
115019
+ const source = validateAliasSource(home, basename19(item.path), item.oldTarget);
114602
115020
  if (source.version !== item.oldVersion || source.binarySha256 !== item.oldBinarySha256)
114603
115021
  throw new Error("ALIAS_SOURCE_DRIFT");
114604
115022
  if (launcherIs(item.path, oldLauncherState(item)))
@@ -114651,7 +115069,7 @@ function rollbackCopyfileAliases(receiptId, options = {}) {
114651
115069
  throw new Error("ALIAS_RECEIPT_BINDING_INVALID");
114652
115070
  const seen = new Set;
114653
115071
  for (const item of receipt.aliases) {
114654
- const name = basename17(item.path), target = layout.bin[name];
115072
+ const name = basename19(item.path), target = layout.bin[name];
114655
115073
  if (!target || seen.has(item.path) || !aliasDirs(home).includes(dirname36(item.path)) || item.newTarget !== join63(layout.currentPackageRoot, target) || item.backupPath !== `${item.path}.skills-alias-prev-${receiptId}` || item.chain !== undefined && (!item.chain || typeof item.chain !== "object" || typeof item.chain.packageLinkPath !== "string" || typeof item.chain.packageLinkTarget !== "string")) {
114656
115074
  throw new Error("ALIAS_RECEIPT_LAUNCHER_INVALID");
114657
115075
  }
@@ -114707,7 +115125,7 @@ function rollbackCopyfileAliases(receiptId, options = {}) {
114707
115125
  restored++;
114708
115126
  }
114709
115127
  for (const item of receipt.aliases) {
114710
- inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename17(item.path)], item.chain, true);
115128
+ inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename19(item.path)], item.chain, true);
114711
115129
  }
114712
115130
  receipt.state = "rolled-back";
114713
115131
  persistAliasReceipt(receiptPath, receipt);
@@ -114760,8 +115178,8 @@ async function updateCopyfileRuntime(version2, options = {}) {
114760
115178
  const finalPath = join63(layout.runtimeRoot, `${version2}-copyfile`);
114761
115179
  const launcherRuntime = pinnedLauncherRuntime();
114762
115180
  const launchers = layout.launchers.map((item) => {
114763
- const newTarget = join63(finalPath, "node_modules", "@hasna", "skills", layout.bin[basename17(item.path)]);
114764
- return { ...item, newTarget, ...pinnedRecordFields(launcherRuntime, finalPath, newTarget, launcherProfileForBin(basename17(item.path), layout.bin[basename17(item.path)])) };
115181
+ const newTarget = join63(finalPath, "node_modules", "@hasna", "skills", layout.bin[basename19(item.path)]);
115182
+ return { ...item, newTarget, ...pinnedRecordFields(launcherRuntime, finalPath, newTarget, launcherProfileForBin(basename19(item.path), layout.bin[basename19(item.path)])) };
114765
115183
  });
114766
115184
  const releaseLock = acquireRuntimeLock(layout.runtimeRoot);
114767
115185
  let switched = [];
@@ -114983,11 +115401,11 @@ function rollbackCopyfileRuntime(receiptId, options = {}) {
114983
115401
  const switched = [];
114984
115402
  const seen = new Set;
114985
115403
  for (const item of receipt.launchers) {
114986
- const bin = oldBins[basename17(item.path)];
115404
+ const bin = oldBins[basename19(item.path)];
114987
115405
  if (!bin || !isAbsolute33(item.path) || seen.has(item.path) || item.oldTarget !== join63(receipt.currentPackageRoot, bin) || item.newTarget !== join63(receipt.targetPackageRoot, bin) || !/^.+\.skills-prev-[0-9a-f-]{36}$/.test(item.backupPath) || !item.backupPath.startsWith(`${item.path}.skills-prev-`) || typeof item.oldLinkTarget !== "string" || resolve31(dirname36(item.path), item.oldLinkTarget) !== item.oldTarget)
114988
115406
  throw new Error("RECEIPT_LAUNCHER_BINDING_INVALID");
114989
115407
  assertNoSymlinkAncestors(dirname36(item.path));
114990
- assertLauncherRecordShapes(item, launcherProfileForBin(basename17(item.path), bin));
115408
+ assertLauncherRecordShapes(item, launcherProfileForBin(basename19(item.path), bin));
114991
115409
  seen.add(item.path);
114992
115410
  const oldState = oldLauncherState(item), newState = newLauncherState(item);
114993
115411
  let activeTarget;
@@ -115053,7 +115471,7 @@ __export(exports_runtime, {
115053
115471
  });
115054
115472
  import { createHash as createHash39, randomUUID as randomUUID11 } from "crypto";
115055
115473
  import { lstatSync as lstatSync36, mkdirSync as mkdirSync27, readFileSync as readFileSync41, writeFileSync as writeFileSync25 } from "fs";
115056
- import { basename as basename18, isAbsolute as isAbsolute34, join as join64 } from "path";
115474
+ import { basename as basename20, isAbsolute as isAbsolute34, join as join64 } from "path";
115057
115475
  import { createInterface } from "readline";
115058
115476
  function registerRuntime(parent) {
115059
115477
  parent.command("run").argument("<skill>", "Skill name").argument("[args...]", "Arguments to pass to the skill; use -- to end Skills options").allowUnknownOption(true).passThroughOptions(true).option("--json", "Output result as JSON", false).option("--remote", "Run on the configured server, using its catalog and quote", false).option("--target <target>", "Execution target: local or cloud").option("--secret-bindings <path>", "Reviewed, exact-selection local execution vault bindings (JSON file)").option("--secret-bindings-template", "Print a scoped bindings template without resolving secrets or running the skill", false).option("--input <json>", "Structured cloud execution input").option("--skill-version <version>", "Exact selected skill version").option("--selection-profile <id>", "Selected Skills profile").option("--session <id>", "Pinned Skills session").option("--cached", "Use the explicit verified selected cache", false).option("--yes", "Approve the server's quoted credit cost for this run", false).option("--idempotency-key <key>", "Reuse this key when retrying the same remote submission").option("--file <path>", "Attach a local input file to a remote run (repeatable)", (value, prior) => [...prior, value], []).option("--wait", "Poll remote runs until a terminal status", false).option("--poll-interval-ms <ms>", "Remote polling interval in milliseconds", "1000").option("--poll-timeout-ms <ms>", "Maximum time to wait for a remote run", "300000").description("Run a skill directly").action(async (name, args2, options, command) => {
@@ -115586,7 +116004,7 @@ async function handleRun(name, args2, options, separatorBeforeSkill) {
115586
116004
  const info = lstatSync36(path);
115587
116005
  if (!info.isFile() || info.size > 20 * 1024 * 1024)
115588
116006
  throw new Error("Input must be a regular file no larger than 20 MiB");
115589
- return { name: basename18(path), bytes: new Uint8Array(readFileSync41(path)) };
116007
+ return { name: basename20(path), bytes: new Uint8Array(readFileSync41(path)) };
115590
116008
  });
115591
116009
  describeRemoteFiles(inputFiles);
115592
116010
  client2 = new RemoteSkillsClient(routing.apiKey, routing.apiOrigin);
@@ -118034,128 +118452,10 @@ var init_codex_native_trust = __esm(() => {
118034
118452
  HEX_DIGEST2 = /^[0-9a-f]{64}$/;
118035
118453
  });
118036
118454
 
118037
- // src/lib/hook-diagnostics.ts
118038
- function hookChildError(stdout, stage) {
118039
- let code2;
118040
- if (stdout.length <= 16 * 1024) {
118041
- try {
118042
- code2 = JSON.parse(stdout)?.error?.code;
118043
- } catch {}
118044
- }
118045
- return new HookDiagnosticError(typeof code2 === "string" && CONTEXT_ERROR_CODES.has(code2) ? code2 : "SKILLS_CONTEXT_FAILED", stage);
118046
- }
118047
- function hookFailureReason(error61, profileId) {
118048
- const owned = error61 instanceof HookDiagnosticError;
118049
- const code2 = owned && (CONTEXT_ERROR_CODES.has(error61.code) || ["SKILLS_HOOK_TIMEOUT", "SKILLS_HOOK_INVALID_RESPONSE"].includes(error61.code)) ? error61.code : "SKILLS_HOOK_FAILED";
118050
- const profile = typeof profileId === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(profileId) && !profileId.includes("..") ? profileId : undefined;
118051
- const identity6 = [profile ? `profile=${profile}` : undefined, owned ? `stage=${error61.stage}` : undefined].filter(Boolean).join(", ");
118052
- const prefix = `Skills context is unavailable [${code2}]${identity6 ? ` (${identity6})` : ""}.`;
118053
- if (code2 === "SKILLS_STORAGE_FULL") {
118054
- return `${prefix} The filesystem is full or its quota is exhausted. Free space through your approved recoverable cleanup process, then retry the same session. Do not delete session receipts or change credentials to bypass this failure.`;
118055
- }
118056
- if (code2 === "PROFILE_LOCK_MISMATCH") {
118057
- return `${prefix} This session or project is pinned to a different profile. Inspect the session with skills sessions show <session-id> --json; review skills sessions reconcile for an intentional session change, or explicit project sync for a project change. Sync alone does not change that pin.`;
118058
- }
118059
- if (code2 === "SESSION_RECONCILIATION_REQUIRED") {
118060
- return `${prefix} A loaded skill changed or was removed. Inspect this session with skills sessions show <session-id> --json, then review skills sessions reconcile for an intentional change. Sync alone does not change this pin. A refusal is re-checked with the authority within five minutes, or sooner after the receipt or the synced profile revision changes.`;
118061
- }
118062
- if (code2 === "SESSION_RENEWAL_TIMEOUT") {
118063
- return `${prefix} Renewing this session's expired authorization did not finish within the hook's renewal window; no request was refused. Sync and diagnosis do not change this window. The existing pin is unchanged and the next prompt retries the renewal. If it keeps recurring, inspect the session with skills sessions show <session-id> --json and review skills sessions reconcile.`;
118064
- }
118065
- if (code2.startsWith("SESSION_")) {
118066
- return `${prefix} Inspect the session with skills sessions show <session-id> --json and review its parent or concurrent writer before retrying. Do not remove or rewrite its receipt to bypass this refusal.`;
118067
- }
118068
- if (["PROFILE_IDENTITY_MISMATCH", "BUNDLE_DIGEST_MISMATCH", "BUNDLE_IDENTITY_MISMATCH", "INVALID_RECEIPT", "INVALID_CACHE_FILE", "UNSAFE_CACHE_PATH"].includes(code2)) {
118069
- return `${prefix} Verify the selected authority and receipt integrity before repairing the profile; do not bypass verification or substitute another cache.`;
118070
- }
118071
- const selected = profile ?? "<id>";
118072
- return `${prefix} Run skills sync --selection-profile ${selected} --json, then diagnose with skills context --stdin --json --cached --selection-profile ${selected} using the same hook session and working-directory fields. Do not include credentials in diagnostic input.`;
118073
- }
118074
- function isOptionalHookContextFailure(error61) {
118075
- return error61 instanceof HookDiagnosticError && OPTIONAL_CONTEXT_FAILURES.has(error61.code);
118076
- }
118077
- function hookUnavailableContext(error61, profileId) {
118078
- return `${hookFailureReason(error61, profileId)} No Skills instructions were delivered by this hook. Ordinary work may continue. Do not perform actions that depend on unavailable Skills instructions, including previously loaded instructions. Explicit skills load, context and run still require verification. Do not substitute native, unmanaged or stale payloads.`;
118079
- }
118080
- var CONTEXT_ERROR_CODES, HookDiagnosticError, OPTIONAL_CONTEXT_FAILURES;
118081
- var init_hook_diagnostics = __esm(() => {
118082
- CONTEXT_ERROR_CODES = new Set([
118083
- "SKILLS_API_CREDENTIAL_UNAVAILABLE",
118084
- "SKILLS_API_UNAVAILABLE",
118085
- "SKILLS_API_UNAUTHORIZED",
118086
- "SKILLS_API_FORBIDDEN",
118087
- "SKILLS_API_RESOURCE_UNAVAILABLE",
118088
- "SESSION_WRITE_BUSY",
118089
- "SKILLS_CONTEXT_FAILED",
118090
- "SKILLS_STORAGE_FULL",
118091
- "PROFILE_LOCK_MISMATCH",
118092
- "PROFILE_IDENTITY_MISMATCH",
118093
- "PROFILE_APPLIED_REPORT_FAILED",
118094
- "CACHED_AUTHORITY_REQUIRED",
118095
- "CACHED_PROFILE_EXPIRED",
118096
- "CACHED_PROFILE_MISSING",
118097
- "CACHED_BUNDLE_MISSING",
118098
- "INVALID_CONTEXT_INPUT",
118099
- "CONTEXT_INPUT_TOO_LARGE",
118100
- "INVALID_CONTEXT_BUDGET",
118101
- "INVALID_SELECTION",
118102
- "INVALID_SELECTION_ALIASES",
118103
- "INVALID_RECEIPT",
118104
- "INVALID_CACHE_FILE",
118105
- "UNSAFE_CACHE_PATH",
118106
- "RECEIPT_TOO_LARGE",
118107
- "BUNDLE_DIGEST_MISMATCH",
118108
- "BUNDLE_IDENTITY_MISMATCH",
118109
- "BUNDLE_UNAVAILABLE",
118110
- "BUNDLE_TOO_LARGE",
118111
- "SKILL_NOT_SELECTED",
118112
- "INVALID_SESSION",
118113
- "SESSION_PARENT_NOT_FOUND",
118114
- "SESSION_PARENT_MISMATCH",
118115
- "SESSION_NOT_FOUND",
118116
- "SESSION_GENERATION_CHANGED",
118117
- "SESSION_GENERATION_EXHAUSTED",
118118
- "SESSION_PARENT_CHANGED",
118119
- "SESSION_RECEIPT_CHANGED",
118120
- "SESSION_RECONCILIATION_REQUIRED",
118121
- "SESSION_RENEWAL_TIMEOUT",
118122
- "SESSION_CONTEXT_CONFLICT",
118123
- "SESSION_RECONCILIATION_INCOMPLETE",
118124
- "SESSION_WRITE_LOCKED",
118125
- "SESSION_WRITE_LOCK_CHANGED",
118126
- "SESSION_LOCK_RECOVERY_INCOMPLETE"
118127
- ]);
118128
- HookDiagnosticError = class HookDiagnosticError extends Error {
118129
- code;
118130
- stage;
118131
- constructor(code2, stage) {
118132
- super("Skills hook operation failed");
118133
- this.code = code2;
118134
- this.stage = stage;
118135
- }
118136
- };
118137
- OPTIONAL_CONTEXT_FAILURES = new Set([
118138
- "SKILLS_API_CREDENTIAL_UNAVAILABLE",
118139
- "SKILLS_API_UNAVAILABLE",
118140
- "SKILLS_API_UNAUTHORIZED",
118141
- "SKILLS_API_FORBIDDEN",
118142
- "SKILLS_API_RESOURCE_UNAVAILABLE",
118143
- "CACHED_PROFILE_EXPIRED",
118144
- "CACHED_PROFILE_MISSING",
118145
- "CACHED_BUNDLE_MISSING",
118146
- "SESSION_RECONCILIATION_REQUIRED",
118147
- "SESSION_RENEWAL_TIMEOUT",
118148
- "SESSION_WRITE_BUSY",
118149
- "SESSION_PARENT_CHANGED",
118150
- "SESSION_CONTEXT_CONFLICT",
118151
- "SKILLS_HOOK_TIMEOUT"
118152
- ]);
118153
- });
118154
-
118155
118455
  // src/lib/claude-prospective-review.ts
118156
118456
  import { createHash as createHash43 } from "crypto";
118157
118457
  import { lstatSync as lstatSync41, readFileSync as readFileSync45, readdirSync as readdirSync25, realpathSync as realpathSync22 } from "fs";
118158
- import { basename as basename19, dirname as dirname42, isAbsolute as isAbsolute38, join as join71, relative as relative14, resolve as resolve37, sep as sep17 } from "path";
118458
+ import { basename as basename21, dirname as dirname42, isAbsolute as isAbsolute38, join as join71, relative as relative14, resolve as resolve37, sep as sep17 } from "path";
118159
118459
  import { isDeepStrictEqual as isDeepStrictEqual18 } from "util";
118160
118460
  function hashClaudeDiscoveryBinding(binding) {
118161
118461
  return sha8(JSON.stringify(binding));
@@ -118172,7 +118472,7 @@ function object11(value) {
118172
118472
  function hasOwn2(value, key) {
118173
118473
  return object11(value) && Object.hasOwn(value, key);
118174
118474
  }
118175
- function assertJsonValue(value, name, depth = 0) {
118475
+ function assertJsonValue2(value, name, depth = 0) {
118176
118476
  if (depth > 32)
118177
118477
  refuse8(`${name} exceeds the JSON nesting bound`);
118178
118478
  if (value === null || typeof value === "string" || typeof value === "boolean")
@@ -118184,12 +118484,12 @@ function assertJsonValue(value, name, depth = 0) {
118184
118484
  }
118185
118485
  if (Array.isArray(value)) {
118186
118486
  for (const item of value)
118187
- assertJsonValue(item, name, depth + 1);
118487
+ assertJsonValue2(item, name, depth + 1);
118188
118488
  return;
118189
118489
  }
118190
118490
  if (object11(value)) {
118191
118491
  for (const item of Object.values(value))
118192
- assertJsonValue(item, name, depth + 1);
118492
+ assertJsonValue2(item, name, depth + 1);
118193
118493
  return;
118194
118494
  }
118195
118495
  refuse8(`${name} contains a non-JSON value`);
@@ -118480,13 +118780,13 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
118480
118780
  pluginNames.add(selected.plugin);
118481
118781
  if (!object11(selected.optionsPatch))
118482
118782
  refuse8("selected plugin optionsPatch must be a JSON object");
118483
- assertJsonValue(selected.optionsPatch, "selected plugin optionsPatch");
118783
+ assertJsonValue2(selected.optionsPatch, "selected plugin optionsPatch");
118484
118784
  if (selected.optionsPatch.enabled !== true)
118485
118785
  refuse8("selected plugin optionsPatch must explicitly declare enabled: true");
118486
118786
  if (Buffer.byteLength(canonicalJson3(selected.optionsPatch), "utf8") > 16 * 1024)
118487
118787
  refuse8("selected plugin optionsPatch exceeds its byte bound");
118488
118788
  abs(selected.manifestPath, "manifest path");
118489
- if (!selected.manifestPath.startsWith(candidate2.root + sep17) || basename19(dirname42(selected.manifestPath)) !== ".claude-plugin" || selected.manifestPath !== join71(dirname42(dirname42(selected.manifestPath)), ".claude-plugin", "plugin.json"))
118789
+ if (!selected.manifestPath.startsWith(candidate2.root + sep17) || basename21(dirname42(selected.manifestPath)) !== ".claude-plugin" || selected.manifestPath !== join71(dirname42(dirname42(selected.manifestPath)), ".claude-plugin", "plugin.json"))
118490
118790
  refuse8("plugin manifest must be under a plugin root inside the frozen marketplace closure");
118491
118791
  const pluginRoot2 = dirname42(dirname42(selected.manifestPath));
118492
118792
  const relativePluginRoot = relative14(candidate2.root, pluginRoot2);
@@ -119229,13 +119529,13 @@ function registerAgentIntegration(parent) {
119229
119529
  process.exitCode = 1;
119230
119530
  }
119231
119531
  });
119232
- hook.command("rebind-cache").requiredOption("--agent <agent>", "codex").requiredOption("--expected-policy-sha256 <sha256>", "Exact current managed policy bytes").requiredOption("--expected-settings-sha256 <sha256>", "Exact current Codex configuration bytes").option("--app-only-parent <path>", "Explicit canonical remote cache parent with app capabilities and no Skill docs", (value, previous) => [...previous, value], []).option("--expected-cache-witness-sha256 <sha256>", "Exact semantic cache digest from the reviewed preview; required with --apply").option("--apply", "Apply the reviewed policy-only cache witness migration", false).option("--json", "Return metadata-only preview or receipt", false).description("Explicitly migrate an enrolled Codex cache to semantic skill and capability identity").action(async (options) => {
119532
+ hook.command("rebind-cache").requiredOption("--agent <agent>", "codex").requiredOption("--expected-policy-sha256 <sha256>", "Exact current managed policy bytes").requiredOption("--expected-settings-sha256 <sha256>", "Exact current Codex configuration bytes").option("--app-only-parent <path>", "Explicit canonical remote cache parent with app capabilities and no Skill docs", (value, previous) => [...previous, value], []).option("--reviewed-hook-parent <path>", "Explicit canonical cache parent with already-reviewed native mcp_tool hooks", (value, previous) => [...previous, value], []).option("--expected-cache-witness-sha256 <sha256>", "Exact semantic cache digest from the reviewed preview; required with --apply").option("--apply", "Apply the reviewed policy-only cache witness migration", false).option("--json", "Return metadata-only preview or receipt", false).description("Explicitly migrate an enrolled Codex cache to semantic skill and capability identity").action(async (options) => {
119233
119533
  try {
119234
119534
  if (options.agent !== "codex")
119235
119535
  throw new Error("Cache witness rebind accepts codex only");
119236
119536
  if (options.apply && options.expectedCacheWitnessSha256 === undefined)
119237
119537
  throw new Error("Cache witness apply requires --expected-cache-witness-sha256 from an exact reviewed preview");
119238
- const plan = planCodexSemanticCacheWitnessUpgrade({ agent: "codex", appOnlyParents: options.appOnlyParent, expectedPolicySha256: options.expectedPolicySha256, expectedSettingsSha256: options.expectedSettingsSha256, ...options.expectedCacheWitnessSha256 !== undefined ? { expectedCacheWitnessSha256: options.expectedCacheWitnessSha256 } : {} });
119538
+ const plan = planCodexSemanticCacheWitnessUpgrade({ agent: "codex", appOnlyParents: options.appOnlyParent, reviewedHookParents: options.reviewedHookParent, expectedPolicySha256: options.expectedPolicySha256, expectedSettingsSha256: options.expectedSettingsSha256, ...options.expectedCacheWitnessSha256 !== undefined ? { expectedCacheWitnessSha256: options.expectedCacheWitnessSha256 } : {} });
119239
119539
  const result2 = options.apply ? applyAgentIntegration(plan) : { changed: [], backups: [] };
119240
119540
  const proof = plan.discoveryAfter[0].codexSemanticCache;
119241
119541
  await writeCliOutput(options.json ? JSON.stringify({ applied: options.apply, cacheWitnessUpgrade: plan.cacheWitnessUpgrade, witness: proof.witness, previousDirectories: proof.supersededDirectories, previousSources: proof.supersededSources, ...result2 }) : `Codex semantic cache witness ${options.apply ? "upgraded" : "planned"}; native configuration and cache were not changed.`);
@@ -120833,7 +121133,7 @@ var init_registry_sync = __esm(() => {
120833
121133
 
120834
121134
  // src/lib/review-export.ts
120835
121135
  import { constants as constants30, closeSync as closeSync31, existsSync as existsSync38, fstatSync as fstatSync31, fsyncSync as fsyncSync10, lstatSync as lstatSync45, openSync as openSync31, realpathSync as realpathSync24, statSync as statSync25, writeFileSync as writeFileSync33 } from "fs";
120836
- import { basename as basename20, dirname as dirname47, isAbsolute as isAbsolute43, join as join74, parse as parse13, resolve as resolve41, sep as sep21 } from "path";
121136
+ import { basename as basename22, dirname as dirname47, isAbsolute as isAbsolute43, join as join74, parse as parse13, resolve as resolve41, sep as sep21 } from "path";
120837
121137
  function exactVersion(spec) {
120838
121138
  const at2 = spec.indexOf("@");
120839
121139
  const slug2 = spec.slice(0, at2);
@@ -120844,7 +121144,7 @@ function exactVersion(spec) {
120844
121144
  return { slug: slug2, version: version2 };
120845
121145
  }
120846
121146
  function assertPrivateDestination(output2) {
120847
- if (!isAbsolute43(output2) || resolve41(output2) !== output2 || !basename20(output2).endsWith(".tar.gz")) {
121147
+ if (!isAbsolute43(output2) || resolve41(output2) !== output2 || !basename22(output2).endsWith(".tar.gz")) {
120848
121148
  throw new Error("Review output must be an absolute, normalized .tar.gz path.");
120849
121149
  }
120850
121150
  const parent = dirname47(output2);