@hasna/skills 0.10.20 → 0.10.23

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,16 @@ 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", "mcpSha256", "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));
9049
+ if (control.mcpSha256 !== undefined)
9050
+ requireBound(typeof control.mcpSha256 === "string" && /^[a-f0-9]{64}$/.test(control.mcpSha256));
9047
9051
  if (control.appSha256 !== undefined)
9048
9052
  requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
9049
9053
  }
@@ -9066,8 +9070,8 @@ function assertAgentPolicyCollections(policy) {
9066
9070
  text(source.path);
9067
9071
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
9068
9072
  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")
9073
+ 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));
9074
+ if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2")
9071
9075
  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
9076
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
9073
9077
  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 +9107,10 @@ function assertAgentPolicyCollections(policy) {
9103
9107
  }
9104
9108
 
9105
9109
  // src/lib/agent-discovery.ts
9106
- import { createHash as createHash17 } from "crypto";
9110
+ import { createHash as createHash18 } from "crypto";
9107
9111
  import { lstatSync as lstatSync21, readFileSync as readFileSync17, readdirSync as readdirSync10, realpathSync as realpathSync7 } from "fs";
9108
9112
  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";
9113
+ import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
9110
9114
 
9111
9115
  // node_modules/yaml/dist/index.js
9112
9116
  var composer = require_composer();
@@ -10877,7 +10881,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
10877
10881
  // package.json
10878
10882
  var package_default = {
10879
10883
  name: "@hasna/skills",
10880
- version: "0.10.20",
10884
+ version: "0.10.23",
10881
10885
  description: "Skills library for AI coding agents",
10882
10886
  type: "module",
10883
10887
  bin: {
@@ -13027,6 +13031,25 @@ function hashNativeJsonControls(text2, excludedMetadata) {
13027
13031
  import { basename as basename5, dirname as dirname10, join as join14, relative as relative4, resolve as resolve9, sep as sep4 } from "path";
13028
13032
  import { lstatSync as lstatSync12, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync5 } from "fs";
13029
13033
 
13034
+ // src/lib/codex-native-compatibility.ts
13035
+ var MEASURED_CODEX_CAPABILITIES = {
13036
+ "codex-cli 0.153.0": ["hooks"],
13037
+ "codex-cli 0.154.0": ["hooks"],
13038
+ "codex-cli 0.155.0": ["hooks"],
13039
+ "codex-cli 0.155.1": ["hooks"],
13040
+ "codex-cli 0.156.1": ["hooks"],
13041
+ "codex-cli 0.157.0": ["hooks"],
13042
+ "codex-cli 0.157.1": ["hooks"],
13043
+ "codex-cli 0.158.0": ["hooks"],
13044
+ "codex-cli 0.159.0": ["hooks"],
13045
+ "codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
13046
+ "codex-cli 0.160.0": ["hooks", "qualified-skill-catalog"]
13047
+ };
13048
+ function supportsCodexNativeCapability(version, capability) {
13049
+ return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
13050
+ }
13051
+ var SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
13052
+
13030
13053
  // src/lib/codex-native-skill-catalog.ts
13031
13054
  import { realpathSync as realpathSync4 } from "fs";
13032
13055
  import { isAbsolute as isAbsolute10, normalize as normalize2 } from "path";
@@ -13036,7 +13059,6 @@ import { execFile, spawn } from "child_process";
13036
13059
  import { realpathSync as realpathSync3, statSync as statSync7 } from "fs";
13037
13060
  import { isAbsolute as isAbsolute9 } from "path";
13038
13061
  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
13062
  async function connectCodexHookRpc(options) {
13041
13063
  const timeoutMs = options.timeoutMs ?? 20000;
13042
13064
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 20000)
@@ -13054,7 +13076,7 @@ async function connectCodexHookRpc(options) {
13054
13076
  } catch {
13055
13077
  throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE");
13056
13078
  }
13057
- if (!SUPPORTED_CODEX_HOOK_VERSIONS.includes(version))
13079
+ if (!supportsCodexNativeCapability(version, "hooks"))
13058
13080
  throw new Error("CODEX_HOOK_TRUST_NATIVE_UNSUPPORTED_VERSION");
13059
13081
  const current = statSync7(binary);
13060
13082
  if (realpathSync3(found) !== binary || ["dev", "ino", "size", "mtimeMs", "ctimeMs"].some((k) => stat[k] !== current[k]))
@@ -13179,12 +13201,12 @@ var MAX_SKILLS = 4096;
13179
13201
  var MAX_NAME_BYTES = 1024;
13180
13202
  var MAX_PATH_BYTES = 16384;
13181
13203
  var MAX_PLUGIN_ID_BYTES = 1024;
13182
- var SUPPORTED_NAME_CONTROL_VERSIONS = new Set(["codex-cli 0.159.2"]);
13183
13204
  function refuse() {
13184
13205
  throw new Error("CODEX_NATIVE_SKILL_CATALOG_INVALID");
13185
13206
  }
13186
13207
  var object4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
13187
13208
  var scalar = (value, max) => typeof value === "string" && value.length > 0 && value.trim() === value && Buffer.byteLength(value, "utf8") <= max && !/[\u0000-\u001f\u007f]/u.test(value);
13209
+ var remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value);
13188
13210
  var absolutePath2 = (value) => scalar(value, MAX_PATH_BYTES) && isAbsolute10(value);
13189
13211
  function projectCodexNativeSkillCatalog(response, cwd) {
13190
13212
  if (!absolutePath2(cwd) || !object4(response) || !Array.isArray(response.data) || response.data.length !== 1)
@@ -13212,7 +13234,11 @@ function projectCodexInstalledPlugins(response) {
13212
13234
  for (const value of marketplace.plugins) {
13213
13235
  if (!object4(value) || !scalar(value.id, MAX_PLUGIN_ID_BYTES) || !scalar(value.name, MAX_NAME_BYTES) || value.id !== `${value.name}@${marketplace.name}`)
13214
13236
  refuse();
13215
- plugins.push({ ...value, marketplace: marketplace.name });
13237
+ plugins.push({
13238
+ ...value,
13239
+ marketplace: marketplace.name,
13240
+ sourceType: object4(value.source) && value.source.type === "remote" ? "remote" : undefined
13241
+ });
13216
13242
  }
13217
13243
  }
13218
13244
  return projectCodexInstalledPluginEntries(plugins);
@@ -13222,13 +13248,21 @@ function projectCodexInstalledPluginEntries(value) {
13222
13248
  refuse();
13223
13249
  const ids = new Set;
13224
13250
  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))
13251
+ 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
13252
  refuse();
13227
13253
  const at = item.id.lastIndexOf("@");
13228
13254
  if (at < 1 || item.id.slice(0, at) !== item.name || at === item.id.length - 1 || ids.has(item.id))
13229
13255
  refuse();
13230
13256
  ids.add(item.id);
13231
- return { id: item.id, name: item.name, installed: item.installed, enabled: item.enabled, localVersion: item.localVersion ?? null };
13257
+ return {
13258
+ id: item.id,
13259
+ name: item.name,
13260
+ installed: item.installed,
13261
+ enabled: item.enabled,
13262
+ localVersion: item.localVersion ?? null,
13263
+ ...item.sourceType === "remote" ? { sourceType: "remote" } : {},
13264
+ ...item.remotePluginId == null ? {} : { remotePluginId: item.remotePluginId }
13265
+ };
13232
13266
  });
13233
13267
  }
13234
13268
  async function captureCodexNativeSkillCatalog(options, connect = connectCodexHookRpc) {
@@ -13241,7 +13275,7 @@ async function captureCodexNativeSkillCatalog(options, connect = connectCodexHoo
13241
13275
  ...options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }
13242
13276
  });
13243
13277
  try {
13244
- if (!SUPPORTED_NAME_CONTROL_VERSIONS.has(rpc.version))
13278
+ if (!supportsCodexNativeCapability(rpc.version, "qualified-skill-catalog"))
13245
13279
  throw new Error("CODEX_NATIVE_SKILL_CATALOG_UNSUPPORTED_VERSION");
13246
13280
  const response = await rpc.request("skills/list", { cwds: [options.cwd], forceReload: true });
13247
13281
  const installed = await rpc.request("plugin/installed", { cwds: [options.cwd] });
@@ -13299,6 +13333,21 @@ var CODEX_DEFAULT_AGENT_PLUGIN_VERSION = "1.0.0";
13299
13333
  function refuse2() {
13300
13334
  throw new Error("CODEX_NATIVE_SKILL_IDENTITY_UNSUPPORTED: review native plugin names and hook controls");
13301
13335
  }
13336
+ function remoteInstallationMatches(parent, remotePluginId, read2) {
13337
+ try {
13338
+ if (!remotePluginIdentifier(remotePluginId) || !lstatSync12(parent).isDirectory() || realpathSync5(parent) !== parent)
13339
+ return false;
13340
+ const path = join14(parent, ".codex-remote-plugin-install.json"), stat = lstatSync12(path, { throwIfNoEntry: false });
13341
+ if (!stat?.isFile() || stat.isSymbolicLink() || stat.size > 16384)
13342
+ return false;
13343
+ const text2 = read2(path);
13344
+ hashNativeJsonControls(text2);
13345
+ const value = JSON.parse(text2);
13346
+ return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 2 && value.schema_version === 1 && value.remote_plugin_id === remotePluginId);
13347
+ } catch {
13348
+ return false;
13349
+ }
13350
+ }
13302
13351
  function appControls(path, read2) {
13303
13352
  const text2 = read2(path);
13304
13353
  let value, sha2562;
@@ -13320,6 +13369,42 @@ function appControls(path, read2) {
13320
13369
  }
13321
13370
  return sha2562;
13322
13371
  }
