@hasna/skills 0.10.64 → 0.10.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sdk/index.js CHANGED
@@ -32371,7 +32371,7 @@ var init_event_sink = __esm(() => {
32371
32371
  init_dist();
32372
32372
  });
32373
32373
  // package.json
32374
- var version = "0.10.64";
32374
+ var version = "0.10.66";
32375
32375
  var package_default = {
32376
32376
  name: "@hasna/skills",
32377
32377
  version,
@@ -38326,7 +38326,7 @@ import {
38326
38326
  writeFileSync as writeFileSync8
38327
38327
  } from "fs";
38328
38328
  import { homedir as homedir5 } from "os";
38329
- import { basename as basename13, dirname as dirname25, join as join35 } from "path";
38329
+ import { basename as basename15, dirname as dirname25, join as join35 } from "path";
38330
38330
 
38331
38331
  // src/lib/sumi-settings-witness.ts
38332
38332
  import { createHash as createHash8 } from "crypto";
@@ -40671,7 +40671,7 @@ function upgradeCodexSettingsWitnessV5(previous, reviewedSettingsPath) {
40671
40671
  // src/lib/codex-semantic-cache-witness.ts
40672
40672
  import { createHash as createHash13 } from "crypto";
40673
40673
  import { constants as constants9, closeSync as closeSync12, fstatSync as fstatSync12, lstatSync as lstatSync10, openSync as openSync12, readSync as readSync6, readdirSync as readdirSync4, realpathSync as realpathSync9 } from "fs";
40674
- import { dirname as dirname9, isAbsolute as isAbsolute8, join as join15, relative as relative6, resolve as resolve8, sep as sep5 } from "path";
40674
+ import { basename as basename5, dirname as dirname9, isAbsolute as isAbsolute8, join as join15, relative as relative6, resolve as resolve8, sep as sep5 } from "path";
40675
40675
  var CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA = "hasna.codex-semantic-cache-witness.v1";
40676
40676
  var identifier4 = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(value);
40677
40677
  var digest2 = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
@@ -40680,6 +40680,24 @@ var refuse5 = () => {
40680
40680
  };
40681
40681
  var MAX_DOCUMENT_BYTES = 1024 * 1024;
40682
40682
  var MAX_RECEIPT_BYTES = 16 * 1024;
40683
+ function codexLocalMarketplaceMapsPlugin(text, marketplace, plugin) {
40684
+ try {
40685
+ hashNativeJsonControls(text);
40686
+ const value = JSON.parse(text);
40687
+ if (!plainObject(value) || !exactKeys2(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)
40688
+ return false;
40689
+ const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
40690
+ if (rows.length !== 1)
40691
+ return false;
40692
+ const row = rows[0];
40693
+ if (!exactKeys2(row, ["name", "source"]) || !plainObject(row.source) || !exactKeys2(row.source, ["source", "path"]) || row.source.source !== "local" || typeof row.source.path !== "string" || !row.source.path.startsWith("./") || row.source.path.includes("\\"))
40694
+ return false;
40695
+ const parts = row.source.path.split("/");
40696
+ 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;
40697
+ } catch {
40698
+ return false;
40699
+ }
40700
+ }
40683
40701
  function defaultRead(path) {
40684
40702
  const before = lstatSync10(path, { throwIfNoEntry: false, bigint: true });
40685
40703
  if (before === undefined)
@@ -40741,7 +40759,7 @@ function validPluginParent(cacheRoot, pluginParent, namespace, pluginId) {
40741
40759
  return parts.length === 2 && parts.every(identifier4) && parts[1] === namespace && pluginId === `${namespace}@${parts[0]}`;
40742
40760
  }
40743
40761
  function assertWitnessShape(value) {
40744
- if (!plainObject(value) || !exactKeys2(value, ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256"]) || value.schema !== CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA || typeof value.cacheRoot !== "string" || !isAbsolute8(value.cacheRoot) || resolve8(value.cacheRoot) !== value.cacheRoot || !digest2(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)
40762
+ if (!plainObject(value) || !exactKeys2(value, ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256"], ["hookParents"]) || value.schema !== CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA || typeof value.cacheRoot !== "string" || !isAbsolute8(value.cacheRoot) || resolve8(value.cacheRoot) !== value.cacheRoot || !digest2(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)
40745
40763
  refuse5();
40746
40764
  const witness = value;
40747
40765
  const controlParents = new Set;
@@ -40762,6 +40780,20 @@ function assertWitnessShape(value) {
40762
40780
  refuse5();
40763
40781
  appOnlyParentPaths.add(row.pluginParent);
40764
40782
  }
40783
+ const hookParentPaths = new Set;
40784
+ const hookParents = witness.hookParents === undefined ? [] : witness.hookParents;
40785
+ if (!Array.isArray(hookParents) || hookParents.length > 4096)
40786
+ refuse5();
40787
+ for (const candidate of hookParents) {
40788
+ if (!plainObject(candidate))
40789
+ refuse5();
40790
+ const row = candidate;
40791
+ const remote = row.installation === "remote-receipt";
40792
+ const local = row.installation === "local-marketplace";
40793
+ if (!exactKeys2(row, ["role", "pluginId", "namespace", "pluginParent", "installation", "manifestSha256", "hooksSha256"], remote ? ["remotePluginId", "receiptSha256"] : local ? ["marketplaceSourcePath", "marketplaceSha256"] : []) || row.role !== "reviewed-hook" || !identifier4(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) || !digest2(row.receiptSha256)) || local && (typeof row.marketplaceSourcePath !== "string" || !isAbsolute8(row.marketplaceSourcePath) || resolve8(row.marketplaceSourcePath) !== row.marketplaceSourcePath || basename5(row.marketplaceSourcePath) !== "marketplace.json" || !digest2(row.marketplaceSha256)) || !remote && !local || !digest2(row.manifestSha256) || !digest2(row.hooksSha256) || hookParentPaths.has(row.pluginParent) || controlParents.has(row.pluginParent) || appOnlyParentPaths.has(row.pluginParent))
40794
+ refuse5();
40795
+ hookParentPaths.add(row.pluginParent);
40796
+ }
40765
40797
  const skillNames = new Set;
40766
40798
  const qualifiedNames = new Set;
40767
40799
  for (const candidate of witness.skills) {
@@ -40781,6 +40813,7 @@ function assertWitnessShape(value) {
40781
40813
  cacheRoot: witness.cacheRoot,
40782
40814
  controls: witness.controls,
40783
40815
  appOnlyParents: witness.appOnlyParents,
40816
+ ...witness.hookParents === undefined ? {} : { hookParents: witness.hookParents },
40784
40817
  skills: witness.skills
40785
40818
  };
40786
40819
  if (hash2(projection) !== witness.sha256)
@@ -40971,10 +41004,147 @@ function appOnlyParentProjection(cacheRoot, parents2, read) {
40971
41004
  }
40972
41005
  return rows.sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.pluginParent.localeCompare(b.pluginParent));
40973
41006
  }
40974
- function assertParents(cacheRoot, controls, appOnlyParents, files, members) {
41007
+ var codexHookEvents = new Set([
41008
+ "SessionStart",
41009
+ "UserPromptSubmit",
41010
+ "PreToolUse",
41011
+ "PostToolUse",
41012
+ "PostToolUseFailure",
41013
+ "Stop",
41014
+ "SubagentStart",
41015
+ "SubagentStop",
41016
+ "PreCompact",
41017
+ "Notification",
41018
+ "PermissionRequest",
41019
+ "SessionEnd",
41020
+ "ConfigChange",
41021
+ "WorktreeCreate",
41022
+ "WorktreeRemove",
41023
+ "TeammateIdle",
41024
+ "TaskComplete"
41025
+ ]);
41026
+ function assertJsonValue(value, depth = 0, budget2 = { members: 0 }) {
41027
+ if (depth > 32 || ++budget2.members > 4096)
41028
+ refuse5();
41029
+ if (value === null || typeof value === "string" || typeof value === "boolean")
41030
+ return;
41031
+ if (typeof value === "number" && Number.isFinite(value))
41032
+ return;
41033
+ if (Array.isArray(value)) {
41034
+ for (const item of value)
41035
+ assertJsonValue(item, depth + 1, budget2);
41036
+ return;
41037
+ }
41038
+ if (!plainObject(value))
41039
+ refuse5();
41040
+ for (const [key, item] of Object.entries(value)) {
41041
+ if (!key || /[\x00-\x1f\x7f]/.test(key))
41042
+ refuse5();
41043
+ assertJsonValue(item, depth + 1, budget2);
41044
+ }
41045
+ }
41046
+ function reviewedHookControls(root, read) {
41047
+ const manifestPath = join15(root, ".codex-plugin/plugin.json"), hooksPath = join15(root, "hooks/hooks.json");
41048
+ let manifest, manifestSha256, hooks, hooksSha256;
41049
+ try {
41050
+ const manifestText = read(manifestPath);
41051
+ manifestSha256 = hashNativeJsonControls(manifestText, "version");
41052
+ manifest = JSON.parse(manifestText);
41053
+ const hooksText = read(hooksPath);
41054
+ hashNativeJsonControls(hooksText);
41055
+ hooksSha256 = createHash13("sha256").update(hooksText).digest("hex");
41056
+ hooks = JSON.parse(hooksText);
41057
+ } catch {
41058
+ return refuse5();
41059
+ }
41060
+ if (!plainObject(manifest) || !exactKeys2(manifest, ["name", "version", "hooks"], ["description"]) || !identifier4(manifest.name) || typeof manifest.version !== "string" || manifest.version !== basename5(root) || manifest.hooks !== "./hooks/hooks.json" || manifest.description !== undefined && (typeof manifest.description !== "string" || !manifest.description.trim() || manifest.description.length > 1024))
41061
+ refuse5();
41062
+ const manifestRecord = manifest;
41063
+ if (!plainObject(hooks) || !exactKeys2(hooks, ["hooks"]) || !plainObject(hooks.hooks))
41064
+ refuse5();
41065
+ const hookTable = hooks.hooks;
41066
+ const events = Object.entries(hookTable);
41067
+ if (!events.length || events.length > 32)
41068
+ refuse5();
41069
+ for (const [event, groups] of events) {
41070
+ if (!codexHookEvents.has(event) || !Array.isArray(groups) || !groups.length || groups.length > 32)
41071
+ refuse5();
41072
+ for (const group of groups) {
41073
+ if (!plainObject(group) || !exactKeys2(group, ["hooks"]) || !Array.isArray(group.hooks) || !group.hooks.length || group.hooks.length > 32)
41074
+ refuse5();
41075
+ const handlers = group.hooks;
41076
+ for (const handler of handlers) {
41077
+ if (!plainObject(handler) || !exactKeys2(handler, ["type", "server", "tool", "input"], ["timeout"]) || handler.type !== "mcp_tool" || !identifier4(handler.server) || !identifier4(handler.tool) || !plainObject(handler.input) || handler.timeout !== undefined && (!Number.isSafeInteger(handler.timeout) || Number(handler.timeout) < 1 || Number(handler.timeout) > 300))
41078
+ refuse5();
41079
+ assertJsonValue(handler.input);
41080
+ }
41081
+ }
41082
+ }
41083
+ return { namespace: manifestRecord.name, manifestSha256, hooksSha256 };
41084
+ }
41085
+ function reviewedHookParentProjection(cacheRoot, parents2, read) {
41086
+ if (!Array.isArray(parents2) || parents2.length > 4096)
41087
+ refuse5();
41088
+ const seen = new Set, rows = [];
41089
+ for (const parent of parents2) {
41090
+ const remote = parent?.installation === "remote-receipt", local = parent?.installation === "local-marketplace";
41091
+ if (!parent || !exactKeys2(parent, ["installation", "namespace", "pluginId", "pluginParent", "role"], remote ? ["remotePluginId"] : local ? ["marketplaceSourcePath", "marketplaceSha256"] : []) || parent.role !== "reviewed-hook" || !identifier4(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" || !isAbsolute8(parent.marketplaceSourcePath) || resolve8(parent.marketplaceSourcePath) !== parent.marketplaceSourcePath || basename5(parent.marketplaceSourcePath) !== "marketplace.json" || !digest2(parent.marketplaceSha256)) || !remote && !local)
41092
+ refuse5();
41093
+ const parts = relative6(cacheRoot, parent.pluginParent).split(sep5);
41094
+ if (!isAbsolute8(parent.pluginParent) || resolve8(parent.pluginParent) !== parent.pluginParent || parts.length !== 2 || parts.some((part) => !identifier4(part)) || parts[1] !== parent.namespace || parent.pluginId !== `${parent.namespace}@${parts[0]}` || seen.has(parent.pluginParent))
41095
+ refuse5();
41096
+ seen.add(parent.pluginParent);
41097
+ const parentStat = lstatSync10(parent.pluginParent, { throwIfNoEntry: false });
41098
+ if (!parentStat?.isDirectory() || parentStat.isSymbolicLink() || realpathSync9(parent.pluginParent) !== parent.pluginParent)
41099
+ refuse5();
41100
+ const receiptPath = join15(parent.pluginParent, ".codex-remote-plugin-install.json"), receiptStat = lstatSync10(receiptPath, { throwIfNoEntry: false });
41101
+ let receiptText;
41102
+ if (remote) {
41103
+ if (!receiptStat?.isFile() || receiptStat.isSymbolicLink() || receiptStat.size > MAX_RECEIPT_BYTES)
41104
+ refuse5();
41105
+ try {
41106
+ receiptText = read(receiptPath);
41107
+ hashNativeJsonControls(receiptText);
41108
+ const receipt = JSON.parse(receiptText);
41109
+ if (!plainObject(receipt) || !exactKeys2(receipt, ["schema_version", "remote_plugin_id"]) || receipt.schema_version !== 1 || receipt.remote_plugin_id !== parent.remotePluginId)
41110
+ refuse5();
41111
+ } catch {
41112
+ refuse5();
41113
+ }
41114
+ } else {
41115
+ const marketplaceSourcePath = parent.marketplaceSourcePath ?? refuse5();
41116
+ if (receiptStat !== undefined || !parent.marketplaceSha256)
41117
+ refuse5();
41118
+ let marketplaceText;
41119
+ try {
41120
+ marketplaceText = read(marketplaceSourcePath);
41121
+ } catch {
41122
+ return refuse5();
41123
+ }
41124
+ if (createHash13("sha256").update(marketplaceText).digest("hex") !== parent.marketplaceSha256 || !codexLocalMarketplaceMapsPlugin(marketplaceText, parts[0], parent.namespace))
41125
+ refuse5();
41126
+ }
41127
+ const materializations = readdirSync4(parent.pluginParent).filter((name) => name !== ".codex-remote-plugin-install.json").map((version2) => {
41128
+ if (!/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2))
41129
+ refuse5();
41130
+ const root = join15(parent.pluginParent, version2), stat = lstatSync10(root, { throwIfNoEntry: false });
41131
+ if (!stat?.isDirectory() || stat.isSymbolicLink() || realpathSync9(root) !== root)
41132
+ refuse5();
41133
+ const controls = reviewedHookControls(root, read);
41134
+ if (controls.namespace !== parent.namespace)
41135
+ refuse5();
41136
+ return { manifestSha256: controls.manifestSha256, hooksSha256: controls.hooksSha256 };
41137
+ });
41138
+ if (!materializations.length || materializations.some((item) => canonical4(item) !== canonical4(materializations[0])))
41139
+ refuse5();
41140
+ rows.push({ ...parent, ...receiptText === undefined ? {} : { receiptSha256: createHash13("sha256").update(receiptText).digest("hex") }, ...materializations[0] });
41141
+ }
41142
+ return rows.sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.pluginParent.localeCompare(b.pluginParent));
41143
+ }
41144
+ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files, members) {
40975
41145
  const expected = new Set;
40976
41146
  const expectedMarkets = new Set;
40977
- for (const control of [...controls, ...appOnlyParents]) {
41147
+ for (const control of [...controls, ...appOnlyParents, ...hookParents]) {
40978
41148
  const rel = relative6(cacheRoot, control.pluginParent), parts = rel.split(sep5);
40979
41149
  if (resolve8(control.pluginParent) !== control.pluginParent || parts.length !== 2 || parts.some((part) => !identifier4(part)) || control.pluginId !== `${control.namespace}@${parts[0]}` || parts[1] !== control.namespace)
40980
41150
  refuse5();
@@ -41015,7 +41185,9 @@ function assertParents(cacheRoot, controls, appOnlyParents, files, members) {
41015
41185
  refuse5();
41016
41186
  if ([".app.json", ".mcp.json"].includes(base) && (parts.length !== 4 || dirname9(member) !== root))
41017
41187
  refuse5();
41018
- if (parts.includes("hooks"))
41188
+ if ([".app.json", ".mcp.json"].includes(base) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
41189
+ refuse5();
41190
+ if (parts.includes("hooks") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base !== "hooks" : base !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join15(cacheRoot, parts[0], parts[1]))))
41019
41191
  refuse5();
41020
41192
  }
41021
41193
  }
@@ -41125,13 +41297,14 @@ function captureCodexSemanticCacheWitness(options) {
41125
41297
  const initialTree = treeSnapshot(options.cacheRoot);
41126
41298
  const controls = controlProjection(options.controls, read);
41127
41299
  const appOnlyParents = appOnlyParentProjection(options.cacheRoot, options.appOnlyParents ?? [], read);
41300
+ const hookParents = reviewedHookParentProjection(options.cacheRoot, options.reviewedHookParents ?? [], read);
41128
41301
  const skillParents = new Set(controls.map((control) => control.pluginParent));
41129
- if (appOnlyParents.some((parent) => skillParents.has(parent.pluginParent)))
41302
+ if (appOnlyParents.some((parent) => skillParents.has(parent.pluginParent)) || hookParents.some((parent) => skillParents.has(parent.pluginParent) || appOnlyParents.some((app) => app.pluginParent === parent.pluginParent)))
41130
41303
  refuse5();
41131
41304
  if (!reviewedCodexPluginCapabilitiesUnchanged(options.cacheRoot, options.controls, read, options.rules))
41132
41305
  refuse5();
41133
41306
  const files = initialTree.files;
41134
- assertParents(options.cacheRoot, controls, appOnlyParents, files, initialTree.state.members);
41307
+ assertParents(options.cacheRoot, controls, appOnlyParents, hookParents, files, initialTree.state.members);
41135
41308
  const expectedNamesByRoot = assertSkillMaterializations(options.cacheRoot, options.controls, files, read);
41136
41309
  const docs = files.filter((path) => path.endsWith(`${sep5}SKILL.md`));
41137
41310
  const skillRows = [];
@@ -41140,6 +41313,8 @@ function captureCodexSemanticCacheWitness(options) {
41140
41313
  const rel = relative6(options.cacheRoot, document).split(sep5);
41141
41314
  if (appOnlyParents.some((parent) => parent.pluginParent === join15(options.cacheRoot, rel[0], rel[1])))
41142
41315
  refuse5();
41316
+ if (hookParents.some((parent) => parent.pluginParent === join15(options.cacheRoot, rel[0], rel[1])))
41317
+ refuse5();
41143
41318
  const documentStat = lstatSync10(document, { throwIfNoEntry: false });
41144
41319
  if (!documentStat?.isFile() || documentStat.isSymbolicLink() || documentStat.size > MAX_DOCUMENT_BYTES)
41145
41320
  refuse5();
@@ -41203,7 +41378,7 @@ function captureCodexSemanticCacheWitness(options) {
41203
41378
  tracked.verify();
41204
41379
  const finalTree = treeSnapshot(options.cacheRoot);
41205
41380
  assertSameTree(initialTree.state, finalTree.state, tracked.paths());
41206
- const completeProjection = { schema: CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA, cacheRoot: options.cacheRoot, controls, appOnlyParents, skills: uniqueSkills };
41381
+ const completeProjection = { schema: CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA, cacheRoot: options.cacheRoot, controls, appOnlyParents, hookParents, skills: uniqueSkills };
41207
41382
  return { ...completeProjection, sha256: hash2(completeProjection) };
41208
41383
  } catch (error) {
41209
41384
  if (error instanceof Error && error.message.startsWith("CODEX_SEMANTIC_CACHE_WITNESS_UNSUPPORTED"))
@@ -41219,6 +41394,7 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
41219
41394
  cacheRoot: previous.cacheRoot,
41220
41395
  controls: previous.controls,
41221
41396
  appOnlyParents: previous.appOnlyParents,
41397
+ ...previous.hookParents === undefined ? {} : { hookParents: previous.hookParents },
41222
41398
  skills: previous.skills
41223
41399
  };
41224
41400
  const currentProjection = {
@@ -41226,9 +41402,11 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
41226
41402
  cacheRoot: current.cacheRoot,
41227
41403
  controls: current.controls,
41228
41404
  appOnlyParents: current.appOnlyParents,
41405
+ ...current.hookParents === undefined ? {} : { hookParents: current.hookParents },
41229
41406
  skills: current.skills
41230
41407
  };
41231
- if (hash2(priorProjection) !== previous.sha256 || hash2(currentProjection) !== current.sha256 || canonical4(priorProjection) !== canonical4(currentProjection))
41408
+ const comparable = (projection) => ({ ...projection, hookParents: projection.hookParents ?? [] });
41409
+ if (hash2(priorProjection) !== previous.sha256 || hash2(currentProjection) !== current.sha256 || canonical4(comparable(priorProjection)) !== canonical4(comparable(currentProjection)))
41232
41410
  refuse5();
41233
41411
  }
41234
41412
 
@@ -41257,7 +41435,7 @@ var NATIVE_SKILL_ROOTS = [
41257
41435
  import { createHash as createHash28, randomUUID as randomUUID4 } from "crypto";
41258
41436
  import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
41259
41437
  import { existsSync as existsSync7, lstatSync as lstatSync26, statSync as statSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync18, readdirSync as readdirSync9, opendirSync as opendirSync5, readlinkSync as readlinkSync7, realpathSync as realpathSync13, renameSync as renameSync4, rmdirSync as rmdirSync3, writeFileSync as writeFileSync5, unlinkSync as unlinkSync5, chmodSync, openSync as openSync21, closeSync as closeSync21, fsyncSync as fsyncSync7, fstatSync as fstatSync21, readSync as readSync14, constants as constants19, linkSync as linkSync3 } from "fs";
41260
- import { dirname as dirname21, join as join30, resolve as resolve23, relative as relative8, isAbsolute as isAbsolute25, sep as sep10 } from "path";
41438
+ import { basename as basename11, dirname as dirname21, join as join30, resolve as resolve23, relative as relative8, isAbsolute as isAbsolute25, sep as sep10 } from "path";
41261
41439
  import { homedir as homedir3 } from "os";
41262
41440
 
41263
41441
  // src/lib/managed-policy.ts
@@ -41265,11 +41443,11 @@ import { closeSync as closeSync13, constants as constants10, fstatSync as fstatS
41265
41443
  import { join as join17 } from "path";
41266
41444
 
41267
41445
  // src/lib/agent-policy-limits.ts
41268
- import { basename as basename6, isAbsolute as isAbsolute10, join as join16, resolve as resolve10, sep as sep6 } from "path";
41446
+ import { basename as basename7, isAbsolute as isAbsolute10, join as join16, resolve as resolve10, sep as sep6 } from "path";
41269
41447
 
41270
41448
  // src/lib/claude-marketplace-entry-witness.ts
41271
41449
  import { createHash as createHash14 } from "crypto";
41272
- import { basename as basename5, dirname as dirname10, isAbsolute as isAbsolute9, resolve as resolve9 } from "path";
41450
+ import { basename as basename6, dirname as dirname10, isAbsolute as isAbsolute9, resolve as resolve9 } from "path";
41273
41451
  var CLAUDE_MARKETPLACE_ENTRY_HASH_MODE = "claude-marketplace-entry-v1";
41274
41452
  var DOMAIN = "hasna.skills.claude-marketplace-entry.v1\x00";
41275
41453
  var PATH_CHARACTERS = 4096;
@@ -41334,7 +41512,7 @@ var stringArray = (value) => value?.kind === "array" && value.items.every(isStri
41334
41512
  var stringObject = (value) => value?.kind === "object" && value.entries.every(([, item]) => isString(item));
41335
41513
  function claudeMarketplaceEntrySourceValid(source) {
41336
41514
  const path = source?.path;
41337
- return typeof path === "string" && path.length > 0 && path.length <= PATH_CHARACTERS && !/[\x00-\x1f\x7f]/.test(path) && isAbsolute9(path) && resolve9(path) === path && basename5(path) === "marketplace.json" && basename5(dirname10(path)) === ".claude-plugin" && typeof source.marketplace === "string" && NAME.test(source.marketplace) && !source.marketplace.includes("..") && typeof source.plugin === "string" && NAME.test(source.plugin);
41515
+ return typeof path === "string" && path.length > 0 && path.length <= PATH_CHARACTERS && !/[\x00-\x1f\x7f]/.test(path) && isAbsolute9(path) && resolve9(path) === path && basename6(path) === "marketplace.json" && basename6(dirname10(path)) === ".claude-plugin" && typeof source.marketplace === "string" && NAME.test(source.marketplace) && !source.marketplace.includes("..") && typeof source.plugin === "string" && NAME.test(source.plugin);
41338
41516
  }
41339
41517
  function canonical5(value) {
41340
41518
  if (value.kind === "string")
@@ -41604,10 +41782,10 @@ function assertAgentPolicyCollections(policy) {
41604
41782
  }
41605
41783
  }
41606
41784
  if (value.codexSemanticCache !== undefined) {
41607
- const proof = record3(value.codexSemanticCache, 8), witness = record3(proof.witness, 6);
41608
- 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));
41785
+ const proof = record3(value.codexSemanticCache, 9), witness = record3(proof.witness, 7);
41786
+ 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));
41609
41787
  text(proof.configPath);
41610
- requireBound(isAbsolute10(proof.configPath) && resolve10(proof.configPath) === proof.configPath && basename6(proof.configPath) === "config.toml");
41788
+ requireBound(isAbsolute10(proof.configPath) && resolve10(proof.configPath) === proof.configPath && basename7(proof.configPath) === "config.toml");
41611
41789
  text(witness.cacheRoot);
41612
41790
  requireBound(isAbsolute10(witness.cacheRoot) && resolve10(witness.cacheRoot) === witness.cacheRoot);
41613
41791
  if (proof.hookRootAlias !== null) {
@@ -41624,6 +41802,20 @@ function assertAgentPolicyCollections(policy) {
41624
41802
  text(parent[key]);
41625
41803
  requireBound(isAbsolute10(parent.pluginParent) && resolve10(parent.pluginParent) === parent.pluginParent);
41626
41804
  }
41805
+ if (proof.reviewedHookParents !== undefined)
41806
+ for (const parent of array(proof.reviewedHookParents, 4096)) {
41807
+ requireBound(object4(parent) && parent.role === "reviewed-hook" && (parent.installation === "remote-receipt" && Object.keys(parent).length === 6 || parent.installation === "local-marketplace" && Object.keys(parent).length === 7));
41808
+ 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");
41809
+ for (const key of ["pluginId", "namespace", "pluginParent", "installation"])
41810
+ text(parent[key]);
41811
+ if (parent.installation === "remote-receipt")
41812
+ text(parent.remotePluginId);
41813
+ else {
41814
+ text(parent.marketplaceSourcePath);
41815
+ requireBound(typeof parent.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256) && isAbsolute10(parent.marketplaceSourcePath) && resolve10(parent.marketplaceSourcePath) === parent.marketplaceSourcePath && basename7(parent.marketplaceSourcePath) === "marketplace.json");
41816
+ }
41817
+ requireBound(isAbsolute10(parent.pluginParent) && resolve10(parent.pluginParent) === parent.pluginParent);
41818
+ }
41627
41819
  for (const row of [...array(witness.controls, 4096), ...array(witness.appOnlyParents, 4096)]) {
41628
41820
  requireBound(object4(row));
41629
41821
  for (const key of ["pluginId", "namespace", "pluginParent"])
@@ -41632,6 +41824,26 @@ function assertAgentPolicyCollections(policy) {
41632
41824
  if (row[key] !== undefined)
41633
41825
  requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
41634
41826
  }
41827
+ if (witness.hookParents !== undefined)
41828
+ for (const row of array(witness.hookParents, 4096)) {
41829
+ requireBound(object4(row) && row.role === "reviewed-hook" && (row.installation === "remote-receipt" && Object.keys(row).length === 9 || row.installation === "local-marketplace" && Object.keys(row).length === 9));
41830
+ 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");
41831
+ for (const key of ["pluginId", "namespace", "pluginParent", "installation"])
41832
+ text(row[key]);
41833
+ if (row.installation === "remote-receipt")
41834
+ text(row.remotePluginId);
41835
+ else {
41836
+ text(row.marketplaceSourcePath);
41837
+ requireBound(typeof row.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(row.marketplaceSha256) && isAbsolute10(row.marketplaceSourcePath) && resolve10(row.marketplaceSourcePath) === row.marketplaceSourcePath && basename7(row.marketplaceSourcePath) === "marketplace.json");
41838
+ }
41839
+ requireBound(isAbsolute10(row.pluginParent) && resolve10(row.pluginParent) === row.pluginParent);
41840
+ for (const key of ["manifestSha256", "hooksSha256"])
41841
+ requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
41842
+ if (row.installation === "remote-receipt")
41843
+ requireBound(typeof row.receiptSha256 === "string" && /^[a-f0-9]{64}$/.test(row.receiptSha256));
41844
+ else
41845
+ requireBound(row.receiptSha256 === undefined);
41846
+ }
41635
41847
  for (const skill of array(witness.skills, 4096)) {
41636
41848
  requireBound(object4(skill));
41637
41849
  for (const key of ["name", "pluginId", "namespace", "pluginParent"])
@@ -41661,13 +41873,13 @@ function assertAgentPolicyCollections(policy) {
41661
41873
  if (source.hashMode === "claude-plugin-manifest-v1")
41662
41874
  requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute10(source.path) && resolve10(source.path) === source.path && source.path.endsWith(join16(sep6, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
41663
41875
  if (source.hashMode === "sumi-settings-v1")
41664
- requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute10(source.path) && resolve10(source.path) === source.path && basename6(source.path) === "sumi.json");
41876
+ requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute10(source.path) && resolve10(source.path) === source.path && basename7(source.path) === "sumi.json");
41665
41877
  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"))
41666
- requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute10(source.path) && resolve10(source.path) === source.path && basename6(source.path) === "config.toml");
41878
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute10(source.path) && resolve10(source.path) === source.path && basename7(source.path) === "config.toml");
41667
41879
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
41668
- requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute10(source.path) && resolve10(source.path) === source.path && basename6(source.path) === "settings.json");
41880
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute10(source.path) && resolve10(source.path) === source.path && basename7(source.path) === "settings.json");
41669
41881
  if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
41670
- requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute10(source.path) && resolve10(source.path) === source.path && basename6(source.path) === "known_marketplaces.json");
41882
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute10(source.path) && resolve10(source.path) === source.path && basename7(source.path) === "known_marketplaces.json");
41671
41883
  if (source.hashMode === "claude-marketplace-entry-v1")
41672
41884
  requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && claudeMarketplaceEntrySourceValid(source));
41673
41885
  if (source.hashMode === "claude-plugin-registry") {
@@ -41832,7 +42044,7 @@ function isOwnedCliBridge(path, expectedPaths) {
41832
42044
  // src/lib/agent-discovery.ts
41833
42045
  import { createHash as createHash26 } from "crypto";
41834
42046
  import { lstatSync as lstatSync24, readFileSync as readFileSync16, readdirSync as readdirSync8, realpathSync as realpathSync11 } from "fs";
41835
- import { basename as basename9, dirname as dirname20, isAbsolute as isAbsolute23, join as join29, relative as relative7, resolve as resolve22, sep as sep9 } from "path";
42047
+ import { basename as basename10, dirname as dirname20, isAbsolute as isAbsolute23, join as join29, relative as relative7, resolve as resolve22, sep as sep9 } from "path";
41836
42048
  import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
41837
42049
 
41838
42050
  // node_modules/yaml/dist/index.js
@@ -42523,7 +42735,7 @@ function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
42523
42735
 
42524
42736
  // src/lib/plugin-discovery.ts
42525
42737
  import { closeSync as closeSync16, constants as constants13, fstatSync as fstatSync16, lstatSync as lstatSync19, openSync as openSync16, opendirSync as opendirSync4, readSync as readSync10 } from "fs";
42526
- import { basename as basename7, dirname as dirname16, isAbsolute as isAbsolute18, join as join26, resolve as resolve17 } from "path";
42738
+ import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute18, join as join26, resolve as resolve17 } from "path";
42527
42739
 
42528
42740
  // src/lib/plugin-admission.ts
42529
42741
  import { homedir as homedir2 } from "os";
@@ -44004,7 +44216,7 @@ function hashManagedPluginRegistry(path, managed) {
44004
44216
  pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join26(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
44005
44217
  const approval = (producer) => {
44006
44218
  pluginText(producer);
44007
- const digest3 = `sha256:${basename7(producer)}`;
44219
+ const digest3 = `sha256:${basename8(producer)}`;
44008
44220
  const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest3);
44009
44221
  pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
44010
44222
  if (!verified.has(producer)) {
@@ -44068,7 +44280,7 @@ function captureManagedPluginRegistry(path, managedPlugins) {
44068
44280
  // src/lib/claude-marketplace-registry.ts
44069
44281
  import { createHash as createHash22 } from "crypto";
44070
44282
  import { closeSync as closeSync17, constants as constants14, fstatSync as fstatSync17, lstatSync as lstatSync20, openSync as openSync17, readSync as readSync11 } from "fs";
44071
- import { basename as basename8, dirname as dirname17, isAbsolute as isAbsolute19, resolve as resolve18 } from "path";
44283
+ import { basename as basename9, dirname as dirname17, isAbsolute as isAbsolute19, resolve as resolve18 } from "path";
44072
44284
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
44073
44285
  function need6(value, reason2) {
44074
44286
  if (!value)
@@ -44090,7 +44302,7 @@ function parents2(path) {
44090
44302
  }
44091
44303
  function readRegistry2(path, budget2) {
44092
44304
  absolutePath3(path);
44093
- need6(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
44305
+ need6(basename9(path) === "known_marketplaces.json", "requires known_marketplaces.json");
44094
44306
  need6(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
44095
44307
  const ancestors2 = parents2(path), initial = lstatSync20(path, { bigint: true });
44096
44308
  need6(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
@@ -44349,6 +44561,78 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
44349
44561
  import { spawnSync as spawnSync5 } from "child_process";
44350
44562
  import { accessSync as accessSync2, constants as constants16, existsSync as existsSync6, lstatSync as lstatSync22, realpathSync as realpathSync10, statSync as statSync7 } from "fs";
44351
44563
  import { delimiter as delimiter2, isAbsolute as isAbsolute21, join as join27, resolve as resolve20 } from "path";
44564
+
44565
+ // src/lib/hook-diagnostics.ts
44566
+ var CONTEXT_ERROR_CODES = new Set([
44567
+ "SKILLS_API_CREDENTIAL_UNAVAILABLE",
44568
+ "SKILLS_API_UNAVAILABLE",
44569
+ "SKILLS_API_UNAUTHORIZED",
44570
+ "SKILLS_API_FORBIDDEN",
44571
+ "SKILLS_API_RESOURCE_UNAVAILABLE",
44572
+ "SESSION_WRITE_BUSY",
44573
+ "SKILLS_CONTEXT_FAILED",
44574
+ "SKILLS_STORAGE_FULL",
44575
+ "PROFILE_LOCK_MISMATCH",
44576
+ "PROFILE_IDENTITY_MISMATCH",
44577
+ "PROFILE_APPLIED_REPORT_FAILED",
44578
+ "CACHED_AUTHORITY_REQUIRED",
44579
+ "CACHED_PROFILE_EXPIRED",
44580
+ "CACHED_PROFILE_MISSING",
44581
+ "CACHED_BUNDLE_MISSING",
44582
+ "INVALID_CONTEXT_INPUT",
44583
+ "CONTEXT_INPUT_TOO_LARGE",
44584
+ "INVALID_CONTEXT_BUDGET",
44585
+ "INVALID_SELECTION",
44586
+ "INVALID_SELECTION_ALIASES",
44587
+ "INVALID_RECEIPT",
44588
+ "INVALID_CACHE_FILE",
44589
+ "UNSAFE_CACHE_PATH",
44590
+ "RECEIPT_TOO_LARGE",
44591
+ "BUNDLE_DIGEST_MISMATCH",
44592
+ "BUNDLE_IDENTITY_MISMATCH",
44593
+ "BUNDLE_UNAVAILABLE",
44594
+ "BUNDLE_TOO_LARGE",
44595
+ "SKILL_NOT_SELECTED",
44596
+ "INVALID_SESSION",
44597
+ "SESSION_PARENT_NOT_FOUND",
44598
+ "SESSION_PARENT_MISMATCH",
44599
+ "SESSION_NOT_FOUND",
44600
+ "SESSION_GENERATION_CHANGED",
44601
+ "SESSION_GENERATION_EXHAUSTED",
44602
+ "SESSION_PARENT_CHANGED",
44603
+ "SESSION_RECEIPT_CHANGED",
44604
+ "SESSION_RECONCILIATION_REQUIRED",
44605
+ "SESSION_RENEWAL_TIMEOUT",
44606
+ "SESSION_CONTEXT_CONFLICT",
44607
+ "SESSION_RECONCILIATION_INCOMPLETE",
44608
+ "SESSION_WRITE_LOCKED",
44609
+ "SESSION_WRITE_LOCK_CHANGED",
44610
+ "SESSION_LOCK_RECOVERY_INCOMPLETE"
44611
+ ]);
44612
+ var HOOK_REFUSAL_REASON_CODES = Object.freeze([
44613
+ ...CONTEXT_ERROR_CODES,
44614
+ "SKILLS_HOOK_FAILED",
44615
+ "SKILLS_HOOK_TIMEOUT",
44616
+ "SKILLS_HOOK_INVALID_RESPONSE"
44617
+ ]);
44618
+ var OPTIONAL_CONTEXT_FAILURES = new Set([
44619
+ "SKILLS_API_CREDENTIAL_UNAVAILABLE",
44620
+ "SKILLS_API_UNAVAILABLE",
44621
+ "SKILLS_API_UNAUTHORIZED",
44622
+ "SKILLS_API_FORBIDDEN",
44623
+ "SKILLS_API_RESOURCE_UNAVAILABLE",
44624
+ "CACHED_PROFILE_EXPIRED",
44625
+ "CACHED_PROFILE_MISSING",
44626
+ "CACHED_BUNDLE_MISSING",
44627
+ "SESSION_RECONCILIATION_REQUIRED",
44628
+ "SESSION_RENEWAL_TIMEOUT",
44629
+ "SESSION_WRITE_BUSY",
44630
+ "SESSION_PARENT_CHANGED",
44631
+ "SESSION_CONTEXT_CONFLICT",
44632
+ "SKILLS_HOOK_TIMEOUT"
44633
+ ]);
44634
+
44635
+ // src/lib/agent-sumi.ts
44352
44636
  var SUMI_PATH_SELECTORS = ["SUMI_HOME", "SUMI_CONFIG_DIR", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_CONFIG_HOME", "XDG_STATE_HOME"];
44353
44637
  var SUMI_PATH_KEYS = ["data", "cache", "config", "state"];
44354
44638
  var SUMI_PATH_OUTPUT_LIMIT = 128 * 1024;
@@ -44490,24 +44774,33 @@ function sumiConfigPath(home, cwd = process.cwd(), env = process.env) {
44490
44774
  return join27(sumiConfigDirectory(home, env, cwd), "sumi.json");
44491
44775
  }
44492
44776
  function renderSumiPlugin(command, profile) {
44493
- return renderSumiPluginVersion(command, profile, false);
44777
+ return renderSumiPluginVersion(command, profile, 3);
44494
44778
  }
44495
44779
  function isManagedSumiPlugin(text2, command, profile) {
44496
- return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, true);
44780
+ return text2 === renderSumiPlugin(command, profile) || text2 === renderSumiPluginVersion(command, profile, 2) || text2 === renderSumiPluginVersion(command, profile, 1);
44497
44781
  }
44498
- function renderSumiPluginVersion(command, profile, legacy) {
44782
+ function renderSumiPluginVersion(command, profile, version2) {
44783
+ const legacy = version2 === 1;
44499
44784
  const failure = (message) => legacy ? `new Error(${JSON.stringify(message)})` : "refusal()";
44500
44785
  return `// Managed by @hasna/skills. Regenerate with skills hook install --agent sumi.
44501
44786
  import { spawn } from "node:child_process";
44502
44787
  const command = ${JSON.stringify(command)};
44503
44788
  const profile = ${JSON.stringify(profile)};
44504
- ${legacy ? "" : `function refusal() {
44505
- const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry.");
44789
+ ${legacy ? "" : `function refusal(${version2 === 3 ? "reasonCode" : ""}) {
44790
+ const error = new Error("Skills verification blocked this request. Review the Sumi Skills hook configuration, then retry."${version2 === 3 ? ' + (reasonCode ? " [" + reasonCode + "]" : "")' : ""});
44506
44791
  error.name = "SkillsHookRefusal";
44507
- error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED" };
44792
+ error.skillsHookRefusal = { version: 1, code: "SKILLS_HOOK_REFUSED"${version2 === 3 ? ", ...(reasonCode ? { reasonCode } : {})" : ""} };
44508
44793
  return error;
44509
44794
  }
44510
- `}function context(input) {
44795
+ `}${version2 === 3 ? `const reasonCodes = new Set(${JSON.stringify(HOOK_REFUSAL_REASON_CODES)});
44796
+ function refusedResult(result) {
44797
+ const reason = result.reason ?? result.stopReason ?? result.systemMessage;
44798
+ if (typeof reason !== "string" || reason.length > 16384) return refusal();
44799
+ if (reason.startsWith("NATIVE_SKILL_DRIFT:")) return refusal("NATIVE_SKILL_DRIFT");
44800
+ const code = /^Skills context is unavailable \\[([A-Z_]+)\\]/.exec(reason)?.[1];
44801
+ return refusal(reasonCodes.has(code) ? code : undefined);
44802
+ }
44803
+ ` : ""}function context(input) {
44511
44804
  return new Promise((resolve, reject) => {
44512
44805
  const child = spawn(command, ["hook", "user-prompt", "--agent", "sumi", "--selection-profile", profile], { cwd: input.cwd, stdio: ["pipe", "pipe", "pipe"] });
44513
44806
  const chunks = []; let bytes = 0, failure;
@@ -44522,12 +44815,12 @@ ${legacy ? "" : `function refusal() {
44522
44815
  try {
44523
44816
  const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
44524
44817
  if (!result || typeof result !== "object" || Array.isArray(result)) throw ${failure("Invalid Skills hook response")};
44525
- if (result.decision === "block" || result.continue === false) throw ${failure("Skills prompt hook refused")};
44818
+ if (result.decision === "block" || result.continue === false) throw ${version2 === 3 ? "refusedResult(result)" : failure("Skills prompt hook refused")};
44526
44819
  if (Object.keys(result).length && (!result.hookSpecificOutput || result.hookSpecificOutput.hookEventName !== input.hook_event_name)) throw ${failure("Invalid Skills hook response")};
44527
44820
  const text = result.hookSpecificOutput?.additionalContext ?? "";
44528
44821
  if (typeof text !== "string") throw ${failure("Invalid Skills prompt context")};
44529
44822
  resolve(text);
44530
- } catch (error) { reject(${legacy ? "error" : "refusal()"}); }
44823
+ } catch (error) { reject(${legacy ? "error" : version2 === 3 ? 'error instanceof Error && error.name === "SkillsHookRefusal" ? error : refusal()' : "refusal()"}); }
44531
44824
  });
44532
44825
  child.stdin.end(JSON.stringify(input));
44533
44826
  });
@@ -44560,9 +44853,13 @@ export default {
44560
44853
  sessions.add(event.sessionID);
44561
44854
  }));
44562
44855
  registrations.push(await ctx.session.hook("context", async event => {
44563
- const last = event.messages.findLast(message => message.role === "user");
44856
+ ${version2 === 3 ? ` // Model messages also contain checkpoints, attachments and synthetic
44857
+ // user-role history, with no owner-prompt provenance. Prompt admission
44858
+ // already matched and persisted Skills; restore that verified state only.
44859
+ const prompt = "";
44860
+ ` : ` const last = event.messages.findLast(message => message.role === "user");
44564
44861
  const prompt = (last?.content ?? []).filter(part => part.type === "text").map(part => part.text).join("\\n");
44565
- // Recheck on every primary request, including resumed and synthetic
44862
+ `} // Recheck on every primary request, including resumed and synthetic
44566
44863
  // sessions. The CLI owns optional failures versus explicit refusals.
44567
44864
  const text = await context(await input(event.sessionID, prompt, !sessions.has(event.sessionID)));
44568
44865
  if (text) event.system.push({ type: "text", text });
@@ -44829,7 +45126,7 @@ function assertReviewedClaudeCacheClosures(home, config, review, canonical9) {
44829
45126
  throw new Error("Reviewed Claude enabled plugin registration requires an object with an installPath string");
44830
45127
  }
44831
45128
  if ([cacheAliasRoot, cacheRoot].some((root2) => row.installPath === root2 || row.installPath.startsWith(root2 + sep9))) {
44832
- if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename9(row.installPath) !== row.version)) {
45129
+ if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename10(row.installPath) !== row.version)) {
44833
45130
  throw new Error("Reviewed Claude discovery has a malformed active cache registration");
44834
45131
  }
44835
45132
  if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute23(row.projectPath) && resolve22(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
@@ -45107,7 +45404,7 @@ function absentRetiredClaudeRoots(binding) {
45107
45404
  if (!targetValid || !targetScopes.has("user\x00"))
45108
45405
  continue;
45109
45406
  const active = rows[0]?.installPath;
45110
- if (typeof active !== "string" || rows[0]?.version !== basename9(active) || dirname20(active) !== dirname20(root2) || active === root2 || !lstatSync24(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync11(active) !== active)
45407
+ if (typeof active !== "string" || rows[0]?.version !== basename10(active) || dirname20(active) !== dirname20(root2) || active === root2 || !lstatSync24(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync11(active) !== active)
45111
45408
  continue;
45112
45409
  const activeManifest = full(join29(active, ".claude-plugin/plugin.json"));
45113
45410
  if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
@@ -45116,14 +45413,15 @@ function absentRetiredClaudeRoots(binding) {
45116
45413
  }
45117
45414
  return [...result];
45118
45415
  }
45119
- function codexSemanticCacheSourceRole(source, cacheRoot, parents3) {
45416
+ function codexSemanticCacheSourceRole(source, cacheRoot, parents3, hookParents = []) {
45120
45417
  if (!isAbsolute23(source.path) || resolve22(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")
45121
45418
  return false;
45122
45419
  const parts = relative7(cacheRoot, source.path).split(sep9);
45123
45420
  if (parts.length < 3 || !parents3.includes(join29(cacheRoot, parts[0], parts[1])))
45124
45421
  return false;
45125
45422
  const tail = parts.slice(3).join("/");
45126
- return parts.length === 5 && tail === ".codex-plugin/plugin.json" && source.sha256 !== null || parts.length === 4 && [".app.json", ".mcp.json"].includes(tail) && source.sha256 !== null || source.sha256 === null && (parts.length === 4 && tail === "SKILL.md" || parts.length === 5 && tail === "hooks/hooks.json");
45423
+ const parent = join29(cacheRoot, parts[0], parts[1]);
45424
+ return parts.length === 5 && tail === ".codex-plugin/plugin.json" && source.sha256 !== null || parts.length === 4 && [".app.json", ".mcp.json"].includes(tail) && source.sha256 !== null || parts.length === 5 && tail === "hooks/hooks.json" && hookParents.includes(parent) && source.sha256 !== null || source.sha256 === null && (parts.length === 4 && tail === "SKILL.md" || parts.length === 5 && tail === "hooks/hooks.json");
45127
45425
  }
45128
45426
  function codexSemanticCacheDirectoryRole(path, cacheRoot) {
45129
45427
  return isAbsolute23(path) && resolve22(path) === path && !/[\x00-\x1f\x7f]/.test(path) && (path === cacheRoot || path.startsWith(cacheRoot + sep9));
@@ -45155,20 +45453,28 @@ function verifyCodexSemanticCacheDiscovery(binding, reviewedRootAliases) {
45155
45453
  const cacheRoot = proof.witness.cacheRoot;
45156
45454
  if (controls.some((control) => dirname20(dirname20(control.pluginParent)) !== cacheRoot))
45157
45455
  throw new Error("Codex semantic cache names another enrolled cache");
45158
- const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent)])];
45159
- const sources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3));
45456
+ const reviewedHookParents = proof.reviewedHookParents ?? [];
45457
+ 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 || !isDeepStrictEqual6((proof.witness.hookParents ?? []).map(({ manifestSha256: _manifest, hooksSha256: _hooks, receiptSha256: _receipt, ...parent }) => parent), reviewedHookParents)) {
45458
+ throw new Error("Invalid reviewed native-hook cache role");
45459
+ }
45460
+ for (const parent of reviewedHookParents)
45461
+ 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")))
45462
+ throw new Error("Local marketplace hook parent lost its enrolled source witness");
45463
+ const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent), ...reviewedHookParents.map((parent) => parent.pluginParent)])];
45464
+ const hookParentPaths = reviewedHookParents.map((parent) => parent.pluginParent);
45465
+ const sources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3, hookParentPaths));
45160
45466
  const directories = (binding.directories ?? []).filter((directory) => codexSemanticCacheDirectoryRole(directory.path, cacheRoot));
45161
45467
  if (!directories.length || !isDeepStrictEqual6(sources, proof.supersededSources) || !isDeepStrictEqual6(directories, proof.supersededDirectories))
45162
45468
  throw new Error("Codex semantic cache provenance changed");
45163
45469
  const configPath = proof.configPath;
45164
- if (!isAbsolute23(configPath) || resolve22(configPath) !== configPath || basename9(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
45470
+ if (!isAbsolute23(configPath) || resolve22(configPath) !== configPath || basename10(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
45165
45471
  throw new Error("Invalid semantic cache configuration source");
45166
45472
  const configSource = binding.sources.find((source) => source.path === configPath && source.sha256 !== null && source.format === undefined && source.fields === undefined);
45167
45473
  if (!configSource || projected(configSource) !== configSource.sha256)
45168
45474
  throw new Error("Codex semantic cache requires unchanged reviewed configuration");
45169
45475
  assertCodexSemanticCacheHookSafety(cacheRoot, parents3, configPath, proof.hookRootAlias);
45170
45476
  const rules = parseConfig(read2(configPath), configPath, true).skills?.config ?? [];
45171
- const current = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents: proof.appOnlyParents, rules });
45477
+ const current = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents: proof.appOnlyParents, reviewedHookParents, rules });
45172
45478
  assertCodexSemanticCacheWitnessUnchanged(proof.witness, current);
45173
45479
  return proof;
45174
45480
  }
@@ -45252,7 +45558,7 @@ function codexInstallationRoots(binding) {
45252
45558
  const config = join29(dirname20(dirname20(cache)), "config.toml"), home = dirname20(dirname20(dirname20(cache)));
45253
45559
  const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
45254
45560
  const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join29(home, path));
45255
- if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep9) || input.sourceRoot.startsWith(root2 + sep9)) || binding.sources.some((source) => basename9(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep9)))))
45561
+ if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep9) || input.sourceRoot.startsWith(root2 + sep9)) || binding.sources.some((source) => basename10(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep9)))))
45256
45562
  throw new Error("Native installation input overlaps configuration or loading roots");
