@hasna/skills 0.10.49 → 0.10.51

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.
@@ -1909,7 +1909,7 @@ var {
1909
1909
  // src/server/maintenance-entry.ts
1910
1910
  import { closeSync, constants, fstatSync, lstatSync, openSync, readSync } from "fs";
1911
1911
  import { createHash as createHash3 } from "crypto";
1912
- var {SQL: SQL2 } = globalThis.Bun;
1912
+ var {SQL: SQL3 } = globalThis.Bun;
1913
1913
 
1914
1914
  // src/server/execution-grant-store.ts
1915
1915
  import { randomUUID } from "crypto";
@@ -3991,6 +3991,140 @@ async function inspectEnrollment(databaseUrl, input) {
3991
3991
  }
3992
3992
  }
3993
3993
 
3994
+ // src/server/schema-census.ts
3995
+ var {SQL: SQL2 } = globalThis.Bun;
3996
+
3997
+ class SchemaCensusError extends Error {
3998
+ code;
3999
+ constructor(code) {
4000
+ super(code);
4001
+ this.code = code;
4002
+ this.name = "SchemaCensusError";
4003
+ }
4004
+ }
4005
+ var refuse2 = (code) => {
4006
+ throw new SchemaCensusError(code);
4007
+ };
4008
+ var FIXED_SCHEMA = "public";
4009
+ var STATEMENT_TIMEOUT_MS = 5000;
4010
+ var LOCK_TIMEOUT_MS = 1000;
4011
+ var MAX_MIGRATION_VERSIONS = 128;
4012
+ var MAX_MIGRATION_VERSION_CHARS = 64;
4013
+ var MAX_IDENTIFIER_CHARS = 128;
4014
+ var MAX_SERVER_VERSION_CHARS = 256;
4015
+ var MAX_CENSUS_BYTES = 16 * 1024;
4016
+ var boundedText = (value, max) => {
4017
+ if (typeof value !== "string" || value.length === 0 || /[\u0000-\u001f\u007f]/.test(value))
4018
+ return refuse2("CENSUS_INVALID_RESULT");
4019
+ if (value.length > max)
4020
+ return refuse2("CENSUS_OUTPUT_UNBOUNDED");
4021
+ return value;
4022
+ };
4023
+ var boundedCount = (value) => {
4024
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
4025
+ return refuse2("CENSUS_INVALID_RESULT");
4026
+ return value;
4027
+ };
4028
+ async function inspectSchemaCensus(databaseUrl) {
4029
+ try {
4030
+ const url = new URL(databaseUrl);
4031
+ if (!["postgres:", "postgresql:"].includes(url.protocol) || !url.hostname)
4032
+ refuse2("CENSUS_POSTGRES_REQUIRED");
4033
+ } catch {
4034
+ refuse2("CENSUS_POSTGRES_REQUIRED");
4035
+ }
4036
+ let sql;
4037
+ try {
4038
+ sql = new SQL2(databaseUrl, { max: 1, connectionTimeout: 5, idleTimeout: 5 });
4039
+ return await sql.begin("ISOLATION LEVEL REPEATABLE READ READ ONLY", async (tx) => {
4040
+ await tx.unsafe("SET LOCAL statement_timeout = '5s'");
4041
+ await tx.unsafe("SET LOCAL lock_timeout = '1s'");
4042
+ const mode = await tx`SELECT current_setting('transaction_read_only') AS read_only,
4043
+ current_setting('transaction_isolation') AS isolation,
4044
+ current_setting('statement_timeout') AS statement_timeout,
4045
+ current_setting('lock_timeout') AS lock_timeout`;
4046
+ if (mode.length !== 1 || mode[0].read_only !== "on" || mode[0].isolation !== "repeatable read" || mode[0].statement_timeout !== "5s" || mode[0].lock_timeout !== "1s") {
4047
+ refuse2("CENSUS_FAILED");
4048
+ }
4049
+ const identity = await tx`SELECT current_database() AS database, current_user AS role,
4050
+ version() AS server_version, current_schema() AS schema`;
4051
+ if (identity.length !== 1)
4052
+ refuse2("CENSUS_INVALID_RESULT");
4053
+ const database = boundedText(identity[0].database, MAX_IDENTIFIER_CHARS);
4054
+ const role = boundedText(identity[0].role, MAX_IDENTIFIER_CHARS);
4055
+ const serverVersion = boundedText(identity[0].server_version, MAX_SERVER_VERSION_CHARS);
4056
+ if (identity[0].schema !== FIXED_SCHEMA)
4057
+ refuse2("CENSUS_TARGET_MISMATCH");
4058
+ const ledger = await tx`SELECT version FROM public.schema_migrations ORDER BY version LIMIT ${MAX_MIGRATION_VERSIONS + 1}`;
4059
+ if (ledger.length > MAX_MIGRATION_VERSIONS)
4060
+ refuse2("CENSUS_OUTPUT_UNBOUNDED");
4061
+ const versions = ledger.map((row) => boundedText(row.version, MAX_MIGRATION_VERSION_CHARS));
4062
+ const registryCount = await tx`SELECT count(*)::int AS rows FROM public.skills_registry`;
4063
+ const versionCount = await tx`SELECT count(*)::int AS rows FROM public.skills_versions`;
4064
+ if (registryCount.length !== 1 || versionCount.length !== 1)
4065
+ refuse2("CENSUS_INVALID_RESULT");
4066
+ const registryRows = boundedCount(registryCount[0].rows);
4067
+ const versionRows = boundedCount(versionCount[0].rows);
4068
+ const groups = await tx`SELECT lifecycle, (tombstoned_at IS NOT NULL) AS tombstoned, count(*)::int AS rows
4069
+ FROM public.skills_registry GROUP BY 1, 2 LIMIT 5`;
4070
+ if (groups.length > 4)
4071
+ refuse2("CENSUS_INVALID_RESULT");
4072
+ const lifecycle = { active: { live: 0, tombstoned: 0 }, archived: { live: 0, tombstoned: 0 } };
4073
+ for (const group of groups) {
4074
+ const name = group.lifecycle;
4075
+ const tombstoned = group.tombstoned;
4076
+ if (name !== "active" && name !== "archived" || typeof tombstoned !== "boolean")
4077
+ return refuse2("CENSUS_INVALID_RESULT");
4078
+ lifecycle[name][tombstoned ? "tombstoned" : "live"] = boundedCount(group.rows);
4079
+ }
4080
+ const epochColumn = await tx`SELECT count(*)::int AS rows FROM information_schema.columns
4081
+ WHERE table_schema = ${FIXED_SCHEMA} AND table_name = 'skills_registry' AND column_name = 'authorization_epoch'`;
4082
+ if (epochColumn.length !== 1)
4083
+ refuse2("CENSUS_INVALID_RESULT");
4084
+ const epochPresent = boundedCount(epochColumn[0].rows);
4085
+ if (epochPresent > 1)
4086
+ refuse2("CENSUS_INVALID_RESULT");
4087
+ let authorizationEpoch;
4088
+ if (epochPresent === 0)
4089
+ authorizationEpoch = { exists: false };
4090
+ else {
4091
+ const epochNulls = await tx`SELECT count(*)::int AS rows FROM public.skills_registry WHERE authorization_epoch IS NULL`;
4092
+ if (epochNulls.length !== 1)
4093
+ refuse2("CENSUS_INVALID_RESULT");
4094
+ authorizationEpoch = { exists: true, nullCount: boundedCount(epochNulls[0].rows) };
4095
+ }
4096
+ const result = {
4097
+ status: "inspected",
4098
+ database,
4099
+ role,
4100
+ serverVersion,
4101
+ schema: FIXED_SCHEMA,
4102
+ transaction: { readOnly: true, isolation: "repeatable read", statementTimeoutMs: STATEMENT_TIMEOUT_MS, lockTimeoutMs: LOCK_TIMEOUT_MS },
4103
+ migrations: { count: versions.length, versions },
4104
+ registry: { rows: registryRows },
4105
+ versions: { rows: versionRows },
4106
+ lifecycle,
4107
+ authorizationEpoch
4108
+ };
4109
+ if (Buffer.byteLength(JSON.stringify(result), "utf8") > MAX_CENSUS_BYTES)
4110
+ refuse2("CENSUS_OUTPUT_UNBOUNDED");
4111
+ return result;
4112
+ });
4113
+ } catch (error) {
4114
+ if (error instanceof SchemaCensusError)
4115
+ throw error;
4116
+ const state2 = error instanceof SQL2.PostgresError ? error.errno : undefined;
4117
+ const code = state2 === "42P01" || state2 === "42703" ? "CENSUS_SCHEMA_UNAVAILABLE" : state2 === "42501" ? "CENSUS_ACCESS_DENIED" : state2 === "25006" ? "CENSUS_WRITE_REFUSED" : state2 === "57014" || state2 === "55P03" ? "CENSUS_TIMEOUT" : ["08000", "08001", "08003", "08004", "08006", "08007", "08P01", "28000", "28P01", "57P03"].includes(state2 ?? "") ? "CENSUS_CONNECTION_FAILED" : "CENSUS_FAILED";
4118
+ throw new SchemaCensusError(code);
4119
+ } finally {
4120
+ try {
4121
+ await sql?.close({ timeout: 1 });
4122
+ } catch {
4123
+ throw new SchemaCensusError("CENSUS_FAILED");
4124
+ }
4125
+ }
4126
+ }
4127
+
3994
4128
  // src/server/maintenance-entry.ts