13372
+ function mcpControls(path, read2) {
13373
+ const text2 = read2(path);
13374
+ let value, sha2562;
13375
+ try {
13376
+ sha2562 = hashNativeJsonControls(text2);
13377
+ value = JSON.parse(text2);
13378
+ } catch {
13379
+ refuse2();
13380
+ }
13381
+ const object5 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
13382
+ const textField = (v, empty = false) => typeof v === "string" && (empty || Boolean(v.trim())) && Buffer.byteLength(v) <= 16384 && !/[\x00-\x1f\x7f]/.test(v);
13383
+ const envName = (v) => typeof v === "string" && /^[A-Za-z_][A-Za-z0-9_]{0,255}$/.test(v);
13384
+ if (!object5(value) || Object.keys(value).length !== 1 || !object5(value.mcpServers))
13385
+ refuse2();
13386
+ const servers = Object.entries(value.mcpServers);
13387
+ if (!servers.length || servers.length > 256)
13388
+ refuse2();
13389
+ for (const [name, server] of servers) {
13390
+ if (!identifier2(name) || !object5(server) || !Object.keys(server).every((key) => ["command", "args", "cwd", "env_vars"].includes(key)) || !textField(server.command) || server.args !== undefined && (!Array.isArray(server.args) || server.args.length > 256 || !server.args.every((arg) => textField(arg, true))) || server.cwd !== undefined && !textField(server.cwd))
13391
+ refuse2();
13392
+ if (server.env_vars !== undefined) {
13393
+ if (!Array.isArray(server.env_vars) || server.env_vars.length > 256)
13394
+ refuse2();
13395
+ const names = server.env_vars.map((entry) => {
13396
+ if (envName(entry))
13397
+ return entry;
13398
+ if (!object5(entry) || !Object.keys(entry).every((key) => ["name", "source"].includes(key)) || !envName(entry.name) || entry.source !== undefined && entry.source !== "local")
13399
+ refuse2();
13400
+ return entry.name;
13401
+ });
13402
+ if (new Set(names).size !== names.length)
13403
+ refuse2();
13404
+ }
13405
+ }
13406
+ return sha2562;
13407
+ }
13323
13408
  function rootControls(root, read2) {
13324
13409
  const manifestPath = join14(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync12(manifestPath, { throwIfNoEntry: false });
13325
13410
  if (!lstatSync12(join14(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
@@ -13337,15 +13422,21 @@ function rootControls(root, read2) {
13337
13422
  const cacheVersion = basename5(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
13338
13423
  if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
13339
13424
  refuse2();
13340
- if (manifest.hooks !== undefined || manifest.mcpServers !== undefined || manifest.commands !== undefined || manifest.apps !== undefined && manifest.apps !== "./.app.json" || ["hooks", ".mcp.json"].some((path) => lstatSync12(join14(root, path), { throwIfNoEntry: false })))
13425
+ if (manifest.hooks !== undefined || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync12(join14(root, "hooks"), { throwIfNoEntry: false }))
13426
+ refuse2();
13427
+ const mcp = join14(root, ".mcp.json"), mcpPresent = lstatSync12(mcp, { throwIfNoEntry: false });
13428
+ if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync5(mcp) !== mcp))
13341
13429
  refuse2();
13430
+ if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
13431
+ refuse2();
13432
+ const mcpSha256 = mcpPresent ? mcpControls(mcp, read2) : undefined;
13342
13433
  const app = join14(root, ".app.json"), present = lstatSync12(app, { throwIfNoEntry: false });
13343
13434
  if (present && (!present.isFile() || present.isSymbolicLink() || present.size > 1024 * 1024))
13344
13435
  refuse2();
13345
13436
  if (manifest.apps !== undefined && !present)
13346
13437
  refuse2();
13347
13438
  const appSha256 = present ? appControls(app, read2) : undefined;
13348
- return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {} };
13439
+ return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
13349
13440
  }
13350
13441
  function identity3(document2, cache, read2) {
13351
13442
  if (!document2.startsWith(cache + sep4) || !document2.endsWith(sep4 + "SKILL.md"))
@@ -13388,6 +13479,10 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
13388
13479
  for (const parent of new Set(controls.map((control) => control.pluginParent))) {
13389
13480
  if (!parent.startsWith(cache + sep4) || !lstatSync12(parent, { throwIfNoEntry: false })?.isDirectory())
13390
13481
  return false;
13482
+ for (const control of controls.filter((control2) => control2.pluginParent === parent)) {
13483
+ if (control.remotePluginId !== undefined && !remoteInstallationMatches(parent, control.remotePluginId, read2))
13484
+ return false;
13485
+ }
13391
13486
  const names = readdirSync6(parent);
13392
13487
  entries += names.length;
13393
13488
  if (entries > 4096)
@@ -13404,7 +13499,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
13404
13499
  if (!stat.isDirectory() || ![".codex-plugin/plugin.json", ".app.json", "hooks", ".mcp.json"].some((path) => lstatSync12(join14(root, path), { throwIfNoEntry: false })))
13405
13500
  continue;
13406
13501
  const parsed = rootControls(root, read2);
13407
- if (!controls.some((control) => control.pluginParent === parent && control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && control.appSha256 === parsed.appSha256))
13502
+ if (!controls.some((control) => control.pluginParent === parent && control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && control.appSha256 === parsed.appSha256 && control.mcpSha256 === parsed.mcpSha256))
13408
13503
  return false;
13409
13504
  }
13410
13505
  }
@@ -13414,7 +13509,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
13414
13509
  }
13415
13510
  }
13416
13511
  function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read2, rules) {
13417
- if (catalog?.version !== "codex-cli 0.159.2" || catalog.cwd !== cwd)
13512
+ if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd)
13418
13513
  refuse2();
13419
13514
  const skills = projectCodexNativeSkillCatalog({ data: [{ cwd, errors: [], skills: catalog.skills }] }, cwd);
13420
13515
  const allowed = new Set(documents), result = [];
@@ -13441,22 +13536,30 @@ function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read2, r
13441
13536
  const listed = skills.filter((skill) => skill.path === document2);
13442
13537
  if (listed.length > 1)
13443
13538
  refuse2();
13539
+ let remotePluginId;
13444
13540
  if (listed.length === 1) {
13445
13541
  if (listed[0].name !== parsed.name || listed[0].pluginId !== expectedPluginId)
13446
13542
  refuse2();
13447
13543
  } 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)
13544
+ const installed = installedPlugins.filter((plugin2) => plugin2.id === expectedPluginId && plugin2.name === pluginName);
13545
+ if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled)
13450
13546
  refuse2();
13451
- if (installed[0].localVersion !== basename5(dirname10(dirname10(dirname10(document2)))))
13452
- continue;
13547
+ const plugin = installed[0];
13548
+ if (plugin.localVersion !== null) {
13549
+ if (plugin.localVersion !== basename5(dirname10(dirname10(dirname10(document2)))))
13550
+ continue;
13551
+ } else {
13552
+ if (plugin.sourceType !== "remote" || !plugin.remotePluginId || !remoteInstallationMatches(parsed.pluginParent, plugin.remotePluginId, read2) || !lstatSync12(document2).isFile() || realpathSync5(document2) !== document2)
13553
+ refuse2();
13554
+ remotePluginId = plugin.remotePluginId;
13555
+ }
13453
13556
  const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
13454
13557
  if (!exactPathDeny || !isCodexNativeSkillDisabled({ name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true }, rules))
13455
13558
  refuse2();
13456
13559
  }
13457
- result.push({ ...parsed, pluginId: expectedPluginId });
13560
+ result.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
13458
13561
  }
13459
- if (!result.length || new Set(result.map((item) => item.name)).size !== result.length)
13562
+ if (!result.length && documents.some((document2) => document2.startsWith(cache + sep4)) || new Set(result.map((item) => item.name)).size !== result.length)
13460
13563
  refuse2();
13461
13564
  return result;
13462
13565
  }
@@ -13469,8 +13572,8 @@ function isReviewedCodexPluginSkillDisabled(document2, cache, controls, rules, r
13469
13572
  } catch {
13470
13573
  return false;
13471
13574
  }
13472
- 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)
13575
+ 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 && item.mcpSha256 === parsed.mcpSha256);
13576
+ if (!reviewed || reviewed.remotePluginId !== undefined && !remoteInstallationMatches(reviewed.pluginParent, reviewed.remotePluginId, read2))
13474
13577
  return false;
13475
13578
  return isCodexNativeSkillDisabled({ name: parsed.name, path: document2, pluginId: reviewed.pluginId, enabled: true }, rules);
13476
13579
  }
@@ -13489,7 +13592,7 @@ enabled = false
13489
13592
  return text2;
13490
13593
  }
13491
13594
 
13492
- // src/lib/codex-settings-witness.ts
13595
+ // src/lib/codex-settings-witness-v1.ts
13493
13596
  import { createHash as createHash9 } from "crypto";
13494
13597
  var sha = (text2) => createHash9("sha256").update(text2).digest("hex");
13495
13598
  var EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
@@ -13583,19 +13686,167 @@ function captureCodexSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
13583
13686
  const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
13584
13687
  return { path, hashMode: "codex-settings-v1", sha256: hashCodexSettingsReplacement(text2) };
13585
13688
  }
