@hasna/skills 0.10.14 → 0.10.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/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.14",
20022
+ version: "0.10.16",
20023
20023
  description: "Skills library for AI coding agents",
20024
20024
  type: "module",
20025
20025
  bin: {
@@ -38251,6 +38251,17 @@ function assertAgentPolicyCollections(policy) {
38251
38251
  for (const key of ["agent", "home", "alias", "target", "link", "aliasIdentity", "targetIdentity"])
38252
38252
  text(alias[key]);
38253
38253
  }
38254
+ if (bridge.codexPluginSkills !== undefined)
38255
+ for (const control of array3(bridge.codexPluginSkills, 4096)) {
38256
+ requireBound(object4(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256"].includes(key)));
38257
+ text(control.name, 129);
38258
+ text(control.pluginId, 1024);
38259
+ text(control.namespace, 64);
38260
+ text(control.pluginParent);
38261
+ requireBound(isAbsolute3(control.pluginParent) && resolve2(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
38262
+ if (control.appSha256 !== undefined)
38263
+ requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
38264
+ }
38254
38265
  if (bridge.disabledBuiltins !== undefined)
38255
38266
  for (const builtin of array3(bridge.disabledBuiltins, AGENT_POLICY_LIMITS.builtinNames)) {
38256
38267
  requireBound(object4(builtin));
@@ -38270,7 +38281,9 @@ function assertAgentPolicyCollections(policy) {
38270
38281
  text(source.path);
38271
38282
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
38272
38283
  if (source.hashMode !== undefined)
38273
- requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
38284
+ 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));
38285
+ if (source.hashMode === "codex-settings-v1")
38286
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve2(source.path) === source.path && basename3(source.path) === "config.toml");
38274
38287
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
38275
38288
  requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve2(source.path) === source.path && basename3(source.path) === "settings.json");
38276
38289
  if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
@@ -45605,294 +45618,8 @@ var init_agent_discovery_path_bytes = __esm(() => {
45605
45618
  init_agent_discovery_bytes();
45606
45619
  });
45607
45620
 
45608
- // src/lib/remote-files.ts
45609
- var exports_remote_files = {};
45610
- __export(exports_remote_files, {
45611
- sha256: () => sha256,
45612
- readBoundedResponse: () => readBoundedResponse,
45613
- describeRemoteFiles: () => describeRemoteFiles,
45614
- decodeRemoteFiles: () => decodeRemoteFiles,
45615
- MAX_REMOTE_FILE_BYTES: () => MAX_REMOTE_FILE_BYTES
45616
- });
45617
- import { createHash as createHash5 } from "crypto";
45618
- function describeRemoteFiles(files) {
45619
- if (files.length > 10)
45620
- throw new Error("At most 10 input files are supported");
45621
- const names = new Set;
45622
- let total = 0;
45623
- return files.map((file2) => {
45624
- if (typeof file2.name !== "string" || file2.name.length === 0 || file2.name.length > 255 || /[/\\\x00-\x1f\x7f]/.test(file2.name) || /[\ud800-\udfff]/u.test(file2.name) || file2.name === "." || file2.name === ".." || names.has(file2.name))
45625
- throw new Error("Input file names must be unique safe basenames");
45626
- names.add(file2.name);
45627
- total += file2.bytes.byteLength;
45628
- if (file2.bytes.byteLength > 20 * 1024 * 1024 || total > 50 * 1024 * 1024)
45629
- throw new Error("Input files exceed the supported size limit");
45630
- return { name: file2.name, sizeBytes: file2.bytes.byteLength, sha256: sha256(file2.bytes), contentType: file2.contentType ?? "application/octet-stream" };
45631
- });
45632
- }
45633
- function sha256(bytes) {
45634
- return createHash5("sha256").update(bytes).digest("hex");
45635
- }
45636
- async function readBoundedResponse(response, maximum) {
45637
- if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
45638
- throw new Error("Invalid artifact size limit");
45639
- const length = response.headers.get("content-length");
45640
- if (length && (!/^\d+$/.test(length) || Number(length) > maximum)) {
45641
- await response.body?.cancel();
45642
- throw new Error("Artifact exceeds its declared size limit");
45643
- }
45644
- const reader = response.body?.getReader();
45645
- if (!reader)
45646
- return new Uint8Array;
45647
- const chunks = [];
45648
- let size = 0;
45649
- try {
45650
- while (true) {
45651
- const next = await reader.read();
45652
- if (next.done)
45653
- break;
45654
- size += next.value.byteLength;
45655
- if (size > maximum)
45656
- throw new Error("Artifact exceeds its declared size limit");
45657
- chunks.push(next.value);
45658
- }
45659
- } catch (error61) {
45660
- await reader.cancel().catch(() => {});
45661
- throw error61;
45662
- } finally {
45663
- reader.releaseLock();
45664
- }
45665
- const bytes = new Uint8Array(size);
45666
- let offset = 0;
45667
- for (const chunk of chunks) {
45668
- bytes.set(chunk, offset);
45669
- offset += chunk.byteLength;
45670
- }
45671
- return bytes;
45672
- }
45673
- function decodeRemoteFiles(files) {
45674
- let total = 0;
45675
- const decoded = files.map((file2) => {
45676
- if (file2.base64.length > 1398104 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(file2.base64))
45677
- throw new Error("Invalid inline input encoding");
45678
- const bytes = Buffer.from(file2.base64, "base64");
45679
- total += bytes.byteLength;
45680
- if (total > 1024 * 1024 || bytes.toString("base64") !== file2.base64)
45681
- throw new Error("Inline inputs must total at most 1 MiB");
45682
- return { name: file2.name, bytes, contentType: file2.contentType };
45683
- });
45684
- describeRemoteFiles(decoded);
45685
- return decoded;
45686
- }
45687
- var MAX_REMOTE_FILE_BYTES;
45688
- var init_remote_files = __esm(() => {
45689
- MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
45690
- });
45691
-
45692
- // src/lib/selection-aliases.ts
45693
- function selectionAliasError(selections) {
45694
- const names = new Set(selections.map((selection) => selection.slug));
45695
- for (const selection of selections) {
45696
- if (selection.aliases === undefined)
45697
- continue;
45698
- if (!Array.isArray(selection.aliases) || selection.aliases.length > 32)
45699
- return "Selection aliases must be an array of at most 32 names.";
45700
- for (const alias of selection.aliases) {
45701
- if (typeof alias !== "string" || alias.length > 128 || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(alias))
45702
- return "Selection aliases must be bounded kebab-case names.";
45703
- if (names.has(alias))
45704
- return "Selection aliases must be unique and cannot shadow a selected canonical name.";
45705
- names.add(alias);
45706
- }
45707
- }
45708
- }
45709
- function selectionSnapshotsEqual(left, right) {
45710
- const snapshot = (selections) => selections.map((selection) => [
45711
- selection.slug,
45712
- selection.version,
45713
- selection.bundleDigest,
45714
- selection.aliases ?? [],
45715
- selection.triggers?.keywords ?? [],
45716
- selection.triggers?.paths ?? [],
45717
- selection.triggers?.always ?? null
45718
- ]).sort((a, b) => String(a[0]).localeCompare(String(b[0])));
45719
- return JSON.stringify(snapshot(left)) === JSON.stringify(snapshot(right));
45720
- }
45721
-
45722
- // src/lib/profile-limits.ts
45723
- function profileDocumentBytes(value) {
45724
- return new TextEncoder().encode(JSON.stringify(value)).byteLength;
45725
- }
45726
- function requiresProfileCapacity(selections, bodyBytes) {
45727
- return selections.length > LEGACY_PROFILE_SELECTIONS || bodyBytes > LEGACY_PROFILE_DOCUMENT_BYTES;
45728
- }
45729
- function assertAdvertisedProfileCapacity(value, count, bodyBytes) {
45730
- const limits = value?.profileLimits;
45731
- if (!limits || !Number.isSafeInteger(limits.maxSelections) || Number(limits.maxSelections) < count || !Number.isSafeInteger(limits.maxDocumentBytes) || Number(limits.maxDocumentBytes) < bodyBytes || !Number.isSafeInteger(limits.requestBodyLimitBytes) || Number(limits.requestBodyLimitBytes) < bodyBytes || !Number.isSafeInteger(limits.maxResolvedProfileBytes) || Number(limits.maxResolvedProfileBytes) <= 0) {
45732
- throw new Error("The Skills API does not advertise enough profile capacity. Review the API version and its configured request-body limit before saving this profile.");
45733
- }
45734
- }
45735
- function profileSelectionSnapshot(selections) {
45736
- return selections.map(({ slug, version: version2, bundleDigest, aliases, triggers }) => ({
45737
- slug,
45738
- version: version2,
45739
- bundleDigest,
45740
- ...aliases !== undefined ? { aliases } : {},
45741
- ...triggers !== undefined ? { triggers } : {}
45742
- }));
45743
- }
45744
- var MAX_PROFILE_SELECTIONS = 4096, MAX_PROFILE_DOCUMENT_BYTES, MAX_SKILL_SESSION_ID_CHARS = 256, PROFILE_SESSION_ENVELOPE_BYTES, MAX_RESOLVED_PROFILE_BYTES, LEGACY_PROFILE_SELECTIONS = 256, LEGACY_PROFILE_DOCUMENT_BYTES = 1e6;
45745
- var init_profile_limits = __esm(() => {
45746
- MAX_PROFILE_DOCUMENT_BYTES = 8 * 1024 * 1024;
45747
- PROFILE_SESSION_ENVELOPE_BYTES = MAX_PROFILE_SELECTIONS * 67 + MAX_SKILL_SESSION_ID_CHARS * 12 + 512;
45748
- MAX_RESOLVED_PROFILE_BYTES = MAX_PROFILE_DOCUMENT_BYTES - PROFILE_SESSION_ENVELOPE_BYTES;
45749
- });
45750
-
45751
- // src/lib/profile-client.ts
45752
- function object6(value) {
45753
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
45754
- }
45755
- function identifier(value) {
45756
- return typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(value) && !value.includes("..");
45757
- }
45758
- function invalid() {
45759
- throw new Error("The Skills API returned an invalid profile response");
45760
- }
45761
- function validateSelection(value) {
45762
- if (!object6(value) || !identifier(value.slug) || typeof value.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(value.version) || value.version.includes("..") || typeof value.bundleDigest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value.bundleDigest))
45763
- invalid();
45764
- if (selectionAliasError([value]))
45765
- invalid();
45766
- if (value.triggers !== undefined) {
45767
- if (!object6(value.triggers) || Object.keys(value.triggers).some((key) => !["keywords", "paths", "always"].includes(key)))
45768
- invalid();
45769
- if (value.triggers.always !== undefined && typeof value.triggers.always !== "boolean")
45770
- invalid();
45771
- for (const key of ["keywords", "paths"]) {
45772
- const values = value.triggers[key];
45773
- if (values !== undefined && (!Array.isArray(values) || values.length > 32 || values.some((term) => typeof term !== "string" || !term.trim() || term.length > 256)))
45774
- invalid();
45775
- }
45776
- }
45777
- }
45778
-
45779
- class HttpProfileClient {
45780
- key;
45781
- authority;
45782
- origin;
45783
- constructor(key, origin) {
45784
- this.key = key;
45785
- if (!key.trim())
45786
- throw new Error("A Skills credential is required");
45787
- this.origin = normalizeSkillsApiOrigin(origin);
45788
- this.authority = skillsApiRequestUrl(this.origin, "/api/v1/").replace(/\/+$/, "");
45789
- }
45790
- async requestRoute(route, init = {}) {
45791
- let response;
45792
- try {
45793
- response = await fetch(skillsApiRequestUrl(this.origin, route), {
45794
- ...init,
45795
- redirect: "error",
45796
- credentials: "omit",
45797
- signal: AbortSignal.timeout(15000),
45798
- headers: {
45799
- "Content-Type": "application/json",
45800
- "User-Agent": `hasna-skills/${package_default.version}`,
45801
- Authorization: `Bearer ${this.key}`,
45802
- ...init.headers
45803
- }
45804
- });
45805
- } catch {
45806
- throw new Error("Unable to reach the configured Skills API");
45807
- }
45808
- if (!response.ok) {
45809
- response.body?.cancel().catch(() => {});
45810
- throw new Error(`Skills API request failed (HTTP ${response.status})`);
45811
- }
45812
- return response;
45813
- }
45814
- request(path, init = {}) {
45815
- return this.requestRoute(`/api/v1${path}`, init);
45816
- }
45817
- async read(response, limit = MAX_PROFILE_DOCUMENT_BYTES) {
45818
- try {
45819
- return JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, limit)));
45820
- } catch {
45821
- invalid();
45822
- }
45823
- }
45824
- async resolveProfile(id) {
45825
- if (!identifier(id))
45826
- throw new Error("Invalid selection profile id");
45827
- const result = await this.read(await this.request(`/profiles/${encodeURIComponent(id)}/resolve`), MAX_RESOLVED_PROFILE_BYTES);
45828
- if (!object6(result) || result.profileId !== id || result.authority !== this.authority || !identifier(result.workspaceId) || !identifier(result.profileRevision) || !Array.isArray(result.selections) || result.selections.length > MAX_PROFILE_SELECTIONS)
45829
- invalid();
45830
- const seen = new Set;
45831
- for (const entry of result.selections) {
45832
- validateSelection(entry);
45833
- const item = entry;
45834
- if (seen.has(item.slug) || item.authority !== this.authority || item.workspaceId !== result.workspaceId || item.profileRevision !== result.profileRevision)
45835
- invalid();
45836
- seen.add(item.slug);
45837
- }
45838
- if (selectionAliasError(result.selections))
45839
- invalid();
45840
- return result;
45841
- }
45842
- async resolvePrincipal() {
45843
- const result = await this.read(await this.requestRoute("/api/auth/whoami"), 16 * 1024);
45844
- if (!object6(result) || Object.keys(result).sort().join(",") !== "organization,user" || !object6(result.user) || Object.keys(result.user).sort().join(",") !== "email,id,role" || !object6(result.organization) || Object.keys(result.organization).sort().join(",") !== "id,name,slug" || !identifier(result.user.id) || typeof result.user.email !== "string" || !result.user.email.trim() || !["owner", "admin", "member", "viewer"].includes(String(result.user.role)) || !identifier(result.organization.id) || !identifier(result.organization.slug) || typeof result.organization.name !== "string" || !result.organization.name.trim())
45845
- invalid();
45846
- return { userId: result.user.id, accountId: result.organization.id, role: result.user.role };
45847
- }
45848
- async recordStation(id, input2) {
45849
- if (!identifier(id) || !identifier(input2.profileId) || !identifier(input2.profileRevision) || !Array.isArray(input2.selections) || input2.selections.length > MAX_PROFILE_SELECTIONS)
45850
- throw new Error("Invalid station state");
45851
- input2.selections.forEach(validateSelection);
45852
- if (selectionAliasError(input2.selections))
45853
- throw new Error("Invalid station selection aliases");
45854
- const snapshot = { ...input2, selections: profileSelectionSnapshot(input2.selections) };
45855
- const bodyBytes = profileDocumentBytes(snapshot);
45856
- if (bodyBytes > MAX_PROFILE_DOCUMENT_BYTES)
45857
- throw new Error("Station state exceeds the profile document size limit");
45858
- if (requiresProfileCapacity(input2.selections, bodyBytes)) {
45859
- const capabilities = await this.read(await this.request("/capabilities"));
45860
- assertAdvertisedProfileCapacity(capabilities, input2.selections.length, bodyBytes);
45861
- }
45862
- const result = await this.read(await this.request(`/stations/${encodeURIComponent(id)}/state`, {
45863
- method: "PUT",
45864
- body: JSON.stringify(snapshot)
45865
- }));
45866
- if (!object6(result) || result.stationId !== id || result.profileId !== input2.profileId || result.profileRevision !== input2.profileRevision || !identifier(result.workspaceId) || !identifier(result.actorId) || typeof result.appliedAt !== "string" || !Array.isArray(result.selections) || result.selections.length > MAX_PROFILE_SELECTIONS)
45867
- invalid();
45868
- result.selections.forEach(validateSelection);
45869
- if (selectionAliasError(result.selections))
45870
- invalid();
45871
- if (!selectionSnapshotsEqual(input2.selections, result.selections))
45872
- invalid();
45873
- return result;
45874
- }
45875
- async getBundle(slug, version2) {
45876
- if (!identifier(slug) || !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2) || version2.includes(".."))
45877
- throw new Error("Invalid immutable skill reference");
45878
- return this.request(`/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version2)}/bundle`);
45879
- }
45880
- }
45881
- async function createProfileClient() {
45882
- const connection = await resolveSkillsConnection();
45883
- if (!connection)
45884
- throw new Error("Selection profiles require a configured Skills API");
45885
- return new HttpProfileClient(connection.apiKey, connection.apiOrigin);
45886
- }
45887
- var init_profile_client = __esm(() => {
45888
- init_package();
45889
- init_fleet_credentials();
45890
- init_remote_files();
45891
- init_profile_limits();
45892
- });
45893
-
45894
45621
  // src/lib/skill-bundle.ts