45257
45563
  if (proof.plugins.some((input) => dirname20(dirname20(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join29(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
45258
45564
  throw new Error("Missing native installation input source witness");
@@ -45264,13 +45570,13 @@ function codexInstallationRoots(binding) {
45264
45570
  });
45265
45571
  }
45266
45572
  function assertMarketplaceBinding(binding, source) {
45267
- if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename9(source.path) !== "plugin.json" || !source.path.endsWith(join29(sep9, ".claude-plugin", "plugin.json"))))
45573
+ if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename10(source.path) !== "plugin.json" || !source.path.endsWith(join29(sep9, ".claude-plugin", "plugin.json"))))
45268
45574
  throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
45269
- if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename9(source.path) !== "sumi.json"))
45575
+ if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename10(source.path) !== "sumi.json"))
45270
45576
  throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
45271
- 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" || basename9(source.path) !== "config.toml"))
45577
+ 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" || basename10(source.path) !== "config.toml"))
45272
45578
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
45273
- 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" || basename9(source.path) !== "settings.json"))
45579
+ if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename10(source.path) !== "settings.json"))
45274
45580
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
45275
45581
  if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
45276
45582
  throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
@@ -45388,7 +45694,7 @@ function verifyAutomaticDiscoveryClosure(binding, options) {
45388
45694
  const current = resolveAgentDiscovery({ home: options.home, agent: binding.agent, canonical: options.canonical, executable: runtime });
45389
45695
  if (JSON.stringify(current) === JSON.stringify(binding))
45390
45696
  return;
45391
- const reviewedWithRuntime = (binding.builtinNames?.length ?? 0) > 0 || binding.sources.some((source) => basename9(source.path) === "package.json");
45697
+ const reviewedWithRuntime = (binding.builtinNames?.length ?? 0) > 0 || binding.sources.some((source) => basename10(source.path) === "package.json");
45392
45698
  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) => isDeepStrictEqual6(saved, source)));
