@hasna/skills 0.10.20 → 0.10.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7262,7 +7262,7 @@ import {
7262
7262
  statSync as statSync10,
7263
7263
  writeFileSync as writeFileSync8
7264
7264
  } from "fs";
7265
- import { basename as basename11, dirname as dirname19, isAbsolute as isAbsolute17, join as join29, normalize as normalize3, relative as relative6, resolve as resolve17, sep as sep8 } from "path";
7265
+ import { basename as basename11, dirname as dirname19, isAbsolute as isAbsolute17, join as join29, normalize as normalize4, relative as relative6, resolve as resolve17, sep as sep8 } from "path";
7266
7266
 
7267
7267
  // src/lib/registry-data/index.ts
7268
7268
  var SKILLS = [];
@@ -9038,12 +9038,14 @@ function assertAgentPolicyCollections(policy) {
9038
9038
  }
9039
9039
  if (bridge.codexPluginSkills !== undefined)
9040
9040
  for (const control of array(bridge.codexPluginSkills, 4096)) {
9041
- requireBound(object(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256"].includes(key)));
9041
+ requireBound(object(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "remotePluginId"].includes(key)));
9042
9042
  text(control.name, 129);
9043
9043
  text(control.pluginId, 1024);
9044
9044
  text(control.namespace, 64);
9045
9045
  text(control.pluginParent);
9046
9046
  requireBound(isAbsolute2(control.pluginParent) && resolve2(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
9047
+ if (control.remotePluginId !== undefined)
9048
+ requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
9047
9049
  if (control.appSha256 !== undefined)
9048
9050
  requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
9049
9051
  }
@@ -9066,8 +9068,8 @@ function assertAgentPolicyCollections(policy) {
9066
9068
  text(source.path);
9067
9069
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
9068
9070
  if (source.hashMode !== undefined)
9069
- requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
9070
- if (source.hashMode === "codex-settings-v1")
9071
+ requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
9072
+ if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2")
9071
9073
  requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve2(source.path) === source.path && basename2(source.path) === "config.toml");
9072
9074
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
9073
9075
  requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute2(source.path) && resolve2(source.path) === source.path && basename2(source.path) === "settings.json");
@@ -9103,10 +9105,10 @@ function assertAgentPolicyCollections(policy) {
9103
9105
  }
9104
9106
 
9105
9107
  // src/lib/agent-discovery.ts
9106
- import { createHash as createHash17 } from "crypto";
9108
+ import { createHash as createHash18 } from "crypto";
9107
9109
  import { lstatSync as lstatSync21, readFileSync as readFileSync17, readdirSync as readdirSync10, realpathSync as realpathSync7 } from "fs";
9108
9110
  import { basename as basename9, dirname as dirname17, isAbsolute as isAbsolute16, join as join27, resolve as resolve15, sep as sep6 } from "path";
9109
- import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
9111
+ import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
9110
9112
 
9111
9113
  // node_modules/yaml/dist/index.js
9112
9114
  var composer = require_composer();
@@ -10877,7 +10879,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
10877
10879
  // package.json
10878
10880
  var package_default = {
10879
10881
  name: "@hasna/skills",
10880
- version: "0.10.20",
10882
+ version: "0.10.22",
10881
10883
  description: "Skills library for AI coding agents",
10882
10884
  type: "module",
10883
10885
  bin: {
@@ -13027,6 +13029,25 @@ function hashNativeJsonControls(text2, excludedMetadata) {
13027
13029
  import { basename as basename5, dirname as dirname10, join as join14, relative as relative4, resolve as resolve9, sep as sep4 } from "path";
13028
13030
  import { lstatSync as lstatSync12, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync5 } from "fs";
13029
13031
 
13032
+ // src/lib/codex-native-compatibility.ts
13033
+ var MEASURED_CODEX_CAPABILITIES = {
13034
+ "codex-cli 0.153.0": ["hooks"],
13035
+ "codex-cli 0.154.0": ["hooks"],
13036
+ "codex-cli 0.155.0": ["hooks"],
13037
+ "codex-cli 0.155.1": ["hooks"],
13038
+ "codex-cli 0.156.1": ["hooks"],
13039
+ "codex-cli 0.157.0": ["hooks"],
13040
+ "codex-cli 0.157.1": ["hooks"],
13041
+ "codex-cli 0.158.0": ["hooks"],
13042
+ "codex-cli 0.159.0": ["hooks"],
13043
+ "codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
13044
+ "codex-cli 0.160.0": ["hooks", "qualified-skill-catalog"]
13045
+ };
13046
+ function supportsCodexNativeCapability(version, capability) {
13047
+ return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
13048
+ }
13049
+ var SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
13050
+
13030
13051
  // src/lib/codex-native-skill-catalog.ts
13031
13052
  import { realpathSync as realpathSync4 } from "fs";
13032
13053
  import { isAbsolute as isAbsolute10, normalize as normalize2 } from "path";
@@ -13036,7 +13057,6 @@ import { execFile, spawn } from "child_process";
13036
13057
  import { realpathSync as realpathSync3, statSync as statSync7 } from "fs";
13037
13058
  import { isAbsolute as isAbsolute9 } from "path";
13038
13059
  import { promisify } from "util";
13039
- var SUPPORTED_CODEX_HOOK_VERSIONS = ["codex-cli 0.153.0", "codex-cli 0.154.0", "codex-cli 0.155.0", "codex-cli 0.155.1", "codex-cli 0.156.1", "codex-cli 0.157.0", "codex-cli 0.157.1", "codex-cli 0.158.0", "codex-cli 0.159.0", "codex-cli 0.159.2"];
13040
13060
  async function connectCodexHookRpc(options) {
13041
13061
  const timeoutMs = options.timeoutMs ?? 20000;
13042
13062
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 20000)
@@ -13054,7 +13074,7 @@ async function connectCodexHookRpc(options) {
13054
13074
  } catch {
13055
13075
  throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE");
13056
13076
  }
13057
- if (!SUPPORTED_CODEX_HOOK_VERSIONS.includes(version))
13077
+ if (!supportsCodexNativeCapability(version, "hooks"))
13058
13078
  throw new Error("CODEX_HOOK_TRUST_NATIVE_UNSUPPORTED_VERSION");
13059
13079
  const current = statSync7(binary);
13060
13080
  if (realpathSync3(found) !== binary || ["dev", "ino", "size", "mtimeMs", "ctimeMs"].some((k) => stat[k] !== current[k]))
@@ -13179,12 +13199,12 @@ var MAX_SKILLS = 4096;
13179
13199
  var MAX_NAME_BYTES = 1024;
13180
13200
  var MAX_PATH_BYTES = 16384;
13181
13201
  var MAX_PLUGIN_ID_BYTES = 1024;
13182
- var SUPPORTED_NAME_CONTROL_VERSIONS = new Set(["codex-cli 0.159.2"]);
13183
13202
  function refuse() {
13184
13203
  throw new Error("CODEX_NATIVE_SKILL_CATALOG_INVALID");
13185
13204
  }
13186
13205
  var object4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
13187
13206
  var scalar = (value, max) => typeof value === "string" && value.length > 0 && value.trim() === value && Buffer.byteLength(value, "utf8") <= max && !/[\u0000-\u001f\u007f]/u.test(value);
13207
+ var remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value);
13188
13208
  var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute10(value);
13189
13209
  function projectCodexNativeSkillCatalog(response, cwd) {
13190
13210
  if (!absolutePath2(cwd) || !object4(response) || !Array.isArray(response.data) || response.data.length !== 1)
@@ -13212,7 +13232,11 @@ function projectCodexInstalledPlugins(response) {
13212
13232
  for (const value of marketplace.plugins) {
13213
13233
  if (!object4(value) || !scalar(value.id, MAX_PLUGIN_ID_BYTES) || !scalar(value.name, MAX_NAME_BYTES) || value.id !== `${value.name}@${marketplace.name}`)
13214
13234
  refuse();
13215
- plugins.push({ ...value, marketplace: marketplace.name });
13235
+ plugins.push({
13236
+ ...value,
13237
+ marketplace: marketplace.name,
13238
+ sourceType: object4(value.source) && value.source.type === "remote" ? "remote" : undefined
13239
+ });
13216
13240
  }
13217
13241
  }
13218
13242
  return projectCodexInstalledPluginEntries(plugins);
@@ -13222,13 +13246,21 @@ function projectCodexInstalledPluginEntries(value) {
13222
13246
  refuse();
13223
13247
  const ids = new Set;
13224
13248
  return value.map((item) => {
13225
- if (!object4(item) || !scalar(item.id, MAX_PLUGIN_ID_BYTES) || !scalar(item.name, MAX_NAME_BYTES) || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean" || item.localVersion !== null && !scalar(item.localVersion, MAX_NAME_BYTES))
13249
+ if (!object4(item) || !scalar(item.id, MAX_PLUGIN_ID_BYTES) || !scalar(item.name, MAX_NAME_BYTES) || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean" || item.localVersion !== null && !scalar(item.localVersion, MAX_NAME_BYTES) || item.remotePluginId !== undefined && item.remotePluginId !== null && !remotePluginIdentifier(item.remotePluginId) || item.sourceType !== undefined && item.sourceType !== "remote")
13226
13250
  refuse();
13227
13251
  const at = item.id.lastIndexOf("@");
13228
13252
  if (at < 1 || item.id.slice(0, at) !== item.name || at === item.id.length - 1 || ids.has(item.id))
13229
13253
  refuse();
13230
13254
  ids.add(item.id);
13231
- return { id: item.id, name: item.name, installed: item.installed, enabled: item.enabled, localVersion: item.localVersion ?? null };
13255
+ return {
13256
+ id: item.id,
13257
+ name: item.name,
13258
+ installed: item.installed,
13259
+ enabled: item.enabled,
13260
+ localVersion: item.localVersion ?? null,
13261
+ ...item.sourceType === "remote" ? { sourceType: "remote" } : {},
13262
+ ...item.remotePluginId == null ? {} : { remotePluginId: item.remotePluginId }
13263
+ };
13232
13264
  });
13233
13265
  }
13234
13266
  async function captureCodexNativeSkillCatalog(options, connect = connectCodexHookRpc) {
@@ -13241,7 +13273,7 @@ async function captureCodexNativeSkillCatalog(options, connect = connectCodexHoo
13241
13273
  ...options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }
13242
13274
  });
13243
13275
  try {
13244
- if (!SUPPORTED_NAME_CONTROL_VERSIONS.has(rpc.version))
13276
+ if (!supportsCodexNativeCapability(rpc.version, "qualified-skill-catalog"))
13245
13277
  throw new Error("CODEX_NATIVE_SKILL_CATALOG_UNSUPPORTED_VERSION");
13246
13278
  const response = await rpc.request("skills/list", { cwds: [options.cwd], forceReload: true });
13247
13279
  const installed = await rpc.request("plugin/installed", { cwds: [options.cwd] });
@@ -13299,6 +13331,21 @@ var CODEX_DEFAULT_AGENT_PLUGIN_VERSION = "1.0.0";
13299
13331
  function refuse2() {
13300
13332
  throw new Error("CODEX_NATIVE_SKILL_IDENTITY_UNSUPPORTED: review native plugin names and hook controls");
13301
13333
  }
13334
+ function remoteInstallationMatches(parent, remotePluginId, read2) {
13335
+ try {
13336
+ if (!remotePluginIdentifier(remotePluginId) || !lstatSync12(parent).isDirectory() || realpathSync5(parent) !== parent)
13337
+ return false;
13338
+ const path = join14(parent, ".codex-remote-plugin-install.json"), stat = lstatSync12(path, { throwIfNoEntry: false });
13339
+ if (!stat?.isFile() || stat.isSymbolicLink() || stat.size > 16384)
13340
+ return false;
13341
+ const text2 = read2(path);
13342
+ hashNativeJsonControls(text2);
13343
+ const value = JSON.parse(text2);
13344
+ return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 2 && value.schema_version === 1 && value.remote_plugin_id === remotePluginId);
13345
+ } catch {
13346
+ return false;
13347
+ }
13348
+ }
13302
13349
  function appControls(path, read2) {
13303
13350
  const text2 = read2(path);
13304
13351
  let value, sha2562;
@@ -13388,6 +13435,10 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
13388
13435
  for (const parent of new Set(controls.map((control) => control.pluginParent))) {
13389
13436
  if (!parent.startsWith(cache + sep4) || !lstatSync12(parent, { throwIfNoEntry: false })?.isDirectory())
13390
13437
  return false;
13438
+ for (const control of controls.filter((control2) => control2.pluginParent === parent)) {
13439
+ if (control.remotePluginId !== undefined && !remoteInstallationMatches(parent, control.remotePluginId, read2))
13440
+ return false;
13441
+ }
13391
13442
  const names = readdirSync6(parent);
13392
13443
  entries += names.length;
13393
13444
  if (entries > 4096)
@@ -13414,7 +13465,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
13414
13465
  }
13415
13466
  }
13416
13467
  function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read2, rules) {
13417
- if (catalog?.version !== "codex-cli 0.159.2" || catalog.cwd !== cwd)
13468
+ if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd)
13418
13469
  refuse2();
13419
13470
  const skills = projectCodexNativeSkillCatalog({ data: [{ cwd, errors: [], skills: catalog.skills }] }, cwd);
13420
13471
  const allowed = new Set(documents), result = [];
@@ -13441,20 +13492,28 @@ function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read2, r
13441
13492
  const listed = skills.filter((skill) => skill.path === document2);
13442
13493
  if (listed.length > 1)
13443
13494
  refuse2();
13495
+ let remotePluginId;
13444
13496
  if (listed.length === 1) {
13445
13497
  if (listed[0].name !== parsed.name || listed[0].pluginId !== expectedPluginId)
13446
13498
  refuse2();
13447
13499
  } else {
13448
- const installed = installedPlugins.filter((plugin) => plugin.id === expectedPluginId && plugin.name === pluginName);
13449
- if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled || !installed[0].localVersion)
13500
+ const installed = installedPlugins.filter((plugin2) => plugin2.id === expectedPluginId && plugin2.name === pluginName);
13501
+ if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled)
13450
13502
  refuse2();
13451
- if (installed[0].localVersion !== basename5(dirname10(dirname10(dirname10(document2)))))
13452
- continue;
13503
+ const plugin = installed[0];
13504
+ if (plugin.localVersion !== null) {
13505
+ if (plugin.localVersion !== basename5(dirname10(dirname10(dirname10(document2)))))
13506
+ continue;
13507
+ } else {
13508
+ if (plugin.sourceType !== "remote" || !plugin.remotePluginId || !remoteInstallationMatches(parsed.pluginParent, plugin.remotePluginId, read2) || !lstatSync12(document2).isFile() || realpathSync5(document2) !== document2)
13509
+ refuse2();
13510
+ remotePluginId = plugin.remotePluginId;
13511
+ }
13453
13512
  const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
13454
13513
  if (!exactPathDeny || !isCodexNativeSkillDisabled({ name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true }, rules))
13455
13514
  refuse2();
13456
13515
  }
13457
- result.push({ ...parsed, pluginId: expectedPluginId });
13516
+ result.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
13458
13517
  }
13459
13518
  if (!result.length || new Set(result.map((item) => item.name)).size !== result.length)
13460
13519
  refuse2();
@@ -13470,7 +13529,7 @@ function isReviewedCodexPluginSkillDisabled(document2, cache, controls, rules, r
13470
13529
  return false;
13471
13530
  }
13472
13531
  const reviewed = controls.find((item) => item.name === parsed.name && item.namespace === parsed.namespace && item.pluginParent === parsed.pluginParent && item.manifestSha256 === parsed.manifestSha256 && item.appSha256 === parsed.appSha256);
13473
- if (!reviewed)
13532
+ if (!reviewed || reviewed.remotePluginId !== undefined && !remoteInstallationMatches(reviewed.pluginParent, reviewed.remotePluginId, read2))
13474
13533
  return false;
13475
13534
  return isCodexNativeSkillDisabled({ name: parsed.name, path: document2, pluginId: reviewed.pluginId, enabled: true }, rules);
13476
13535
  }
@@ -13489,7 +13548,7 @@ enabled = false
13489
13548
  return text2;
13490
13549
  }