13689
+
13690
+ // src/lib/codex-settings-witness.ts
13691
+ import { createHash as createHash10 } from "crypto";
13692
+ var sha2 = (text2) => createHash10("sha256").update(text2).digest("hex");
13693
+ var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
13694
+ var VERBOSITY2 = new Set(["low", "medium", "high"]);
13695
+ var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
13696
+ var CODEX_DISCOVERY_PROJECTION_FIELDS = ["plugins", "marketplaces", "skills"];
13697
+ var INFERENCE = ["model", "model_reasoning_effort", "model_verbosity"];
13698
+ function need4(value) {
13699
+ if (!value)
13700
+ throw new Error("Invalid Codex settings witness");
13701
+ }
13702
+ function isRecord3(value) {
13703
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
13704
+ }
13705
+ function isDate(value) {
13706
+ return value instanceof Date;
13707
+ }
13708
+ function numericTokens2(text2) {
13709
+ const result = [];
13710
+ for (let at = 0;at < text2.length; ) {
13711
+ const char = text2[at];
13712
+ if (char === "#") {
13713
+ while (at < text2.length && text2[at] !== `
13714
+ `)
13715
+ at++;
13716
+ continue;
13717
+ }
13718
+ if (char === '"' || char === "'") {
13719
+ const quote = char, triple = text2.slice(at, at + 3) === quote.repeat(3);
13720
+ at += triple ? 3 : 1;
13721
+ while (at < text2.length) {
13722
+ if (quote === '"' && text2[at] === "\\") {
13723
+ at += 2;
13724
+ continue;
13725
+ }
13726
+ if (text2.slice(at, at + (triple ? 3 : 1)) === quote.repeat(triple ? 3 : 1)) {
13727
+ at += triple ? 3 : 1;
13728
+ break;
13729
+ }
13730
+ at++;
13731
+ }
13732
+ continue;
13733
+ }
13734
+ if (/[0-9+-]/.test(char)) {
13735
+ const start = at;
13736
+ while (at < text2.length && !/[\s,\]{}#=]/.test(text2[at]))
13737
+ at++;
13738
+ if (at > start) {
13739
+ result.push(text2.slice(start, at));
13740
+ continue;
13741
+ }
13742
+ }
13743
+ at++;
13744
+ }
13745
+ return result.sort();
13746
+ }
13747
+ function canonical4(value, depth = 0, budget = { remaining: 65536 }) {
13748
+ need4(depth <= 32 && --budget.remaining >= 0);
13749
+ if (value === null)
13750
+ return ["null"];
13751
+ if (typeof value === "string")
13752
+ return ["string", value];
13753
+ if (typeof value === "boolean")
13754
+ return ["boolean", value];
13755
+ if (typeof value === "number") {
13756
+ need4(Number.isFinite(value));
13757
+ return ["number", String(value)];
13758
+ }
13759
+ if (isDate(value)) {
13760
+ need4(Number.isFinite(value.getTime()));
13761
+ return ["date", value.toISOString()];
13762
+ }
13763
+ if (Array.isArray(value))
13764
+ return ["array", value.map((child) => canonical4(child, depth + 1, budget))];
13765
+ need4(isRecord3(value));
13766
+ return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1, budget)])];
13767
+ }
13768
+ function normalize3(config) {
13769
+ if (isRecord3(config.hooks)) {
13770
+ delete config.hooks.state;
13771
+ if (!Object.keys(config.hooks).length)
13772
+ delete config.hooks;
13773
+ }
13774
+ if (isRecord3(config.skills) && Array.isArray(config.skills.config)) {
13775
+ const skills = config.skills;
13776
+ const retained = skills.config.filter((entry) => !disablesOneSkill(entry));
13777
+ if (retained.length !== skills.config.length) {
13778
+ if (retained.length)
13779
+ skills.config = retained;
13780
+ else
13781
+ delete skills.config;
13782
+ }
13783
+ if (!Object.keys(skills).length)
13784
+ delete config.skills;
13785
+ }
13786
+ inference(config);
13787
+ if (isRecord3(config.profiles)) {
13788
+ for (const profile of Object.values(config.profiles))
13789
+ if (isRecord3(profile))
13790
+ inference(profile);
13791
+ }
13792
+ }
13793
+ function disablesOneSkill(entry) {
13794
+ if (!isRecord3(entry) || entry.enabled !== false)
13795
+ return false;
13796
+ const keys = Object.keys(entry);
13797
+ if (!keys.every((key) => key === "path" || key === "name" || key === "enabled"))
13798
+ return false;
13799
+ const selector = ["path", "name"].filter((key) => Object.hasOwn(entry, key));
13800
+ return selector.length === 1 && typeof entry[selector[0]] === "string" && entry[selector[0]].length > 0;
13801
+ }
13802
+ function inference(target) {
13803
+ for (const key of INFERENCE)
13804
+ if (Object.hasOwn(target, key)) {
13805
+ const value = target[key];
13806
+ if (key === "model")
13807
+ need4(typeof value === "string" && MODEL.test(value));
13808
+ else
13809
+ need4(typeof value === "string" && (key === "model_reasoning_effort" ? EFFORT2.has(value) : VERBOSITY2.has(value)));
13810
+ delete target[key];
13811
+ }
13812
+ }
13813
+ function settingsDigest2(text2) {
13814
+ let config;
13815
+ try {
13816
+ config = Bun.TOML.parse(text2);
13817
+ } catch {
13818
+ throw new Error("Invalid Codex settings witness");
13819
+ }
13820
+ need4(isRecord3(config));
13821
+ normalize3(config);
13822
+ return sha2(`hasna.skills.codex-settings.v2\x00${JSON.stringify([canonical4(config), numericTokens2(text2)])}`);
13823
+ }
13824
+ function hashCodexSettingsReplacementV2(text2, budget = { remaining: 256 * 1024 * 1024 }) {
13825
+ need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
13826
+ budget.remaining -= Buffer.byteLength(text2);
13827
+ return settingsDigest2(text2);
13828
+ }
13829
+ function captureCodexSettingsV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
13830
+ const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
13831
+ return { path, hashMode: "codex-settings-v2", sha256: settingsDigest2(text2) };
13832
+ }
13833
+ function readCodexSettingsPreimage(path, budget = { remaining: 2 * 1024 * 1024 }) {
13834
+ return readNativeSettingsWitnessFile(path, budget, "config.toml");
13835
+ }
13586
13836
  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));
13837
+ 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
13838
  const budget = { remaining: 2 * 1024 * 1024 };
13589
13839
  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)
13840
+ need4((previous.hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(before) : sha2(before)) === previous.sha256);
13841
+ const sha2562 = settingsDigest2(current);
13842
+ if (settingsDigest2(before) !== sha2562)
13593
13843
  throw new Error("Codex non-preference settings changed; explicit discovery review required");
13594
- return { path: previous.path, hashMode: "codex-settings-v1", sha256: sha2562 };
13844
+ return { path: previous.path, hashMode: "codex-settings-v2", sha256: sha2562 };
13595
13845
  }
13596
13846
 
13597
13847
  // src/lib/agent-integration.ts
13598
- import { createHash as createHash11, randomUUID as randomUUID2 } from "crypto";
13848
+ import { createHash as createHash12, randomUUID as randomUUID2 } from "crypto";
13849
+ import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
13599
13850
  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
13851
  import { dirname as dirname11, join as join17, resolve as resolve10, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
13601
13852
  import { homedir as homedir2 } from "os";
@@ -13681,7 +13932,7 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
13681
13932
  }
13682
13933
 
13683
13934
  // src/lib/agent-bridge.ts
13684
- import { createHash as createHash10 } from "crypto";
13935
+ import { createHash as createHash11 } from "crypto";
13685
13936
  import { lstatSync as lstatSync14, readFileSync as readFileSync11, readdirSync as readdirSync7 } from "fs";
13686
13937
  import { join as join16 } from "path";
13687
13938
  var CLI_BRIDGE_NAME = "skills-cli";
@@ -13700,7 +13951,7 @@ Use the Skills CLI as the skill authority for this station.
13700
13951
 
13701
13952
  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