45393
45699
  if (command && gap && runtime === null && reviewedWithRuntime && otherwiseCurrent)
45394
45700
  throw discoveryRootUnresolved(binding.agent, command, gap);
@@ -45531,7 +45837,7 @@ function resolveAgentDiscovery(options) {
45531
45837
  throw new Error("Discovery review must include the agent configuration source");
45532
45838
  const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical9(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical9(configPath), source)) : sources;
45533
45839
  const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
45534
- if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename9(source.path) !== "plugin.json" || !source.path.endsWith(join29(sep9, ".claude-plugin", "plugin.json")))
45840
+ if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename10(source.path) !== "plugin.json" || !source.path.endsWith(join29(sep9, ".claude-plugin", "plugin.json")))
45535
45841
  return source;
45536
45842
  const pluginRoot = dirname20(dirname20(source.path)), hooksPath = join29(pluginRoot, "hooks/hooks.json");
45537
45843
  const hookStat = lstatSync24(hooksPath, { throwIfNoEntry: false });
@@ -47439,7 +47745,7 @@ function planAgentSettingsWitnessUpgrade(options) {
47439
47745
  return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join30(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join30(dataDir2, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], ...policy.bridge.discoveryExecutables ? { discoveryExecutables: policy.bridge.discoveryExecutables } : {}, rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
47440
47746
  }
47441
47747
  function planCodexSemanticCacheWitnessUpgrade(options) {
47442
- 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)
47748
+ 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))
47443
47749
  throw new Error("Invalid Codex semantic cache upgrade request");