3995
4129
  class InspectionInputError extends Error {
3996
4130
  code;
@@ -3999,6 +4133,14 @@ class InspectionInputError extends Error {
3999
4133
  this.code = code;
4000
4134
  }
4001
4135
  }
4136
+
4137
+ class SchemaCensusInputError extends Error {
4138
+ code;
4139
+ constructor(code) {
4140
+ super(code);
4141
+ this.code = code;
4142
+ }
4143
+ }
4002
4144
  var MAX_MANIFEST_BYTES = 32 * 1024;
4003
4145
  function sha256(value) {
4004
4146
  return createHash3("sha256").update(value).digest("hex");
@@ -4109,6 +4251,41 @@ function readInspectionManifest(path, receiptPath) {
4109
4251
  }
4110
4252
  return parsed;
4111
4253
  }
4254
+ function readSchemaCensusManifest(path, receiptPath) {
4255
+ let input;
4256
+ let parsed;
4257
+ try {
4258
+ input = readBoundedJson(path);
4259
+ const value = input.value;
4260
+ if (!value || typeof value !== "object" || Array.isArray(value))
4261
+ throw new Error;
4262
+ const fields = ["app", "operation", "manifestVersion", "operationId", "stationId", "keyId", "orgId", "expiresAt", "manifestDigest"];
4263
+ if (Object.keys(value).length !== fields.length || fields.some((field) => !Object.hasOwn(value, field)))
4264
+ throw new Error;
4265
+ if (value.app !== "skills" || value.operation !== "inspect-schema" || value.manifestVersion !== 1)
4266
+ throw new Error;
4267
+ for (const field of ["operationId", "stationId", "keyId", "orgId"]) {
4268
+ const item = value[field];
4269
+ if (typeof item !== "string" || item.trim().length === 0 || item.length > 256 || /[\u0000-\u001f\u007f]/.test(item))
4270
+ throw new Error;
4271
+ }
4272
+ if (typeof value.expiresAt !== "string" || value.expiresAt.length > 64 || !Number.isFinite(Date.parse(value.expiresAt)) || Date.parse(value.expiresAt) <= Date.now())
4273
+ throw new Error;
4274
+ if (typeof value.manifestDigest !== "string" || !/^[a-f0-9]{64}$/.test(value.manifestDigest))
4275
+ throw new Error;
4276
+ if (sha256(canonical(value)) !== value.manifestDigest)
4277
+ throw new Error;
4278
+ parsed = value;
4279
+ } catch {
4280
+ throw new SchemaCensusInputError("INVALID_CENSUS_MANIFEST");
4281
+ }
4282
+ try {
4283
+ verifyOperatorReceipt(input, readBoundedJson(receiptPath).value);
4284
+ } catch {
4285
+ throw new SchemaCensusInputError("INVALID_OPERATOR_RECEIPT");
4286
+ }
4287
+ return parsed;
4288
+ }
4112
4289
  function registerMaintenance(parent) {
4113
4290
  const maintenance = parent.command("maintenance").description("Run protected, metadata-only Skills maintenance operations");
4114
4291
  maintenance.command("inspect-enrollment").description("Inspect one enrollment with a database-enforced read-only snapshot").requiredOption("--manifest <path>", "Fresh inspection authorization supplied by the protected task wrapper").requiredOption("--operator-receipt <path>", "Verified actor receipt written by the protected task wrapper").option("--json", "Output a safe JSON receipt", false).action(async (options) => {
@@ -4133,6 +4310,26 @@ function registerMaintenance(parent) {
4133
4310
  process.exitCode = 1;
4134
4311
  }
4135
4312
  });
4313
+ maintenance.command("inspect-schema").description("Census the fixed Skills schema and migration ledger with a database-enforced read-only snapshot").requiredOption("--manifest <path>", "Fresh census authorization supplied by the protected task wrapper").requiredOption("--operator-receipt <path>", "Verified actor receipt written by the protected task wrapper").option("--json", "Output a safe JSON receipt", false).action(async (options) => {
4314
+ try {
4315
+ const manifest = readSchemaCensusManifest(options.manifest, options.operatorReceipt);
4316
+ const census = await inspectSchemaCensus(process.env.HASNA_SKILLS_DATABASE_URL ?? "");
4317
+ console.log(JSON.stringify({
4318
+ ...census,
4319
+ operationId: manifest.operationId,
4320
+ keyId: manifest.keyId,
4321
+ orgId: manifest.orgId,
4322
+ stationId: manifest.stationId
4323
+ }));
4324
+ } catch (error) {
4325
+ const code = error instanceof SchemaCensusInputError || error instanceof SchemaCensusError ? error.code : "CENSUS_FAILED";
4326
+ if (options.json || !process.stdout.isTTY)
4327
+ console.log(JSON.stringify({ status: "failed", code }));
4328
+ else
4329
+ console.error(code);
4330
+ process.exitCode = 1;
4331
+ }
4332
+ });
4136
4333
  maintenance.command("enroll-publish").requiredOption("--manifest <path>", "Bounded target manifest supplied by the protected task wrapper").requiredOption("--operator-receipt <path>", "Verified actor receipt written by the protected task wrapper").option("--apply", "Commit the bounded scope update; defaults to dry-run", false).option("--json", "Output a safe JSON receipt", false).action(async (options) => {
4137
4334
  let manifest;
4138
4335
  let store;
@@ -4167,7 +4364,7 @@ function registerMaintenance(parent) {
4167
4364
  process.exitCode = 1;
4168
4365
  } catch (error) {
4169
4366
  const message = error instanceof Error ? error.message : "";
4170
- const state2 = error instanceof SQL2.PostgresError ? error.errno : undefined;
4367
+ const state2 = error instanceof SQL3.PostgresError ? error.errno : undefined;
4171
4368
  const code = state2 === "42P01" || state2 === "42703" ? "MAINTENANCE_SCHEMA_UNAVAILABLE" : state2 === "42501" ? "MAINTENANCE_ACCESS_DENIED" : state2 === "25006" ? "MAINTENANCE_WRITE_REFUSED" : state2 === "57014" || state2 === "55P03" ? "MAINTENANCE_TIMEOUT" : ["08000", "08001", "08003", "08004", "08006", "08007", "08P01", "28000", "28P01", "57P03"].includes(state2 ?? "") ? "MAINTENANCE_CONNECTION_FAILED" : message.includes("expired") || message.includes("expiry") ? "INVALID_EXPIRY" : message.includes("receipt") || message.includes("operator") ? "INVALID_OPERATOR_RECEIPT" : message.includes("manifest") || message.includes("input") ? "INVALID_MANIFEST" : message.includes("database") || message.includes("store") ? "STORE_UNAVAILABLE" : "MAINTENANCE_FAILED";
4172
4369
  if (options.json || !process.stdout.isTTY)
4173
4370
  console.log(JSON.stringify({ status: "failed", code }));
package/bin/mcp.js CHANGED
@@ -20019,7 +20019,7 @@ var package_default;
20019
20019
  var init_package = __esm(() => {
20020
20020
  package_default = {
20021
20021
  name: "@hasna/skills",
20022
- version: "0.10.49",
20022
+ version: "0.10.51",
20023
20023
  description: "Skills library for AI coding agents",
20024
20024
  type: "module",
20025
20025
  bin: {
@@ -38370,7 +38370,9 @@ function inferencePreference(key, child, version2) {
38370
38370
  function settingsDigest(text, version2 = 1) {
38371
38371
  const value = parse6(text);
38372
38372
  value.entries = value.entries.filter(([key, child]) => {
38373
- if (version2 !== 1 && inferencePreference(key, child, version2))
38373
+ if (version2 === 4 && key === "theme" && child.kind === "string" && BUILTIN_THEMES.has(child.value))
38374
+ return false;
38375
+ if (version2 !== 1 && inferencePreference(key, child, version2 === 4 ? 3 : version2))
38374
38376
  return false;
38375
38377
  if (key === "model") {
38376
38378
  need(child.kind === "string", "contains an invalid model selection");
@@ -38418,6 +38420,16 @@ function hashClaudeSettingsReplacementV3(text, budget) {
38418
38420
  budget.remaining -= bytes;
38419
38421
  return settingsDigest(text, 3);
38420
38422
  }
38423
+ function captureClaudeSettingsV4(path, budget = { remaining: 256 * 1024 * 1024 }) {
38424
+ return { path, hashMode: "claude-settings-v4", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 4) };
38425
+ }
38426
+ function hashClaudeSettingsReplacementV4(text, budget) {
38427
+ need(typeof text === "string", "requires settings text");
38428
+ const bytes = Buffer.byteLength(text);
38429
+ need(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
38430
+ budget.remaining -= bytes;
38431
+ return settingsDigest(text, 4);
38432
+ }
38421
38433
  function hashNativeJsonControls(text, excludedMetadata) {
38422
38434
  need(Buffer.byteLength(text) <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes, "exceeds its byte limit");
38423
38435
  const value = parse6(text);
@@ -38450,7 +38462,7 @@ function hashSumiNativeJsonControls(text) {
38450
38462
  });
38451
38463
  return createHash2("sha256").update("hasna.skills.sumi-settings.v1\x00").update(JSON.stringify(value)).digest("hex");
38452
38464
  }
38453
- var CLAUDE_SETTINGS_WITNESS_LIMITS, sameFile = (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, BOOLEAN_PREFERENCES, ENUM_PREFERENCES, BUILTIN_MODEL_SELECTIONS, PERSISTED_EFFORT_LEVELS;
38465
+ var CLAUDE_SETTINGS_WITNESS_LIMITS, sameFile = (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, BOOLEAN_PREFERENCES, ENUM_PREFERENCES, BUILTIN_MODEL_SELECTIONS, CLAUDE_BUILTIN_THEMES, BUILTIN_THEMES, PERSISTED_EFFORT_LEVELS;
38454
38466
  var init_claude_settings_witness = __esm(() => {
38455
38467
  CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
38456
38468
  BOOLEAN_PREFERENCES = new Set([
@@ -38504,6 +38516,8 @@ var init_claude_settings_witness = __esm(() => {
38504
38516
  "claude-opus-4-6[1m]",
38505
38517
  "claude-sonnet-4-6[1m]"
38506
38518
  ]);
38519
+ CLAUDE_BUILTIN_THEMES = Object.freeze(["auto", "dark", "light", "dark-daltonized", "light-daltonized", "dark-ansi", "light-ansi"]);
38520
+ BUILTIN_THEMES = new Set(CLAUDE_BUILTIN_THEMES);
38507
38521
  PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
38508
38522
  });
38509
38523
 
@@ -38524,6 +38538,29 @@ var init_sumi_settings_witness = __esm(() => {
38524
38538
  SUMI_DISCOVERY_PROJECTION_FIELDS = ["skills", "plugin", "plugins", "permissions"];
38525
38539
  });
38526
38540
 
38541
+ // src/lib/codex-native-compatibility.ts
38542
+ function supportsCodexNativeCapability(version2, capability) {
38543
+ return typeof version2 === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version2) && MEASURED_CODEX_CAPABILITIES[version2].includes(capability);
38544
+ }
38545
+ var MEASURED_CODEX_CAPABILITIES, SUPPORTED_CODEX_HOOK_VERSIONS;
38546
+ var init_codex_native_compatibility = __esm(() => {
38547
+ MEASURED_CODEX_CAPABILITIES = {
38548
+ "codex-cli 0.153.0": ["hooks"],
38549
+ "codex-cli 0.154.0": ["hooks"],
38550
+ "codex-cli 0.155.0": ["hooks"],
38551
+ "codex-cli 0.155.1": ["hooks"],
38552
+ "codex-cli 0.156.1": ["hooks"],
38553
+ "codex-cli 0.157.0": ["hooks"],
38554
+ "codex-cli 0.157.1": ["hooks"],
38555
+ "codex-cli 0.158.0": ["hooks"],
38556
+ "codex-cli 0.159.0": ["hooks"],
38557
+ "codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
38558
+ "codex-cli 0.160.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
38559
+ "codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
38560
+ };
38561
+ SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version2) => supportsCodexNativeCapability(version2, "hooks")));
38562
+ });
38563
+
38527
38564
  // src/lib/agent-policy-limits.ts
38528
38565
  import { basename as basename4, isAbsolute as isAbsolute4, join as join10, resolve as resolve3, sep as sep3 } from "path";
38529
38566
  function object4(value) {
@@ -38590,7 +38627,7 @@ function assertAgentPolicyCollections(policy) {
38590
38627
  }
38591
38628
  if (bridge.codexInactivePlugins !== undefined) {
38592
38629
  const controls = array3(bridge.codexInactivePlugins, 4096), ids = new Set;
38593
- requireBound(controls.length === 0 || object4(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === "codex-cli 0.160.0" && typeof bridge.codexPluginSkillReview.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(bridge.codexPluginSkillReview.catalogSha256));
38630
+ requireBound(controls.length === 0 || object4(bridge.codexPluginSkillReview) && supportsCodexNativeCapability(bridge.codexPluginSkillReview.version, "installed-plugin-review") && typeof bridge.codexPluginSkillReview.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(bridge.codexPluginSkillReview.catalogSha256));
38594
38631
  for (const control of controls) {
38595
38632
  requireBound(object4(control) && Object.keys(control).every((key) => ["pluginId", "namespace", "pluginParent", "sourceType", "sourceSha256"].includes(key)));
38596
38633
  text(control.pluginId, 1024);
@@ -38642,7 +38679,7 @@ function assertAgentPolicyCollections(policy) {
38642
38679
  }
38643
38680
  if (value.codexInstallationInputs !== undefined) {
38644
38681
  const proof = record3(value.codexInstallationInputs, 4), inputs = array3(proof.plugins, 4096), ids = new Set;
38645
- requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && proof.version === "codex-cli 0.160.0" && typeof proof.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(proof.catalogSha256) && inputs.length > 0 && object4(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === proof.version && bridge.codexPluginSkillReview.catalogSha256 === proof.catalogSha256);
38682
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && supportsCodexNativeCapability(proof.version, "installed-plugin-review") && typeof proof.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(proof.catalogSha256) && inputs.length > 0 && object4(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === proof.version && bridge.codexPluginSkillReview.catalogSha256 === proof.catalogSha256);
38646
38683
  for (const input2 of inputs) {
38647
38684
  requireBound(object4(input2) && Object.keys(input2).every((key) => ["pluginId", "namespace", "pluginParent", "sourceRoot", "sourceSha256"].includes(key)));
38648
38685
  text(input2.pluginId, 1024);
@@ -38666,14 +38703,14 @@ function assertAgentPolicyCollections(policy) {
38666
38703
  text(source.path);
38667
38704
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
38668
38705
  if (source.hashMode !== undefined)
38669
- requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "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", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
38706
+ requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
38670
38707
  if (source.hashMode === "claude-plugin-manifest-v1")
38671
38708
  requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && source.path.endsWith(join10(sep3, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
38672
38709
  if (source.hashMode === "sumi-settings-v1")
38673
38710
  requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && basename4(source.path) === "sumi.json");
38674
38711
  if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
38675
38712
  requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && basename4(source.path) === "config.toml");
38676
- if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
38713
+ if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
38677
38714
  requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && basename4(source.path) === "settings.json");
38678
38715
  if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
38679
38716
  requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve3(source.path) === source.path && basename4(source.path) === "known_marketplaces.json");
@@ -38707,6 +38744,7 @@ function assertAgentPolicyCollections(policy) {
38707
38744
  }
38708
38745
  var AGENT_POLICY_LIMITS;
38709
38746
  var init_agent_policy_limits = __esm(() => {
38747
+ init_codex_native_compatibility();
38710
38748
  AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryPathLinks: 40, discoveryPathSteps: 256, discoveryPathSourceMetadataBytes: 64 * 1024, discoveryPathTotalMetadataBytes: 8 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
38711
38749
  });
38712
38750
 
@@ -47938,28 +47976,6 @@ function isCodexLocalStdioMcpServer(name, server) {
47938
47976
  }
47939
47977
  }
47940
47978
 
47941
- // src/lib/codex-native-compatibility.ts
47942
- function supportsCodexNativeCapability(version2, capability) {
47943
- return typeof version2 === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version2) && MEASURED_CODEX_CAPABILITIES[version2].includes(capability);
47944
- }
47945
- var MEASURED_CODEX_CAPABILITIES, SUPPORTED_CODEX_HOOK_VERSIONS;
47946
- var init_codex_native_compatibility = __esm(() => {
47947
- MEASURED_CODEX_CAPABILITIES = {
47948
- "codex-cli 0.153.0": ["hooks"],
47949
- "codex-cli 0.154.0": ["hooks"],
47950
- "codex-cli 0.155.0": ["hooks"],
47951
- "codex-cli 0.155.1": ["hooks"],
47952
- "codex-cli 0.156.1": ["hooks"],
47953
- "codex-cli 0.157.0": ["hooks"],
47954
- "codex-cli 0.157.1": ["hooks"],
47955
- "codex-cli 0.158.0": ["hooks"],
47956
- "codex-cli 0.159.0": ["hooks"],
47957
- "codex-cli 0.159.2": ["hooks", "qualified-skill-catalog"],
47958
- "codex-cli 0.160.0": ["hooks", "qualified-skill-catalog"]
47959
- };
47960
- SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version2) => supportsCodexNativeCapability(version2, "hooks")));
47961
- });
47962
-
47963
47979
  // src/lib/codex-hook-rpc.ts
47964
47980
  var init_codex_hook_rpc = __esm(() => {
47965
47981
  init_codex_corpus_write();
@@ -50580,13 +50596,13 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
50580
50596
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
50581
50597
  return changes?.has(source.path) ? (source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : current;
50582
50598
  }
50583
- if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
50599
+ if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) {
50584
50600
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
50585
50601
  throw new Error("Claude settings witnesses require exact typed metadata");
50586
- const current = (source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
50602
+ const current = (source.hashMode === "claude-settings-v4" ? captureClaudeSettingsV4 : source.hashMode === "claude-settings-v3" ? captureClaudeSettingsV3 : source.hashMode === "claude-settings-v2" ? captureClaudeSettingsV2 : captureClaudeSettings)(source.path, budget2).sha256;
50587
50603
  if (current !== source.sha256)
50588
50604
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
50589
- return changes?.has(source.path) ? (source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
50605
+ return changes?.has(source.path) ? (source.hashMode === "claude-settings-v4" ? hashClaudeSettingsReplacementV4 : source.hashMode === "claude-settings-v3" ? hashClaudeSettingsReplacementV3 : source.hashMode === "claude-settings-v2" ? hashClaudeSettingsReplacementV2 : hashClaudeSettingsReplacement)(changes.get(source.path), budget2) : current;
50590
50606
  }
50591
50607
  if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") {
50592
50608
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256) || changes?.has(source.path))
@@ -50781,7 +50797,7 @@ function codexInstallationRoots(binding) {
50781
50797
  const proof = binding.codexInstallationInputs;
50782
50798
  if (proof === undefined)
50783
50799
  return [];
50784
- if (binding.agent !== "codex" || binding.method !== "reviewed" || proof.version !== "codex-cli 0.160.0" || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
50800
+ if (binding.agent !== "codex" || binding.method !== "reviewed" || !supportsCodexNativeCapability(proof.version, "installed-plugin-review") || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
50785
50801
  throw new Error("Invalid native installation input proof");
50786
50802
  const cache = dirname15(dirname15(proof.plugins[0].pluginParent));
50787
50803
  const config2 = join26(dirname15(dirname15(cache)), "config.toml"), home = dirname15(dirname15(dirname15(cache)));
@@ -50805,7 +50821,7 @@ function assertMarketplaceBinding(binding, source) {
50805
50821
  throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
50806
50822
  if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename8(source.path) !== "config.toml"))
50807
50823
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
50808
- if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename8(source.path) !== "settings.json"))
50824
+ if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename8(source.path) !== "settings.json"))
50809
50825
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
50810
50826
  if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
50811
50827
  throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
@@ -50927,7 +50943,7 @@ function resolveAgentDiscovery(options) {
50927
50943
  verifyAgentDiscovery(retained);
50928
50944
  if (!retained.sources.some((source) => source.path === canonical7(configPath) && source.format === undefined && source.fields === undefined))
50929
50945
  unresolved("retained review is missing the full agent configuration source");
50930
- if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || 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.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
50946
+ if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
50931
50947
  unresolved("retained settings witness names another configuration source");
50932
50948
  const covered = supersedesSettingsProjection(agent, retained.sources, canonical7(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical7(configPath), source)) : sources;
50933
50949
  if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual4(saved, source))) || [...roots].some((root) => !retained.roots.includes(root)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
@@ -50941,7 +50957,7 @@ function resolveAgentDiscovery(options) {
50941
50957
  if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
50942
50958
  throw new Error("Explicit discovery reviews require full source-file hashes");
50943
50959
  verifyAgentDiscovery(supplied);
50944
- if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || 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.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
50960
+ if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
50945
50961
  throw new Error("Semantic settings witnesses must name the configured agent configuration source");
50946
50962
  if (!review.sources.some((source) => source.path === canonical7(configPath)))
50947
50963
  throw new Error("Discovery review must include the agent configuration source");
@@ -51108,6 +51124,7 @@ var init_agent_discovery = __esm(() => {
51108
51124
  init_codex_plugin_skill_controls();
51109
51125
  init_agent_sumi();
51110
51126
  init_native_discovery_roots();
51127
+ init_codex_native_compatibility();
51111
51128
  init_agent_discovery_directories();
51112
51129
  });
51113
51130
 
package/bin/migrate.js CHANGED
@@ -25,7 +25,7 @@ import { join as join5 } from "path";
25
25
  // package.json
26
26
  var package_default = {
27
27
  name: "@hasna/skills",
28
- version: "0.10.49",
28
+ version: "0.10.51",
29
29
  description: "Skills library for AI coding agents",
30
30
  type: "module",
31
31
  bin: {
package/bin/server.js CHANGED
@@ -24140,7 +24140,7 @@ restoreLaunchCwd();
24140
24140
  // package.json
24141
24141
  var package_default = {
24142
24142
  name: "@hasna/skills",
24143
- version: "0.10.49",
24143
+ version: "0.10.51",
24144
24144
  description: "Skills library for AI coding agents",
24145
24145
  type: "module",
24146
24146
  bin: {
package/bin/worker.js CHANGED
@@ -24143,7 +24143,7 @@ import { randomUUID as randomUUID6 } from "crypto";
24143
24143
  // package.json
24144
24144
  var package_default = {
24145
24145
  name: "@hasna/skills",
24146
- version: "0.10.49",
24146
+ version: "0.10.51",
24147
24147
  description: "Skills library for AI coding agents",
24148
24148
  type: "module",
24149
24149
  bin: {
package/dist/index.d.ts CHANGED
@@ -64,7 +64,7 @@ export { planAgentIntegration, planClaudeStopHookUpdate, planClaudePreToolUseHoo
64
64
  export type { ClaudeManagedHookProjection, ClaudeManagedHookProjectionOptions } from "./lib/agent-integration.js";
65
65
  export { captureDiscoveryDirectories, captureDiscoveryByteSources, captureDiscoveryPathSources, type DiscoveryDirectory, type DiscoverySource } from "./lib/agent-discovery.js";
66
66
  export { captureClaudeMarketplaceRegistry, captureClaudeMarketplaceRegistryV2 } from "./lib/claude-marketplace-registry.js";
67
- export { captureClaudeSettings, captureClaudeSettingsV2, captureClaudeSettingsV3, upgradeClaudeSettingsWitness } from "./lib/claude-settings-witness.js";
67
+ export { captureClaudeSettings, captureClaudeSettingsV2, captureClaudeSettingsV3, captureClaudeSettingsV4, upgradeClaudeSettingsWitness, upgradeClaudeSettingsWitnessV4 } from "./lib/claude-settings-witness.js";
68
68
  export { buildPluginProjection, pluginTreeDigest, type PluginProjectionManifest, type PluginPayloadMapping, type PluginFileWitness } from "./lib/plugin-projection.js";
69
69
  export { planPluginAdmission, admitPlugin, resolveAdmittedPlugin, pluginResolverCommand, type PluginAdmissionTarget, type PluginAdmissionBinding, type PluginAdmissionPrincipal, type PluginAdmissionPlan, type PluginAdmissionIdentity, type PluginBundleIdentity, type PluginRoutingIdentity, type PluginAdmissionReceipt } from "./lib/plugin-admission.js";
70
70
  export { captureManagedPluginRegistry, type ManagedPluginRegistrationWitness } from "./lib/plugin-discovery.js";