13953
  `;
13703
- var CLI_BRIDGE_DIGEST = createHash10("sha256").update(markdown).digest("hex");
13954
+ var CLI_BRIDGE_DIGEST = createHash11("sha256").update(markdown).digest("hex");
13704
13955
  var CLI_BRIDGE_FILES = Object.freeze({
13705
13956
  "SKILL.md": markdown,
13706
13957
  ".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 +14243,7 @@ function disableCodexBundledSkills(text2) {
13992
14243
  }
13993
14244
 
13994
14245
  // src/lib/agent-integration.ts
13995
- var sha2 = (data) => createHash11("sha256").update(data).digest("hex");
14246
+ var sha3 = (data) => createHash12("sha256").update(data).digest("hex");
13996
14247
  var ROOTS = NATIVE_SKILL_ROOTS;
13997
14248
  function assertSafePath(path) {
13998
14249
  let cursor = resolve10(path);
@@ -14099,7 +14350,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
14099
14350
  }
14100
14351
  }
14101
14352
  function treeHash(root) {
14102
- const hash2 = createHash11("sha256");
14353
+ const hash2 = createHash12("sha256");
14103
14354
  let bytes = 0, files = 0;
14104
14355
  function visit2(path) {
14105
14356
  const stat = lstatSync15(path);
@@ -14484,7 +14735,7 @@ function planAgentIntegration(options) {
14484
14735
  const observedNativeSources = [];
14485
14736
  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
14737
  const bytes = readNativeBytes(path, 1024 * 1024);
14487
- observedNativeSources.push({ path, sha256: sha2(bytes) });
14738
+ observedNativeSources.push({ path, sha256: sha3(bytes) });
14488
14739
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
14489
14740
  }, codexConfig.skills?.config ?? []) : policy.bridge?.codexPluginSkills ?? [];
14490
14741
  const changes = [];
@@ -14512,7 +14763,7 @@ function planAgentIntegration(options) {
14512
14763
  throw new Error("Refusing to overwrite an unrecognized Hermes supervisor");
14513
14764
  if (supervisorBefore !== supervisorAfter)
14514
14765
  changes.push({ path: supervisorPath, before: supervisorBefore, after: supervisorAfter });
14515
- const supervisor = { path: supervisorPath, runtime: process.execPath, sha256: sha2(supervisorAfter) };
14766
+ const supervisor = { path: supervisorPath, runtime: process.execPath, sha256: sha3(supervisorAfter) };
14516
14767
  const after2 = configureHermesHooks(before, supervisor, previous);
14517
14768
  if (before !== after2)
14518
14769
  changes.push({ path, before, after: after2 });
@@ -14559,12 +14810,12 @@ function planAgentIntegration(options) {
14559
14810
  agents: [...new Set([...priorAgents, ...options.agents])].sort(),
14560
14811
  home: resolve10(home),
14561
14812
  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)) } } } : {},
14813
+ ...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
14814
  commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
14564
14815
  profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
14565
14816
  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
14817
  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 } : {},
14818
+ ...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
14819
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
14569
14820
  } };
14570
14821
  const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
@@ -14580,12 +14831,12 @@ function planClaudePreToolUseHookUpdate(options) {
14580
14831
  return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
14581
14832
  }
14582
14833
  function claudeSettingsConsumers(home, path, discoveries, aliases) {
14583
- const canonical4 = (value) => canonicalAgentPath(value, aliases);
14834
+ const canonical5 = (value) => canonicalAgentPath(value, aliases);
14584
14835
  const containsSettings = (root) => {
14585
- const distance = relative5(canonical4(root), path);
14836
+ const distance = relative5(canonical5(root), path);
14586
14837
  return distance === "" || distance !== ".." && !distance.startsWith(`..${sep5}`) && !isAbsolute11(distance);
14587
14838
  };
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);
14839
+ 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
14840
  }
14590
14841
  function verifyCoordinatedDiscovery(binding, home, aliases) {
14591
14842
  try {
@@ -14617,7 +14868,7 @@ function planClaudeHookEventsUpdate(options) {
14617
14868
  const path = canonicalAgentPath(join17(home, ".claude", "settings.json"), aliases);
14618
14869
  assertSafePath(path);
14619
14870
  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)
14871
+ if ((before === null ? "absent" : sha3(before)) !== options.expectedSettingsSha256)
14621
14872
  throw new Error("Claude settings preimage changed");
14622
14873
  assertClaudeHookEventsReplacement(before, options.replacement, options.events);
14623
14874
  const agents = bridge.agents;
@@ -14683,7 +14934,7 @@ function atomicWrite2(path, content) {
14683
14934
  function planAgentSettingsWitnessUpgrade(options) {
14684
14935
  const home = resolve10(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
14685
14936
  const snapshot = readManagedSkillPolicySnapshot(dataDir2);
14686
- if (!snapshot || sha2(snapshot.text) !== options.expectedPolicySha256)
14937
+ if (!snapshot || sha3(snapshot.text) !== options.expectedPolicySha256)
14687
14938
  throw new Error("Managed policy preimage changed");
14688
14939
  const policy = snapshot.value, aliases = policy.bridge?.rootAliases ?? [];
14689
14940
  recheckRootAliases(aliases);
@@ -14692,7 +14943,7 @@ function planAgentSettingsWitnessUpgrade(options) {
14692
14943
  throw new Error("Settings witness upgrade requires an existing reviewed native binding");
14693
14944
  const configPath = canonicalAgentPath(join17(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
14694
14945
  const settings = readOptional(configPath);
14695
- if (settings === null || sha2(settings) !== options.expectedSettingsSha256)
14946
+ if (settings === null || sha3(settings) !== options.expectedSettingsSha256)
14696
14947
  throw new Error("Native settings preimage changed");
14697
14948
  const sources = binding.sources.filter((source) => source.path === configPath && source.format === undefined && source.fields === undefined);
14698
14949
  if (sources.length !== 1)
@@ -14700,15 +14951,40 @@ function planAgentSettingsWitnessUpgrade(options) {
14700
14951
  const previous = sources[0];
14701
14952
  if (typeof previous.sha256 !== "string")
14702
14953
  throw new Error("Settings witness upgrade requires a preserved settings preimage");
14954
+ const replaced = [previous];
14955
+ if (options.agent === "codex") {
14956
+ if (previous.hashMode !== undefined && previous.hashMode !== "bytes" && previous.hashMode !== "codex-settings-v1")
14957
+ throw new Error("Codex settings witness upgrade requires a preserved raw or codex-settings-v1 configuration witness");
14958
+ const preimage = readCodexSettingsPreimage(options.reviewedPreimage);
14959
+ for (const source of binding.sources) {
14960
+ if (source === previous || source.path !== configPath)
14961
+ continue;
14962
+ const fields = source.fields;
14963
+ if (source.hashMode !== undefined || source.format !== "toml" || !Array.isArray(fields) || !isDeepStrictEqual3(fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]) || typeof source.sha256 !== "string")
14964
+ throw new Error("Codex settings witness upgrade found an unrecognized configuration witness");
14965
+ if (projectNativeDiscoveryFields(preimage, "toml", fields, configPath) !== source.sha256)
14966
+ throw new Error("Reviewed preimage does not explain the Codex discovery projection witness");
14967
+ replaced.push(source);
14968
+ }
14969
+ }
14703
14970
  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) };
14971
+ const rebound = [];
14972
+ for (const source of binding.sources) {
14973
+ if (!replaced.includes(source)) {
14974
+ rebound.push(source);
14975
+ continue;
14976
+ }
14977
+ if (source === replaced[0])
14978
+ rebound.push(next);
14979
+ }
14980
+ const replacement = { ...binding, sources: rebound };
14705
14981
  verifyAgentDiscovery(replacement);
14706
14982
  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 } };
14983
+ 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
14984
  }
14709
14985
  function applyAgentIntegration(plan) {
14710
14986
  for (const source of plan.observedNativeSources ?? [])
14711
- if (sha2(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
14987
+ if (sha3(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
14712
14988
  throw new Error("Native identity source changed after planning");
14713
14989
  if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
14714
14990
  throw new Error("Native settings changed after witness planning");
@@ -14728,7 +15004,7 @@ function applyAgentIntegration(plan) {
14728
15004
  const upgrade = plan.settingsWitnessUpgrade;
14729
15005
  if (!plan.managedAgentChecks || !plan.observedPolicy?.before)
14730
15006
  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 });
15007
+ 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
15008
  if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.settingsWitnessUpgrade) !== JSON.stringify(upgrade))
14733
15009
  throw new Error("Settings witness upgrade plan changed");
14734
15010
  provenDiscovery = verified.discoveryBefore[0];
@@ -14756,7 +15032,7 @@ function applyAgentIntegration(plan) {
14756
15032
  chmodSync(backupRoot, 448);
14757
15033
  for (const [index, change] of plan.changes.entries())
14758
15034
  if (change.before !== null) {
14759
- const backup = join17(backupRoot, `${index}-${sha2(change.path).slice(0, 12)}.backup`);
15035
+ const backup = join17(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
14760
15036
  writeFileSync4(backup, change.before, { mode: 384, flag: "wx" });
14761
15037
  if (readFileSync12(backup, "utf8") !== change.before)
14762
15038
  throw new Error("Native configuration preservation readback failed");
@@ -14775,7 +15051,7 @@ function applyAgentIntegration(plan) {
14775
15051
  }
14776
15052
  recheckRootAliases(aliases);
14777
15053
  for (const source of plan.observedNativeSources ?? [])
14778
- if (sha2(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
15054
+ if (sha3(readNativeBytes(source.path, 1024 * 1024)) !== source.sha256)
14779
15055
  throw new Error("Native identity source changed during application");
14780
15056
  if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
14781
15057
  throw new Error("Native settings changed during witness application");
@@ -14787,7 +15063,7 @@ function applyAgentIntegration(plan) {
14787
15063
  }
14788
15064
  for (const agent of plan.managedAgentChecks?.agents ?? [])
14789
15065
  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 } : {} }) + `