13491
13550
 
13492
- // src/lib/codex-settings-witness.ts
13551
+ // src/lib/codex-settings-witness-v1.ts
13493
13552
  import { createHash as createHash9 } from "crypto";
13494
13553
  var sha = (text2) => createHash9("sha256").update(text2).digest("hex");
13495
13554
  var EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
@@ -13583,19 +13642,167 @@ function captureCodexSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
13583
13642
  const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
13584
13643
  return { path, hashMode: "codex-settings-v1", sha256: hashCodexSettingsReplacement(text2) };
13585
13644
  }
13645
+
13646
+ // src/lib/codex-settings-witness.ts
13647
+ import { createHash as createHash10 } from "crypto";
13648
+ var sha2 = (text2) => createHash10("sha256").update(text2).digest("hex");
13649
+ var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
13650
+ var VERBOSITY2 = new Set(["low", "medium", "high"]);
13651
+ var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
13652
+ var CODEX_DISCOVERY_PROJECTION_FIELDS = ["plugins", "marketplaces", "skills"];
13653
+ var INFERENCE = ["model", "model_reasoning_effort", "model_verbosity"];
13654
+ function need4(value) {
13655
+ if (!value)
13656
+ throw new Error("Invalid Codex settings witness");
13657
+ }
13658
+ function isRecord3(value) {
13659
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
13660
+ }
13661
+ function isDate(value) {
13662
+ return value instanceof Date;
13663
+ }
13664
+ function numericTokens2(text2) {
13665
+ const result = [];
13666
+ for (let at = 0;at < text2.length; ) {
13667
+ const char = text2[at];
13668
+ if (char === "#") {
13669
+ while (at < text2.length && text2[at] !== `
13670
+ `)
13671
+ at++;
13672
+ continue;
13673
+ }
13674
+ if (char === '"' || char === "'") {
13675
+ const quote = char, triple = text2.slice(at, at + 3) === quote.repeat(3);
13676
+ at += triple ? 3 : 1;
13677
+ while (at < text2.length) {
13678
+ if (quote === '"' && text2[at] === "\\") {
13679
+ at += 2;
13680
+ continue;
13681
+ }
13682
+ if (text2.slice(at, at + (triple ? 3 : 1)) === quote.repeat(triple ? 3 : 1)) {
13683
+ at += triple ? 3 : 1;
13684
+ break;
13685
+ }
13686
+ at++;
13687
+ }
13688
+ continue;
13689
+ }
13690
+ if (/[0-9+-]/.test(char)) {
13691
+ const start = at;
13692
+ while (at < text2.length && !/[\s,\]{}#=]/.test(text2[at]))
13693
+ at++;
13694
+ if (at > start) {
13695
+ result.push(text2.slice(start, at));
13696
+ continue;
13697
+ }
13698
+ }
13699
+ at++;
13700
+ }
13701
+ return result.sort();
13702
+ }
13703
+ function canonical4(value, depth = 0, budget = { remaining: 65536 }) {
13704
+ need4(depth <= 32 && --budget.remaining >= 0);
13705
+ if (value === null)
13706
+ return ["null"];
13707
+ if (typeof value === "string")
13708
+ return ["string", value];
13709
+ if (typeof value === "boolean")
13710
+ return ["boolean", value];
13711
+ if (typeof value === "number") {
13712
+ need4(Number.isFinite(value));
13713
+ return ["number", String(value)];
13714
+ }
13715
+ if (isDate(value)) {
13716
+ need4(Number.isFinite(value.getTime()));
13717
+ return ["date", value.toISOString()];
13718
+ }
13719
+ if (Array.isArray(value))
13720
+ return ["array", value.map((child) => canonical4(child, depth + 1, budget))];
13721
+ need4(isRecord3(value));
13722
+ return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1, budget)])];
13723
+ }
13724
+ function normalize3(config) {
13725
+ if (isRecord3(config.hooks)) {
13726
+ delete config.hooks.state;
13727
+ if (!Object.keys(config.hooks).length)
13728
+ delete config.hooks;
13729
+ }
13730
+ if (isRecord3(config.skills) && Array.isArray(config.skills.config)) {
13731
+ const skills = config.skills;
13732
+ const retained = skills.config.filter((entry) => !disablesOneSkill(entry));
13733
+ if (retained.length !== skills.config.length) {
13734
+ if (retained.length)
13735
+ skills.config = retained;
13736
+ else
13737
+ delete skills.config;
13738
+ }
13739
+ if (!Object.keys(skills).length)
13740
+ delete config.skills;
13741
+ }
13742
+ inference(config);
13743
+ if (isRecord3(config.profiles)) {
13744
+ for (const profile of Object.values(config.profiles))
13745
+ if (isRecord3(profile))
13746
+ inference(profile);
13747
+ }
13748
+ }
13749
+ function disablesOneSkill(entry) {
13750
+ if (!isRecord3(entry) || entry.enabled !== false)
13751
+ return false;
13752
+ const keys = Object.keys(entry);
13753
+ if (!keys.every((key) => key === "path" || key === "name" || key === "enabled"))
13754
+ return false;
13755
+ const selector = ["path", "name"].filter((key) => Object.hasOwn(entry, key));
13756
+ return selector.length === 1 && typeof entry[selector[0]] === "string" && entry[selector[0]].length > 0;
13757
+ }
13758
+ function inference(target) {
13759
+ for (const key of INFERENCE)
13760
+ if (Object.hasOwn(target, key)) {
13761
+ const value = target[key];
13762
+ if (key === "model")
13763
+ need4(typeof value === "string" && MODEL.test(value));
13764
+ else
13765
+ need4(typeof value === "string" && (key === "model_reasoning_effort" ? EFFORT2.has(value) : VERBOSITY2.has(value)));
13766
+ delete target[key];
13767
+ }
13768
+ }
13769
+ function settingsDigest2(text2) {
13770
+ let config;
13771
+ try {
13772
+ config = Bun.TOML.parse(text2);
13773
+ } catch {
13774
+ throw new Error("Invalid Codex settings witness");
13775
+ }
13776
+ need4(isRecord3(config));
13777
+ normalize3(config);
13778
+ return sha2(`hasna.skills.codex-settings.v2\x00${JSON.stringify([canonical4(config), numericTokens2(text2)])}`);
13779
+ }
13780
+ function hashCodexSettingsReplacementV2(text2, budget = { remaining: 256 * 1024 * 1024 }) {
13781
+ need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
13782
+ budget.remaining -= Buffer.byteLength(text2);
13783
+ return settingsDigest2(text2);
13784
+ }
13785
+ function captureCodexSettingsV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
13786
+ const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
13787
+ return { path, hashMode: "codex-settings-v2", sha256: settingsDigest2(text2) };
13788
+ }
13789
+ function readCodexSettingsPreimage(path, budget = { remaining: 2 * 1024 * 1024 }) {
13790
+ return readNativeSettingsWitnessFile(path, budget, "config.toml");
13791
+ }
13586
13792
  function upgradeCodexSettingsWitness(previous, reviewedSettingsPath) {
13587
- need3(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes") && /^[a-f0-9]{64}$/.test(previous.sha256));
13793
+ need4(previous && Object.keys(previous).every((key) => ["path", "hashMode", "sha256"].includes(key)) && (previous.hashMode === undefined || previous.hashMode === "bytes" || previous.hashMode === "codex-settings-v1") && /^[a-f0-9]{64}$/.test(previous.sha256));
13588
13794
  const budget = { remaining: 2 * 1024 * 1024 };
13589
13795
  const before = readNativeSettingsWitnessFile(reviewedSettingsPath, budget, "config.toml"), current = readNativeSettingsWitnessFile(previous.path, budget, "config.toml");
13590
- need3(sha(before) === previous.sha256);
13591
- const sha2562 = hashCodexSettingsReplacement(current);
13592
- if (hashCodexSettingsReplacement(before) !== sha2562)
13796
+ need4((previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before)) === previous.sha256);
13797
+ const sha2562 = settingsDigest2(current);
13798
+ if (settingsDigest2(before) !== sha2562)
13593
13799
  throw new Error("Codex non-preference settings changed; explicit discovery review required");
13594
- return { path: previous.path, hashMode: "codex-settings-v1", sha256: sha2562 };
13800
+ return { path: previous.path, hashMode: "codex-settings-v2", sha256: sha2562 };
13595
13801
  }