47444
47750
  const home = resolve23(options.home ?? homedir3()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
47445
47751
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
@@ -47463,14 +47769,23 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
47463
47769
  throw new Error("Native settings preimage changed");
47464
47770
  const boundedRead = (path) => new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024));
47465
47771
  const unchangedFullSource = (path) => {
47466
- 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"));
47467
- if (!source)
47468
- throw new Error("Cache upgrade lacks prior full identity source");
47772
+ 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"));
47773
+ if (sources.length !== 1)
47774
+ throw new Error("Cache upgrade requires one unambiguous prior full identity source");
47775
+ const source = sources[0];
47469
47776
  const text2 = boundedRead(path);
47470
47777
  if (sha5(text2) !== source.sha256)
47471
47778
  throw new Error("Reviewed cache identity source changed");
47472
47779
  return text2;
47473
47780
  };
47781
+ const unchangedReviewedHookSource = (path) => {
47782
+ const sources = binding.sources.filter((source) => source.path === path);
47783
+ if (!sources.length)
47784
+ throw new Error("Cache upgrade lacks prior full identity source");
47785
+ if (sources.length !== 1)
47786
+ throw new Error("Reviewed hook identity source is ambiguous");
47787
+ return unchangedFullSource(path);
47788
+ };
47474
47789
  const appOnlyParents = options.appOnlyParents.map((parent) => {
47475
47790
  const parts = relative8(cacheRoot, parent).split(sep10);
47476
47791
  if (!isAbsolute25(parent) || resolve23(parent) !== parent || parts.length !== 2 || parts.some((part) => !/^[A-Za-z0-9_-]{1,64}$/.test(part)) || controls.some((control) => control.pluginParent === parent))
@@ -47485,11 +47800,60 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
47485
47800
  unchangedFullSource(source.path);
47486
47801
  return { role: "app-only", pluginId: `${parts[1]}@${parts[0]}`, namespace: parts[1], pluginParent: parent, remotePluginId: receipt.remote_plugin_id };
47487
47802
  }).sort((a, b) => a.pluginParent.localeCompare(b.pluginParent));