15066
+ 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
15067
  `);
14792
15068
  } catch (error) {
14793
15069
  for (const change of written.reverse()) {
@@ -14866,8 +15142,8 @@ function archiveNativeSkills(inventory, options) {
14866
15142
  const operationId = randomUUID2(), archiveRoot = join17(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join17(archiveRoot, "receipt.json");
14867
15143
  const entries = selected.map((entry, index) => ({
14868
15144
  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,
15145
+ archive: join17(archiveRoot, `${index}-${sha3(entry.path).slice(0, 12)}`),
15146
+ hash: entry.vendor ? sha3(readNativeBytes(join17(entry.path, "SKILL.md"))) : entry.hash,
14871
15147
  ...entry.vendor ? { discoveryOnly: true } : {},
14872
15148
  status: "planned"
14873
15149
  }));
@@ -14878,7 +15154,7 @@ function archiveNativeSkills(inventory, options) {
14878
15154
  const stat = lstatSync15(entry.archive);
14879
15155
  if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
14880
15156
  throw new Error("Archive type changed");
14881
- if ((entry.discoveryOnly ? sha2(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
15157
+ if ((entry.discoveryOnly ? sha3(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
14882
15158
  throw new Error("Archive bytes changed");
14883
15159
  };
14884
15160
  assertSafePath(archiveRoot);
@@ -14998,7 +15274,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
14998
15274
  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
15275
  if (agent === "hermes") {
15000
15276
  const supervisor = binding.supervisors?.hermes;
15001
- if (supervisor?.path !== join17(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha2(renderHermesSupervisor(command, profile)))
15277
+ if (supervisor?.path !== join17(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha3(renderHermesSupervisor(command, profile)))
15002
15278
  throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
15003
15279
  assertHermesProtection(home, config, command, profile, supervisor);
15004
15280
  } else if (agent === "opencode") {
@@ -16296,7 +16572,7 @@ function exactProfileSelection(spec, profile) {
16296
16572
  }
16297
16573
 
16298
16574
  // src/lib/plugin-projection.ts
16299
- import { createHash as createHash12 } from "crypto";
16575
+ import { createHash as createHash13 } from "crypto";
16300
16576
  import { posix } from "path";
16301
16577
  var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
16302
16578
  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 +16583,7 @@ function pluginNeed(value, message) {
16307
16583
  if (!value)
16308
16584
  pluginRefusal(message);
16309
16585
  }
16310
- var pluginHash = (bytes) => createHash12("sha256").update(bytes).digest("hex");
16586
+ var pluginHash = (bytes) => createHash13("sha256").update(bytes).digest("hex");
16311
16587
  function pluginObject(value) {
16312
16588
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
16313
16589
  }
@@ -16495,7 +16771,7 @@ function buildPluginProjection(entries) {
16495
16771
  }
16496
16772
 
16497
16773
  // src/lib/plugin-projection-store.ts
16498
- import { createHash as createHash13, randomUUID as randomUUID3 } from "crypto";
16774
+ import { createHash as createHash14, randomUUID as randomUUID3 } from "crypto";
16499
16775
  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
16776
  import { dirname as dirname13, isAbsolute as isAbsolute12, join as join23, parse as parse5, resolve as resolve11 } from "path";
16501
16777
  var READ_FLAGS = constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK;
@@ -16511,7 +16787,7 @@ function stat(path) {
16511
16787
  return pluginRefusal("Cannot inspect plugin filesystem entry");
16512
16788
  }
16513
16789
  }
16514
- function canonical4(path) {
16790
+ function canonical5(path) {
16515
16791
  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
16792
  }
16517
16793
  function owner(stats, system = false) {
@@ -16522,7 +16798,7 @@ function same3(left, right, identityOnly = false) {
16522
16798
  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
16799
  }
16524
16800
  function ancestors(path, create = false) {
16525
- canonical4(path);
16801
+ canonical5(path);
16526
16802
  const result = [], parent = dirname13(path), filesystemRoot = parse5(parent).root;
16527
16803
  let current = filesystemRoot;
16528
16804
  for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
@@ -16837,7 +17113,7 @@ function pluginExecutableDigest(path) {
16837
17113
  const opened = fstatSync9(fd, { bigint: true });
16838
17114
  same3(before, opened);
16839
17115
  fileShape(opened, maximum, "executable");
16840
- const hash2 = createHash13("sha256"), buffer = new Uint8Array(64 * 1024);
17116
+ const hash2 = createHash14("sha256"), buffer = new Uint8Array(64 * 1024);
16841
17117
  let remaining = Number(opened.size);
16842
17118
  while (remaining > 0) {
16843
17119
  const count = readSync7(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
@@ -17396,23 +17672,23 @@ function captureManagedPluginRegistry(path, managedPlugins) {
17396
17672
  }
17397
17673
 
17398
17674
  // src/lib/claude-marketplace-registry.ts
17399
- import { createHash as createHash14 } from "crypto";
17675
+ import { createHash as createHash15 } from "crypto";
17400
17676
  import { closeSync as closeSync11, constants as constants10, fstatSync as fstatSync11, lstatSync as lstatSync19, openSync as openSync11, readSync as readSync9 } from "fs";
17401
17677
  import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute15, resolve as resolve14 } from "path";
17402
17678
  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) {
17679
+ function need5(value, reason) {
17404
17680
  if (!value)
17405
17681
  throw new Error(`Claude marketplace registry ${reason}`);
17406
17682
  }
17407
17683
  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");
17684
+ 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
17685
  }
17410
17686
  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
17687
  function parents2(path) {
17412
17688
  const result = [];
17413
17689
  for (let at = dirname15(path);; at = dirname15(at)) {
17414
17690
  const stat2 = lstatSync19(at, { bigint: true });
17415
- need4(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
17691
+ need5(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
17416
17692
  result.push([at, stat2]);
17417
17693
  if (at === dirname15(at))
17418
17694
  return result;
@@ -17420,15 +17696,15 @@ function parents2(path) {
17420
17696
  }
17421
17697
  function readRegistry2(path, budget) {
17422
17698
  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");
17699
+ need5(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
17700
+ need5(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
17425
17701
  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");
17702
+ need5(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
17703
+ need5(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
17428
17704
  const fd = openSync11(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
17429
17705
  try {
17430
17706
  const opened = fstatSync11(fd, { bigint: true });
17431
- need4(opened.isFile() && sameFile2(initial, opened), "changed during open");
17707
+ need5(opened.isFile() && sameFile2(initial, opened), "changed during open");
17432
17708
  const bytes = Buffer.alloc(Number(opened.size) + 1);
17433
17709
  let length = 0;
17434
17710
  while (length < bytes.length) {
@@ -17438,10 +17714,10 @@ function readRegistry2(path, budget) {
17438
17714
  length += count;
17439
17715
  }
17440
17716
  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");
17717
+ need5(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
17442
17718
  for (const [ancestor, before] of ancestors2) {
17443
17719
  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");
17720
+ need5(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
17445
17721
  }
17446
17722
  budget.remaining -= length;
17447
17723
  try {
@@ -17461,7 +17737,7 @@ function parse6(text2) {
17461
17737
  at++;
17462
17738
  };
17463
17739
  function string() {
17464
- need4(text2[at] === '"', "contains invalid JSON");
17740
+ need5(text2[at] === '"', "contains invalid JSON");
17465
17741
  const start = at++;
17466
17742
  while (at < text2.length) {
17467
17743
  const character = text2[at++];
@@ -17472,7 +17748,7 @@ function parse6(text2) {
17472
17748
  } catch {
17473
17749
  throw new Error("Claude marketplace registry contains invalid JSON string");
17474
17750
  }
17475
- need4(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
17751
+ need5(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
17476
17752
  return value2;
17477
17753
  }
17478
17754
  if (character === "\\")
@@ -17481,7 +17757,7 @@ function parse6(text2) {
17481
17757
  throw new Error("Claude marketplace registry contains an unterminated JSON string");
17482
17758
  }
17483
17759
  function value(depth) {
17484
- need4(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
17760
+ need5(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
17485
17761
  space();
17486
17762
  const character = text2[at];
17487
17763
  if (character === '"')
@@ -17495,17 +17771,17 @@ function parse6(text2) {
17495
17771
  if (object5) {
17496
17772
  space();
17497
17773
  const key = string();
17498
- need4(!keys.has(key), "contains a duplicate JSON key");
17774
+ need5(!keys.has(key), "contains a duplicate JSON key");
17499
17775
  keys.add(key);
17500
17776
  space();
17501
- need4(text2[at++] === ":", "contains invalid JSON");
17777
+ need5(text2[at++] === ":", "contains invalid JSON");
17502
17778
  entries.push([key, value(depth + 1)]);
17503
17779
  } else
17504
17780
  items.push(value(depth + 1));
17505
17781
  space();
17506
17782
  if (text2[at] === end)
17507
17783
  break;
17508
- need4(text2[at++] === ",", "contains invalid JSON");
17784
+ need5(text2[at++] === ",", "contains invalid JSON");
17509
17785
  }
17510
17786
  at++;
17511
17787
  return object5 ? { kind: "object", entries } : { kind: "array", items };
@@ -17517,14 +17793,14 @@ function parse6(text2) {
17517
17793
  }
17518
17794
  numberPattern.lastIndex = at;
17519
17795
  const number = numberPattern.exec(text2);
17520
- need4(number, "contains invalid JSON");
17796
+ need5(number, "contains invalid JSON");
17521
17797
  at += number[0].length;
17522
17798
  return { kind: "number", value: number[0] };
17523
17799
  }
17524
17800
  const root2 = value(0);
17525
17801
  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");
17802
+ need5(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
17803
+ need5(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
17528
17804
  return root2;
17529
17805
  }
17530
17806
  function exactKeys(value, keys) {
@@ -17532,7 +17808,7 @@ function exactKeys(value, keys) {
17532
17808
  }
17533
17809
  var field = (object5, key) => object5.entries.find(([name]) => name === key)?.[1];
17534
17810
  function textField(value) {
17535
- need4(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
17811
+ need5(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
17536
17812
  return value.value;
17537
17813
  }
17538
17814
  function normalizeRow(row, version = 1) {
@@ -17540,7 +17816,7 @@ function normalizeRow(row, version = 1) {
17540
17816
  if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
17541
17817
  return;
17542
17818
  const autoUpdate = field(row, "autoUpdate");
17543
- need4(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
17819
+ need5(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
17544
17820
  }
17545
17821
  const source = field(row, "source");
17546
17822
  if (source?.kind !== "object")
@@ -17556,34 +17832,34 @@ function normalizeRow(row, version = 1) {
17556
17832
  absolutePath3(sourceLocation);
17557
17833
  absolutePath3(textField(field(row, "installLocation")));
17558
17834
  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");
17835
+ 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
17836
  const millis = Date.parse(updated);
17561
- need4(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
17837
+ need5(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
17562
17838
  row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
17563
17839
  }
17564
17840
  function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
17565
17841
  const value = parse6(readRegistry2(path, budget));
17566
17842
  for (const [, row] of value.entries)
17567
17843
  normalizeRow(row);
17568
- const sha2562 = createHash14("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
17844
+ const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
17569
17845
  return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
17570
17846
  }
17571
17847
  function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
17572
17848
  const value = parse6(readRegistry2(path, budget));
17573
17849
  for (const [, row] of value.entries)
17574
17850
  normalizeRow(row, 2);
17575
- const sha2562 = createHash14("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
17851
+ const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
17576
17852
  return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
17577
17853
  }
17578
17854
 
17579
17855
  // src/lib/codex-hook-discovery-recovery.ts
17580
- import { createHash as createHash16 } from "crypto";
17856
+ import { createHash as createHash17 } from "crypto";
17581
17857
 
17582
17858
  // src/lib/codex-hook-trust-files.ts
17583
17859
  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";
17860
+ import { createHash as createHash16 } from "crypto";
17585
17861
  import { dirname as dirname16 } from "path";
17586
- function need5(value, code) {
17862
+ function need6(value, code) {
17587
17863
  if (!value)
17588
17864
  throw new Error(`CODEX_HOOK_TRUST_${code}`);
17589
17865
  }
@@ -17591,8 +17867,8 @@ var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs
17591
17867
  function safeParents(file) {
17592
17868
  for (let p = dirname16(file);; p = dirname16(p)) {
17593
17869
  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");
17870
+ need6(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
17871
+ need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
17596
17872
  if (dirname16(p) === p)
17597
17873
  break;
17598
17874
  }
@@ -17601,12 +17877,12 @@ function snapshot(file, privateFile = false, options = {}) {
17601
17877
  const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
17602
17878
  safeParents(file);
17603
17879
  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");
17880
+ 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");
17881
+ need6(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
17606
17882
  const fd = openSync12(file, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
17607
17883
  try {
17608
17884
  const before = fstatSync12(fd, { bigint: true });
17609
- need5(sameStat(initial, before), "INPUT_CHANGED");
17885
+ need6(sameStat(initial, before), "INPUT_CHANGED");
17610
17886
  const bytes = Buffer.alloc(Number(before.size) + 1);
17611
17887
  let length = 0;
17612
17888
  while (length < bytes.length) {
@@ -17615,16 +17891,16 @@ function snapshot(file, privateFile = false, options = {}) {
17615
17891
  break;
17616
17892
  length += n2;
17617
17893
  }
17618
- need5(length === Number(before.size) && sameStat(before, fstatSync12(fd, { bigint: true })) && sameStat(before, lstatSync20(file, { bigint: true })), "INPUT_CHANGED");
17894
+ need6(length === Number(before.size) && sameStat(before, fstatSync12(fd, { bigint: true })) && sameStat(before, lstatSync20(file, { bigint: true })), "INPUT_CHANGED");
17619
17895
  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") };
17896
+ return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash16("sha256").update(content).digest("hex") };
17621
17897
  } finally {
17622
17898
  closeSync12(fd);
17623
17899
  }
17624
17900
  }
17625
17901
  function unchanged2(before) {
17626
17902
  const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
17627
- need5(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
17903
+ need6(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
17628
17904
  }
17629
17905
 
17630
17906
  // src/lib/codex-hook-additions-review.ts
@@ -17633,11 +17909,12 @@ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
17633
17909
  var proofs = new WeakMap;
17634
17910
 
17635
17911
  // src/lib/codex-hook-discovery-recovery.ts
17636
- var sha3 = (text2) => createHash16("sha256").update(text2).digest("hex");
17912
+ var sha4 = (text2) => createHash17("sha256").update(text2).digest("hex");
17913
+ var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
17637
17914
  var proofs2 = new WeakMap;
17638
17915
  function assertCodexHookDiscoveryRecovery(binding, witness) {
17639
17916
  const proof = proofs2.get(witness);
17640
- need5(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha3(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
17917
+ need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
17641
17918
  unchanged2(proof.before);
17642
17919
  unchanged2(proof.current);
17643
17920
  proof.recheckAdditions();
@@ -17646,12 +17923,12 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
17646
17923
  if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
17647
17924
  return false;
17648
17925
  assertCodexHookDiscoveryRecovery(binding, witness);
17649
- need5(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
17926
+ need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
17650
17927
  return true;
17651
17928
  }
17652
17929
 
17653
17930
  // src/lib/agent-discovery.ts
17654
- var digest = (text2) => createHash17("sha256").update(text2).digest("hex");
17931
+ var digest = (text2) => createHash18("sha256").update(text2).digest("hex");
17655
17932
  function parseConfig(text2, path, toml = false) {
17656
17933
  try {
17657
17934
  const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
@@ -17683,14 +17960,26 @@ function read2(path, changes) {
17683
17960
  throw new Error(`Unsupported or oversized native discovery input: ${path}`);
17684
17961
  return readFileSync17(path, "utf8");
17685
17962
  }
17963
+ function projectNativeDiscoveryFields(text2, format, fields2, path) {
17964
+ const object5 = format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, path, format === "toml");
17965
+ if (!object5 || typeof object5 !== "object" || Array.isArray(object5))
17966
+ throw new Error("Expected native discovery configuration object");
17967
+ return digest(JSON.stringify(Object.fromEntries(fields2.map((field2) => [field2, object5[field2] ?? null]))));
17968
+ }
17969
+ function isSupersededCodexProjection(agent, configPath, source) {
17970
+ return agent === "codex" && source.path === configPath && source.format === "toml" && isDeepStrictEqual4(source.fields, [...CODEX_DISCOVERY_PROJECTION_FIELDS]);
17971
+ }
17972
+ function supersedesCodexProjection(sources, configPath) {
17973
+ return sources.some((source) => source.path === configPath && source.hashMode === "codex-settings-v2");
17974
+ }
17686
17975
  function projected(source, changes, budget = discoveryByteBudget()) {
17687
- if (source.hashMode === "codex-settings-v1") {
17976
+ if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") {
17688
17977
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
17689
17978
  throw new Error("Codex settings witnesses require exact typed metadata");
17690
- const current = captureCodexSettings(source.path, budget).sha256;
17979
+ const current = (source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget).sha256;
17691
17980
  if (current !== source.sha256)
17692
17981
  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;
17982
+ return changes?.has(source.path) ? (source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget) : current;
17694
17983
  }
17695
17984
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
17696
17985
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
@@ -17729,10 +18018,7 @@ function projected(source, changes, budget = discoveryByteBudget()) {
17729
18018
  return null;
17730
18019
  if (!source.format)
17731
18020
  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]))));
18021
+ return projectNativeDiscoveryFields(text2, source.format, source.fields ?? [], source.path);
17736
18022
  }
17737
18023
  function verifyAgentDiscovery(binding, codexRecovery) {
17738
18024
  if (codexRecovery)
@@ -17760,7 +18046,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
17760
18046
  safe4(root2);
17761
18047
  }
17762
18048
  function assertMarketplaceBinding(binding, source) {
17763
- if (source.hashMode === "codex-settings-v1" && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
18049
+ if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2") && (binding.agent !== "codex" || binding.method !== "reviewed" || basename9(source.path) !== "config.toml"))
17764
18050
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
17765
18051
  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
18052
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
@@ -17789,8 +18075,8 @@ function captureDiscoveryPathSources(paths) {
17789
18075
  const budget = discoveryByteBudget();
17790
18076
  return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget) }));
17791
18077
  }
17792
- function assertProjectDiscovery(agent, directories, home, canonical5 = resolve15, reviewed) {
17793
- const homeConfigPath = canonical5(agentDiscoveryConfigPath(home, agent));
18078
+ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve15, reviewed) {
18079
+ const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
17794
18080
  for (const directory of directories) {
17795
18081
  const isHome = resolve15(directory) === resolve15(home);
17796
18082
  if (agent === "hermes") {
@@ -17801,7 +18087,7 @@ function assertProjectDiscovery(agent, directories, home, canonical5 = resolve15
17801
18087
  continue;
17802
18088
  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
18089
  for (const suffix of paths) {
17804
- const path = canonical5(join27(directory, suffix)), raw = read2(path);
18090
+ const path = canonical6(join27(directory, suffix)), raw = read2(path);
17805
18091
  if (raw === null)
17806
18092
  continue;
17807
18093
  const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
@@ -17817,14 +18103,14 @@ function assertProjectDiscovery(agent, directories, home, canonical5 = resolve15
17817
18103
  const managed = reviewed.sources.flatMap((source) => source.hashMode === "claude-plugin-registry" ? source.managedPlugins ?? [] : []);
17818
18104
  if (exact && projected(exact) === exact.sha256 && managed.length) {
17819
18105
  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))));
18106
+ 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
18107
  }
17822
18108
  }
17823
18109
  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
18110
  throw new Error(`NATIVE_SKILL_DRIFT: higher-precedence project skill or hook configuration requires review: ${path}`);
17825
18111
  }
17826
18112
  if (agent === "claude") {
17827
- const commands = canonical5(join27(directory, ".claude/commands"));
18113
+ const commands = canonical6(join27(directory, ".claude/commands"));
17828
18114
  safe4(commands);
17829
18115
  if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
17830
18116
  throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
@@ -17835,10 +18121,10 @@ function agentDiscoveryConfigPath(home, agent) {
17835
18121
  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
18122
  }
17837
18123
  function resolveAgentDiscovery(options) {
17838
- const canonical5 = options.canonical ?? resolve15, home = resolve15(options.home), agent = options.agent;
18124
+ const canonical6 = options.canonical ?? resolve15, home = resolve15(options.home), agent = options.agent;
17839
18125
  const sources = [], roots = new Set, builtinNames = [];
17840
- const witness = (path, format, fields) => {
17841
- const source = { path: canonical5(path), sha256: null, ...format ? { format, fields } : {} };
18126
+ const witness = (path, format, fields2) => {
18127
+ const source = { path: canonical6(path), sha256: null, ...format ? { format, fields: fields2 } : {} };
17842
18128
  source.sha256 = projected(source);
17843
18129
  sources.push(source);
17844
18130
  return read2(source.path);
@@ -17846,7 +18132,7 @@ function resolveAgentDiscovery(options) {
17846
18132
  if (agent === "hermes")
17847
18133
  assertHermesEnvironment(home);
17848
18134
  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"]);
18135
+ 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
18136
  const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
17851
18137
  const unresolved = (detail) => {
17852
18138
  throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
@@ -17905,11 +18191,12 @@ function resolveAgentDiscovery(options) {
17905
18191
  if (retained.agent !== agent || retained.method !== "reviewed")
17906
18192
  unresolved("invalid retained review");
17907
18193
  verifyAgentDiscovery(retained);
17908
- if (!retained.sources.some((source) => source.path === canonical5(configPath) && source.format === undefined && source.fields === undefined))
18194
+ if (!retained.sources.some((source) => source.path === canonical6(configPath) && source.format === undefined && source.fields === undefined))
17909
18195
  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)))
18196
+ 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
18197
  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))
18198
+ const covered = supersedesCodexProjection(retained.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
18199
+ 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
18200
  unresolved("retained review is missing current runtime discovery coverage");
17914
18201
  return retained;
17915
18202
  }
@@ -17923,14 +18210,15 @@ function resolveAgentDiscovery(options) {
17923
18210
  if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
17924
18211
  throw new Error("Explicit discovery reviews require full source-file hashes");
17925
18212
  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)))
18213
+ 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
18214
  throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
17928
- if (!review.sources.some((source) => source.path === canonical5(configPath)))
18215
+ if (!review.sources.some((source) => source.path === canonical6(configPath)))
17929
18216
  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 } : {} };
18217
+ const reviewedSources = supersedesCodexProjection(review.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededCodexProjection(agent, canonical6(configPath), source)) : sources;
18218
+ return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...reviewedSources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
17931
18219
  }
17932
18220
  function plugin(root2) {
17933
- root2 = canonical5(root2);
18221
+ root2 = canonical6(root2);
17934
18222
  safe4(root2);
17935
18223
  const manifestPath = join27(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
17936
18224
  const raw = witness(manifestPath);
@@ -17962,7 +18250,7 @@ function resolveAgentDiscovery(options) {
17962
18250
  if (agent === "claude") {
17963
18251
  const registrations = witness(join27(home, ".claude/plugins/installed_plugins.json"));
17964
18252
  const installed = registrations === null ? {} : parseConfig(registrations, join27(home, ".claude/plugins/installed_plugins.json")).plugins;
17965
- const commands = canonical5(join27(home, ".claude/commands"));
18253
+ const commands = canonical6(join27(home, ".claude/commands"));
17966
18254
  safe4(commands);
17967
18255
  if (lstatSync21(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
17968
18256
  unresolved("legacy home command discovery requires a dedicated format adapter");
@@ -18036,7 +18324,7 @@ function resolveAgentDiscovery(options) {
18036
18324
  roots.add(join27(root2, "skills"));
18037
18325
  }
18038
18326
  }
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))) } : {} };
18327
+ 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
18328
  }
18041
18329
 
18042
18330
  // src/lib/authoring-path.ts
@@ -18228,7 +18516,7 @@ function scaffoldPortableSkill(name, options = {}) {
18228
18516
  return { name: skillName, path: skillPath, manifest, created: true };
18229
18517
  }
18230
18518
  function portPortableSkillDirectory(sourceDir, options = {}) {
18231
- const absoluteSource = normalize3(sourceDir);
18519
+ const absoluteSource = normalize4(sourceDir);
18232
18520
  if (!existsSync13(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
18233
18521
  throw new Error(`Import directory not found: ${sourceDir}`);
18234
18522
  }
@@ -18295,7 +18583,7 @@ function assertDisjointImportPaths(source, destination) {
18295
18583
  }
18296
18584
  }
18297
18585
  function portPortableSkill(sourcePath, options = {}) {
18298
- const absoluteSource = normalize3(sourcePath);
18586
+ const absoluteSource = normalize4(sourcePath);
18299
18587
  if (!existsSync13(absoluteSource) || !statSync10(absoluteSource).isDirectory()) {
18300
18588
  throw new Error(`Skill source directory not found: ${sourcePath}`);
18301
18589
  }
@@ -18571,7 +18859,7 @@ function safeIsDirectory(path) {
18571
18859
  function isSafeRelativePath2(value) {
18572
18860
  if (!value.trim() || isAbsolute17(value))
18573
18861
  return false;
18574
- const normalized = normalize3(value).replace(/\\/g, "/");
18862
+ const normalized = normalize4(value).replace(/\\/g, "/");
18575
18863
  return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
18576
18864
  }
18577
18865
  function add3(target, code, message) {
@@ -18885,7 +19173,7 @@ function getAllTags() {
18885
19173
  return Array.from(tagSet).sort();
18886
19174
  }
18887
19175
  // src/lib/run-state.ts
18888
- import { createHash as createHash18, randomBytes } from "crypto";
19176
+ import { createHash as createHash19, randomBytes } from "crypto";
18889
19177
  import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync19, readdirSync as readdirSync13, statSync as statSync11, writeFileSync as writeFileSync9 } from "fs";
18890
19178
  import { extname, join as join31, relative as relative7 } from "path";
18891
19179
  function createSkillRun(params, targetDir = process.cwd()) {
@@ -19012,7 +19300,7 @@ function collectRunArtifacts(context) {
19012
19300
  artifacts.push({
19013
19301
  path: toProjectRelative(context.targetDir, path),
19014
19302
  mime: mimeForPath(path),
19015
- sha256: createHash18("sha256").update(bytes).digest("hex"),
19303
+ sha256: createHash19("sha256").update(bytes).digest("hex"),
19016
19304
  sizeBytes: stat2.size
19017
19305
  });
19018
19306
  }
@@ -22370,15 +22658,15 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
22370
22658
  } catch (_err) {}
22371
22659
  }
22372
22660
  const input = payload.value;
22373
- const isDate = input instanceof Date;
22374
- const isValidDate = isDate && !Number.isNaN(input.getTime());
22661
+ const isDate2 = input instanceof Date;
22662
+ const isValidDate = isDate2 && !Number.isNaN(input.getTime());
22375
22663
  if (isValidDate)
22376
22664
  return payload;
22377
22665
  payload.issues.push({
22378
22666
  expected: "date",
22379
22667
  code: "invalid_type",
22380
22668
  input,
22381
- ...isDate ? { received: "Invalid Date" } : {},
22669
+ ...isDate2 ? { received: "Invalid Date" } : {},
22382
22670
  inst
22383
22671
  });
22384
22672
  return payload;
@@ -38431,7 +38719,7 @@ function clone2(value) {
38431
38719
  return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
38432
38720
  }
38433
38721
  // src/lib/canonical-json.ts
38434
- import { createHash as createHash19 } from "crypto";
38722
+ import { createHash as createHash20 } from "crypto";
38435
38723
  function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
38436
38724
  return canonicalJsonAtDepth(value, maximumBytes, 64);
38437
38725
  }
@@ -38475,7 +38763,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
38475
38763
  return visit3(value, 0);
38476
38764
  }
38477
38765
  function canonicalJsonSha256(value, maximumBytes) {
38478
- return createHash19("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
38766
+ return createHash20("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
38479
38767
  }
38480
38768
 
38481
38769
  // src/lib/remote-recurring-contract.ts
@@ -39231,7 +39519,7 @@ function getApiUrl(action, env = process.env, options = {}) {
39231
39519
  // src/lib/remote-run-contract.ts
39232
39520
  var REMOTE_SKILL_RUN_CONTRACT_VERSION = 1;
39233
39521
  function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
39234
- const record6 = isRecord3(payload) ? payload : {};
39522
+ const record6 = isRecord4(payload) ? payload : {};
39235
39523
  return {
39236
39524
  contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION,
39237
39525
  ...pickString(record6, "id"),
@@ -39253,7 +39541,7 @@ function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
39253
39541
  ...hasOwn(record6, "details") ? { details: record6.details } : {}
39254
39542
  };
39255
39543
  }
39256
- function isRecord3(value) {
39544
+ function isRecord4(value) {
39257
39545
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
39258
39546
  }
39259
39547
  function hasOwn(record6, key) {
@@ -39385,7 +39673,7 @@ function creditCheckoutFailure(value, status, key) {
39385
39673
 
39386
39674
  // src/lib/remote-profile.ts
39387
39675
  function customerNamePatch(input2, field2) {
39388
- if (!isRecord4(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field2))
39676
+ if (!isRecord5(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field2))
39389
39677
  throw new Error("Provide only the requested name field.");
39390
39678
  const value = input2[field2];
39391
39679
  if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
@@ -39393,22 +39681,22 @@ function customerNamePatch(input2, field2) {
39393
39681
  }
39394
39682
  return { [field2]: value.trim() };
39395
39683
  }
39396
- function isRecord4(value) {
39684
+ function isRecord5(value) {
39397
39685
  return !!value && typeof value === "object" && !Array.isArray(value);
39398
39686
  }
39399
39687
  function string4(value) {
39400
39688
  return typeof value === "string" && value.length > 0;
39401
39689
  }
39402
39690
  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)) {
39691
+ const user = isRecord5(value) && value.user;
39692
+ 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
39693
  throw new Error("The server returned an invalid account profile.");
39406
39694
  }
39407
39695
  return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
39408
39696
  }
39409
39697
  function parseUpdatedWorkspace(value) {
39410
- const organization2 = isRecord4(value) && value.organization;
39411
- if (!isRecord4(organization2) || !string4(organization2.id) || !string4(organization2.slug) || !string4(organization2.name)) {
39698
+ const organization2 = isRecord5(value) && value.organization;
39699
+ if (!isRecord5(organization2) || !string4(organization2.id) || !string4(organization2.slug) || !string4(organization2.name)) {
39412
39700
  throw new Error("The server returned an invalid workspace.");
39413
39701
  }
39414
39702
  return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
@@ -40597,11 +40885,11 @@ function validateCronField(expr, min, max, label) {
40597
40885
  return { valid: true };
40598
40886
  }
40599
40887
  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"` };