13596
13802
 
13597
13803
  // src/lib/agent-integration.ts
13598
- import { createHash as createHash11, randomUUID as randomUUID2 } from "crypto";
13804
+ import { createHash as createHash12, randomUUID as randomUUID2 } from "crypto";
13805
+ import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
13599
13806
  import { existsSync as existsSync8, lstatSync as lstatSync15, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync12, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync4, realpathSync as realpathSync6, renameSync as renameSync2, rmdirSync as rmdirSync2, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, chmodSync, openSync as openSync8, closeSync as closeSync8, fsyncSync as fsyncSync2, fstatSync as fstatSync8, readSync as readSync6, constants as constants7, linkSync } from "fs";
13600
13807
  import { dirname as dirname11, join as join17, resolve as resolve10, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
13601
13808
  import { homedir as homedir2 } from "os";
@@ -13681,7 +13888,7 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
13681
13888
  }
13682
13889
 
13683
13890
  // src/lib/agent-bridge.ts
13684
- import { createHash as createHash10 } from "crypto";
13891
+ import { createHash as createHash11 } from "crypto";
13685
13892
  import { lstatSync as lstatSync14, readFileSync as readFileSync11, readdirSync as readdirSync7 } from "fs";
13686
13893
  import { join as join16 } from "path";
13687
13894
  var CLI_BRIDGE_NAME = "skills-cli";
@@ -13700,7 +13907,7 @@ Use the Skills CLI as the skill authority for this station.
13700
13907
 
13701
13908
  This bridge contains no skill payloads. Do not copy skill content into agent-native skill directories or load other native skill copies. If a Skills command refuses authority, selection, integrity, or native drift, report that refusal and use its repair guidance; do not substitute bundled or stale local content. Explicit local draft authoring remains available.
13702
13909
  `;
13703
- var CLI_BRIDGE_DIGEST = createHash10("sha256").update(markdown).digest("hex");
13910
+ var CLI_BRIDGE_DIGEST = createHash11("sha256").update(markdown).digest("hex");
13704
13911
  var CLI_BRIDGE_FILES = Object.freeze({
13705
13912
  "SKILL.md": markdown,
13706
13913
  ".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
@@ -13992,7 +14199,7 @@ function disableCodexBundledSkills(text2) {
13992
14199
  }
13993
14200
 
13994
14201
  // src/lib/agent-integration.ts
13995
- var sha2 = (data) => createHash11("sha256").update(data).digest("hex");
14202
+ var sha3 = (data) => createHash12("sha256").update(data).digest("hex");
13996
14203
  var ROOTS = NATIVE_SKILL_ROOTS;
13997
14204
  function assertSafePath(path) {
13998
14205
  let cursor = resolve10(path);
@@ -14099,7 +14306,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
14099
14306
  }
14100
14307
  }
14101
14308
  function treeHash(root) {
14102
- const hash2 = createHash11("sha256");
14309
+ const hash2 = createHash12("sha256");
14103
14310
  let bytes = 0, files = 0;
14104
14311
  function visit2(path) {
14105
14312
  const stat = lstatSync15(path);
@@ -14484,7 +14691,7 @@ function planAgentIntegration(options) {
14484
14691
  const observedNativeSources = [];
14485
14692
  const codexPluginSkills = options.codexNativeCatalog ? reviewCodexPluginSkillControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join17(entry.path, "SKILL.md")), canonicalAgentPath(join17(home, ".codex/plugins/cache"), aliases), resolve10(options.projectDir ?? home), (path) => {
14486
14693
  const bytes = readNativeBytes(path, 1024 * 1024);
14487
- observedNativeSources.push({ path, sha256: sha2(bytes) });
14694
+ observedNativeSources.push({ path, sha256: sha3(bytes) });
14488
14695
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
14489
14696
  }, codexConfig.skills?.config ?? []) : policy.bridge?.codexPluginSkills ?? [];
14490
14697
  const changes = [];
@@ -14512,7 +14719,7 @@ function planAgentIntegration(options) {
14512
14719
  throw new Error("Refusing to overwrite an unrecognized Hermes supervisor");
14513
14720
  if (supervisorBefore !== supervisorAfter)
14514
14721
  changes.push({ path: supervisorPath, before: supervisorBefore, after: supervisorAfter });
14515
- const supervisor = { path: supervisorPath, runtime: process.execPath, sha256: sha2(supervisorAfter) };
14722
+ const supervisor = { path: supervisorPath, runtime: process.execPath, sha256: sha3(supervisorAfter) };
14516
14723
  const after2 = configureHermesHooks(before, supervisor, previous);
14517
14724
  if (before !== after2)
14518
14725
  changes.push({ path, before, after: after2 });
@@ -14559,12 +14766,12 @@ function planAgentIntegration(options) {
14559
14766
  agents: [...new Set([...priorAgents, ...options.agents])].sort(),
14560
14767
  home: resolve10(home),
14561
14768
  includeVendor: true,
14562
- ...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join17(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha2(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
14769
+ ...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join17(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha3(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
14563
14770
  commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
14564
14771
  profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
14565
14772
  disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join17(home, ".codex", "skills", ".system"), aliases) + sep5)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
14566
14773
  rootAliases: aliases,
14567
- ...codexPluginSkills.length ? { codexPluginSkills, codexPluginSkillReview: options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha2(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join17(home, ".codex/config.toml"), aliases))?.before === null ? null : sha2(readOptional(canonicalAgentPath(join17(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview } : {},
14774
+ ...codexPluginSkills.length ? { codexPluginSkills, codexPluginSkillReview: options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join17(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join17(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview } : {},
14568
14775
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
14569
14776
  } };
14570
14777
  const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
@@ -14580,12 +14787,12 @@ function planClaudePreToolUseHookUpdate(options) {
14580
14787
  return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
14581
14788
  }
14582
14789
  function claudeSettingsConsumers(home, path, discoveries, aliases) {
14583
- const canonical4 = (value) => canonicalAgentPath(value, aliases);
14790
+ const canonical5 = (value) => canonicalAgentPath(value, aliases);
14584
14791
  const containsSettings = (root) => {
14585
- const distance = relative5(canonical4(root), path);
14792
+ const distance = relative5(canonical5(root), path);
14586
14793
  return distance === "" || distance !== ".." && !distance.startsWith(`..${sep5}`) && !isAbsolute11(distance);
14587
14794
  };
14588
- return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical4(join17(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical4(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
14795
+ return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical5(join17(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical5(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
14589
14796
  }
14590
14797
  function verifyCoordinatedDiscovery(binding, home, aliases) {
14591
14798
  try {
@@ -14617,7 +14824,7 @@ function planClaudeHookEventsUpdate(options) {
14617
14824
  const path = canonicalAgentPath(join17(home, ".claude", "settings.json"), aliases);
14618
14825
  assertSafePath(path);
14619
14826
  const before = lstatSync15(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
14620
- if ((before === null ? "absent" : sha2(before)) !== options.expectedSettingsSha256)
14827
+ if ((before === null ? "absent" : sha3(before)) !== options.expectedSettingsSha256)
14621
14828
  throw new Error("Claude settings preimage changed");
14622
14829
  assertClaudeHookEventsReplacement(before, options.replacement, options.events);
14623
14830
  const agents = bridge.agents;
@@ -14683,7 +14890,7 @@ function atomicWrite2(path, content) {
14683
14890
  function planAgentSettingsWitnessUpgrade(options) {
14684
14891
  const home = resolve10(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
14685
14892
  const snapshot = readManagedSkillPolicySnapshot(dataDir2);
14686
- if (!snapshot || sha2(snapshot.text) !== options.expectedPolicySha256)
14893
+ if (!snapshot || sha3(snapshot.text) !== options.expectedPolicySha256)
14687
14894
  throw new Error("Managed policy preimage changed");
14688
14895
  const policy = snapshot.value, aliases = policy.bridge?.rootAliases ?? [];
14689
14896
  recheckRootAliases(aliases);
@@ -14692,7 +14899,7 @@ function planAgentSettingsWitnessUpgrade(options) {
14692
14899
  throw new Error("Settings witness upgrade requires an existing reviewed native binding");
14693
14900
  const configPath = canonicalAgentPath(join17(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
14694
14901
  const settings = readOptional(configPath);
14695
- if (settings === null || sha2(settings) !== options.expectedSettingsSha256)
14902
+ if (settings === null || sha3(settings) !== options.expectedSettingsSha256)
14696
14903
  throw new Error("Native settings preimage changed");
14697
14904
  const sources = binding.sources.filter((source) => source.path === configPath && source.format === undefined && source.fields === undefined);
14698
14905
  if (sources.length !== 1)
@@ -14700,15 +14907,40 @@ function planAgentSettingsWitnessUpgrade(options) {
14700
14907
  const previous = sources[0];
14701
14908
  if (typeof previous.sha256 !== "string")
14702
14909
  throw new Error("Settings witness upgrade requires a preserved settings preimage");
14910
+ const replaced = [previous];
14911
+ if (options.agent === "codex") {
14912
+ if (previous.hashMode !== undefined && previous.hashMode !== "bytes" && previous.hashMode !== "codex-settings-v1")
14913
+ throw new Error("Codex settings witness upgrade requires a preserved raw or codex-settings-v1 configuration witness");
14914
+ const preimage = readCodexSettingsPreimage(options.reviewedPreimage);
14915
+ for (const source of binding.sources) {
14916
+ if (source === previous || source.path !== configPath)
14917
+ continue;
14918
+ const fields = source.fields;
14919
+ if (source.hashMode !== undefined || source.format !== "toml" || !Array.isArray(fields) || !isDeepStrictEqual3(fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]) || typeof source.sha256 !== "string")
14920
+ throw new Error("Codex settings witness upgrade found an unrecognized configuration witness");
14921
+ if (projectNativeDiscoveryFields(preimage, "toml", fields, configPath) !== source.sha256)
14922
+ throw new Error("Reviewed preimage does not explain the Codex discovery projection witness");
14923
+ replaced.push(source);
14924
+ }
14925
+ }
14703
14926
  const next = options.agent === "claude" ? upgradeClaudeSettingsWitness(previous, options.reviewedPreimage) : upgradeCodexSettingsWitness(previous, options.reviewedPreimage);
14704
- const replacement = { ...binding, sources: binding.sources.map((source) => source === previous ? next : source) };
14927
+ const rebound = [];
14928
+ for (const source of binding.sources) {
14929
+ if (!replaced.includes(source)) {
14930
+ rebound.push(source);
14931
+ continue;
14932
+ }
14933
+ if (source === replaced[0])
14934
+ rebound.push(next);
14935
+ }
14936
+ const replacement = { ...binding, sources: rebound };
14705
14937
  verifyAgentDiscovery(replacement);
14706
14938
  const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
14707
- return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join17(dataDir2, "agent-policy.json"), before: snapshot.text, after }], nativeSkills: [], observedPolicy: { path: join17(dataDir2, "agent-policy.json"), before: snapshot.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256 } };
14939
+ return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join17(dataDir2, "agent-policy.json"), before: snapshot.text, after }], nativeSkills: [], observedPolicy: { path: join17(dataDir2, "agent-policy.json"), before: snapshot.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
14708
14940
  }
14709
14941
  function applyAgentIntegration(plan) {
14710
14942
  for (const source of plan.observedNativeSources ?? [])
14711
- if (sha2(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
14943
+ if (sha3(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
14712
14944
  throw new Error("Native identity source changed after planning");
14713
14945
  if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
14714
14946
  throw new Error("Native settings changed after witness planning");
@@ -14728,7 +14960,7 @@ function applyAgentIntegration(plan) {
14728
14960
  const upgrade = plan.settingsWitnessUpgrade;
14729
14961
  if (!plan.managedAgentChecks || !plan.observedPolicy?.before)
14730
14962
  throw new Error("Invalid settings witness upgrade plan");
14731
- const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha2(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
14963
+ const verified = planAgentSettingsWitnessUpgrade({ agent: upgrade.agent, reviewedPreimage: upgrade.reviewedPreimage, home: plan.managedAgentChecks.home, dataDir: plan.dataDir, expectedPolicySha256: sha3(plan.observedPolicy.before), expectedSettingsSha256: upgrade.currentSettingsSha256 });
14732
14964
  if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.settingsWitnessUpgrade) !== JSON.stringify(upgrade))
14733
14965
  throw new Error("Settings witness upgrade plan changed");
14734
14966
  provenDiscovery = verified.discoveryBefore[0];
@@ -14756,7 +14988,7 @@ function applyAgentIntegration(plan) {
14756
14988
  chmodSync(backupRoot, 448);
14757
14989
  for (const [index, change] of plan.changes.entries())
14758
14990
  if (change.before !== null) {
14759
- const backup = join17(backupRoot, `${index}-${sha2(change.path).slice(0, 12)}.backup`);
14991
+ const backup = join17(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
14760
14992
  writeFileSync4(backup, change.before, { mode: 384, flag: "wx" });
14761
14993
  if (readFileSync12(backup, "utf8") !== change.before)
14762
14994
  throw new Error("Native configuration preservation readback failed");
@@ -14775,7 +15007,7 @@ function applyAgentIntegration(plan) {
14775
15007
  }
14776
15008
  recheckRootAliases(aliases);
14777
15009
  for (const source of plan.observedNativeSources ?? [])
14778
- if (sha2(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
15010
+ if (sha3(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
14779
15011
  throw new Error("Native identity source changed during application");
14780
15012
  if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
14781
15013
  throw new Error("Native settings changed during witness application");
@@ -14787,7 +15019,7 @@ function applyAgentIntegration(plan) {
14787
15019
  }
14788
15020
  for (const agent of plan.managedAgentChecks?.agents ?? [])
14789
15021
  assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
14790
- atomicWrite2(join17(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha2(change.before), afterHash: sha2(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
15022
+ atomicWrite2(join17(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha3(change.before), afterHash: sha3(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
14791
15023
  `);