45895
- import { createHash as createHash6 } from "crypto";
45622
+ import { createHash as createHash5 } from "crypto";
45896
45623
  import { readFileSync as readFileSync9, readdirSync as readdirSync4, statSync as statSync6 } from "fs";
45897
45624
  import { join as join13, relative as relative3 } from "path";
45898
45625
  import { createGunzip } from "zlib";
@@ -45924,7 +45651,7 @@ function ownBytes(view) {
45924
45651
  return out;
45925
45652
  }
45926
45653
  function sha256Hex(bytes) {
45927
- return createHash6("sha256").update(bytes).digest("hex");
45654
+ return createHash5("sha256").update(bytes).digest("hex");
45928
45655
  }
45929
45656
  function collectSkillBundleEntries(dir) {
45930
45657
  const entries = [];
@@ -46076,7 +45803,7 @@ async function inspectSkillBundle(bundle, options = {}) {
46076
45803
  throw new SkillBundleInspectionError("BUNDLE_LIMIT", "Compressed bundle exceeds byte limit");
46077
45804
  const snapshot = ownBytes(bundle);
46078
45805
  check2();
46079
- const sha2562 = sha256Hex(snapshot);
45806
+ const sha256 = sha256Hex(snapshot);
46080
45807
  check2();
46081
45808
  const parser2 = new BoundedTarReader(limits, check2);
46082
45809
  const streamOptions = { chunkSize: 16 * 1024, highWaterMark: 16 * 1024 };
@@ -46111,7 +45838,7 @@ async function inspectSkillBundle(bundle, options = {}) {
46111
45838
  const entries = parser2.finish();
46112
45839
  return {
46113
45840
  entries,
46114
- sha256: sha2562,
45841
+ sha256,
46115
45842
  compressedByteSize: snapshot.byteLength,
46116
45843
  decompressedByteSize,
46117
45844
  unpackedByteSize: parser2.fileBytes,
@@ -46399,8 +46126,67 @@ var init_skill_version = __esm(() => {
46399
46126
  SKILL_VERSION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/;
46400
46127
  });
46401
46128
 
46129
+ // src/lib/selection-aliases.ts
46130
+ function selectionAliasError(selections) {
46131
+ const names = new Set(selections.map((selection) => selection.slug));
46132
+ for (const selection of selections) {
46133
+ if (selection.aliases === undefined)
46134
+ continue;
46135
+ if (!Array.isArray(selection.aliases) || selection.aliases.length > 32)
46136
+ return "Selection aliases must be an array of at most 32 names.";
46137
+ for (const alias of selection.aliases) {
46138
+ if (typeof alias !== "string" || alias.length > 128 || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(alias))
46139
+ return "Selection aliases must be bounded kebab-case names.";
46140
+ if (names.has(alias))
46141
+ return "Selection aliases must be unique and cannot shadow a selected canonical name.";
46142
+ names.add(alias);
46143
+ }
46144
+ }
46145
+ }
46146
+ function selectionSnapshotsEqual(left, right) {
46147
+ const snapshot = (selections) => selections.map((selection) => [
46148
+ selection.slug,
46149
+ selection.version,
46150
+ selection.bundleDigest,
46151
+ selection.aliases ?? [],
46152
+ selection.triggers?.keywords ?? [],
46153
+ selection.triggers?.paths ?? [],
46154
+ selection.triggers?.always ?? null
46155
+ ]).sort((a, b) => String(a[0]).localeCompare(String(b[0])));
46156
+ return JSON.stringify(snapshot(left)) === JSON.stringify(snapshot(right));
46157
+ }
46158
+
46159
+ // src/lib/profile-limits.ts
46160
+ function profileDocumentBytes(value) {
46161
+ return new TextEncoder().encode(JSON.stringify(value)).byteLength;
46162
+ }
46163
+ function requiresProfileCapacity(selections, bodyBytes) {
46164
+ return selections.length > LEGACY_PROFILE_SELECTIONS || bodyBytes > LEGACY_PROFILE_DOCUMENT_BYTES;
46165
+ }
46166
+ function assertAdvertisedProfileCapacity(value, count, bodyBytes) {
46167
+ const limits = value?.profileLimits;
46168
+ if (!limits || !Number.isSafeInteger(limits.maxSelections) || Number(limits.maxSelections) < count || !Number.isSafeInteger(limits.maxDocumentBytes) || Number(limits.maxDocumentBytes) < bodyBytes || !Number.isSafeInteger(limits.requestBodyLimitBytes) || Number(limits.requestBodyLimitBytes) < bodyBytes || !Number.isSafeInteger(limits.maxResolvedProfileBytes) || Number(limits.maxResolvedProfileBytes) <= 0) {
46169
+ throw new Error("The Skills API does not advertise enough profile capacity. Review the API version and its configured request-body limit before saving this profile.");
46170
+ }
46171
+ }
46172
+ function profileSelectionSnapshot(selections) {
46173
+ return selections.map(({ slug, version: version2, bundleDigest, aliases, triggers }) => ({
46174
+ slug,
46175
+ version: version2,
46176
+ bundleDigest,
46177
+ ...aliases !== undefined ? { aliases } : {},
46178
+ ...triggers !== undefined ? { triggers } : {}
46179
+ }));
46180
+ }
46181
+ var MAX_PROFILE_SELECTIONS = 4096, MAX_PROFILE_DOCUMENT_BYTES, MAX_SKILL_SESSION_ID_CHARS = 256, PROFILE_SESSION_ENVELOPE_BYTES, MAX_RESOLVED_PROFILE_BYTES, LEGACY_PROFILE_SELECTIONS = 256, LEGACY_PROFILE_DOCUMENT_BYTES = 1e6;
46182
+ var init_profile_limits = __esm(() => {
46183
+ MAX_PROFILE_DOCUMENT_BYTES = 8 * 1024 * 1024;
46184
+ PROFILE_SESSION_ENVELOPE_BYTES = MAX_PROFILE_SELECTIONS * 67 + MAX_SKILL_SESSION_ID_CHARS * 12 + 512;
46185
+ MAX_RESOLVED_PROFILE_BYTES = MAX_PROFILE_DOCUMENT_BYTES - PROFILE_SESSION_ENVELOPE_BYTES;
46186
+ });
46187
+
46402
46188
  // src/lib/selection-cache.ts
46403
- import { createHash as createHash7, randomUUID } from "crypto";
46189
+ import { createHash as createHash6, randomUUID } from "crypto";
46404
46190
  import { spawnSync as spawnSync2 } from "child_process";
46405
46191
  import { closeSync as closeSync6, constants as constants3, fstatSync as fstatSync6, fsyncSync, lstatSync as lstatSync8, mkdirSync as mkdirSync3, openSync as openSync6, readFileSync as readFileSync10, readlinkSync as readlinkSync2, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
46406
46192
  import { hostname as hostname3 } from "os";
@@ -46410,13 +46196,13 @@ function selectionCacheRoot(options = {}) {
46410
46196
  return resolve7(options.cacheDir ?? join14(getDataDirReadOnly(), "selection-cache"));
46411
46197
  }
46412
46198
  function selectionKey(selection) {
46413
- validateSelection2(selection);
46199
+ validateSelection(selection);
46414
46200
  return hash2([selection.authority, selection.workspaceId, selection.slug, selection.version, selection.bundleDigest]);
46415
46201
  }
46416
46202
  function hash2(value) {
46417
- return createHash7("sha256").update(JSON.stringify(value)).digest("hex");
46203
+ return createHash6("sha256").update(JSON.stringify(value)).digest("hex");
46418
46204
  }
46419
- function validateSelection2(selection) {
46205
+ function validateSelection(selection) {
46420
46206
  if (!selection || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(selection.slug) || !isValidSkillVersion(selection.version) || !/^sha256:[a-f0-9]{64}$/.test(selection.bundleDigest) || typeof selection.workspaceId !== "string" || !selection.workspaceId.trim() || typeof selection.profileRevision !== "string" || !selection.profileRevision.trim()) {
46421
46207
  throw new SkillSelectionError("INVALID_SELECTION", "A skill selection must carry an exact version, digest, workspace and profile revision.");
46422
46208
  }
@@ -46439,7 +46225,7 @@ function validateResolvedProfile(profile, authority) {
46439
46225
  throw new SkillSelectionError("RECEIPT_TOO_LARGE", "The resolved Skills profile exceeds its size limit.");
46440
46226
  const slugs = new Set;
46441
46227
  for (const selection of profile.selections) {
46442
- validateSelection2(selection);
46228
+ validateSelection(selection);
46443
46229
  if (selection.authority !== profile.authority || selection.workspaceId !== profile.workspaceId || selection.profileRevision !== profile.profileRevision || slugs.has(selection.slug)) {
46444
46230
  throw new SkillSelectionError("PROFILE_IDENTITY_MISMATCH", "The resolved profile contains conflicting selection identities.");
46445
46231
  }
@@ -46450,7 +46236,7 @@ function validateResolvedProfile(profile, authority) {
46450
46236
  throw new SkillSelectionError("INVALID_SELECTION_ALIASES", aliasError);
46451
46237
  }
46452
46238
  function selectionBundlePath(selection, options = {}) {
46453
- validateSelection2(selection);
46239
+ validateSelection(selection);
46454
46240
  return join14(selectionCacheRoot(options), "objects", hash2(selection.authority), hash2(selection.workspaceId), `${selection.bundleDigest.slice(7)}.tar.gz`);
46455
46241
  }
46456
46242
  function assertRegularPath(path, createParents = false) {
@@ -46554,7 +46340,7 @@ async function readCachedSelection(selection, options = {}) {
46554
46340
  return bytes ? verifiedEntries(selection, bytes) : null;
46555
46341
  }
46556
46342
  async function cacheSelectionBundle(selection, response, options = {}) {
46557
- validateSelection2(selection);
46343
+ validateSelection(selection);
46558
46344
  if (!response?.ok)
46559
46345
  throw new SkillSelectionError("BUNDLE_UNAVAILABLE", `The selected skill bundle is unavailable${response ? ` (HTTP ${response.status})` : ""}.`);
46560
46346
  const declared = response.headers.get("X-Skill-Bundle-Sha256");
@@ -46618,7 +46404,11 @@ function assertFreshCachedProfile(receipt, options = {}) {
46618
46404
  validateSelectionReceipt(receipt);
46619
46405
  const age = (options.now ?? Date.now)() - Date.parse(receipt.verifiedAt);
46620
46406
  const maximum = options.maxAgeMs ?? MAX_CACHED_PROFILE_AGE_MS;
46621
- if (!Number.isFinite(maximum) || maximum <= 0 || maximum > MAX_CACHED_PROFILE_AGE_MS || age < 0 || age > maximum) {
46407
+ if (!Number.isFinite(maximum) || maximum <= 0 || maximum > MAX_CACHED_PROFILE_AGE_MS)
46408
+ throw new SkillSelectionError("INVALID_CONTEXT_BUDGET", "The cached profile maximum age must be positive and within the supported bound.");
46409
+ if (!Number.isFinite(age) || age < 0)
46410
+ throw new SkillSelectionError("INVALID_RECEIPT", "The cached profile verification timestamp is invalid or in the future.");
46411
+ if (age > maximum) {
46622
46412
  throw new SkillSelectionError("CACHED_PROFILE_EXPIRED", "The cached Skills profile has expired; authenticate and sync it again.");
46623
46413
  }
46624
46414
  }
@@ -46667,8 +46457,8 @@ function readSkillSessionSnapshotIfExists(sessionId, options = {}) {
46667
46457
  throw new SkillSelectionError("INVALID_RECEIPT", "The Skills session receipt is unreadable.");
46668
46458
  }
46669
46459
  validateSkillSessionReceipt(receipt, sessionId);
46670
- const sha2562 = sha256Hex(bytes);
46671
- return { path, bytes, receipt, sha256: sha2562, sessionId, generation: skillSessionGeneration(receipt), receiptSha256: sha2562 };
46460
+ const sha256 = sha256Hex(bytes);
46461
+ return { path, bytes, receipt, sha256, sessionId, generation: skillSessionGeneration(receipt), receiptSha256: sha256 };
46672
46462
  }
46673
46463
  function skillSessionSnapshotBinding(snapshot) {
46674
46464
  return { sessionId: snapshot.sessionId, generation: snapshot.generation, receiptSha256: snapshot.receiptSha256 };
@@ -46734,12 +46524,12 @@ function readSessionLock(sessionId, options) {
46734
46524
  const before = lstatSync8(path, { throwIfNoEntry: false });
46735
46525
  if (!before)
46736
46526
  return null;
46737
- if (!before.isFile() || before.nlink !== 1 || process.getuid && before.uid !== process.getuid()) {
46527
+ if (!before.isFile() || before.nlink !== 1 || (before.mode & 63) !== 0 || process.getuid && before.uid !== process.getuid()) {
46738
46528
  throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is not a private regular file; recovery requires review.");
46739
46529
  }
46740
46530
  const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
46741
46531
  const after = lstatSync8(path, { throwIfNoEntry: false });
46742
- if (!bytes || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs) {
46532
+ if (!bytes || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs || before.mode !== after.mode || before.uid !== after.uid || before.gid !== after.gid || before.nlink !== after.nlink) {
46743
46533
  throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock changed during inspection; recovery requires review.");
46744
46534
  }
46745
46535
  let marker;
@@ -46865,8 +46655,11 @@ function withSessionWriteLocks(sessionIds, options, action) {
46865
46655
  try {
46866
46656
  fd = openSync6(lock.path, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
46867
46657
  } catch (retryError) {
46868
- if (retryError.code === "EEXIST")
46869
- throw new SkillSelectionError("SESSION_WRITE_LOCKED", "Another Skills process owns a required session write lock; retry after it completes. A surviving legacy lock requires explicit recovery review.");
46658
+ if (retryError.code === "EEXIST") {
46659
+ if (!readSessionLock(lock.sessionId, options))
46660
+ throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "The session lock changed during acquisition.");
46661
+ throw new SkillSelectionError("SESSION_WRITE_BUSY", "Another Skills process owns a required session write lock; retry after it completes. A surviving legacy lock requires explicit recovery review.");
46662
+ }
46870
46663
  throw retryError;
46871
46664
  }
46872
46665
  }
@@ -46908,7 +46701,7 @@ function writeSkillSession(receipt, expected, options = {}) {
46908
46701
  withSessionWriteLocks(lockIds, options, (assertOwned) => {
46909
46702
  const current = readSkillSessionSnapshotIfExists(receipt.sessionId, options);
46910
46703
  if (!sameSnapshot(current, expected.current) || current && !isDeepStrictEqual(current.receipt.profile, receipt.profile)) {
46911
- throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The Skills session receipt changed while context was loading; resolve context again without replacing the current pin.");
46704
+ throw new SkillSelectionError("SESSION_CONTEXT_CONFLICT", "The Skills session receipt changed while context was loading; resolve context again without replacing the current pin.");
46912
46705
  }
46913
46706
  let parent = null;
46914
46707
  if (expected.parent) {
@@ -46963,6 +46756,246 @@ var init_selection_cache = __esm(() => {
46963
46756
  MAX_CACHED_PROFILE_AGE_MS = 24 * 60 * 60 * 1000;
46964
46757
  });
46965
46758
 
46759
+ // src/lib/remote-files.ts
46760
+ var exports_remote_files = {};
46761
+ __export(exports_remote_files, {
46762
+ sha256: () => sha256,
46763
+ readBoundedResponse: () => readBoundedResponse,
46764
+ describeRemoteFiles: () => describeRemoteFiles,
46765
+ decodeRemoteFiles: () => decodeRemoteFiles,
46766
+ MAX_REMOTE_FILE_BYTES: () => MAX_REMOTE_FILE_BYTES
46767
+ });
46768
+ import { createHash as createHash7 } from "crypto";
46769
+ function describeRemoteFiles(files) {
46770
+ if (files.length > 10)
46771
+ throw new Error("At most 10 input files are supported");
46772
+ const names = new Set;
46773
+ let total = 0;
46774
+ return files.map((file2) => {
46775
+ if (typeof file2.name !== "string" || file2.name.length === 0 || file2.name.length > 255 || /[/\\\x00-\x1f\x7f]/.test(file2.name) || /[\ud800-\udfff]/u.test(file2.name) || file2.name === "." || file2.name === ".." || names.has(file2.name))
46776
+ throw new Error("Input file names must be unique safe basenames");
46777
+ names.add(file2.name);
46778
+ total += file2.bytes.byteLength;
46779
+ if (file2.bytes.byteLength > 20 * 1024 * 1024 || total > 50 * 1024 * 1024)
46780
+ throw new Error("Input files exceed the supported size limit");
46781
+ return { name: file2.name, sizeBytes: file2.bytes.byteLength, sha256: sha256(file2.bytes), contentType: file2.contentType ?? "application/octet-stream" };
46782
+ });
46783
+ }
46784
+ function sha256(bytes) {
46785
+ return createHash7("sha256").update(bytes).digest("hex");
46786
+ }
46787
+ async function readBoundedResponse(response, maximum) {
46788
+ if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
46789
+ throw new Error("Invalid artifact size limit");
46790
+ const length = response.headers.get("content-length");
46791
+ if (length && (!/^\d+$/.test(length) || Number(length) > maximum)) {
46792
+ await response.body?.cancel();
46793
+ throw new Error("Artifact exceeds its declared size limit");
46794
+ }
46795
+ const reader = response.body?.getReader();
46796
+ if (!reader)
46797
+ return new Uint8Array;
46798
+ const chunks = [];
46799
+ let size = 0;
46800
+ try {
46801
+ while (true) {
46802
+ const next = await reader.read();
46803
+ if (next.done)
46804
+ break;
46805
+ size += next.value.byteLength;
46806
+ if (size > maximum)
46807
+ throw new Error("Artifact exceeds its declared size limit");
46808
+ chunks.push(next.value);
46809
+ }
46810
+ } catch (error61) {
46811
+ await reader.cancel().catch(() => {});
46812
+ throw error61;
46813
+ } finally {
46814
+ reader.releaseLock();
46815
+ }
46816
+ const bytes = new Uint8Array(size);
46817
+ let offset = 0;
46818
+ for (const chunk of chunks) {
46819
+ bytes.set(chunk, offset);
46820
+ offset += chunk.byteLength;
46821
+ }
46822
+ return bytes;
46823
+ }
46824
+ function decodeRemoteFiles(files) {
46825
+ let total = 0;
46826
+ const decoded = files.map((file2) => {
46827
+ if (file2.base64.length > 1398104 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(file2.base64))
46828
+ throw new Error("Invalid inline input encoding");
46829
+ const bytes = Buffer.from(file2.base64, "base64");
46830
+ total += bytes.byteLength;
46831
+ if (total > 1024 * 1024 || bytes.toString("base64") !== file2.base64)
46832
+ throw new Error("Inline inputs must total at most 1 MiB");
46833
+ return { name: file2.name, bytes, contentType: file2.contentType };
46834
+ });
46835
+ describeRemoteFiles(decoded);
46836
+ return decoded;
46837
+ }
46838
+ var MAX_REMOTE_FILE_BYTES;
46839
+ var init_remote_files = __esm(() => {
46840
+ MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
46841
+ });
46842
+
46843
+ // src/lib/profile-client.ts
46844
+ function object6(value) {
46845
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
46846
+ }
46847
+ function identifier(value) {
46848
+ return typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(value) && !value.includes("..");
46849
+ }
46850
+ function invalid() {
46851
+ throw new Error("The Skills API returned an invalid profile response");
46852
+ }
46853
+ function validateSelection2(value) {
46854
+ if (!object6(value) || !identifier(value.slug) || typeof value.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(value.version) || value.version.includes("..") || typeof value.bundleDigest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value.bundleDigest))
46855
+ invalid();
46856
+ if (selectionAliasError([value]))
46857
+ invalid();
46858
+ if (value.triggers !== undefined) {
46859
+ if (!object6(value.triggers) || Object.keys(value.triggers).some((key) => !["keywords", "paths", "always"].includes(key)))
46860
+ invalid();
46861
+ if (value.triggers.always !== undefined && typeof value.triggers.always !== "boolean")
46862
+ invalid();
46863
+ for (const key of ["keywords", "paths"]) {
46864
+ const values = value.triggers[key];
46865
+ if (values !== undefined && (!Array.isArray(values) || values.length > 32 || values.some((term) => typeof term !== "string" || !term.trim() || term.length > 256)))
46866
+ invalid();
46867
+ }
46868
+ }
46869
+ }
46870
+
46871
+ class HttpProfileClient {
46872
+ key;
46873
+ authority;
46874
+ origin;
46875
+ constructor(key, origin) {
46876
+ this.key = key;
46877
+ if (!key.trim())
46878
+ throw new Error("A Skills credential is required");
46879
+ this.origin = normalizeSkillsApiOrigin(origin);
46880
+ this.authority = skillsApiRequestUrl(this.origin, "/api/v1/").replace(/\/+$/, "");
46881
+ }
46882
+ async requestRoute(route, init = {}) {
46883
+ const requestUrl = skillsApiRequestUrl(this.origin, route);
46884
+ let response;
46885
+ try {
46886
+ response = await fetch(requestUrl, {
46887
+ ...init,
46888
+ redirect: "error",
46889
+ credentials: "omit",
46890
+ signal: AbortSignal.timeout(15000),
46891
+ headers: {
46892
+ "Content-Type": "application/json",
46893
+ "User-Agent": `hasna-skills/${package_default.version}`,
46894
+ Authorization: `Bearer ${this.key}`,
46895
+ ...init.headers
46896
+ }
46897
+ });
46898
+ } catch {
46899
+ throw new SkillSelectionError("SKILLS_API_UNAVAILABLE", "Unable to reach the configured Skills API");
46900
+ }
46901
+ if (!response.ok) {
46902
+ response.body?.cancel().catch(() => {});
46903
+ const code = response.status === 401 ? "SKILLS_API_UNAUTHORIZED" : response.status === 403 ? "SKILLS_API_FORBIDDEN" : [404, 410].includes(response.status) ? "SKILLS_API_RESOURCE_UNAVAILABLE" : response.status === 429 || response.status >= 500 ? "SKILLS_API_UNAVAILABLE" : undefined;
46904
+ if (code)
46905
+ throw new SkillSelectionError(code, `Skills API request failed (HTTP ${response.status})`);
46906
+ throw new Error(`Skills API request failed (HTTP ${response.status})`);
46907
+ }
46908
+ return response;
46909
+ }
46910
+ request(path, init = {}) {
46911
+ return this.requestRoute(`/api/v1${path}`, init);
46912
+ }
46913
+ async read(response, limit = MAX_PROFILE_DOCUMENT_BYTES) {
46914
+ try {
46915
+ return JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, limit)));
46916
+ } catch {
46917
+ invalid();
46918
+ }
46919
+ }
46920
+ async resolveProfile(id) {
46921
+ if (!identifier(id))
46922
+ throw new Error("Invalid selection profile id");
46923
+ const result = await this.read(await this.request(`/profiles/${encodeURIComponent(id)}/resolve`), MAX_RESOLVED_PROFILE_BYTES);
46924
+ if (!object6(result) || result.profileId !== id || result.authority !== this.authority || !identifier(result.workspaceId) || !identifier(result.profileRevision) || !Array.isArray(result.selections) || result.selections.length > MAX_PROFILE_SELECTIONS)
46925
+ invalid();
46926
+ const seen = new Set;
46927
+ for (const entry of result.selections) {
46928
+ validateSelection2(entry);
46929
+ const item = entry;
46930
+ if (seen.has(item.slug) || item.authority !== this.authority || item.workspaceId !== result.workspaceId || item.profileRevision !== result.profileRevision)
46931
+ invalid();
46932
+ seen.add(item.slug);
46933
+ }
46934
+ if (selectionAliasError(result.selections))
46935
+ invalid();
46936
+ return result;
46937
+ }
46938
+ async resolvePrincipal() {
46939
+ const result = await this.read(await this.requestRoute("/api/auth/whoami"), 16 * 1024);
46940
+ if (!object6(result) || Object.keys(result).sort().join(",") !== "organization,user" || !object6(result.user) || Object.keys(result.user).sort().join(",") !== "email,id,role" || !object6(result.organization) || Object.keys(result.organization).sort().join(",") !== "id,name,slug" || !identifier(result.user.id) || typeof result.user.email !== "string" || !result.user.email.trim() || !["owner", "admin", "member", "viewer"].includes(String(result.user.role)) || !identifier(result.organization.id) || !identifier(result.organization.slug) || typeof result.organization.name !== "string" || !result.organization.name.trim())
46941
+ invalid();
46942
+ return { userId: result.user.id, accountId: result.organization.id, role: result.user.role };
46943
+ }
46944
+ async recordStation(id, input2) {
46945
+ if (!identifier(id) || !identifier(input2.profileId) || !identifier(input2.profileRevision) || !Array.isArray(input2.selections) || input2.selections.length > MAX_PROFILE_SELECTIONS)
46946
+ throw new Error("Invalid station state");
46947
+ input2.selections.forEach(validateSelection2);
46948
+ if (selectionAliasError(input2.selections))
46949
+ throw new Error("Invalid station selection aliases");
46950
+ const snapshot = { ...input2, selections: profileSelectionSnapshot(input2.selections) };
46951
+ const bodyBytes = profileDocumentBytes(snapshot);
46952
+ if (bodyBytes > MAX_PROFILE_DOCUMENT_BYTES)
46953
+ throw new Error("Station state exceeds the profile document size limit");
46954
+ if (requiresProfileCapacity(input2.selections, bodyBytes)) {
46955
+ const capabilities = await this.read(await this.request("/capabilities"));
46956
+ assertAdvertisedProfileCapacity(capabilities, input2.selections.length, bodyBytes);
46957
+ }
46958
+ const result = await this.read(await this.request(`/stations/${encodeURIComponent(id)}/state`, {
46959
+ method: "PUT",
46960
+ body: JSON.stringify(snapshot)
46961
+ }));
46962
+ if (!object6(result) || result.stationId !== id || result.profileId !== input2.profileId || result.profileRevision !== input2.profileRevision || !identifier(result.workspaceId) || !identifier(result.actorId) || typeof result.appliedAt !== "string" || !Array.isArray(result.selections) || result.selections.length > MAX_PROFILE_SELECTIONS)
46963
+ invalid();
46964
+ result.selections.forEach(validateSelection2);
46965
+ if (selectionAliasError(result.selections))
46966
+ invalid();
46967
+ if (!selectionSnapshotsEqual(input2.selections, result.selections))
46968
+ invalid();
46969
+ return result;
46970
+ }
46971
+ async getBundle(slug, version2) {
46972
+ if (!identifier(slug) || !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2) || version2.includes(".."))
46973
+ throw new Error("Invalid immutable skill reference");
46974
+ return this.request(`/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version2)}/bundle`);
46975
+ }
46976
+ }
46977
+ async function createProfileClient() {
46978
+ let connection;
46979
+ try {
46980
+ connection = await resolveSkillsConnection();
46981
+ } catch (error61) {
46982
+ if (isSkillsFleetCredentialError(error61) && error61.code === "MISSING_API_CREDENTIAL") {
46983
+ throw new SkillSelectionError("SKILLS_API_CREDENTIAL_UNAVAILABLE", "The configured Skills authority has no usable credential. Sign in through the configured credential resolver; no payload was delivered.");
46984
+ }
46985
+ throw error61;
46986
+ }
46987
+ if (!connection)
46988
+ throw new Error("Selection profiles require a configured Skills API");
46989
+ return new HttpProfileClient(connection.apiKey, connection.apiOrigin);
46990
+ }
46991
+ var init_profile_client = __esm(() => {
46992
+ init_selection_cache();
46993
+ init_package();
46994
+ init_fleet_credentials();
46995
+ init_remote_files();
46996
+ init_profile_limits();
46997
+ });
46998
+
46966
46999
  // src/lib/preparation-process.ts
46967
47000
  function capturePreparationProcess(child, platform = process.platform, signalGroup = (pid, signal) => process.kill(pid, signal)) {
46968
47001
  let spawnedPid;
@@ -47167,116 +47200,9 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
47167
47200
  var DEPENDENCY_PREPARATION_MARKER = ".skills-dependency-preparation", RECOVERY = "Skill dependency preparation is incomplete. Confirm no installer is running, successfully run bun install --no-save in the skill directory, then remove only .skills-dependency-preparation. See docs/skill-standard.md#recovering-interrupted-dependency-preparation.", DEFAULT_PREPARATION_TIMEOUT_MS = 60000;
47168
47201
  var init_dependency_preparation = () => {};
47169
47202
 
47170
- // src/lib/managed-policy.ts
47171
- import { closeSync as closeSync8, constants as constants5, fstatSync as fstatSync8, lstatSync as lstatSync10, openSync as openSync8, readSync as readSync4 } from "fs";
47172
- import { join as join16 } from "path";
47173
- function refusal(reason = "is unreadable") {
47174
- return new SkillSelectionError("INVALID_AGENT_POLICY", `The Skills agent loading policy ${reason}; refusing legacy fallback.`);
47175
- }
47176
- function parseManagedSkillPolicy(text2) {
47177
- if (Buffer.byteLength(text2) > AGENT_POLICY_LIMITS.bytes)
47178
- throw refusal("exceeds its size limit");
47179
- let value;
47180
- try {
47181
- value = JSON.parse(text2);
47182
- } catch {
47183
- throw refusal();
47184
- }
47185
- if (!value || typeof value !== "object" || Array.isArray(value) || value.loading !== "cli" || value.version !== undefined && value.version !== 1)
47186
- throw refusal();
47187
- if (value.profileId !== undefined && (typeof value.profileId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value.profileId) || value.profileId.includes("..")))
47188
- throw refusal();
47189
- try {
47190
- assertAgentPolicyCollections(value);
47191
- } catch {
47192
- throw refusal("has invalid collection bounds");
47193
- }
47194
- return value;
47195
- }
47196
- function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
47197
- const path = join16(dataDir2, "agent-policy.json");
47198
- let descriptor, observed = false;
47199
- try {
47200
- const stat = lstatSync10(path);
47201
- observed = true;
47202
- if (!stat.isFile() || stat.isSymbolicLink())
47203
- throw refusal();
47204
- if (stat.size > AGENT_POLICY_LIMITS.bytes)
47205
- throw refusal("exceeds its size limit");
47206
- descriptor = openSync8(path, constants5.O_RDONLY | (constants5.O_NOFOLLOW ?? 0) | constants5.O_NONBLOCK);
47207
- const opened = fstatSync8(descriptor);
47208
- if (!opened.isFile())
47209
- throw refusal();
47210
- if (opened.size > AGENT_POLICY_LIMITS.bytes)
47211
- throw refusal("exceeds its size limit");
47212
- const bytes = Buffer.allocUnsafe(AGENT_POLICY_LIMITS.bytes + 1);
47213
- let length = 0;
47214
- while (length < bytes.length) {
47215
- const count = readSync4(descriptor, bytes, length, bytes.length - length, null);
47216
- if (count === 0)
47217
- break;
47218
- length += count;
47219
- }
47220
- if (length > AGENT_POLICY_LIMITS.bytes)
47221
- throw refusal("exceeds its size limit");
47222
- const text2 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
47223
- return { text: text2, value: parseManagedSkillPolicy(text2) };
47224
- } catch (error61) {
47225
- if (error61.code === "ENOENT" && !observed) {
47226
- if (lstatSync10(path, { throwIfNoEntry: false }) === undefined)
47227
- return null;
47228
- }
47229
- if (error61 instanceof SkillSelectionError)
47230
- throw error61;
47231
- throw refusal();
47232
- } finally {
47233
- if (descriptor !== undefined)
47234
- closeSync8(descriptor);
47235
- }
47236
- }
47237
- function readManagedSkillPolicy(dataDir2 = getDataDirReadOnly()) {
47238
- return readManagedSkillPolicySnapshot(dataDir2)?.value ?? null;
47239
- }
47240
- function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
47241
- return readManagedSkillPolicy(dataDir2)?.loading === "cli";
47242
- }
47243
- var init_managed_policy = __esm(() => {
47244
- init_config();
47245
- init_selection_cache();
47246
- init_agent_policy_limits();
47247
- });
47248
-
47249
- // src/lib/agent-bridge.ts
47250
- import { createHash as createHash8 } from "crypto";
47251
- var CLI_BRIDGE_VERSION = 1, markdown = `---
47252
- name: skills-cli
47253
- description: Discover, load, author, and run versioned skills through the Skills CLI when a task needs reusable instructions or an executable skill.
47254
- ---
47255
-
47256
- Use the Skills CLI as the skill authority for this station.
47257
-
47258
- - Discover available skills with \`skills list --json\` or \`skills search <query> --json\`.
47259
- - Read selected instructions with \`skills load <slug>\`. Use \`skills sync\` to refresh the station's shared selection profile.
47260
- - Create local drafts with \`skills new <name> --kind instruction\` or \`--kind executable\` in the Skills-owned source directory. Validate with \`skills validate <name>\`; use \`skills push --help\` for explicit versioned publication.
47261
- - Run a skill only when the user's task calls for execution. Use \`skills run --help\` and select the intended local or cloud target explicitly.
47262
-
47263
- 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.
47264
- `, CLI_BRIDGE_DIGEST, CLI_BRIDGE_FILES;
47265
- var init_agent_bridge = __esm(() => {
47266
- CLI_BRIDGE_DIGEST = createHash8("sha256").update(markdown).digest("hex");
47267
- CLI_BRIDGE_FILES = Object.freeze({
47268
- "SKILL.md": markdown,
47269
- ".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
47270
- `
47271
- });
47272
- });
47273
-
47274
- // src/lib/agent-codex.ts
47275
- var init_agent_codex = () => {};
47276
-
47277
47203
  // src/lib/claude-settings-witness.ts
47278
- import { createHash as createHash9 } from "crypto";
47279
- import { closeSync as closeSync9, constants as constants6, fstatSync as fstatSync9, lstatSync as lstatSync11, openSync as openSync9, readSync as readSync5 } from "fs";
47204
+ import { createHash as createHash8 } from "crypto";
47205
+ import { closeSync as closeSync8, constants as constants5, fstatSync as fstatSync8, lstatSync as lstatSync10, openSync as openSync8, readSync as readSync4 } from "fs";
47280
47206
  import { basename as basename4, dirname as dirname9, isAbsolute as isAbsolute8, resolve as resolve8 } from "path";
47281
47207
  function need2(value, reason) {
47282
47208
  if (!value)
@@ -47288,36 +47214,36 @@ function absolutePath(value) {
47288
47214
  function parents(path) {
47289
47215
  const result = [];
47290
47216
  for (let at = dirname9(path);; at = dirname9(at)) {
47291
- const stat = lstatSync11(at, { bigint: true });
47217
+ const stat = lstatSync10(at, { bigint: true });
47292
47218
  need2(stat.isDirectory() && !stat.isSymbolicLink(), "path traverses a symlink or non-directory");
47293
47219
  result.push([at, stat]);
47294
47220
  if (at === dirname9(at))
47295
47221
  return result;
47296
47222
  }
47297
47223
  }
47298
- function readSettings(path, budget) {
47224
+ function readNativeSettingsWitnessFile(path, budget, filename = "settings.json") {
47299
47225
  absolutePath(path);
47300
- need2(basename4(path) === "settings.json", "requires settings.json");
47226
+ need2(basename4(path) === filename, `requires ${filename}`);
47301
47227
  need2(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
47302
- const ancestors = parents(path), initial = lstatSync11(path, { bigint: true });
47228
+ const ancestors = parents(path), initial = lstatSync10(path, { bigint: true });
47303
47229
  need2(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_SETTINGS_WITNESS_LIMITS.bytes), "is not a bounded regular file");
47304
47230
  need2(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
47305
- const fd = openSync9(path, constants6.O_RDONLY | constants6.O_NOFOLLOW | constants6.O_NONBLOCK);
47231
+ const fd = openSync8(path, constants5.O_RDONLY | constants5.O_NOFOLLOW | constants5.O_NONBLOCK);
47306
47232
  try {
47307
- const opened = fstatSync9(fd, { bigint: true });
47233
+ const opened = fstatSync8(fd, { bigint: true });
47308
47234
  need2(opened.isFile() && sameFile(initial, opened), "changed during open");
47309
47235
  const bytes = Buffer.alloc(Number(opened.size) + 1);
47310
47236
  let length = 0;
47311
47237
  while (length < bytes.length) {
47312
- const count = readSync5(fd, bytes, length, bytes.length - length, null);
47238
+ const count = readSync4(fd, bytes, length, bytes.length - length, null);
47313
47239
  if (!count)
47314
47240
  break;
47315
47241
  length += count;
47316
47242
  }
47317
- const after = fstatSync9(fd, { bigint: true }), current = lstatSync11(path, { bigint: true });
47243
+ const after = fstatSync8(fd, { bigint: true }), current = lstatSync10(path, { bigint: true });
47318
47244
  need2(current.isFile() && sameFile(opened, after) && sameFile(after, current) && BigInt(length) === opened.size, "changed during read");
47319
47245
  for (const [ancestor, before] of ancestors) {
47320
- const now = lstatSync11(ancestor, { bigint: true });
47246
+ const now = lstatSync10(ancestor, { bigint: true });
47321
47247
  need2(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
47322
47248
  }
47323
47249
  budget.remaining -= length;
@@ -47327,7 +47253,7 @@ function readSettings(path, budget) {
47327
47253
  throw new Error("Claude settings witness is not strict UTF-8");
47328
47254
  }
47329
47255
  } finally {
47330
- closeSync9(fd);
47256
+ closeSync8(fd);
47331
47257
  }
47332
47258
  }
47333
47259
  function parse8(text2) {
@@ -47451,7 +47377,7 @@ function settingsDigest(text2, version2 = 1) {
47451
47377
  }
47452
47378
  return true;
47453
47379
  });
47454
- return createHash9("sha256").update(`hasna.skills.claude-settings.v${version2}\x00`).update(JSON.stringify(canonical2(value))).digest("hex");
47380
+ return createHash8("sha256").update(`hasna.skills.claude-settings.v${version2}\x00`).update(JSON.stringify(canonical2(value))).digest("hex");
47455
47381
  }
47456
47382
  function hashClaudeSettingsReplacement(text2, budget) {
47457
47383
  need2(typeof text2 === "string", "requires settings text");
@@ -47461,10 +47387,10 @@ function hashClaudeSettingsReplacement(text2, budget) {
47461
47387
  return settingsDigest(text2);
47462
47388
  }
47463
47389
  function captureClaudeSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
47464
- return { path, hashMode: "claude-settings-v1", sha256: settingsDigest(readSettings(path, budget)) };
47390
+ return { path, hashMode: "claude-settings-v1", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget)) };
47465
47391
  }
47466
47392
  function captureClaudeSettingsV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
47467
- return { path, hashMode: "claude-settings-v2", sha256: settingsDigest(readSettings(path, budget), 2) };
47393
+ return { path, hashMode: "claude-settings-v2", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 2) };
47468
47394
  }
47469
47395
  function hashClaudeSettingsReplacementV2(text2, budget) {
47470
47396
  need2(typeof text2 === "string", "requires settings text");
@@ -47474,7 +47400,7 @@ function hashClaudeSettingsReplacementV2(text2, budget) {
47474
47400
  return settingsDigest(text2, 2);
47475
47401
  }
47476
47402
  function captureClaudeSettingsV3(path, budget = { remaining: 256 * 1024 * 1024 }) {
47477
- return { path, hashMode: "claude-settings-v3", sha256: settingsDigest(readSettings(path, budget), 3) };
47403
+ return { path, hashMode: "claude-settings-v3", sha256: settingsDigest(readNativeSettingsWitnessFile(path, budget), 3) };
47478
47404
  }
47479
47405
  function hashClaudeSettingsReplacementV3(text2, budget) {
47480
47406
  need2(typeof text2 === "string", "requires settings text");
@@ -47540,8 +47466,233 @@ var init_claude_settings_witness = __esm(() => {
47540
47466
  PERSISTED_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh"]);
47541
47467
  });
47542
47468
 
47469
+ // src/lib/codex-hook-rpc.ts
47470
+ var init_codex_hook_rpc = () => {};
47471
+
47472
+ // src/lib/codex-native-skill-catalog.ts
47473
+ var SUPPORTED_NAME_CONTROL_VERSIONS;
47474
+ var init_codex_native_skill_catalog = __esm(() => {
47475
+ init_codex_hook_rpc();
47476
+ SUPPORTED_NAME_CONTROL_VERSIONS = new Set(["codex-cli 0.159.2"]);
47477
+ });
47478
+
47479
+ // src/lib/codex-plugin-skill-controls.ts
47480
+ var init_codex_plugin_skill_controls = __esm(() => {
47481
+ init_claude_settings_witness();
47482
+ init_codex_native_skill_catalog();
47483
+ });
47484
+
47485
+ // src/lib/codex-settings-witness.ts
47486
+ import { createHash as createHash9 } from "crypto";
47487
+ function need3(value) {
47488
+ if (!value)
47489
+ throw new Error("Invalid Codex settings witness");
47490
+ }
47491
+ function numericTokens(text2) {
47492
+ const result = [];
47493
+ for (let at = 0;at < text2.length; ) {
47494
+ const char = text2[at];
47495
+ if (char === "#") {
47496
+ while (at < text2.length && text2[at] !== `
47497
+ `)
47498
+ at++;
47499
+ continue;
47500
+ }
47501
+ if (char === '"' || char === "'") {
47502
+ const quote = char, triple = text2.slice(at, at + 3) === quote.repeat(3);
47503
+ at += triple ? 3 : 1;
47504
+ while (at < text2.length) {
47505
+ if (quote === '"' && text2[at] === "\\") {
47506
+ at += 2;
47507
+ continue;
47508
+ }
47509
+ if (text2.slice(at, at + (triple ? 3 : 1)) === quote.repeat(triple ? 3 : 1)) {
47510
+ at += triple ? 3 : 1;
47511
+ break;
47512
+ }
47513
+ at++;
47514
+ }
47515
+ continue;
47516
+ }
47517
+ if (/[0-9+-]/.test(char)) {
47518
+ const start = at;
47519
+ while (at < text2.length && !/[\s,\]{}#=]/.test(text2[at]))
47520
+ at++;
47521
+ if (at > start) {
47522
+ result.push(text2.slice(start, at));
47523
+ continue;
47524
+ }
47525
+ }
47526
+ at++;
47527
+ }
47528
+ return result;
47529
+ }
47530
+ function canonical3(value, depth = 0, budget = { remaining: 65536 }) {
47531
+ need3(depth <= 32 && --budget.remaining >= 0);
47532
+ if (value === null)
47533
+ return ["null"];
47534
+ if (typeof value === "string")
47535
+ return ["string", value];
47536
+ if (typeof value === "boolean")
47537
+ return ["boolean", value];
47538
+ if (typeof value === "number") {
47539
+ need3(Number.isFinite(value));
47540
+ return ["number", String(value)];
47541
+ }
47542
+ if (value instanceof Date) {
47543
+ need3(Number.isFinite(value.getTime()));
47544
+ return ["date", value.toISOString()];
47545
+ }
47546
+ if (Array.isArray(value))
47547
+ return ["array", value.map((child) => canonical3(child, depth + 1, budget))];
47548
+ need3(value && typeof value === "object");
47549
+ return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical3(child, depth + 1, budget)])];
47550
+ }
47551
+ function hashCodexSettingsReplacement(text2, budget = { remaining: 256 * 1024 * 1024 }) {
47552
+ need3(Buffer.byteLength(text2) <= 1024 * 1024 && budget.remaining >= Buffer.byteLength(text2));
47553
+ budget.remaining -= Buffer.byteLength(text2);
47554
+ let config2;
47555
+ try {
47556
+ config2 = Bun.TOML.parse(text2);
47557
+ } catch {
47558
+ throw new Error("Invalid Codex settings witness");
47559
+ }
47560
+ need3(config2 && typeof config2 === "object" && !Array.isArray(config2));
47561
+ if (Object.hasOwn(config2, "model")) {
47562
+ need3(typeof config2.model === "string" && /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/.test(config2.model));
47563
+ delete config2.model;
47564
+ }
47565
+ for (const [key, values] of [["model_reasoning_effort", EFFORT], ["model_verbosity", VERBOSITY]])
47566
+ if (Object.hasOwn(config2, key)) {
47567
+ need3(typeof config2[key] === "string" && values.has(config2[key]));
47568
+ delete config2[key];
47569
+ }
47570
+ return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical3(config2), numericTokens(text2)])}`);
47571
+ }
47572
+ function captureCodexSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
47573
+ const text2 = readNativeSettingsWitnessFile(path, budget, "config.toml");
47574
+ return { path, hashMode: "codex-settings-v1", sha256: hashCodexSettingsReplacement(text2) };
47575
+ }
47576
+ var sha = (text2) => createHash9("sha256").update(text2).digest("hex"), EFFORT, VERBOSITY;
47577
+ var init_codex_settings_witness = __esm(() => {
47578
+ init_claude_settings_witness();
47579
+ EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
47580
+ VERBOSITY = new Set(["low", "medium", "high"]);
47581
+ });
47582
+
47583
+ // src/lib/managed-policy.ts
47584
+ import { closeSync as closeSync9, constants as constants6, fstatSync as fstatSync9, lstatSync as lstatSync11, openSync as openSync9, readSync as readSync5 } from "fs";
47585
+ import { join as join16 } from "path";
47586
+ function refusal(reason = "is unreadable") {
47587
+ return new SkillSelectionError("INVALID_AGENT_POLICY", `The Skills agent loading policy ${reason}; refusing legacy fallback.`);
47588
+ }
47589
+ function parseManagedSkillPolicy(text2) {
47590
+ if (Buffer.byteLength(text2) > AGENT_POLICY_LIMITS.bytes)
47591
+ throw refusal("exceeds its size limit");
47592
+ let value;
47593
+ try {
47594
+ value = JSON.parse(text2);
47595
+ } catch {
47596
+ throw refusal();
47597
+ }
47598
+ if (!value || typeof value !== "object" || Array.isArray(value) || value.loading !== "cli" || value.version !== undefined && value.version !== 1)
47599
+ throw refusal();
47600
+ if (value.profileId !== undefined && (typeof value.profileId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value.profileId) || value.profileId.includes("..")))
47601
+ throw refusal();
47602
+ try {
47603
+ assertAgentPolicyCollections(value);
47604
+ } catch {
47605
+ throw refusal("has invalid collection bounds");
47606
+ }
47607
+ return value;
47608
+ }
47609
+ function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
47610
+ const path = join16(dataDir2, "agent-policy.json");
47611
+ let descriptor, observed = false;
47612
+ try {
47613
+ const stat = lstatSync11(path);
47614
+ observed = true;
47615
+ if (!stat.isFile() || stat.isSymbolicLink())
47616
+ throw refusal();
47617
+ if (stat.size > AGENT_POLICY_LIMITS.bytes)
47618
+ throw refusal("exceeds its size limit");
47619
+ descriptor = openSync9(path, constants6.O_RDONLY | (constants6.O_NOFOLLOW ?? 0) | constants6.O_NONBLOCK);
47620
+ const opened = fstatSync9(descriptor);
47621
+ if (!opened.isFile())
47622
+ throw refusal();
47623
+ if (opened.size > AGENT_POLICY_LIMITS.bytes)
47624
+ throw refusal("exceeds its size limit");
47625
+ const bytes = Buffer.allocUnsafe(AGENT_POLICY_LIMITS.bytes + 1);
47626
+ let length = 0;
47627
+ while (length < bytes.length) {
47628
+ const count = readSync5(descriptor, bytes, length, bytes.length - length, null);
47629
+ if (count === 0)
47630
+ break;
47631
+ length += count;
47632
+ }
47633
+ if (length > AGENT_POLICY_LIMITS.bytes)
47634
+ throw refusal("exceeds its size limit");
47635
+ const text2 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
47636
+ return { text: text2, value: parseManagedSkillPolicy(text2) };
47637
+ } catch (error61) {
47638
+ if (error61.code === "ENOENT" && !observed) {
47639
+ if (lstatSync11(path, { throwIfNoEntry: false }) === undefined)
47640
+ return null;
47641
+ }
47642
+ if (error61 instanceof SkillSelectionError)
47643
+ throw error61;
47644
+ throw refusal();
47645
+ } finally {
47646
+ if (descriptor !== undefined)
47647
+ closeSync9(descriptor);
47648
+ }
47649
+ }
47650
+ function readManagedSkillPolicy(dataDir2 = getDataDirReadOnly()) {
47651
+ return readManagedSkillPolicySnapshot(dataDir2)?.value ?? null;
47652
+ }
47653
+ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
47654
+ return readManagedSkillPolicy(dataDir2)?.loading === "cli";
47655
+ }
47656
+ var init_managed_policy = __esm(() => {
47657
+ init_config();
47658
+ init_selection_cache();
47659
+ init_agent_policy_limits();
47660
+ });
47661
+
47662
+ // src/lib/agent-bridge.ts
47663
+ import { createHash as createHash10 } from "crypto";
47664
+ var CLI_BRIDGE_VERSION = 1, markdown = `---
47665
+ name: skills-cli
47666
+ description: Discover, load, author, and run versioned skills through the Skills CLI when a task needs reusable instructions or an executable skill.
47667
+ ---
47668
+
47669
+ Use the Skills CLI as the skill authority for this station.
47670
+
47671
+ - Discover available skills with \`skills list --json\` or \`skills search <query> --json\`.
47672
+ - Read selected instructions with \`skills load <slug>\`. Use \`skills sync\` to refresh the station's shared selection profile.
47673
+ - Create local drafts with \`skills new <name> --kind instruction\` or \`--kind executable\` in the Skills-owned source directory. Validate with \`skills validate <name>\`; use \`skills push --help\` for explicit versioned publication.
47674
+ - Run a skill only when the user's task calls for execution. Use \`skills run --help\` and select the intended local or cloud target explicitly.
47675
+
47676
+ 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.
47677
+ `, CLI_BRIDGE_DIGEST, CLI_BRIDGE_FILES;
47678
+ var init_agent_bridge = __esm(() => {
47679
+ CLI_BRIDGE_DIGEST = createHash10("sha256").update(markdown).digest("hex");
47680
+ CLI_BRIDGE_FILES = Object.freeze({
47681
+ "SKILL.md": markdown,
47682
+ ".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
47683
+ `
47684
+ });
47685
+ });
47686
+
47687
+ // src/lib/agent-codex.ts
47688
+ var init_agent_codex = () => {};
47689
+
47543
47690
  // src/lib/agent-integration.ts
47544
47691
  var init_agent_integration = __esm(() => {
47692
+ init_codex_plugin_skill_controls();
47693
+ init_codex_native_skill_catalog();
47694
+ init_codex_settings_witness();
47695
+ init_claude_settings_witness();
47545
47696
  init_native_discovery_roots();
47546
47697
  init_config();
47547
47698
  init_managed_policy();
@@ -48219,7 +48370,7 @@ var init_selection_resolver = __esm(() => {
48219
48370
  });
48220
48371
 
48221
48372
  // src/lib/plugin-projection.ts
48222
- import { createHash as createHash10 } from "crypto";
48373
+ import { createHash as createHash11 } from "crypto";
48223
48374
  function pluginRefusal(message) {
48224
48375
  throw new SkillSelectionError("PLUGIN_ADMISSION_REFUSED", message);
48225
48376
  }
@@ -48282,7 +48433,7 @@ function validatePluginManifest(value) {
48282
48433
  pluginNeed(item.kind === "skill" || item.kind === "command", "Unknown plugin payload kind");
48283
48434
  pluginDigest(item.sourceDigest);
48284
48435
  pluginKeys(item.target, ["slug", "version", "bundleDigest"]);
48285
- validateSelection2({ ...item.target, authority: "https://example.com/skills/v1", workspaceId: "validation", profileRevision: "validation" });
48436
+ validateSelection({ ...item.target, authority: "https://example.com/skills/v1", workspaceId: "validation", profileRevision: "validation" });
48286
48437
  }
48287
48438
  }
48288
48439
  function pluginFileWitnesses(entries) {
@@ -48297,7 +48448,7 @@ function pluginFileWitnesses(entries) {
48297
48448
  return { path: item.path, mode: item.mode, size: item.bytes.byteLength, sha256: pluginHash(item.bytes) };
48298
48449
  });
48299
48450
  }
48300
- var PLUGIN_PROJECTION_LIMITS, pluginHash = (bytes) => createHash10("sha256").update(bytes).digest("hex");
48451
+ var PLUGIN_PROJECTION_LIMITS, pluginHash = (bytes) => createHash11("sha256").update(bytes).digest("hex");
48301
48452
  var init_plugin_projection = __esm(() => {
48302
48453
  init_selection_cache();
48303
48454
  PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
@@ -48315,7 +48466,7 @@ function stat(path) {
48315
48466
  return pluginRefusal("Cannot inspect plugin filesystem entry");
48316
48467
  }
48317
48468
  }
48318
- function canonical3(path) {
48469
+ function canonical4(path) {
48319
48470
  pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute9(path) && resolve9(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
48320
48471
  }
48321
48472
  function owner(stats, system = false) {
@@ -48326,7 +48477,7 @@ function same3(left, right, identityOnly = false) {
48326
48477
  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");
48327
48478
  }
48328
48479
  function ancestors(path, create = false) {
48329
- canonical3(path);
48480
+ canonical4(path);
48330
48481
  const result = [], parent = dirname10(path), filesystemRoot = parse9(parent).root;
48331
48482
  let current = filesystemRoot;
48332
48483
  for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
@@ -48617,11 +48768,11 @@ function validateBinding(value) {
48617
48768
  for (const key of ["authority", "workspaceId", "profileId", "bundleSlug"])
48618
48769
  pluginText(value[key]);
48619
48770
  pluginNeed(value.principal.accountId === value.workspaceId, "Plugin principal account differs from its workspace");
48620
- validateSelection2({ slug: value.bundleSlug, version: "1.0.0", bundleDigest: `sha256:${"0".repeat(64)}`, authority: value.authority, workspaceId: value.workspaceId, profileRevision: "validation" });
48771
+ validateSelection({ slug: value.bundleSlug, version: "1.0.0", bundleDigest: `sha256:${"0".repeat(64)}`, authority: value.authority, workspaceId: value.workspaceId, profileRevision: "validation" });
48621
48772
  }
48622
48773
  function validateObservedSelection(value) {
48623
48774
  pluginKeys(value, ["slug", "version", "bundleDigest", "aliases", "triggers", "authority", "workspaceId", "profileRevision"]);
48624
- validateSelection2(value);
48775
+ validateSelection(value);
48625
48776
  if (value.triggers !== undefined) {
48626
48777
  pluginKeys(value.triggers, ["keywords", "paths", "always"]);
48627
48778
  pluginNeed(value.triggers.always === undefined || typeof value.triggers.always === "boolean", "Invalid plugin selection trigger");
@@ -48904,21 +49055,21 @@ var init_plugin_discovery = __esm(() => {
48904
49055
  });
48905
49056
 
48906
49057
  // src/lib/claude-marketplace-registry.ts
48907
- import { createHash as createHash11 } from "crypto";
49058
+ import { createHash as createHash12 } from "crypto";
48908
49059
  import { closeSync as closeSync12, constants as constants9, fstatSync as fstatSync12, lstatSync as lstatSync14, openSync as openSync12, readSync as readSync8 } from "fs";
48909
49060
  import { basename as basename6, dirname as dirname12, isAbsolute as isAbsolute12, resolve as resolve12 } from "path";
48910
- function need3(value, reason) {
49061
+ function need4(value, reason) {
48911
49062
  if (!value)
48912
49063
  throw new Error(`Claude marketplace registry ${reason}`);
48913
49064
  }
48914
49065
  function absolutePath2(value) {
48915
- need3(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute12(value) && resolve12(value) === value, "requires a normalized absolute path");
49066
+ need4(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute12(value) && resolve12(value) === value, "requires a normalized absolute path");
48916
49067
  }
48917
49068
  function parents2(path) {
48918
49069
  const result = [];
48919
49070
  for (let at = dirname12(path);; at = dirname12(at)) {
48920
49071
  const stat2 = lstatSync14(at, { bigint: true });
48921
- need3(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
49072
+ need4(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
48922
49073
  result.push([at, stat2]);
48923
49074
  if (at === dirname12(at))
48924
49075
  return result;
@@ -48926,15 +49077,15 @@ function parents2(path) {
48926
49077
  }
48927
49078
  function readRegistry2(path, budget) {
48928
49079
  absolutePath2(path);
48929
- need3(basename6(path) === "known_marketplaces.json", "requires known_marketplaces.json");
48930
- need3(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
49080
+ need4(basename6(path) === "known_marketplaces.json", "requires known_marketplaces.json");
49081
+ need4(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
48931
49082
  const ancestors2 = parents2(path), initial = lstatSync14(path, { bigint: true });
48932
- need3(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
48933
- need3(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
49083
+ need4(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
49084
+ need4(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
48934
49085
  const fd = openSync12(path, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
48935
49086
  try {
48936
49087
  const opened = fstatSync12(fd, { bigint: true });
48937
- need3(opened.isFile() && sameFile2(initial, opened), "changed during open");
49088
+ need4(opened.isFile() && sameFile2(initial, opened), "changed during open");
48938
49089
  const bytes = Buffer.alloc(Number(opened.size) + 1);
48939
49090
  let length = 0;
48940
49091
  while (length < bytes.length) {
@@ -48944,10 +49095,10 @@ function readRegistry2(path, budget) {
48944
49095
  length += count;
48945
49096
  }
48946
49097
  const after = fstatSync12(fd, { bigint: true }), current = lstatSync14(path, { bigint: true });
48947
- need3(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
49098
+ need4(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
48948
49099
  for (const [ancestor, before] of ancestors2) {
48949
49100
  const now = lstatSync14(ancestor, { bigint: true });
48950
- need3(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
49101
+ need4(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
48951
49102
  }
48952
49103
  budget.remaining -= length;
48953
49104
  try {
@@ -48967,7 +49118,7 @@ function parse10(text2) {
48967
49118
  at++;
48968
49119
  };
48969
49120
  function string5() {
48970
- need3(text2[at] === '"', "contains invalid JSON");
49121
+ need4(text2[at] === '"', "contains invalid JSON");
48971
49122
  const start = at++;
48972
49123
  while (at < text2.length) {
48973
49124
  const character = text2[at++];
@@ -48978,7 +49129,7 @@ function parse10(text2) {
48978
49129
  } catch {
48979
49130
  throw new Error("Claude marketplace registry contains invalid JSON string");
48980
49131
  }
48981
- need3(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
49132
+ need4(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
48982
49133
  return value2;
48983
49134
  }
48984
49135
  if (character === "\\")
@@ -48987,7 +49138,7 @@ function parse10(text2) {
48987
49138
  throw new Error("Claude marketplace registry contains an unterminated JSON string");
48988
49139
  }
48989
49140
  function value(depth) {
48990
- need3(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
49141
+ need4(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
48991
49142
  space();
48992
49143
  const character = text2[at];
48993
49144
  if (character === '"')
@@ -49001,17 +49152,17 @@ function parse10(text2) {
49001
49152
  if (object7) {
49002
49153
  space();
49003
49154
  const key = string5();
49004
- need3(!keys.has(key), "contains a duplicate JSON key");
49155
+ need4(!keys.has(key), "contains a duplicate JSON key");
49005
49156
  keys.add(key);
49006
49157
  space();
49007
- need3(text2[at++] === ":", "contains invalid JSON");
49158
+ need4(text2[at++] === ":", "contains invalid JSON");
49008
49159
  entries.push([key, value(depth + 1)]);
49009
49160
  } else
49010
49161
  items.push(value(depth + 1));
49011
49162
  space();
49012
49163
  if (text2[at] === end)
49013
49164
  break;
49014
- need3(text2[at++] === ",", "contains invalid JSON");
49165
+ need4(text2[at++] === ",", "contains invalid JSON");
49015
49166
  }
49016
49167
  at++;
49017
49168
  return object7 ? { kind: "object", entries } : { kind: "array", items };
@@ -49023,21 +49174,21 @@ function parse10(text2) {
49023
49174
  }
49024
49175
  numberPattern.lastIndex = at;
49025
49176
  const number5 = numberPattern.exec(text2);
49026
- need3(number5, "contains invalid JSON");
49177
+ need4(number5, "contains invalid JSON");
49027
49178
  at += number5[0].length;
49028
49179
  return { kind: "number", value: number5[0] };
49029
49180
  }
49030
49181
  const root = value(0);
49031
49182
  space();
49032
- need3(at === text2.length && root.kind === "object", "requires a JSON object without trailing content");
49033
- need3(root.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
49183
+ need4(at === text2.length && root.kind === "object", "requires a JSON object without trailing content");
49184
+ need4(root.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
49034
49185
  return root;
49035
49186
  }
49036
49187
  function exactKeys(value, keys) {
49037
49188
  return value?.kind === "object" && value.entries.length === keys.length && value.entries.every(([key]) => keys.includes(key));
49038
49189
  }
49039
49190
  function textField(value) {
49040
- need3(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
49191
+ need4(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
49041
49192
  return value.value;
49042
49193
  }
49043
49194
  function normalizeRow(row, version2 = 1) {
@@ -49045,7 +49196,7 @@ function normalizeRow(row, version2 = 1) {
49045
49196
  if (version2 !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
49046
49197
  return;
49047
49198
  const autoUpdate = field(row, "autoUpdate");
49048
- need3(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
49199
+ need4(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
49049
49200
  }
49050
49201
  const source = field(row, "source");
49051
49202
  if (source?.kind !== "object")
@@ -49061,23 +49212,23 @@ function normalizeRow(row, version2 = 1) {
49061
49212
  absolutePath2(sourceLocation);
49062
49213
  absolutePath2(textField(field(row, "installLocation")));
49063
49214
  const updated = textField(field(row, "lastUpdated"));
49064
- need3(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
49215
+ 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");
49065
49216
  const millis = Date.parse(updated);
49066
- need3(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
49217
+ need4(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
49067
49218
  row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
49068
49219
  }
49069
49220
  function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
49070
49221
  const value = parse10(readRegistry2(path, budget));
49071
49222
  for (const [, row] of value.entries)
49072
49223
  normalizeRow(row);
49073
- const sha2562 = createHash11("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
49224
+ const sha2562 = createHash12("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
49074
49225
  return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
49075
49226
  }
49076
49227
  function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
49077
49228
  const value = parse10(readRegistry2(path, budget));
49078
49229
  for (const [, row] of value.entries)
49079
49230
  normalizeRow(row, 2);
49080
- const sha2562 = createHash11("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
49231
+ const sha2562 = createHash12("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
49081
49232
  return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
49082
49233
  }
49083
49234
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS, 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, field = (object7, key) => object7.entries.find(([name]) => name === key)?.[1];
@@ -49087,17 +49238,17 @@ var init_claude_marketplace_registry = __esm(() => {
49087
49238
 
49088
49239
  // src/lib/codex-hook-trust-files.ts
49089
49240
  import { closeSync as closeSync13, constants as constants10, fstatSync as fstatSync13, fsyncSync as fsyncSync3, lstatSync as lstatSync15, openSync as openSync13, readSync as readSync9, writeFileSync as writeFileSync5 } from "fs";
49090
- import { createHash as createHash12 } from "crypto";
49241
+ import { createHash as createHash13 } from "crypto";
49091
49242
  import { dirname as dirname13 } from "path";
49092
- function need4(value, code) {
49243
+ function need5(value, code) {
49093
49244
  if (!value)
49094
49245
  throw new Error(`CODEX_HOOK_TRUST_${code}`);
49095
49246
  }
49096
49247
  function safeParents(file2) {
49097
49248
  for (let p = dirname13(file2);; p = dirname13(p)) {
49098
49249
  const s = lstatSync15(p);
49099
- need4(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
49100
- need4((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
49250
+ need5(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
49251
+ need5((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
49101
49252
  if (dirname13(p) === p)
49102
49253
  break;
49103
49254
  }
@@ -49106,12 +49257,12 @@ function snapshot(file2, privateFile = false, options = {}) {
49106
49257
  const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
49107
49258
  safeParents(file2);
49108
49259
  const initial = lstatSync15(file2, { bigint: true });
49109
- need4(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");
49110
- need4(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
49260
+ 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");
49261
+ need5(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
49111
49262
  const fd = openSync13(file2, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
49112
49263
  try {
49113
49264
  const before = fstatSync13(fd, { bigint: true });
49114
- need4(sameStat(initial, before), "INPUT_CHANGED");
49265
+ need5(sameStat(initial, before), "INPUT_CHANGED");
49115
49266
  const bytes = Buffer.alloc(Number(before.size) + 1);
49116
49267
  let length = 0;
49117
49268
  while (length < bytes.length) {
@@ -49120,16 +49271,16 @@ function snapshot(file2, privateFile = false, options = {}) {
49120
49271
  break;
49121
49272
  length += n2;
49122
49273
  }
49123
- need4(length === Number(before.size) && sameStat(before, fstatSync13(fd, { bigint: true })) && sameStat(before, lstatSync15(file2, { bigint: true })), "INPUT_CHANGED");
49274
+ need5(length === Number(before.size) && sameStat(before, fstatSync13(fd, { bigint: true })) && sameStat(before, lstatSync15(file2, { bigint: true })), "INPUT_CHANGED");
49124
49275
  const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
49125
- return { file: file2, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash12("sha256").update(content).digest("hex") };
49276
+ return { file: file2, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash13("sha256").update(content).digest("hex") };
49126
49277
  } finally {
49127
49278
  closeSync13(fd);
49128
49279
  }
49129
49280
  }
49130
49281
  function unchanged2(before) {
49131
49282
  const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
49132
- need4(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
49283
+ need5(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
49133
49284
  }
49134
49285
  var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
49135
49286
  var init_codex_hook_trust_files = () => {};
@@ -49148,10 +49299,10 @@ var init_codex_hook_additions_review = __esm(() => {
49148
49299
  });
49149
49300
 
49150
49301
  // src/lib/codex-hook-discovery-recovery.ts
49151
- import { createHash as createHash13 } from "crypto";
49302
+ import { createHash as createHash14 } from "crypto";
49152
49303
  function assertCodexHookDiscoveryRecovery(binding, witness) {
49153
49304
  const proof = proofs2.get(witness);
49154
- need4(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
49305
+ need5(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha2(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
49155
49306
  unchanged2(proof.before);
49156
49307
  unchanged2(proof.current);
49157
49308
  proof.recheckAdditions();
@@ -49160,10 +49311,10 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
49160
49311
  if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
49161
49312
  return false;
49162
49313
  assertCodexHookDiscoveryRecovery(binding, witness);
49163
- need4(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
49314
+ need5(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
49164
49315
  return true;
49165
49316
  }
49166
- var sha = (text2) => createHash13("sha256").update(text2).digest("hex"), proofs2;
49317
+ var sha2 = (text2) => createHash14("sha256").update(text2).digest("hex"), proofs2;
49167
49318
  var init_codex_hook_discovery_recovery = __esm(() => {
49168
49319
  init_codex_hook_trust_files();
49169
49320
  init_codex_hook_trust_layout();
@@ -49172,7 +49323,7 @@ var init_codex_hook_discovery_recovery = __esm(() => {
49172
49323
  });
49173
49324
 
49174
49325
  // src/lib/agent-discovery.ts
49175
- import { createHash as createHash14 } from "crypto";
49326
+ import { createHash as createHash15 } from "crypto";
49176
49327
  import { lstatSync as lstatSync16, readFileSync as readFileSync15, readdirSync as readdirSync6, realpathSync as realpathSync3 } from "fs";
49177
49328
  import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute13, join as join23, resolve as resolve13, sep as sep4 } from "path";
49178
49329
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
@@ -49208,6 +49359,14 @@ function read(path, changes) {
49208
49359
  return readFileSync15(path, "utf8");
49209
49360
  }
49210
49361
  function projected(source, changes, budget = discoveryByteBudget()) {
49362
+ if (source.hashMode === "codex-settings-v1") {
49363
+ if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
49364
+ throw new Error("Codex settings witnesses require exact typed metadata");
49365
+ const current = captureCodexSettings(source.path, budget).sha256;
49366
+ if (current !== source.sha256)
49367
+ throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
49368
+ return changes?.has(source.path) ? hashCodexSettingsReplacement(changes.get(source.path), budget) : current;
49369
+ }
49211
49370
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
49212
49371
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
49213
49372
  throw new Error("Claude settings witnesses require exact typed metadata");
@@ -49276,6 +49435,8 @@ function verifyAgentDiscovery(binding, codexRecovery) {
49276
49435
  safe4(root);
49277
49436
  }
49278
49437
  function assertMarketplaceBinding(binding, source) {
49438
+ if (source.hashMode === "codex-settings-v1" && (binding.agent !== "codex" || binding.method !== "reviewed" || basename7(source.path) !== "config.toml"))
49439
+ throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
49279
49440
  if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename7(source.path) !== "settings.json"))
49280
49441
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
49281
49442
  if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
@@ -49285,10 +49446,10 @@ function agentDiscoveryConfigPath(home, agent) {
49285
49446
  return join23(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
49286
49447
  }
49287
49448
  function resolveAgentDiscovery(options) {
49288
- const canonical4 = options.canonical ?? resolve13, home = resolve13(options.home), agent = options.agent;
49449
+ const canonical5 = options.canonical ?? resolve13, home = resolve13(options.home), agent = options.agent;
49289
49450
  const sources = [], roots = new Set, builtinNames = [];
49290
49451
  const witness = (path, format, fields) => {
49291
- const source = { path: canonical4(path), sha256: null, ...format ? { format, fields } : {} };
49452
+ const source = { path: canonical5(path), sha256: null, ...format ? { format, fields } : {} };
49292
49453
  source.sha256 = projected(source);
49293
49454
  sources.push(source);
49294
49455
  return read(source.path);
@@ -49355,9 +49516,9 @@ function resolveAgentDiscovery(options) {
49355
49516
  if (retained.agent !== agent || retained.method !== "reviewed")
49356
49517
  unresolved("invalid retained review");
49357
49518
  verifyAgentDiscovery(retained);
49358
- if (!retained.sources.some((source) => source.path === canonical4(configPath) && source.format === undefined && source.fields === undefined))
49519
+ if (!retained.sources.some((source) => source.path === canonical5(configPath) && source.format === undefined && source.fields === undefined))
49359
49520
  unresolved("retained review is missing the full agent configuration source");
49360
- if (retained.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && source.path !== canonical4(configPath)))
49521
+ 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)))
49361
49522
  unresolved("retained settings witness names another configuration source");
49362
49523
  if (sources.some((source) => !retained.sources.some((saved) => isDeepStrictEqual2(saved, source))) || [...roots].some((root) => !retained.roots.includes(root)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
49363
49524
  unresolved("retained review is missing current runtime discovery coverage");
@@ -49373,14 +49534,14 @@ function resolveAgentDiscovery(options) {
49373
49534
  if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
49374
49535
  throw new Error("Explicit discovery reviews require full source-file hashes");
49375
49536
  verifyAgentDiscovery(supplied);
49376
- if (review.sources.some((source) => (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && source.path !== canonical4(configPath)))
49537
+ 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)))
49377
49538
  throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
49378
- if (!review.sources.some((source) => source.path === canonical4(configPath)))
49539
+ if (!review.sources.some((source) => source.path === canonical5(configPath)))
49379
49540
  throw new Error("Discovery review must include the agent configuration source");
49380
49541
  return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...sources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
49381
49542
  }
49382
49543
  function plugin(root) {
49383
- root = canonical4(root);
49544
+ root = canonical5(root);
49384
49545
  safe4(root);
49385
49546
  const manifestPath = join23(root, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
49386
49547
  const raw = witness(manifestPath);
@@ -49412,7 +49573,7 @@ function resolveAgentDiscovery(options) {
49412
49573
  if (agent === "claude") {
49413
49574
  const registrations = witness(join23(home, ".claude/plugins/installed_plugins.json"));
49414
49575
  const installed = registrations === null ? {} : parseConfig(registrations, join23(home, ".claude/plugins/installed_plugins.json")).plugins;
49415
- const commands = canonical4(join23(home, ".claude/commands"));
49576
+ const commands = canonical5(join23(home, ".claude/commands"));
49416
49577
  safe4(commands);
49417
49578
  if (lstatSync16(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync6(commands).length)
49418
49579
  unresolved("legacy home command discovery requires a dedicated format adapter");
@@ -49486,9 +49647,9 @@ function resolveAgentDiscovery(options) {
49486
49647
  roots.add(join23(root, "skills"));
49487
49648
  }
49488
49649
  }
49489
- return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join23(home, ".hermes/plugins"), join23(home, ".hermes/hermes-agent")].map((path) => canonical4(path))) } : {} };
49650
+ return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join23(home, ".hermes/plugins"), join23(home, ".hermes/hermes-agent")].map((path) => canonical5(path))) } : {} };
49490
49651
  }
49491
- var digest = (text2) => createHash14("sha256").update(text2).digest("hex");
49652
+ var digest = (text2) => createHash15("sha256").update(text2).digest("hex");
49492
49653
  var init_agent_discovery = __esm(() => {
49493
49654
  init_agent_policy_limits();
49494
49655
  init_agent_hermes();
@@ -49500,6 +49661,7 @@ var init_agent_discovery = __esm(() => {
49500
49661
  init_claude_marketplace_registry();
49501
49662
  init_claude_settings_witness();
49502
49663
  init_codex_hook_discovery_recovery();
49664
+ init_codex_settings_witness();
49503
49665
  init_agent_discovery_directories();
49504
49666
  });
49505
49667
 
@@ -50564,7 +50726,7 @@ var init_remote_customer_operations = __esm(() => {
50564
50726
  });
50565
50727
 
50566
50728
  // src/lib/canonical-json.ts
50567
- import { createHash as createHash15 } from "crypto";
50729
+ import { createHash as createHash16 } from "crypto";
50568
50730
  function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
50569
50731
  return canonicalJsonAtDepth(value, maximumBytes, 64);
50570
50732
  }
@@ -50608,7 +50770,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
50608
50770
  return visit3(value, 0);
50609
50771
  }
50610
50772
  function canonicalJsonSha256(value, maximumBytes) {
50611
- return createHash15("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
50773
+ return createHash16("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
50612
50774
  }
50613
50775
  var init_canonical_json = () => {};
50614
50776
 
@@ -52834,7 +52996,7 @@ var init_remote_invitation_recovery = __esm(() => {
52834
52996
  });
52835
52997
 
52836
52998
  // src/lib/remote-private-publications.ts
52837
- import { createHash as createHash16 } from "crypto";
52999
+ import { createHash as createHash17 } from "crypto";
52838
53000
  function checkedPublicationDeclaration(value) {
52839
53001
  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)
52840
53002
  return bad();
@@ -53095,7 +53257,7 @@ var PRIVATE_PUBLICATION_MAX_BYTES, failures2, PrivatePublicationError, bad = ()
53095
53257
  throw new PrivatePublicationError("INVALID_PUBLICATION_INPUT", "Invalid publication input or recovery data.");
53096
53258
  }, invalid4 = () => {
53097
53259
  throw new PrivatePublicationError("INVALID_PUBLICATION_RESPONSE", "The server returned an invalid publication result.");
53098
- }, publicationUuid = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v), hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v), record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v), exact = (v, keys) => record9(v) && Object.keys(v).sort().join(",") === keys.sort().join(","), date6 = (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)), publicationSha256 = (bytes) => createHash16("sha256").update(bytes).digest("hex");
53260
+ }, publicationUuid = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v), hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v), record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v), exact = (v, keys) => record9(v) && Object.keys(v).sort().join(",") === keys.sort().join(","), date6 = (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)), publicationSha256 = (bytes) => createHash17("sha256").update(bytes).digest("hex");
53099
53261
  var init_remote_private_publications = __esm(() => {
53100
53262
  init_fleet_credentials();
53101
53263
  init_remote_workspace_selection();
@@ -53873,7 +54035,7 @@ async function continueRecurringRecovery(customer, directory, options) {
53873
54035
  throw error61;
53874
54036
  }
53875
54037
  }
53876
- var uuid12, sha2, common, intentSchema, stateSchema, invalid5 = () => {
54038
+ var uuid12, sha3, common, intentSchema, stateSchema, invalid5 = () => {
53877
54039
  throw new RecurringCustomerError("RECURRING_RECOVERY_INVALID", "The original recovery directory or record is invalid or changed. Preserve it; do not create a replacement activation automatically.");
53878
54040
  };
53879
54041
  var init_recurring_recovery = __esm(() => {
@@ -53883,7 +54045,7 @@ var init_recurring_recovery = __esm(() => {
53883
54045
  init_remote_recurring();
53884
54046
  init_recurring_customer();
53885
54047
  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}$/);
53886
- sha2 = exports_external.string().regex(/^[a-f0-9]{64}$/);
54048
+ sha3 = exports_external.string().regex(/^[a-f0-9]{64}$/);
53887
54049
  common = exports_external.object({
53888
54050
  contractVersion: exports_external.literal(1),
53889
54051
  apiOrigin: exports_external.string().min(1).max(4096),
@@ -53896,7 +54058,7 @@ var init_recurring_recovery = __esm(() => {
53896
54058
  common.extend({
53897
54059
  operation: exports_external.literal("activate"),
53898
54060
  draftId: uuid12,
53899
- termsSha256: sha2,
54061
+ termsSha256: sha3,
53900
54062
  approvalDeadline: exports_external.string().datetime(),
53901
54063
  approval: recurringActivationSchema
53902
54064
  }).strict(),
@@ -53904,7 +54066,7 @@ var init_recurring_recovery = __esm(() => {
53904
54066
  ]);
53905
54067
  stateSchema = exports_external.object({
53906
54068
  contractVersion: exports_external.literal(1),
53907
- intentSha256: sha2,
54069
+ intentSha256: sha3,
53908
54070
  phase: exports_external.enum(["prepared", "pending", "observed"]),
53909
54071
  consentId: uuid12.nullable()
53910
54072
  }).strict();
@@ -55845,7 +56007,7 @@ var init_credential_state = __esm(() => {
55845
56007
  });
55846
56008
 
55847
56009
  // src/lib/run-state.ts
55848
- import { createHash as createHash17, randomBytes } from "crypto";
56010
+ import { createHash as createHash18, randomBytes } from "crypto";
55849
56011
  import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as readFileSync19, readdirSync as readdirSync9, statSync as statSync9, writeFileSync as writeFileSync10 } from "fs";
55850
56012
  import { extname, join as join29, relative as relative5 } from "path";
55851
56013
  function createSkillRun(params, targetDir = process.cwd()) {
@@ -55960,7 +56122,7 @@ function collectRunArtifacts(context2) {
55960
56122
  artifacts.push({
55961
56123
  path: toProjectRelative(context2.targetDir, path),
55962
56124
  mime: mimeForPath(path),
55963
- sha256: createHash17("sha256").update(bytes).digest("hex"),
56125
+ sha256: createHash18("sha256").update(bytes).digest("hex"),
55964
56126
  sizeBytes: stat3.size
55965
56127
  });
55966
56128
  }
@@ -57774,7 +57936,7 @@ __export(exports_selected_run, {
57774
57936
  prepareSelectedSecretBindings: () => prepareSelectedSecretBindings,
57775
57937
  executeSelectedLocal: () => executeSelectedLocal
57776
57938
  });
57777
- import { createHash as createHash18 } from "crypto";
57939
+ import { createHash as createHash19 } from "crypto";
57778
57940
  import { spawn } from "child_process";
57779
57941
  import { mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync11, realpathSync as realpathSync8 } from "fs";
57780
57942
  import { dirname as dirname19, join as join31, resolve as resolve17 } from "path";
@@ -57900,7 +58062,7 @@ async function executeSelectedLocal(selected, options = {}) {
57900
58062
  selection: selection2,
57901
58063
  target: "local",
57902
58064
  runtime: runtimeName,
57903
- inputDigest: `sha256:${createHash18("sha256").update(input2).digest("hex")}`,
58065
+ inputDigest: `sha256:${createHash19("sha256").update(input2).digest("hex")}`,
57904
58066
  runDirectory,
57905
58067
  ...executionGrant ? { executionGrant } : {},
57906
58068
  ...bindings ? { secretBinding: { schema: bindings.schema, selection: bindings.selection, consumer: bindings.consumer, secretsAuthority: bindings.secretsAuthority, bindings: bindings.bindings } } : {},
@@ -59128,7 +59290,7 @@ var init_schedule_tools = __esm(() => {
59128
59290
  });
59129
59291
 
59130
59292
  // src/lib/native-storage.ts
59131
- import { createHash as createHash19, createHmac } from "crypto";
59293
+ import { createHash as createHash20, createHmac } from "crypto";
59132
59294
  import {
59133
59295
  existsSync as existsSync18,
59134
59296
  mkdirSync as mkdirSync15,
@@ -59205,7 +59367,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
59205
59367
  files.push({
59206
59368
  path: relativePath,
59207
59369
  sizeBytes: bytes.byteLength,
59208
- sha256: createHash19("sha256").update(bytes).digest("hex"),
59370
+ sha256: createHash20("sha256").update(bytes).digest("hex"),
59209
59371
  ...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
59210
59372
  });
59211
59373
  }