47488
- const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...appOnlyParents.map((parent) => parent.pluginParent)])];
47803
+ const reviewedHookParents = (options.reviewedHookParents ?? []).map((parent) => {
47804
+ const parts = relative8(cacheRoot, parent).split(sep10);
47805
+ if (!isAbsolute25(parent) || resolve23(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))
47806
+ throw new Error("Unsupported reviewed native-hook parent role");
47807
+ const receiptPath = join30(parent, ".codex-remote-plugin-install.json"), receiptStat = lstatSync26(receiptPath, { throwIfNoEntry: false });
47808
+ let identity5;
47809
+ if (receiptStat) {
47810
+ if (!receiptStat.isFile() || receiptStat.isSymbolicLink() || receiptStat.size > 16 * 1024)
47811
+ throw new Error("Invalid reviewed native-hook install receipt");
47812
+ const receiptText = unchangedReviewedHookSource(receiptPath);
47813
+ let receipt;
47814
+ try {
47815
+ hashNativeJsonControls(receiptText);
47816
+ receipt = JSON.parse(receiptText);
47817
+ } catch {
47818
+ throw new Error("Invalid reviewed native-hook install receipt");
47819
+ }
47820
+ 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))
47821
+ throw new Error("Invalid reviewed native-hook install receipt");
47822
+ identity5 = { role: "reviewed-hook", pluginId: `${parts[1]}@${parts[0]}`, namespace: parts[1], pluginParent: parent, installation: "remote-receipt", remotePluginId: receipt.remote_plugin_id };
47823
+ } else {
47824
+ const pluginId2 = `${parts[1]}@${parts[0]}`;
47825
+ const marketplaceSources = binding.sources.filter((source2) => basename11(source2.path) === "marketplace.json" && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && source2.reviewArtifact === undefined && (source2.hashMode === undefined || source2.hashMode === "bytes"));
47826
+ const matches = marketplaceSources.flatMap((source2) => {
47827
+ const text2 = unchangedFullSource(source2.path);
47828
+ return codexLocalMarketplaceMapsPlugin(text2, parts[0], parts[1]) ? [{ source: source2, text: text2 }] : [];
47829
+ });
47830
+ if (matches.length !== 1)
47831
+ throw new Error("Local marketplace hook parent lacks one exact enrolled marketplace source");
47832
+ const { source } = matches[0];
47833
+ const config = Bun.TOML.parse(settings);
47834
+ if (config.plugins?.[pluginId2]?.enabled !== true)
47835
+ throw new Error("Local marketplace hook parent is not enabled in the unchanged Codex settings");
47836
+ identity5 = { role: "reviewed-hook", pluginId: pluginId2, namespace: parts[1], pluginParent: parent, installation: "local-marketplace", marketplaceSourcePath: source.path, marketplaceSha256: source.sha256 };
47837
+ }
47838
+ const versions = readdirSync9(parent).filter((name) => name !== ".codex-remote-plugin-install.json");
47839
+ if (!versions.length || versions.length > 4096)
47840
+ throw new Error("Reviewed native-hook parent has no bounded materializations");
47841
+ for (const version2 of versions) {
47842
+ if (!/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2))
47843
+ throw new Error("Invalid reviewed native-hook version identity");
47844
+ const root2 = join30(parent, version2), stat2 = lstatSync26(root2, { throwIfNoEntry: false });
47845
+ if (!stat2?.isDirectory() || stat2.isSymbolicLink() || realpathSync13(root2) !== root2)
47846
+ throw new Error("Invalid reviewed native-hook materialization");
47847
+ unchangedReviewedHookSource(join30(root2, ".codex-plugin/plugin.json"));
47848
+ unchangedReviewedHookSource(join30(root2, "hooks/hooks.json"));
47849
+ }
47850
+ return identity5;
47851
+ }).sort((a, b) => a.pluginParent.localeCompare(b.pluginParent));
47852
+ const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...appOnlyParents.map((parent) => parent.pluginParent), ...reviewedHookParents.map((parent) => parent.pluginParent)])];
47489
47853
  const reviewedAlias = aliases.find((alias) => alias.agent === "codex");