14792
15024
  } catch (error) {
14793
15025
  for (const change of written.reverse()) {
@@ -14866,8 +15098,8 @@ function archiveNativeSkills(inventory, options) {
14866
15098
  const operationId = randomUUID2(), archiveRoot = join17(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join17(archiveRoot, "receipt.json");
14867
15099
  const entries = selected.map((entry, index) => ({
14868
15100
  source: entry.vendor ? join17(entry.path, "SKILL.md") : entry.path,
14869
- archive: join17(archiveRoot, `${index}-${sha2(entry.path).slice(0, 12)}`),
14870
- hash: entry.vendor ? sha2(readNativeBytes(join17(entry.path, "SKILL.md"))) : entry.hash,
15101
+ archive: join17(archiveRoot, `${index}-${sha3(entry.path).slice(0, 12)}`),
15102
+ hash: entry.vendor ? sha3(readNativeBytes(join17(entry.path, "SKILL.md"))) : entry.hash,
14871
15103
  ...entry.vendor ? { discoveryOnly: true } : {},
14872
15104
  status: "planned"
14873
15105
  }));
@@ -14878,7 +15110,7 @@ function archiveNativeSkills(inventory, options) {
14878
15110
  const stat = lstatSync15(entry.archive);
14879
15111
  if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
14880
15112
  throw new Error("Archive type changed");
14881
- if ((entry.discoveryOnly ? sha2(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
15113
+ if ((entry.discoveryOnly ? sha3(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
14882
15114
  throw new Error("Archive bytes changed");
14883
15115
  };
14884
15116
  assertSafePath(archiveRoot);
@@ -14998,7 +15230,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
14998
15230
  throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
14999
15231
  if (agent === "hermes") {
15000
15232
  const supervisor = binding.supervisors?.hermes;
15001
- if (supervisor?.path !== join17(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha2(renderHermesSupervisor(command, profile)))
15233
+ if (supervisor?.path !== join17(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha3(renderHermesSupervisor(command, profile)))
15002
15234
  throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
15003
15235
  assertHermesProtection(home, config, command, profile, supervisor);
15004
15236
  } else if (agent === "opencode") {
@@ -16296,7 +16528,7 @@ function exactProfileSelection(spec, profile) {
16296
16528
  }
16297
16529
 
16298
16530
  // src/lib/plugin-projection.ts
16299
- import { createHash as createHash12 } from "crypto";
16531
+ import { createHash as createHash13 } from "crypto";
16300
16532
  import { posix } from "path";
16301
16533
  var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
16302
16534
  var PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
@@ -16307,7 +16539,7 @@ function pluginNeed(value, message) {
16307
16539
  if (!value)
16308
16540
  pluginRefusal(message);
16309
16541
  }
16310
- var pluginHash = (bytes) => createHash12("sha256").update(bytes).digest("hex");
16542
+ var pluginHash = (bytes) => createHash13("sha256").update(bytes).digest("hex");
16311
16543
  function pluginObject(value) {
16312
16544
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
16313
16545
  }
@@ -16495,7 +16727,7 @@ function buildPluginProjection(entries) {
16495
16727
  }
16496
16728
 
16497
16729
  // src/lib/plugin-projection-store.ts
16498
- import { createHash as createHash13, randomUUID as randomUUID3 } from "crypto";
16730
+ import { createHash as createHash14, randomUUID as randomUUID3 } from "crypto";
16499
16731
  import { constants as constants8, closeSync as closeSync9, fchmodSync, fstatSync as fstatSync9, fsyncSync as fsyncSync3, linkSync as linkSync2, lstatSync as lstatSync17, mkdirSync as mkdirSync8, openSync as openSync9, opendirSync as opendirSync4, readSync as readSync7, renameSync as renameSync4, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync4, writeSync as writeSync2 } from "fs";
16500
16732
  import { dirname as dirname13, isAbsolute as isAbsolute12, join as join23, parse as parse5, resolve as resolve11 } from "path";
16501
16733
  var READ_FLAGS = constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK;
@@ -16511,7 +16743,7 @@ function stat(path) {
16511
16743
  return pluginRefusal("Cannot inspect plugin filesystem entry");
16512
16744
  }
16513
16745
  }
16514
- function canonical4(path) {
16746
+ function canonical5(path) {
16515
16747
  pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute12(path) && resolve11(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
16516
16748
  }
16517
16749
  function owner(stats, system = false) {
@@ -16522,7 +16754,7 @@ function same3(left, right, identityOnly = false) {
16522
16754
  pluginNeed(right && left.dev === right.dev && left.ino === right.ino && left.mode === right.mode && left.uid === right.uid && left.gid === right.gid && (identityOnly || left.size === right.size && left.nlink === right.nlink && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs), "Plugin filesystem entry changed during access");
16523
16755
  }
16524
16756
  function ancestors(path, create = false) {
16525
- canonical4(path);
16757
+ canonical5(path);
16526
16758
  const result = [], parent = dirname13(path), filesystemRoot = parse5(parent).root;
16527
16759
  let current = filesystemRoot;
16528
16760
  for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
@@ -16837,7 +17069,7 @@ function pluginExecutableDigest(path) {
16837
17069
  const opened = fstatSync9(fd, { bigint: true });
16838
17070
  same3(before, opened);
16839
17071
  fileShape(opened, maximum, "executable");
16840
- const hash2 = createHash13("sha256"), buffer = new Uint8Array(64 * 1024);
17072
+ const hash2 = createHash14("sha256"), buffer = new Uint8Array(64 * 1024);
16841
17073
  let remaining = Number(opened.size);
16842
17074
  while (remaining > 0) {
16843
17075
  const count = readSync7(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
@@ -17396,23 +17628,23 @@ function captureManagedPluginRegistry(path, managedPlugins) {
17396
17628
  }
17397
17629
 
17398
17630
  // src/lib/claude-marketplace-registry.ts
17399
- import { createHash as createHash14 } from "crypto";
17631
+ import { createHash as createHash15 } from "crypto";
17400
17632
  import { closeSync as closeSync11, constants as constants10, fstatSync as fstatSync11, lstatSync as lstatSync19, openSync as openSync11, readSync as readSync9 } from "fs";
17401
17633
  import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute15, resolve as resolve14 } from "path";
17402
17634
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
17403
- function need4(value, reason) {
17635
+ function need5(value, reason) {
17404
17636
  if (!value)
17405
17637
  throw new Error(`Claude marketplace registry ${reason}`);
17406
17638
  }
17407
17639
  function absolutePath3(value) {
17408
- need4(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute15(value) && resolve14(value) === value, "requires a normalized absolute path");
17640
+ need5(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute15(value) && resolve14(value) === value, "requires a normalized absolute path");
17409
17641
  }
17410
17642
  var sameFile2 = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
17411
17643
  function parents2(path) {
17412
17644
  const result = [];
17413
17645
  for (let at = dirname15(path);; at = dirname15(at)) {
17414
17646
  const stat2 = lstatSync19(at, { bigint: true });
17415
- need4(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
17647
+ need5(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
17416
17648
  result.push([at, stat2]);
17417
17649
  if (at === dirname15(at))
17418
17650
  return result;
@@ -17420,15 +17652,15 @@ function parents2(path) {
17420
17652
  }
17421
17653
  function readRegistry2(path, budget) {
17422
17654
  absolutePath3(path);
17423
- need4(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
17424
- need4(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
17655
+ need5(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
17656
+ need5(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
17425
17657
  const ancestors2 = parents2(path), initial = lstatSync19(path, { bigint: true });
17426
- need4(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
17427
- need4(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
17658
+ need5(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
17659
+ need5(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
17428
17660
  const fd = openSync11(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
17429
17661
  try {
17430
17662
  const opened = fstatSync11(fd, { bigint: true });
17431
- need4(opened.isFile() && sameFile2(initial, opened), "changed during open");
17663
+ need5(opened.isFile() && sameFile2(initial, opened), "changed during open");
17432
17664
  const bytes = Buffer.alloc(Number(opened.size) + 1);
17433
17665
  let length = 0;
17434
17666
  while (length < bytes.length) {
@@ -17438,10 +17670,10 @@ function readRegistry2(path, budget) {
17438
17670
  length += count;
17439
17671
  }
17440
17672
  const after = fstatSync11(fd, { bigint: true }), current = lstatSync19(path, { bigint: true });
17441
- need4(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
17673
+ need5(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
17442
17674
  for (const [ancestor, before] of ancestors2) {
17443
17675
  const now = lstatSync19(ancestor, { bigint: true });
17444
- need4(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
17676
+ need5(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
17445
17677
  }
17446
17678
  budget.remaining -= length;
17447
17679
  try {
@@ -17461,7 +17693,7 @@ function parse6(text2) {
17461
17693
  at++;
17462
17694
  };
17463
17695
  function string() {
17464
- need4(text2[at] === '"', "contains invalid JSON");
17696
+ need5(text2[at] === '"', "contains invalid JSON");
17465
17697
  const start = at++;
17466
17698
  while (at < text2.length) {
17467
17699
  const character = text2[at++];
@@ -17472,7 +17704,7 @@ function parse6(text2) {
17472
17704
  } catch {
17473
17705
  throw new Error("Claude marketplace registry contains invalid JSON string");
17474
17706
  }
17475
- need4(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
17707
+ need5(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
17476
17708
  return value2;
17477
17709
  }
17478
17710
  if (character === "\\")
@@ -17481,7 +17713,7 @@ function parse6(text2) {
17481
17713
  throw new Error("Claude marketplace registry contains an unterminated JSON string");
17482
17714
  }
17483
17715
  function value(depth) {
17484
- need4(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
17716
+ need5(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
17485
17717
  space();
17486
17718
  const character = text2[at];
17487
17719
  if (character === '"')
@@ -17495,17 +17727,17 @@ function parse6(text2) {
17495
17727
  if (object5) {
17496
17728
  space();
17497
17729
  const key = string();
17498
- need4(!keys.has(key), "contains a duplicate JSON key");
17730
+ need5(!keys.has(key), "contains a duplicate JSON key");
17499
17731
  keys.add(key);
17500
17732
  space();
17501
- need4(text2[at++] === ":", "contains invalid JSON");
17733
+ need5(text2[at++] === ":", "contains invalid JSON");
17502
17734
  entries.push([key, value(depth + 1)]);
17503
17735
  } else
17504
17736
  items.push(value(depth + 1));
17505
17737
  space();
17506
17738
  if (text2[at] === end)
17507
17739
  break;
17508
- need4(text2[at++] === ",", "contains invalid JSON");
17740
+ need5(text2[at++] === ",", "contains invalid JSON");
17509
17741
  }
17510
17742
  at++;
17511
17743
  return object5 ? { kind: "object", entries } : { kind: "array", items };
@@ -17517,14 +17749,14 @@ function parse6(text2) {
17517
17749
  }
17518
17750
  numberPattern.lastIndex = at;
17519
17751
  const number = numberPattern.exec(text2);
17520
- need4(number, "contains invalid JSON");
17752
+ need5(number, "contains invalid JSON");
17521
17753
  at += number[0].length;
17522
17754
  return { kind: "number", value: number[0] };
17523
17755
  }
17524
17756
  const root2 = value(0);
17525
17757
  space();
17526
- need4(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
17527
- need4(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
17758
+ need5(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
17759
+ need5(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
17528
17760
  return root2;
17529
17761
  }
17530
17762
  function exactKeys(value, keys) {
@@ -17532,7 +17764,7 @@ function exactKeys(value, keys) {
17532
17764
  }
17533
17765
  var field = (object5, key) => object5.entries.find(([name]) => name === key)?.[1];
17534
17766
  function textField(value) {
17535
- need4(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
17767
+ need5(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
17536
17768
  return value.value;
17537
17769
  }
17538
17770
  function normalizeRow(row, version = 1) {
@@ -17540,7 +17772,7 @@ function normalizeRow(row, version = 1) {
17540
17772
  if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
17541
17773
  return;
17542
17774
  const autoUpdate = field(row, "autoUpdate");
17543
- need4(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
17775
+ need5(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
17544
17776
  }
17545
17777
  const source = field(row, "source");
17546
17778
  if (source?.kind !== "object")
@@ -17556,34 +17788,34 @@ function normalizeRow(row, version = 1) {
17556
17788
  absolutePath3(sourceLocation);
17557
17789
  absolutePath3(textField(field(row, "installLocation")));
17558
17790
  const updated = textField(field(row, "lastUpdated"));
17559
- need4(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
17791
+ need5(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
17560
17792
  const millis = Date.parse(updated);
17561
- need4(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
17793
+ need5(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
17562
17794
  row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
17563
17795
  }
17564
17796
  function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
17565
17797
  const value = parse6(readRegistry2(path, budget));
17566
17798
  for (const [, row] of value.entries)
17567
17799
  normalizeRow(row);
17568
- const sha2562 = createHash14("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
17800
+ const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
17569
17801
  return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
17570
17802
  }
17571
17803
  function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
17572
17804
  const value = parse6(readRegistry2(path, budget));
17573
17805
  for (const [, row] of value.entries)
17574
17806
  normalizeRow(row, 2);
17575
- const sha2562 = createHash14("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
17807
+ const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
17576
17808
  return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
17577
17809
  }
17578
17810
 
17579
17811
  // src/lib/codex-hook-discovery-recovery.ts
17580
- import { createHash as createHash16 } from "crypto";
17812
+ import { createHash as createHash17 } from "crypto";
17581
17813
 
17582
17814
  // src/lib/codex-hook-trust-files.ts
17583
17815
  import { closeSync as closeSync12, constants as constants11, fstatSync as fstatSync12, fsyncSync as fsyncSync4, lstatSync as lstatSync20, openSync as openSync12, readSync as readSync10, writeFileSync as writeFileSync7 } from "fs";
17584
- import { createHash as createHash15 } from "crypto";
17816
+ import { createHash as createHash16 } from "crypto";
17585
17817
  import { dirname as dirname16 } from "path";
17586
- function need5(value, code) {
17818
+ function need6(value, code) {
17587
17819
  if (!value)
17588
17820
  throw new Error(`CODEX_HOOK_TRUST_${code}`);
17589
17821
  }
@@ -17591,8 +17823,8 @@ var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs
17591
17823
  function safeParents(file) {
17592
17824
  for (let p = dirname16(file);; p = dirname16(p)) {
17593
17825
  const s = lstatSync20(p);
17594
- need5(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
17595
- need5((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
17826
+ need6(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
17827
+ need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
17596
17828
  if (dirname16(p) === p)
17597
17829
  break;
17598
17830
  }
@@ -17601,12 +17833,12 @@ function snapshot(file, privateFile = false, options = {}) {
17601
17833
  const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
17602
17834
  safeParents(file);
17603
17835
  const initial = lstatSync20(file, { bigint: true });
17604
- need5(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
17605
- need5(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
17836
+ need6(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
17837
+ need6(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
17606
17838
  const fd = openSync12(file, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
17607
17839
  try {
17608
17840
  const before = fstatSync12(fd, { bigint: true });
17609
- need5(sameStat(initial, before), "INPUT_CHANGED");
17841
+ need6(sameStat(initial, before), "INPUT_CHANGED");
17610
17842
  const bytes = Buffer.alloc(Number(before.size) + 1);
17611
17843
  let length = 0;
17612
17844
  while (length < bytes.length) {
@@ -17615,16 +17847,16 @@ function snapshot(file, privateFile = false, options = {}) {
17615
17847
  break;
17616
17848
  length += n2;
17617
17849
  }
17618
- need5(length === Number(before.size) && sameStat(before, fstatSync12(fd, { bigint: true })) && sameStat(before, lstatSync20(file, { bigint: true })), "INPUT_CHANGED");
17850
+ need6(length === Number(before.size) && sameStat(before, fstatSync12(fd, { bigint: true })) && sameStat(before, lstatSync20(file, { bigint: true })), "INPUT_CHANGED");
17619
17851
  const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
17620
- return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash15("sha256").update(content).digest("hex") };
17852
+ return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash16("sha256").update(content).digest("hex") };
17621
17853
  } finally {
17622
17854
  closeSync12(fd);
17623
17855
  }
17624
17856
  }
17625
17857
  function unchanged2(before) {
17626
17858
  const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
17627
- need5(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
17859
+ need6(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
17628
17860
  }
17629
17861
 
17630
17862
  // src/lib/codex-hook-additions-review.ts
@@ -17633,11 +17865,12 @@ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
17633
17865
  var proofs = new WeakMap;
17634
17866
 
17635
17867
  // src/lib/codex-hook-discovery-recovery.ts
17636
- var sha3 = (text2) => createHash16("sha256").update(text2).digest("hex");
17868
+ var sha4 = (text2) => createHash17("sha256").update(text2).digest("hex");
17869
+ var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
17637
17870
  var proofs2 = new WeakMap;
17638
17871
  function assertCodexHookDiscoveryRecovery(binding, witness) {
17639
17872
  const proof = proofs2.get(witness);
17640
- need5(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha3(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
17873
+ need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
17641
17874
  unchanged2(proof.before);
17642
17875
  unchanged2(proof.current);
17643
17876
  proof.recheckAdditions();
@@ -17646,12 +17879,12 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
17646
17879
  if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
17647
17880
  return false;
17648
17881
  assertCodexHookDiscoveryRecovery(binding, witness);
17649
- need5(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
17882
+ need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
17650
17883
  return true;
17651
17884
  }
17652
17885
 
17653
17886
  // src/lib/agent-discovery.ts
17654
- var digest = (text2) => createHash17("sha256").update(text2).digest("hex");
17887
+ var digest = (text2) => createHash18("sha256").update(text2).digest("hex");
17655
17888
  function parseConfig(text2, path, toml = false) {
17656
17889
  try {
17657
17890
  const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
@@ -17683,14 +17916,26 @@ function read2(path, changes) {
17683
17916
  throw new Error(`Unsupported or oversized native discovery input: ${path}`);
17684
17917
  return readFileSync17(path, "utf8");
17685
17918
  }
17919
+ function projectNativeDiscoveryFields(text2, format, fields2, path) {
17920
+ const object5 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
17921
+ if (!object5 || typeof object5 !== "object" || Array.isArray(object5))
17922
+ throw new Error("Expected native discovery configuration object");
17923
+ return digest(JSON.stringify(Object.fromEntries(fields2.map((field2) => [field2, object5[field2] ?? null]))));
17924
+ }
17925
+ function isSupersededCodexProjection(agent, configPath, source) {
17926
+ return agent === "codex" && source.path === configPath && source.format === "toml" && isDeepStrictEqual4(source.fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]);
17927
+ }
17928
+ function supersedesCodexProjection(sources, configPath) {
17929
+ return sources.some((source) => source.path === configPath && source.hashMode === "codex-settings-v2");
17930
+ }
17686
17931
  function projected(source, changes, budget = discoveryByteBudget()) {
17687
- if (source.hashMode === "codex-settings-v1") {
17932
+ if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") {
17688
17933
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
17689
17934
  throw new Error("Codex settings witnesses require exact typed metadata");
17690
- const current = captureCodexSettings(source.path, budget).sha256;
17935
+ const current = (source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget).sha256;
17691
17936
  if (current !== source.sha256)
17692
17937
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
17693
- return changes?.has(source.path) ? hashCodexSettingsReplacement(changes.get(source.path), budget) : current;
17938
+ return changes?.has(source.path) ? (source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget) : current;
17694
17939
  }
17695
17940
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
17696
17941
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
@@ -17729,10 +17974,7 @@ function projected(source, changes, budget = discoveryByteBudget()) {
17729
17974
  return null;
17730
17975
  if (!source.format)
17731
17976
  return digest(text2);
17732
- const object5 = source.format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, source.path, source.format === "toml");
17733
- if (!object5 || typeof object5 !== "object" || Array.isArray(object5))
17734
- throw new Error("Expected native discovery configuration object");
17735
- return digest(JSON.stringify(Object.fromEntries((source.fields ?? []).map((field2) => [field2, object5[field2] ?? null]))));
17977
+ return projectNativeDiscoveryFields(text2, source.format, source.fields ?? [], source.path);
17736
17978
  }
17737
17979
  function verifyAgentDiscovery(binding, codexRecovery) {
17738
17980
  if (codexRecovery)
@@ -17760,7 +18002,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
17760
18002
  safe4(root2);
17761
18003
  }
17762
18004
  function assertMarketplaceBinding(binding, source) {
17763
- if (source.hashMode === "codex-settings-v1" && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
18005
+ if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
17764
18006
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
17765
18007
  if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename9(source.path) !== "settings.json"))
17766
18008
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
@@ -17789,8 +18031,8 @@ function captureDiscoveryPathSources(paths) {
17789
18031
  const budget = discoveryByteBudget();
17790
18032
  return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget) }));
17791
18033
  }
17792
- function assertProjectDiscovery(agent, directories, home, canonical5 = resolve15, reviewed) {
17793
- const homeConfigPath = canonical5(agentDiscoveryConfigPath(home, agent));
18034
+ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve15, reviewed) {
18035
+ const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
17794
18036
  for (const directory of directories) {
17795
18037
  const isHome = resolve15(directory) === resolve15(home);
17796
18038
  if (agent === "hermes") {
@@ -17801,7 +18043,7 @@ function assertProjectDiscovery(agent, directories, home, canonical5 = resolve15
17801
18043
  continue;
17802
18044
  const paths = agent === "claude" ? [...isHome ? [] : [".claude/settings.json"], ".claude/settings.local.json"] : agent === "codex" ? [".codex/config.toml"] : agent === "gemini" ? [".gemini/settings.json"] : agent === "opencode" ? ["opencode.json", "opencode.jsonc", ".opencode/opencode.json", ".opencode/opencode.jsonc"] : [".cursor/hooks.json"];
17803
18045
  for (const suffix of paths) {
17804
- const path = canonical5(join27(directory, suffix)), raw = read2(path);
18046
+ const path = canonical6(join27(directory, suffix)), raw = read2(path);
17805
18047
  if (raw === null)
17806
18048
  continue;
17807
18049
  const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
@@ -17817,14 +18059,14 @@ function assertProjectDiscovery(agent, directories, home, canonical5 = resolve15
17817
18059
  const managed = reviewed.sources.flatMap((source) => source.hashMode === "claude-plugin-registry" ? source.managedPlugins ?? [] : []);
17818
18060
  if (exact && projected(exact) === exact.sha256 && managed.length) {
17819
18061
  const bindings = managed.map((item) => readPluginBinding(item.storeRoot, item.bindingId));
17820
- admittedPlugins = Object.entries(config.enabledPlugins).every(([id, enabled]) => typeof enabled === "boolean" && bindings.some((binding) => binding.target.pluginId === id && binding.target.registrations.some((scope) => scope.scope === "project" && scope.projectPath === canonical5(directory))));
18062
+ admittedPlugins = Object.entries(config.enabledPlugins).every(([id, enabled]) => typeof enabled === "boolean" && bindings.some((binding) => binding.target.pluginId === id && binding.target.registrations.some((scope) => scope.scope === "project" && scope.projectPath === canonical6(directory))));
17821
18063
  }
17822
18064
  }
17823
18065
  if (keys.some((key) => config[key] !== undefined && !(key === "enabledPlugins" && admittedPlugins)) || config.disableAllHooks === true || config.disableBundledSkills === false || config.hooksConfig?.enabled === false || config.permission?.skill !== undefined || config.permissions?.deny?.some((rule) => typeof rule === "string" && /^Skill(?:\(|$)/.test(rule)))
17824
18066
  throw new Error(`NATIVE_SKILL_DRIFT: higher-precedence project skill or hook configuration requires review: ${path}`);
17825
18067
  }
17826
18068
  if (agent === "claude") {
17827
- const commands = canonical5(join27(directory, ".claude/commands"));
18069
+ const commands = canonical6(join27(directory, ".claude/commands"));
17828
18070
  safe4(commands);
17829
18071
  if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
17830
18072
  throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
@@ -17835,10 +18077,10 @@ function agentDiscoveryConfigPath(home, agent) {
17835
18077
  return join27(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
17836
18078
  }
17837
18079
  function resolveAgentDiscovery(options) {
17838
- const canonical5 = options.canonical ?? resolve15, home = resolve15(options.home), agent = options.agent;
18080
+ const canonical6 = options.canonical ?? resolve15, home = resolve15(options.home), agent = options.agent;
17839
18081
  const sources = [], roots = new Set, builtinNames = [];
17840
- const witness = (path, format, fields) => {
17841
- const source = { path: canonical5(path), sha256: null, ...format ? { format, fields } : {} };
18082
+ const witness = (path, format, fields2) => {
18083
+ const source = { path: canonical6(path), sha256: null, ...format ? { format, fields: fields2 } : {} };
17842
18084
  source.sha256 = projected(source);
17843
18085
  sources.push(source);
17844
18086
  return read2(source.path);
@@ -17846,7 +18088,7 @@ function resolveAgentDiscovery(options) {
17846
18088
  if (agent === "hermes")
17847
18089
  assertHermesEnvironment(home);
17848
18090
  const configPath = agentDiscoveryConfigPath(home, agent);
17849
- const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? ["plugins", "marketplaces", "skills"] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : ["version"]);
18091
+ const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? [...CODEX_DISCOVERY_PROJECTION_FIELDS] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : ["version"]);
17850
18092
  const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
17851
18093
  const unresolved = (detail) => {
17852
18094
  throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
@@ -17905,11 +18147,12 @@ function resolveAgentDiscovery(options) {
17905
18147
  if (retained.agent !== agent || retained.method !== "reviewed")
17906
18148
  unresolved("invalid retained review");
17907
18149
  verifyAgentDiscovery(retained);
17908
- if (!retained.sources.some((source) => source.path === canonical5(configPath) && source.format === undefined && source.fields === undefined))
18150
+ if (!retained.sources.some((source) => source.path === canonical6(configPath) && source.format === undefined && source.fields === undefined))
17909
18151
  unresolved("retained review is missing the full agent configuration source");
17910
- if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "codex-settings-v1")) && source.path !== canonical5(configPath)))
18152
+ if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2"))) && source.path !== canonical6(configPath)))
17911
18153
  unresolved("retained settings witness names another configuration source");
17912
- if (sources.some((source) => !retained.sources.some((saved) => isDeepStrictEqual3(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
18154
+ const covered = supersedesCodexProjection(retained.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
18155
+ if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual4(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
17913
18156
  unresolved("retained review is missing current runtime discovery coverage");
17914
18157
  return retained;
17915
18158
  }
@@ -17923,14 +18166,15 @@ function resolveAgentDiscovery(options) {
17923
18166
  if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
17924
18167
  throw new Error("Explicit discovery reviews require full source-file hashes");
17925
18168
  verifyAgentDiscovery(supplied);
17926
- if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "codex-settings-v1")) && source.path !== canonical5(configPath)))
18169
+ if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2"))) && source.path !== canonical6(configPath)))
17927
18170
  throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
17928
- if (!review.sources.some((source) => source.path === canonical5(configPath)))
18171
+ if (!review.sources.some((source) => source.path === canonical6(configPath)))
17929
18172
  throw new Error("Discovery review must include the agent configuration source");
17930
- return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...sources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
18173
+ const reviewedSources = supersedesCodexProjection(review.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
18174
+ return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...reviewedSources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
17931
18175
  }
17932
18176
  function plugin(root2) {
17933
- root2 = canonical5(root2);
18177
+ root2 = canonical6(root2);
17934
18178
  safe4(root2);
17935
18179
  const manifestPath = join27(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
17936
18180
  const raw = witness(manifestPath);
@@ -17962,7 +18206,7 @@ function resolveAgentDiscovery(options) {
17962
18206
  if (agent === "claude") {
17963
18207
  const registrations = witness(join27(home, ".claude/plugins/installed_plugins.json"));
17964
18208
  const installed = registrations === null ? {} : parseConfig(registrations, join27(home, ".claude/plugins/installed_plugins.json")).plugins;
17965
- const commands = canonical5(join27(home, ".claude/commands"));
18209
+ const commands = canonical6(join27(home, ".claude/commands"));
17966
18210
  safe4(commands);
17967
18211
  if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
17968
18212
  unresolved("legacy home command discovery requires a dedicated format adapter");
@@ -18036,7 +18280,7 @@ function resolveAgentDiscovery(options) {
18036
18280
  roots.add(join27(root2, "skills"));
18037
18281
  }
18038
18282
  }
18039
- return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join27(home, ".hermes/plugins"), join27(home, ".hermes/hermes-agent")].map((path) => canonical5(path))) } : {} };
18283
+ return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join27(home, ".hermes/plugins"), join27(home, ".hermes/hermes-agent")].map((path) => canonical6(path))) } : {} };
18040
18284
  }
18041
18285
 
18042
18286
  // src/lib/authoring-path.ts
@@ -18228,7 +18472,7 @@ function scaffoldPortableSkill(name, options = {}) {
18228
18472
  return { name: skillName, path: skillPath, manifest, created: true };
18229
18473
  }
18230
18474
  function portPortableSkillDirectory(sourceDir, options = {}) {
18231
- const absoluteSource = normalize3(sourceDir);
18475
+ const absoluteSource = normalize4(sourceDir);
18232
18476
  if (!existsSync13(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
18233
18477
  throw new Error(`Import directory not found: ${sourceDir}`);
18234
18478
  }
@@ -18295,7 +18539,7 @@ function assertDisjointImportPaths(source, destination) {
18295
18539
  }
18296
18540
  }
18297
18541
  function portPortableSkill(sourcePath, options = {}) {
18298
- const absoluteSource = normalize3(sourcePath);
18542
+ const absoluteSource = normalize4(sourcePath);
18299
18543
  if (!existsSync13(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
18300
18544
  throw new Error(`Skill source directory not found: ${sourcePath}`);
18301
18545
  }
@@ -18571,7 +18815,7 @@ function safeIsDirectory(path) {
18571
18815
  function isSafeRelativePath2(value) {
18572
18816
  if (!value.trim() || isAbsolute17(value))
18573
18817
  return false;
18574
- const normalized = normalize3(value).replace(/\\/g, "/");
18818
+ const normalized = normalize4(value).replace(/\\/g, "/");
18575
18819
  return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
18576
18820
  }
18577
18821
  function add3(target, code, message) {
@@ -18885,7 +19129,7 @@ function getAllTags() {
18885
19129
  return Array.from(tagSet).sort();
18886
19130
  }
18887
19131
  // src/lib/run-state.ts
18888
- import { createHash as createHash18, randomBytes } from "crypto";
19132
+ import { createHash as createHash19, randomBytes } from "crypto";
18889
19133
  import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync19, readdirSync as readdirSync13, statSync as statSync11, writeFileSync as writeFileSync9 } from "fs";
18890
19134
  import { extname, join as join31, relative as relative7 } from "path";
18891
19135
  function createSkillRun(params, targetDir = process.cwd()) {
@@ -19012,7 +19256,7 @@ function collectRunArtifacts(context) {
19012
19256
  artifacts.push({
19013
19257
  path: toProjectRelative(context.targetDir, path),
19014
19258
  mime: mimeForPath(path),
19015
- sha256: createHash18("sha256").update(bytes).digest("hex"),
19259
+ sha256: createHash19("sha256").update(bytes).digest("hex"),
19016
19260
  sizeBytes: stat2.size
19017
19261
  });
19018
19262
  }
@@ -22370,15 +22614,15 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
22370
22614
  } catch (_err) {}
22371
22615
  }
22372
22616
  const input = payload.value;
22373
- const isDate = input instanceof Date;
22374
- const isValidDate = isDate && !Number.isNaN(input.getTime());
22617
+ const isDate2 = input instanceof Date;
22618
+ const isValidDate = isDate2 && !Number.isNaN(input.getTime());
22375
22619
  if (isValidDate)
22376
22620
  return payload;
22377
22621
  payload.issues.push({
22378
22622
  expected: "date",
22379
22623
  code: "invalid_type",
22380
22624
  input,
22381
- ...isDate ? { received: "Invalid Date" } : {},
22625
+ ...isDate2 ? { received: "Invalid Date" } : {},
22382
22626
  inst
22383
22627
  });
22384
22628
  return payload;
@@ -38431,7 +38675,7 @@ function clone2(value) {
38431
38675
  return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
38432
38676
  }
38433
38677
  // src/lib/canonical-json.ts
38434
- import { createHash as createHash19 } from "crypto";
38678
+ import { createHash as createHash20 } from "crypto";
38435
38679
  function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
38436
38680
  return canonicalJsonAtDepth(value, maximumBytes, 64);
38437
38681
  }
@@ -38475,7 +38719,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
38475
38719
  return visit3(value, 0);
38476
38720
  }
38477
38721
  function canonicalJsonSha256(value, maximumBytes) {
38478
- return createHash19("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
38722
+ return createHash20("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
38479
38723
  }
38480
38724
 
38481
38725
  // src/lib/remote-recurring-contract.ts
@@ -39231,7 +39475,7 @@ function getApiUrl(action, env = process.env, options = {}) {
39231
39475
  // src/lib/remote-run-contract.ts
39232
39476
  var REMOTE_SKILL_RUN_CONTRACT_VERSION = 1;
39233
39477
  function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
39234
- const record6 = isRecord3(payload) ? payload : {};
39478
+ const record6 = isRecord4(payload) ? payload : {};
39235
39479
  return {
39236
39480
  contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION,
39237
39481
  ...pickString(record6, "id"),
@@ -39253,7 +39497,7 @@ function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
39253
39497
  ...hasOwn(record6, "details") ? { details: record6.details } : {}
39254
39498
  };
39255
39499
  }
39256
- function isRecord3(value) {
39500
+ function isRecord4(value) {
39257
39501
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
39258
39502
  }
39259
39503
  function hasOwn(record6, key) {
@@ -39385,7 +39629,7 @@ function creditCheckoutFailure(value, status, key) {
39385
39629
 
39386
39630
  // src/lib/remote-profile.ts
39387
39631
  function customerNamePatch(input2, field2) {
39388
- if (!isRecord4(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field2))
39632
+ if (!isRecord5(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field2))
39389
39633
  throw new Error("Provide only the requested name field.");
39390
39634
  const value = input2[field2];
39391
39635
  if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
@@ -39393,22 +39637,22 @@ function customerNamePatch(input2, field2) {
39393
39637
  }
39394
39638
  return { [field2]: value.trim() };
39395
39639
  }
39396
- function isRecord4(value) {
39640
+ function isRecord5(value) {
39397
39641
  return !!value && typeof value === "object" && !Array.isArray(value);
39398
39642
  }
39399
39643
  function string4(value) {
39400
39644
  return typeof value === "string" && value.length > 0;
39401
39645
  }
39402
39646
  function parseUpdatedProfile(value) {
39403
- const user = isRecord4(value) && value.user;
39404
- if (!isRecord4(user) || !string4(user.id) || !string4(user.email) || !(user.displayName === null || typeof user.displayName === "string") || typeof user.role !== "string" || !["owner", "admin", "member", "viewer"].includes(user.role)) {
39647
+ const user = isRecord5(value) && value.user;
39648
+ if (!isRecord5(user) || !string4(user.id) || !string4(user.email) || !(user.displayName === null || typeof user.displayName === "string") || typeof user.role !== "string" || !["owner", "admin", "member", "viewer"].includes(user.role)) {
39405
39649
  throw new Error("The server returned an invalid account profile.");
39406
39650
  }
39407
39651
  return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
39408
39652
  }
39409
39653
  function parseUpdatedWorkspace(value) {
39410
- const organization2 = isRecord4(value) && value.organization;
39411
- if (!isRecord4(organization2) || !string4(organization2.id) || !string4(organization2.slug) || !string4(organization2.name)) {
39654
+ const organization2 = isRecord5(value) && value.organization;
39655
+ if (!isRecord5(organization2) || !string4(organization2.id) || !string4(organization2.slug) || !string4(organization2.name)) {
39412
39656
  throw new Error("The server returned an invalid workspace.");
39413
39657
  }
39414
39658
  return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
@@ -40597,11 +40841,11 @@ function validateCronField(expr, min, max, label) {
40597
40841
  return { valid: true };
40598
40842
  }
40599
40843
  function validateCron(expr) {
40600
- const fields = expr.trim().split(/\s+/);
40601
- if (fields.length !== 5) {
40602
- return { valid: false, error: `Expected 5 fields, got ${fields.length}. Format: "minute hour day-of-month month day-of-week"` };
40844
+ const fields2 = expr.trim().split(/\s+/);
40845
+ if (fields2.length !== 5) {
40846
+ return { valid: false, error: `Expected 5 fields, got ${fields2.length}. Format: "minute hour day-of-month month day-of-week"` };
40603
40847
  }
40604
- const [minuteF, hourF, domF, monthF, dowF] = fields;
40848
+ const [minuteF, hourF, domF, monthF, dowF] = fields2;
40605
40849
  const checks3 = [
40606
40850
  { expr: minuteF, min: 0, max: 59, label: "minute" },
40607
40851
  { expr: hourF, min: 0, max: 23, label: "hour" },
@@ -40742,7 +40986,7 @@ function recordScheduleRun(id, status, targetDir) {
40742
40986
  saveSchedules(data, targetDir);
40743
40987
  }
40744
40988
  // src/lib/revision.ts
40745
- import { createHash as createHash20 } from "crypto";
40989
+ import { createHash as createHash21 } from "crypto";
40746
40990
  var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
40747
40991
  function revisionIdOf(content) {
40748
40992
  const canonicalContent = {
@@ -40763,8 +41007,8 @@ function revisionIdOf(content) {
40763
41007
  canonicalContent.archiveReason = content.archiveReason ?? null;
40764
41008
  canonicalContent.replacementSlug = content.replacementSlug ?? null;
40765
41009
  }
40766
- const canonical5 = JSON.stringify(canonicalContent);
40767
- return createHash20("sha256").update(canonical5).digest("hex");
41010
+ const canonical6 = JSON.stringify(canonicalContent);
41011
+ return createHash21("sha256").update(canonical6).digest("hex");
40768
41012
  }
40769
41013
  function revisionIdOfRecord(record7) {
40770
41014
  return revisionIdOf(record7);
@@ -41305,7 +41549,7 @@ async function requestInvitationEmail(origin, action, input2) {
41305
41549
  }
41306
41550
 
41307
41551
  // src/lib/remote-private-publications.ts
41308
- import { createHash as createHash21 } from "crypto";
41552
+ import { createHash as createHash22 } from "crypto";
41309
41553
  var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
41310
41554
  var failures2 = {
41311
41555
  INVALID_REQUEST: [400, "The publication request is invalid."],
@@ -41352,7 +41596,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
41352
41596
  var record8 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
41353
41597
  var exact = (v, keys) => record8(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
41354
41598
  var date5 = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
41355
- var publicationSha256 = (bytes) => createHash21("sha256").update(bytes).digest("hex");
41599
+ var publicationSha256 = (bytes) => createHash22("sha256").update(bytes).digest("hex");
41356
41600
  function checkedPublicationDeclaration(value) {
41357
41601
  if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash3(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
41358
41602
  return bad();
@@ -41651,7 +41895,7 @@ async function requestAuthApi(instance, path, options) {
41651
41895
  const text3 = await res.text();
41652
41896
  const body = text3 ? parseJsonBody(text3) : {};
41653
41897
  if (!res.ok) {
41654
- const record9 = isRecord5(body) ? body : {};
41898
+ const record9 = isRecord6(body) ? body : {};
41655
41899
  const detail = typeof record9.detail === "string" ? record9.detail : undefined;
41656
41900
  const error61 = typeof record9.error === "string" ? record9.error : undefined;
41657
41901
  const code = typeof record9.code === "string" ? record9.code : undefined;
@@ -41679,7 +41923,7 @@ function condenseErrorBody(text3) {
41679
41923
  return collapsed;
41680
41924
  return `${collapsed.slice(0, MAX_ERROR_DETAIL_LENGTH - 1).trimEnd()}\u2026`;
41681
41925
  }
41682
- function isRecord5(value) {
41926
+ function isRecord6(value) {
41683
41927
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
41684
41928
  }
41685
41929
 
@@ -41836,7 +42080,7 @@ class RemoteSkillsAuthClient {
41836
42080
 
41837
42081
  // src/lib/recurring-recovery.ts
41838
42082
  var uuid12 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
41839
- var sha4 = exports_external.string().regex(/^[a-f0-9]{64}$/);
42083
+ var sha5 = exports_external.string().regex(/^[a-f0-9]{64}$/);
41840
42084
  var common = exports_external.object({
41841
42085
  contractVersion: exports_external.literal(1),
41842
42086
  apiOrigin: exports_external.string().min(1).max(4096),
@@ -41849,7 +42093,7 @@ var intentSchema = exports_external.discriminatedUnion("operation", [
41849
42093
  common.extend({
41850
42094
  operation: exports_external.literal("activate"),
41851
42095
  draftId: uuid12,
41852
- termsSha256: sha4,
42096
+ termsSha256: sha5,
41853
42097
  approvalDeadline: exports_external.string().datetime(),
41854
42098
  approval: recurringActivationSchema
41855
42099
  }).strict(),
@@ -41857,7 +42101,7 @@ var intentSchema = exports_external.discriminatedUnion("operation", [
41857
42101
  ]);
41858
42102
  var stateSchema = exports_external.object({
41859
42103
  contractVersion: exports_external.literal(1),
41860
- intentSha256: sha4,
42104
+ intentSha256: sha5,
41861
42105
  phase: exports_external.enum(["prepared", "pending", "observed"]),
41862
42106
  consentId: uuid12.nullable()
41863
42107
  }).strict();
@@ -43218,7 +43462,7 @@ function isApiMode(env = process.env) {
43218
43462
  }
43219
43463
  }
43220
43464
  // src/lib/native-storage.ts
43221
- import { createHash as createHash22, createHmac as createHmac2 } from "crypto";
43465
+ import { createHash as createHash23, createHmac as createHmac2 } from "crypto";
43222
43466
  import {
43223
43467
  existsSync as existsSync19,
43224
43468
  mkdirSync as mkdirSync15,
@@ -43227,7 +43471,7 @@ import {
43227
43471
  statSync as statSync12,
43228
43472
  writeFileSync as writeFileSync13
43229
43473
  } from "fs";
43230
- import { dirname as dirname23, join as join35, normalize as normalize4, relative as relative9, sep as sep9 } from "path";
43474
+ import { dirname as dirname23, join as join35, normalize as normalize5, relative as relative9, sep as sep9 } from "path";
43231
43475
  var SKILLS_STORAGE_TABLES = [
43232
43476
  "skills_sync_records",
43233
43477
  "skills_sync_cursors"
@@ -43394,7 +43638,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
43394
43638
  files.push({
43395
43639
  path: relativePath,
43396
43640
  sizeBytes: bytes.byteLength,
43397
- sha256: createHash22("sha256").update(bytes).digest("hex"),
43641
+ sha256: createHash23("sha256").update(bytes).digest("hex"),
43398
43642
  ...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
43399
43643
  });
43400
43644
  }
@@ -43419,7 +43663,7 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
43419
43663
  continue;
43420
43664
  }
43421
43665
  const bytes = Buffer.from(file2.contentBase64, "base64");
43422
- const hash4 = createHash22("sha256").update(bytes).digest("hex");
43666
+ const hash4 = createHash23("sha256").update(bytes).digest("hex");
43423
43667
  if (hash4 !== file2.sha256) {
43424
43668
  throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
43425
43669
  }
@@ -43731,7 +43975,7 @@ function walkFiles2(dir) {
43731
43975
  return files;
43732
43976
  }
43733
43977
  function resolveSnapshotPath(targetDir, snapshotPath) {
43734
- const normalizedPath = normalize4(snapshotPath);
43978
+ const normalizedPath = normalize5(snapshotPath);
43735
43979
  if (normalizedPath.startsWith("..") || normalizedPath.includes(`${sep9}..${sep9}`) || normalizedPath.startsWith(sep9)) {
43736
43980
  throw new Error(`Unsafe snapshot path: ${snapshotPath}`);
43737
43981
  }
@@ -43751,7 +43995,7 @@ function toIsoString(value) {
43751
43995
  return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
43752
43996
  }
43753
43997
  function sha256Hex2(value) {
43754
- return createHash22("sha256").update(value).digest("hex");
43998
+ return createHash23("sha256").update(value).digest("hex");
43755
43999
  }
43756
44000
  function normalizeHeaders(headers) {
43757
44001
  const result = {};
@@ -43803,7 +44047,7 @@ function toArrayBuffer(bytes) {
43803
44047
  return buffer;
43804
44048
  }
43805
44049
  // src/lib/station-snapshot.ts
43806
- import { createHash as createHash23 } from "crypto";
44050
+ import { createHash as createHash24 } from "crypto";
43807
44051
  import {
43808
44052
  copyFileSync,
43809
44053
  mkdirSync as mkdirSync16,
@@ -43978,7 +44222,7 @@ function validateStationId(stationId) {
43978
44222
  }
43979
44223
  }
43980
44224
  function sha256File(filePath) {
43981
- return createHash23("sha256").update(readFileSync25(filePath)).digest("hex");
44225
+ return createHash24("sha256").update(readFileSync25(filePath)).digest("hex");
43982
44226
  }
43983
44227
  function scanHome(definition, homesRoot) {
43984
44228
  const homePath = homePathFor(definition, homesRoot);
@@ -44129,7 +44373,7 @@ function writeStationSnapshot(options) {
44129
44373
  };
44130
44374
  }
44131
44375
  // src/lib/station-hydrate.ts
44132
- import { createHash as createHash24 } from "crypto";
44376
+ import { createHash as createHash25 } from "crypto";
44133
44377
  import {
44134
44378
  copyFileSync as copyFileSync2,
44135
44379
  mkdirSync as mkdirSync17,
@@ -44335,7 +44579,7 @@ function skillSha256(skill) {
44335
44579
  return sha256File(skill.files[0].winner.fullPath);
44336
44580
  }
44337
44581
  const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
44338
- return createHash24("sha256").update(joined.sort().join(`
44582
+ return createHash25("sha256").update(joined.sort().join(`
44339
44583
  `)).digest("hex");
44340
44584
  }
44341
44585
  function writeStationHydration(options) {
@@ -44663,7 +44907,7 @@ async function inspectLocked(client2, directory2, cancel) {
44663
44907
  return privatePublicationResult(directory2, receipt);
44664
44908
  }
44665
44909
  // src/lib/skill-context.ts
44666
- import { createHash as createHash25 } from "crypto";
44910
+ import { createHash as createHash26 } from "crypto";
44667
44911
  import { resolve as resolve21 } from "path";
44668
44912
  function words(value) {
44669
44913
  return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
@@ -44752,7 +44996,7 @@ ${content}`;
44752
44996
  `);
44753
44997
  const receipt = {
44754
44998
  schemaVersion: 1,
44755
- id: createHash25("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
44999
+ id: createHash26("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
44756
45000
  profileId: profile.profileId,
44757
45001
  profileRevision: profile.profileRevision,
44758
45002
  authority: profile.authority,
@@ -44768,9 +45012,9 @@ ${content}`;
44768
45012
  return { context: context2, selections, omitted, receipt };
44769
45013
  }
44770
45014
  // src/lib/session-reconciliation.ts
44771
- import { createHash as createHash26 } from "crypto";
45015
+ import { createHash as createHash27 } from "crypto";
44772
45016
  var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
44773
- var digest3 = (value) => createHash26("sha256").update(JSON.stringify(value)).digest("hex");
45017
+ var digest3 = (value) => createHash27("sha256").update(JSON.stringify(value)).digest("hex");
44774
45018
  function exactTimestamp(value) {
44775
45019
  const parsed = value === undefined ? Number.NaN : Date.parse(value);
44776
45020
  if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
@@ -45069,6 +45313,7 @@ export {
45069
45313
  captureDiscoveryPathSources,
45070
45314
  captureDiscoveryDirectories,
45071
45315
  captureDiscoveryByteSources,
45316
+ captureCodexSettingsV2,
45072
45317
  captureCodexSettings,
45073
45318
  captureCodexNativeSkillCatalog,
45074
45319
  captureClaudeSettingsV3,