40888
+ const fields2 = expr.trim().split(/\s+/);
40889
+ if (fields2.length !== 5) {
40890
+ return { valid: false, error: `Expected 5 fields, got ${fields2.length}. Format: "minute hour day-of-month month day-of-week"` };
40603
40891
  }
40604
- const [minuteF, hourF, domF, monthF, dowF] = fields;
40892
+ const [minuteF, hourF, domF, monthF, dowF] = fields2;
40605
40893
  const checks3 = [
40606
40894
  { expr: minuteF, min: 0, max: 59, label: "minute" },
40607
40895
  { expr: hourF, min: 0, max: 23, label: "hour" },
@@ -40742,7 +41030,7 @@ function recordScheduleRun(id, status, targetDir) {
40742
41030
  saveSchedules(data, targetDir);
40743
41031
  }
40744
41032
  // src/lib/revision.ts
40745
- import { createHash as createHash20 } from "crypto";
41033
+ import { createHash as createHash21 } from "crypto";
40746
41034
  var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
40747
41035
  function revisionIdOf(content) {
40748
41036
  const canonicalContent = {
@@ -40763,8 +41051,8 @@ function revisionIdOf(content) {
40763
41051
  canonicalContent.archiveReason = content.archiveReason ?? null;
40764
41052
  canonicalContent.replacementSlug = content.replacementSlug ?? null;
40765
41053
  }
40766
- const canonical5 = JSON.stringify(canonicalContent);
40767
- return createHash20("sha256").update(canonical5).digest("hex");
41054
+ const canonical6 = JSON.stringify(canonicalContent);
41055
+ return createHash21("sha256").update(canonical6).digest("hex");
40768
41056
  }
40769
41057
  function revisionIdOfRecord(record7) {
40770
41058
  return revisionIdOf(record7);
@@ -41305,7 +41593,7 @@ async function requestInvitationEmail(origin, action, input2) {
41305
41593
  }
41306
41594
 
41307
41595
  // src/lib/remote-private-publications.ts
41308
- import { createHash as createHash21 } from "crypto";
41596
+ import { createHash as createHash22 } from "crypto";
41309
41597
  var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
41310
41598
  var failures2 = {
41311
41599
  INVALID_REQUEST: [400, "The publication request is invalid."],
@@ -41352,7 +41640,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
41352
41640
  var record8 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
41353
41641
  var exact = (v, keys) => record8(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
41354
41642
  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");
41643
+ var publicationSha256 = (bytes) => createHash22("sha256").update(bytes).digest("hex");
41356
41644
  function checkedPublicationDeclaration(value) {
41357
41645
  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
41646
  return bad();
@@ -41651,7 +41939,7 @@ async function requestAuthApi(instance, path, options) {
41651
41939
  const text3 = await res.text();
41652
41940
  const body = text3 ? parseJsonBody(text3) : {};
41653
41941
  if (!res.ok) {
41654
- const record9 = isRecord5(body) ? body : {};
41942
+ const record9 = isRecord6(body) ? body : {};
41655
41943
  const detail = typeof record9.detail === "string" ? record9.detail : undefined;
41656
41944
  const error61 = typeof record9.error === "string" ? record9.error : undefined;
41657
41945
  const code = typeof record9.code === "string" ? record9.code : undefined;
@@ -41679,7 +41967,7 @@ function condenseErrorBody(text3) {
41679
41967
  return collapsed;
41680
41968
  return `${collapsed.slice(0, MAX_ERROR_DETAIL_LENGTH - 1).trimEnd()}\u2026`;
41681
41969
  }
41682
- function isRecord5(value) {
41970
+ function isRecord6(value) {
41683
41971
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
41684
41972
  }
41685
41973
 
@@ -41836,7 +42124,7 @@ class RemoteSkillsAuthClient {
41836
42124
 
41837
42125
  // src/lib/recurring-recovery.ts
41838
42126
  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}$/);
42127
+ var sha5 = exports_external.string().regex(/^[a-f0-9]{64}$/);
41840
42128
  var common = exports_external.object({
41841
42129
  contractVersion: exports_external.literal(1),
41842
42130
  apiOrigin: exports_external.string().min(1).max(4096),
@@ -41849,7 +42137,7 @@ var intentSchema = exports_external.discriminatedUnion("operation", [
41849
42137
  common.extend({
41850
42138
  operation: exports_external.literal("activate"),
41851
42139
  draftId: uuid12,
41852
- termsSha256: sha4,
42140
+ termsSha256: sha5,
41853
42141
  approvalDeadline: exports_external.string().datetime(),
41854
42142
  approval: recurringActivationSchema
41855
42143
  }).strict(),
@@ -41857,7 +42145,7 @@ var intentSchema = exports_external.discriminatedUnion("operation", [
41857
42145
  ]);
41858
42146
  var stateSchema = exports_external.object({
41859
42147
  contractVersion: exports_external.literal(1),
41860
- intentSha256: sha4,
42148
+ intentSha256: sha5,
41861
42149
  phase: exports_external.enum(["prepared", "pending", "observed"]),
41862
42150
  consentId: uuid12.nullable()
41863
42151
  }).strict();
@@ -43218,7 +43506,7 @@ function isApiMode(env = process.env) {
43218
43506
  }
43219
43507
  }
43220
43508
  // src/lib/native-storage.ts
43221
- import { createHash as createHash22, createHmac as createHmac2 } from "crypto";
43509
+ import { createHash as createHash23, createHmac as createHmac2 } from "crypto";
43222
43510
  import {
43223
43511
  existsSync as existsSync19,
43224
43512
  mkdirSync as mkdirSync15,
@@ -43227,7 +43515,7 @@ import {
43227
43515
  statSync as statSync12,
43228
43516
  writeFileSync as writeFileSync13
43229
43517
  } from "fs";
43230
- import { dirname as dirname23, join as join35, normalize as normalize4, relative as relative9, sep as sep9 } from "path";
43518
+ import { dirname as dirname23, join as join35, normalize as normalize5, relative as relative9, sep as sep9 } from "path";
43231
43519
  var SKILLS_STORAGE_TABLES = [
43232
43520
  "skills_sync_records",
43233
43521
  "skills_sync_cursors"
@@ -43394,7 +43682,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
43394
43682
  files.push({
43395
43683
  path: relativePath,
43396
43684
  sizeBytes: bytes.byteLength,
43397
- sha256: createHash22("sha256").update(bytes).digest("hex"),
43685
+ sha256: createHash23("sha256").update(bytes).digest("hex"),
43398
43686
  ...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
43399
43687
  });
43400
43688
  }
@@ -43419,7 +43707,7 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
43419
43707
  continue;
43420
43708
  }
43421
43709
  const bytes = Buffer.from(file2.contentBase64, "base64");
43422
- const hash4 = createHash22("sha256").update(bytes).digest("hex");
43710
+ const hash4 = createHash23("sha256").update(bytes).digest("hex");
43423
43711
  if (hash4 !== file2.sha256) {
43424
43712
  throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
43425
43713
  }
@@ -43731,7 +44019,7 @@ function walkFiles2(dir) {
43731
44019
  return files;
43732
44020
  }
43733
44021
  function resolveSnapshotPath(targetDir, snapshotPath) {
43734
- const normalizedPath = normalize4(snapshotPath);
44022
+ const normalizedPath = normalize5(snapshotPath);
43735
44023
  if (normalizedPath.startsWith("..") || normalizedPath.includes(`${sep9}..${sep9}`) || normalizedPath.startsWith(sep9)) {
43736
44024
  throw new Error(`Unsafe snapshot path: ${snapshotPath}`);
43737
44025
  }
@@ -43751,7 +44039,7 @@ function toIsoString(value) {
43751
44039
  return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
43752
44040
  }
43753
44041
  function sha256Hex2(value) {
43754
- return createHash22("sha256").update(value).digest("hex");
44042
+ return createHash23("sha256").update(value).digest("hex");
43755
44043
  }
43756
44044
  function normalizeHeaders(headers) {
43757
44045
  const result = {};
@@ -43803,7 +44091,7 @@ function toArrayBuffer(bytes) {
43803
44091
  return buffer;
43804
44092
  }
43805
44093
  // src/lib/station-snapshot.ts
43806
- import { createHash as createHash23 } from "crypto";
44094
+ import { createHash as createHash24 } from "crypto";
43807
44095
  import {
43808
44096
  copyFileSync,
43809
44097
  mkdirSync as mkdirSync16,
@@ -43978,7 +44266,7 @@ function validateStationId(stationId) {
43978
44266
  }
43979
44267
  }
43980
44268
  function sha256File(filePath) {
43981
- return createHash23("sha256").update(readFileSync25(filePath)).digest("hex");
44269
+ return createHash24("sha256").update(readFileSync25(filePath)).digest("hex");
43982
44270
  }
43983
44271
  function scanHome(definition, homesRoot) {
43984
44272
  const homePath = homePathFor(definition, homesRoot);
@@ -44129,7 +44417,7 @@ function writeStationSnapshot(options) {
44129
44417
  };
44130
44418
  }
44131
44419
  // src/lib/station-hydrate.ts
44132
- import { createHash as createHash24 } from "crypto";
44420
+ import { createHash as createHash25 } from "crypto";
44133
44421
  import {
44134
44422
  copyFileSync as copyFileSync2,
44135
44423
  mkdirSync as mkdirSync17,
@@ -44335,7 +44623,7 @@ function skillSha256(skill) {
44335
44623
  return sha256File(skill.files[0].winner.fullPath);
44336
44624
  }
44337
44625
  const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
44338
- return createHash24("sha256").update(joined.sort().join(`
44626
+ return createHash25("sha256").update(joined.sort().join(`
44339
44627
  `)).digest("hex");
44340
44628
  }
44341
44629
  function writeStationHydration(options) {
@@ -44663,7 +44951,7 @@ async function inspectLocked(client2, directory2, cancel) {
44663
44951
  return privatePublicationResult(directory2, receipt);
44664
44952
  }
44665
44953
  // src/lib/skill-context.ts
44666
- import { createHash as createHash25 } from "crypto";
44954
+ import { createHash as createHash26 } from "crypto";
44667
44955
  import { resolve as resolve21 } from "path";
44668
44956
  function words(value) {
44669
44957
  return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
@@ -44752,7 +45040,7 @@ ${content}`;
44752
45040
  `);
44753
45041
  const receipt = {
44754
45042
  schemaVersion: 1,
44755
- id: createHash25("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
45043
+ id: createHash26("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
44756
45044
  profileId: profile.profileId,
44757
45045
  profileRevision: profile.profileRevision,
44758
45046
  authority: profile.authority,
@@ -44768,9 +45056,9 @@ ${content}`;
44768
45056
  return { context: context2, selections, omitted, receipt };
44769
45057
  }
44770
45058
  // src/lib/session-reconciliation.ts
44771
- import { createHash as createHash26 } from "crypto";
45059
+ import { createHash as createHash27 } from "crypto";
44772
45060
  var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
44773
- var digest3 = (value) => createHash26("sha256").update(JSON.stringify(value)).digest("hex");
45061
+ var digest3 = (value) => createHash27("sha256").update(JSON.stringify(value)).digest("hex");
44774
45062
  function exactTimestamp(value) {
44775
45063
  const parsed = value === undefined ? Number.NaN : Date.parse(value);
44776
45064
  if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
@@ -45069,6 +45357,7 @@ export {
45069
45357
  captureDiscoveryPathSources,
45070
45358
  captureDiscoveryDirectories,
45071
45359
  captureDiscoveryByteSources,
45360
+ captureCodexSettingsV2,
45072
45361
  captureCodexSettings,
45073
45362
  captureCodexNativeSkillCatalog,
45074
45363
  captureClaudeSettingsV3,