47490
47854
  const hookRootAlias = reviewedAlias ? { alias: reviewedAlias.alias, target: reviewedAlias.target } : null;
47491
47855
  assertCodexSemanticCacheHookSafety(cacheRoot, parents3, configPath, hookRootAlias);
47492
- const supersededSources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3));
47856
+ const supersededSources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3, reviewedHookParents.map((parent) => parent.pluginParent)));
47493
47857
  for (const source of supersededSources)
47494
47858
  if ([".app.json", ".mcp.json"].includes(source.path.split(sep10).at(-1)))
47495
47859
  unchangedFullSource(source.path);
@@ -47497,7 +47861,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
47497
47861
  if (!supersededDirectories.some((directory) => directory.path === cacheRoot) || (binding.directories ?? []).some((directory) => cacheRoot.startsWith(directory.path + sep10)))
47498
47862
  throw new Error("Cache upgrade requires exact cache directory coverage without an unproved ancestor projection");
47499
47863
  const rules = Bun.TOML.parse(settings).skills?.config ?? [];
47500
- const witness = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents, rules, read: boundedRead });
47864
+ const witness = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents, reviewedHookParents, rules, read: boundedRead });
47501
47865
  if (options.expectedCacheWitnessSha256 !== undefined && witness.sha256 !== options.expectedCacheWitnessSha256)
47502
47866
  throw new Error("Reviewed semantic cache preimage changed");
47503
47867
  const replacement = { ...binding, codexDisabledPluginSkills: controls, codexSemanticCache: {
@@ -47506,6 +47870,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
47506
47870
  hookRootAlias,
47507
47871
  witness,
47508
47872
  appOnlyParents,
47873
+ reviewedHookParents,
47509
47874
  previousPolicySha256: options.expectedPolicySha256,
47510
47875
  supersededSources,
47511
47876
  supersededDirectories
@@ -47524,7 +47889,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
47524
47889
  rootAliases: aliases,
47525
47890
  managedAgentChecks: { home, agents: ["codex"] },
47526
47891
  ...policy.bridge.discoveryExecutables ? { discoveryExecutables: policy.bridge.discoveryExecutables } : {},
47527
- cacheWitnessUpgrade: { ...options, home, dataDir: dataDir2, appOnlyParents: appOnlyParents.map((parent) => parent.pluginParent) }
47892
+ cacheWitnessUpgrade: { ...options, home, dataDir: dataDir2, appOnlyParents: appOnlyParents.map((parent) => parent.pluginParent), reviewedHookParents: reviewedHookParents.map((parent) => parent.pluginParent) }
47528
47893
  };
47529
47894
  }
47530
47895
  function planReviewedArtifactMigration(options) {
@@ -48063,7 +48428,7 @@ import {
48063
48428
  statSync as statSync11,
48064
48429
  writeFileSync as writeFileSync7
48065
48430
  } from "fs";
48066
- import { basename as basename12, dirname as dirname24, isAbsolute as isAbsolute26, join as join34, normalize as normalize4, relative as relative10, resolve as resolve25, sep as sep13 } from "path";
48431
+ import { basename as basename14, dirname as dirname24, isAbsolute as isAbsolute26, join as join34, normalize as normalize4, relative as relative10, resolve as resolve25, sep as sep13 } from "path";
48067
48432
 
48068
48433
  // src/lib/registry-data/index.ts
48069
48434
  var SKILLS = [];
@@ -48588,7 +48953,7 @@ import {
48588
48953
  realpathSync as realpathSync14,
48589
48954
  writeFileSync as writeFileSync6
48590
48955
  } from "fs";
48591
- import { basename as basename10, dirname as dirname22, join as join32, relative as relative9 } from "path";
48956
+ import { basename as basename12, dirname as dirname22, join as join32, relative as relative9 } from "path";
48592
48957
  var ANY_SEGMENT_COPY_EXCLUDES = new Set([
48593
48958
  ".git",
48594
48959
  ".DS_Store",
@@ -48638,11 +49003,11 @@ function normalizeNewPortableSkillName(name) {
48638
49003
  throw new Error(`Invalid skill name '${name}'. Include letters or numbers.`);
48639
49004
  return normalized;
48640
49005
  }
48641
- function readPortableSkillManifest(skillPath, fallbackName = basename10(skillPath)) {
49006
+ function readPortableSkillManifest(skillPath, fallbackName = basename12(skillPath)) {
48642
49007
  return readManifest(skillPath, fallbackName, normalizePortableSkillName);
48643
49008
  }
48644
49009
  function readPortableSkillManifestForImport(skillPath) {
48645
- return readManifest(skillPath, basename10(skillPath), normalizeNewPortableSkillName);
49010
+ return readManifest(skillPath, basename12(skillPath), normalizeNewPortableSkillName);
48646
49011
  }
48647
49012
  function readManifest(skillPath, fallbackName, normalizeName) {
48648
49013
  const skillJsonPath = join32(skillPath, "skill.json");
@@ -49196,7 +49561,7 @@ function inferRuntimeFromCommands(commands) {
49196
49561
  function readJsonObject(path) {
49197
49562
  const parsed = JSON.parse(readFileSync20(path, "utf-8"));
49198
49563
  if (!isRecord3(parsed))
49199
- throw new Error(`${basename10(path)} must contain a JSON object`);
49564
+ throw new Error(`${basename12(path)} must contain a JSON object`);
49200
49565
  return parsed;
49201
49566
  }
49202
49567
  function hasPackageDependencies(pkgPath) {
@@ -49234,7 +49599,7 @@ function escapeJsString(value) {
49234
49599
  // src/lib/authoring-path.ts
49235
49600
  import { lstatSync as lstatSync28, realpathSync as realpathSync15 } from "fs";
49236
49601
  import { homedir as homedir4 } from "os";
49237
- import { basename as basename11, dirname as dirname23, join as join33, resolve as resolve24, sep as sep12 } from "path";
49602
+ import { basename as basename13, dirname as dirname23, join as join33, resolve as resolve24, sep as sep12 } from "path";
49238
49603
  var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
49239
49604
  function pathParts(path) {
49240
49605
  return resolve24(path).split(sep12).filter(Boolean);
@@ -49254,7 +49619,7 @@ function physicalPath(path) {
49254
49619
  let cursor = resolve24(path);
49255
49620
  const suffix = [];
49256
49621
  while (!lstatSync28(cursor, { throwIfNoEntry: false })) {
49257
- suffix.unshift(basename11(cursor));
49622
+ suffix.unshift(basename13(cursor));
49258
49623
  const parent = dirname23(cursor);
49259
49624
  if (parent === cursor)
49260
49625
  return resolve24(path);
@@ -49473,7 +49838,7 @@ function physicalImportPath(path) {
49473
49838
  let ancestor = resolve25(path);
49474
49839
  const suffix = [];
49475
49840
  while (!existsSync10(ancestor)) {
49476
- suffix.unshift(basename12(ancestor));
49841
+ suffix.unshift(basename14(ancestor));
49477
49842
  ancestor = dirname24(ancestor);
49478
49843
  }
49479
49844
  return join34(realpathSync16(ancestor), ...suffix);
@@ -49498,8 +49863,8 @@ function portPortableSkill(sourcePath, options = {}) {
49498
49863
  const explicitName = options.name != null;
49499
49864
  const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
49500
49865
  if (isOfficialSkillName(skillName) && !options.allowShadow) {
49501
- const sourceSlug = safeNormalizeName(basename12(absoluteSource));
49502
- const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename12(absoluteSource)}')` : ""} matches a bundled official skill.`;
49866
+ const sourceSlug = safeNormalizeName(basename14(absoluteSource));
49867
+ 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.`;
49503
49868
  throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
49504
49869
  }
49505
49870
  const readOnlyDestination = join34(resolvePortableSkillsRoot(options, false), skillName);
@@ -50027,7 +50392,7 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
50027
50392
  return { action, path: skillMdPath };
50028
50393
  const parentDir = dirname25(dir);
50029
50394
  mkdirSync10(parentDir, { recursive: true });
50030
- const transactionDir = mkdtempSync2(join35(parentDir, `.hasna-skills-write-${basename13(dir)}-`));
50395
+ const transactionDir = mkdtempSync2(join35(parentDir, `.hasna-skills-write-${basename15(dir)}-`));
50031
50396
  const candidateDir = join35(transactionDir, "candidate");
50032
50397
  const backupDir = join35(transactionDir, "backup");
50033
50398
  const candidateSkillMdPath = join35(candidateDir, "SKILL.md");