@hasna/skills 0.8.10 → 0.8.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9650,7 +9650,7 @@ import { homedir as homedir5 } from "os";
9650
9650
  import {
9651
9651
  cpSync as cpSync2,
9652
9652
  existsSync as existsSync12,
9653
- lstatSync as lstatSync17,
9653
+ lstatSync as lstatSync19,
9654
9654
  mkdirSync as mkdirSync8,
9655
9655
  mkdtempSync as mkdtempSync2,
9656
9656
  readFileSync as readFileSync15,
@@ -9661,12 +9661,12 @@ import {
9661
9661
  writeFileSync as writeFileSync6
9662
9662
  } from "fs";
9663
9663
  import { homedir as homedir4 } from "os";
9664
- import { basename as basename5, dirname as dirname14, join as join26 } from "path";
9664
+ import { basename as basename9, dirname as dirname16, join as join26 } from "path";
9665
9665
 
9666
9666
  // src/lib/agent-integration.ts
9667
- import { createHash as createHash12, randomUUID as randomUUID3 } from "crypto";
9668
- import { existsSync as existsSync11, lstatSync as lstatSync16, statSync as statSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync14, readdirSync as readdirSync10, opendirSync as opendirSync5, readlinkSync as readlinkSync2, realpathSync as realpathSync5, renameSync as renameSync4, rmdirSync as rmdirSync3, writeFileSync as writeFileSync5, unlinkSync as unlinkSync4, chmodSync, openSync as openSync9, closeSync as closeSync9, fsyncSync as fsyncSync2, fstatSync as fstatSync9, readSync as readSync7, constants as constants8, linkSync as linkSync2 } from "fs";
9669
- import { dirname as dirname13, join as join25, resolve as resolve12, relative as relative5, isAbsolute as isAbsolute12, sep as sep6 } from "path";
9667
+ import { createHash as createHash14, randomUUID as randomUUID3 } from "crypto";
9668
+ import { existsSync as existsSync11, lstatSync as lstatSync18, statSync as statSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync14, readdirSync as readdirSync10, opendirSync as opendirSync5, readlinkSync as readlinkSync2, realpathSync as realpathSync5, renameSync as renameSync4, rmdirSync as rmdirSync3, writeFileSync as writeFileSync5, unlinkSync as unlinkSync4, chmodSync, openSync as openSync11, closeSync as closeSync11, fsyncSync as fsyncSync3, fstatSync as fstatSync11, readSync as readSync9, constants as constants10, linkSync as linkSync2 } from "fs";
9669
+ import { dirname as dirname15, join as join25, resolve as resolve15, relative as relative5, isAbsolute as isAbsolute15, sep as sep6 } from "path";
9670
9670
  import { homedir as homedir3 } from "os";
9671
9671
 
9672
9672
  // src/lib/managed-policy.ts
@@ -9675,8 +9675,9 @@ import { join as join11 } from "path";
9675
9675
 
9676
9676
  // src/lib/selection-cache.ts
9677
9677
  import { createHash as createHash3, randomUUID } from "crypto";
9678
- import { closeSync, constants, fstatSync, lstatSync as lstatSync3, mkdirSync as mkdirSync4, openSync, readSync, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
9678
+ import { closeSync, constants, fstatSync, fsyncSync, lstatSync as lstatSync3, mkdirSync as mkdirSync4, openSync, readSync, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
9679
9679
  import { dirname as dirname4, join as join10, parse, resolve as resolve3 } from "path";
9680
+ import { isDeepStrictEqual } from "util";
9680
9681
 
9681
9682
  // src/lib/skill-bundle.ts
9682
9683
  import { createHash as createHash2 } from "crypto";
@@ -10271,7 +10272,7 @@ function selectionSnapshotsEqual(left, right) {
10271
10272
  var MAX_PROFILE_SELECTIONS = 4096;
10272
10273
  var MAX_PROFILE_DOCUMENT_BYTES = 8 * 1024 * 1024;
10273
10274
  var MAX_SKILL_SESSION_ID_CHARS = 256;
10274
- var PROFILE_SESSION_ENVELOPE_BYTES = MAX_PROFILE_SELECTIONS * 67 + MAX_SKILL_SESSION_ID_CHARS * 6 + 256;
10275
+ var PROFILE_SESSION_ENVELOPE_BYTES = MAX_PROFILE_SELECTIONS * 67 + MAX_SKILL_SESSION_ID_CHARS * 12 + 512;
10275
10276
  var MAX_RESOLVED_PROFILE_BYTES = MAX_PROFILE_DOCUMENT_BYTES - PROFILE_SESSION_ENVELOPE_BYTES;
10276
10277
  var LEGACY_PROFILE_SELECTIONS = 256;
10277
10278
  var LEGACY_PROFILE_DOCUMENT_BYTES = 1e6;
@@ -10420,12 +10421,21 @@ function writeSelectionJson(path, value) {
10420
10421
  throw new SkillSelectionError("RECEIPT_TOO_LARGE", "The Skills selection receipt exceeds its size limit.");
10421
10422
  atomicWrite(path, bytes, 384);
10422
10423
  }
10423
- function atomicWrite(path, bytes, mode) {
10424
+ function atomicWrite(path, bytes, mode, durable = false) {
10424
10425
  assertRegularPath(path, true);
10425
10426
  const temporary = join10(dirname4(path), `.selection-${randomUUID()}.tmp`);
10426
10427
  try {
10427
- writeFileSync4(temporary, bytes, { flag: "wx", mode });
10428
+ const fd = openSync(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, mode);
10429
+ try {
10430
+ writeFileSync4(fd, bytes);
10431
+ if (durable)
10432
+ fsyncSync(fd);
10433
+ } finally {
10434
+ closeSync(fd);
10435
+ }
10428
10436
  renameSync2(temporary, path);
10437
+ if (durable)
10438
+ syncDirectory(dirname4(path));
10429
10439
  } finally {
10430
10440
  try {
10431
10441
  unlinkSync(temporary);
@@ -10544,19 +10554,194 @@ function sessionReceiptPath(sessionId, options = {}) {
10544
10554
  throw new SkillSelectionError("INVALID_SESSION", `A Skills session id must contain 1\u2013${MAX_SKILL_SESSION_ID_CHARS} characters.`);
10545
10555
  return join10(selectionCacheRoot(options), "sessions", `${hash(sessionId)}.json`);
10546
10556
  }
10547
- function readSkillSession(sessionId, options = {}) {
10548
- const receipt = readSelectionJson(sessionReceiptPath(sessionId, options));
10549
- if (receipt) {
10550
- validateSelectionReceipt(receipt);
10551
- const keys = new Set(receipt.profile.selections.map(selectionKey));
10552
- if (receipt.sessionId !== sessionId || !Array.isArray(receipt.loaded) || receipt.loaded.length > keys.size || new Set(receipt.loaded).size !== receipt.loaded.length || !receipt.loaded.every((key) => keys.has(key))) {
10553
- throw new SkillSelectionError("INVALID_RECEIPT", "The Skills session receipt is invalid.");
10557
+ function skillSessionGeneration(receipt) {
10558
+ return receipt.generation ?? 0;
10559
+ }
10560
+ function validateSkillSessionBinding(binding) {
10561
+ if (!binding || typeof binding.sessionId !== "string" || !binding.sessionId.trim() || binding.sessionId.length > MAX_SKILL_SESSION_ID_CHARS || !Number.isSafeInteger(binding.generation) || binding.generation < 0 || !/^[a-f0-9]{64}$/.test(binding.receiptSha256)) {
10562
+ throw new SkillSelectionError("INVALID_RECEIPT", "The Skills session receipt contains an invalid parent snapshot binding.");
10563
+ }
10564
+ }
10565
+ function validateSkillSessionReceipt(receipt, sessionId) {
10566
+ validateSelectionReceipt(receipt);
10567
+ const keys = new Set(receipt.profile.selections.map(selectionKey));
10568
+ if (receipt.sessionId !== sessionId || !Array.isArray(receipt.loaded) || receipt.loaded.length > keys.size || new Set(receipt.loaded).size !== receipt.loaded.length || !receipt.loaded.every((key) => keys.has(key)) || receipt.generation !== undefined && (!Number.isSafeInteger(receipt.generation) || receipt.generation < 1)) {
10569
+ throw new SkillSelectionError("INVALID_RECEIPT", "The Skills session receipt is invalid.");
10570
+ }
10571
+ if (receipt.parent) {
10572
+ validateSkillSessionBinding(receipt.parent);
10573
+ if (receipt.parent.sessionId === sessionId)
10574
+ throw new SkillSelectionError("INVALID_RECEIPT", "A Skills child session cannot name itself as its parent.");
10575
+ }
10576
+ }
10577
+ function readSkillSessionSnapshotIfExists(sessionId, options = {}) {
10578
+ const path = sessionReceiptPath(sessionId, options), bytes = readRegularFile(path, MAX_PROFILE_DOCUMENT_BYTES);
10579
+ if (!bytes)
10580
+ return null;
10581
+ let receipt;
10582
+ try {
10583
+ receipt = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
10584
+ } catch {
10585
+ throw new SkillSelectionError("INVALID_RECEIPT", "The Skills session receipt is unreadable.");
10586
+ }
10587
+ validateSkillSessionReceipt(receipt, sessionId);
10588
+ const sha256 = sha256Hex(bytes);
10589
+ return { path, bytes, receipt, sha256, sessionId, generation: skillSessionGeneration(receipt), receiptSha256: sha256 };
10590
+ }
10591
+ function readSkillSessionSnapshot(sessionId, options = {}) {
10592
+ const snapshot = readSkillSessionSnapshotIfExists(sessionId, options);
10593
+ if (!snapshot)
10594
+ throw new SkillSelectionError("SESSION_NOT_FOUND", "The requested Skills session receipt does not exist.");
10595
+ return snapshot;
10596
+ }
10597
+ function skillSessionSnapshotBinding(snapshot) {
10598
+ return { sessionId: snapshot.sessionId, generation: snapshot.generation, receiptSha256: snapshot.receiptSha256 };
10599
+ }
10600
+ function withSessionWriteLocks(sessionIds, options, action) {
10601
+ const operationId = randomUUID();
10602
+ const locks = [...new Set(sessionIds)].map((sessionId) => ({ sessionId, path: `${sessionReceiptPath(sessionId, options)}.write-lock` })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
10603
+ const owned = [];
10604
+ const assertOwned = () => {
10605
+ for (const lock of owned) {
10606
+ const current = lstatSync3(lock.path, { throwIfNoEntry: false });
10607
+ if (!current?.isFile() || current.dev !== lock.dev || current.ino !== lock.ino) {
10608
+ throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "A session write lock changed during the operation; no replacement is permitted.");
10609
+ }
10610
+ }
10611
+ };
10612
+ try {
10613
+ for (const lock of locks) {
10614
+ assertRegularPath(lock.path, true);
10615
+ let fd;
10616
+ try {
10617
+ fd = openSync(lock.path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
10618
+ } catch (error) {
10619
+ if (error.code === "EEXIST")
10620
+ throw new SkillSelectionError("SESSION_WRITE_LOCKED", "Another Skills process owns a required session write lock; retry after it completes. A surviving lock requires explicit recovery review.");
10621
+ throw error;
10622
+ }
10623
+ const stat = fstatSync(fd);
10624
+ owned.push({ path: lock.path, fd, dev: stat.dev, ino: stat.ino });
10625
+ writeFileSync4(fd, `${JSON.stringify({ schemaVersion: 1, operationId, sessionId: lock.sessionId, pid: process.pid })}
10626
+ `);
10554
10627
  }
10628
+ return action(assertOwned);
10629
+ } finally {
10630
+ for (const lock of owned.reverse()) {
10631
+ closeSync(lock.fd);
10632
+ const current = lstatSync3(lock.path, { throwIfNoEntry: false });
10633
+ if (current?.isFile() && current.dev === lock.dev && current.ino === lock.ino)
10634
+ unlinkSync(lock.path);
10635
+ }
10636
+ }
10637
+ }
10638
+ function withSessionWriteLock(sessionId, options, action) {
10639
+ return withSessionWriteLocks([sessionId], options, action);
10640
+ }
10641
+ function sameSnapshot(actual, expected) {
10642
+ return actual === null ? expected === null : expected !== null && actual.sessionId === expected.sessionId && actual.generation === expected.generation && actual.receiptSha256 === expected.receiptSha256;
10643
+ }
10644
+ function nextSkillSessionGeneration(generation) {
10645
+ if (!Number.isSafeInteger(generation) || generation < 0 || generation >= Number.MAX_SAFE_INTEGER) {
10646
+ throw new SkillSelectionError("SESSION_GENERATION_EXHAUSTED", "The Skills session generation cannot advance safely; preserve the receipt and review recovery before continuing.");
10647
+ }
10648
+ return generation + 1;
10649
+ }
10650
+ function parentChanged() {
10651
+ throw new SkillSelectionError("SESSION_PARENT_CHANGED", "The parent Skills session changed while the child was resolving; resolve the child again from the current parent pin.");
10652
+ }
10653
+ function writeSkillSession(receipt, expected, options = {}) {
10654
+ validateSkillSessionReceipt(receipt, receipt.sessionId);
10655
+ if (expected.parent)
10656
+ validateSkillSessionBinding(expected.parent);
10657
+ if (expected.parent?.sessionId === receipt.sessionId)
10658
+ throw new SkillSelectionError("INVALID_RECEIPT", "A Skills child session cannot name itself as its parent.");
10659
+ const lockIds = expected.parent ? [receipt.sessionId, expected.parent.sessionId] : [receipt.sessionId];
10660
+ withSessionWriteLocks(lockIds, options, (assertOwned) => {
10661
+ const current = readSkillSessionSnapshotIfExists(receipt.sessionId, options);
10662
+ if (!sameSnapshot(current, expected.current) || current && !isDeepStrictEqual(current.receipt.profile, receipt.profile)) {
10663
+ throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The Skills session receipt changed while context was loading; resolve context again without replacing the current pin.");
10664
+ }
10665
+ let parent = null;
10666
+ if (expected.parent) {
10667
+ parent = readSkillSessionSnapshotIfExists(expected.parent.sessionId, options);
10668
+ if (!sameSnapshot(parent, expected.parent) || !parent || !isDeepStrictEqual(parent.receipt.profile, receipt.profile))
10669
+ parentChanged();
10670
+ }
10671
+ const generation = nextSkillSessionGeneration(current?.generation ?? 0);
10672
+ const next = current ? { ...current.receipt, generation, loaded: [...new Set([...current.receipt.loaded, ...receipt.loaded])] } : { ...receipt, generation, ...expected.parent ? { parent: expected.parent } : {} };
10673
+ assertOwned();
10674
+ if (expected.parent) {
10675
+ parent = readSkillSessionSnapshotIfExists(expected.parent.sessionId, options);
10676
+ if (!sameSnapshot(parent, expected.parent))
10677
+ parentChanged();
10678
+ }
10679
+ writeSelectionJson(sessionReceiptPath(receipt.sessionId, options), next);
10680
+ });
10681
+ }
10682
+ function syncDirectory(path) {
10683
+ assertRegularPath(path);
10684
+ const fd = openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
10685
+ try {
10686
+ fsyncSync(fd);
10687
+ } finally {
10688
+ closeSync(fd);
10689
+ }
10690
+ }
10691
+ function archiveBytes(path, bytes) {
10692
+ assertRegularPath(path, true);
10693
+ const fd = openSync(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
10694
+ try {
10695
+ writeFileSync4(fd, bytes);
10696
+ fsyncSync(fd);
10697
+ } finally {
10698
+ closeSync(fd);
10555
10699
  }
10556
- return receipt;
10700
+ }
10701
+ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCommit = () => {}) {
10702
+ validateSkillSessionReceipt(replacement, replacement.sessionId);
10703
+ const bytes = Buffer.from(`${JSON.stringify(replacement)}
10704
+ `);
10705
+ if (bytes.length > MAX_PROFILE_DOCUMENT_BYTES)
10706
+ throw new SkillSelectionError("RECEIPT_TOO_LARGE", "The Skills selection receipt exceeds its size limit.");
10707
+ return withSessionWriteLock(replacement.sessionId, options, (assertOwned) => {
10708
+ const before = readSkillSessionSnapshot(replacement.sessionId, options);
10709
+ if (!sameSnapshot(before, expected))
10710
+ throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The Skills session receipt generation or bytes changed after review; inspect it and prepare a new plan.");
10711
+ if (replacement.generation !== nextSkillSessionGeneration(before.generation))
10712
+ throw new SkillSelectionError("SESSION_GENERATION_CHANGED", "The replacement Skills session must advance exactly one generation.");
10713
+ if (before.receipt.profile.authority !== replacement.profile.authority || before.receipt.profile.workspaceId !== replacement.profile.workspaceId) {
10714
+ throw new SkillSelectionError("PROFILE_IDENTITY_MISMATCH", "A session reconciliation cannot change its Skills authority or workspace.");
10715
+ }
10716
+ const directory = join10(selectionCacheRoot(options), "session-reconciliations", randomUUID());
10717
+ const archivePath = join10(directory, "original.json"), replacementPath = join10(directory, "replacement.json"), receiptPath = join10(directory, "receipt.json");
10718
+ const afterSha256 = sha256Hex(bytes);
10719
+ archiveBytes(archivePath, before.bytes);
10720
+ archiveBytes(replacementPath, bytes);
10721
+ const receipt = { schemaVersion: 1, status: "prepared", sessionId: replacement.sessionId, beforeGeneration: before.generation, afterGeneration: replacement.generation, beforeSha256: before.sha256, afterSha256, archivePath, replacementPath, plan };
10722
+ archiveBytes(receiptPath, Buffer.from(`${JSON.stringify(receipt)}
10723
+ `));
10724
+ syncDirectory(directory);
10725
+ syncDirectory(dirname4(directory));
10726
+ syncDirectory(selectionCacheRoot(options));
10727
+ if (sha256Hex(readRegularFile(archivePath, MAX_PROFILE_DOCUMENT_BYTES)) !== before.sha256 || !sameSnapshot(readSkillSessionSnapshot(replacement.sessionId, options), expected)) {
10728
+ throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The session or its preservation archive changed before replacement; retained evidence must be reviewed.");
10729
+ }
10730
+ beforeCommit();
10731
+ assertOwned();
10732
+ try {
10733
+ atomicWrite(before.path, bytes, 384, true);
10734
+ atomicWrite(receiptPath, Buffer.from(`${JSON.stringify({ ...receipt, status: "applied" })}
10735
+ `), 384, true);
10736
+ } catch {
10737
+ throw new SkillSelectionError("SESSION_RECONCILIATION_INCOMPLETE", `Inspect sessions show and the preserved operation receipt at ${receiptPath} before any retry; replacement may already have committed.`);
10738
+ }
10739
+ return { archivePath, receiptPath, beforeSha256: before.sha256, afterSha256 };
10740
+ });
10557
10741
  }
10558
10742
 
10559
10743
  // src/lib/agent-policy-limits.ts
10744
+ import { basename as basename3, isAbsolute as isAbsolute3, resolve as resolve4 } from "path";
10560
10745
  var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryPathLinks: 40, discoveryPathSteps: 256, discoveryPathSourceMetadataBytes: 64 * 1024, discoveryPathTotalMetadataBytes: 8 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
10561
10746
  function object(value) {
10562
10747
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -10622,7 +10807,11 @@ function assertAgentPolicyCollections(policy) {
10622
10807
  text(source.path);
10623
10808
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
10624
10809
  if (source.hashMode !== undefined)
10625
- requireBound(["bytes", "path-bytes", "claude-plugin-registry"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
10810
+ requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
10811
+ if (source.hashMode === "claude-settings-v1")
10812
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve4(source.path) === source.path && basename3(source.path) === "settings.json");
10813
+ if (source.hashMode === "claude-marketplace-registry")
10814
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve4(source.path) === source.path && basename3(source.path) === "known_marketplaces.json");
10626
10815
  if (source.hashMode === "claude-plugin-registry") {
10627
10816
  requireBound(agent === "claude");
10628
10817
  const managed = array(source.managedPlugins, 64);
@@ -10774,9 +10963,9 @@ function isOwnedCliBridge(path, expectedPaths) {
10774
10963
  }
10775
10964
 
10776
10965
  // src/lib/agent-discovery.ts
10777
- import { createHash as createHash11 } from "crypto";
10778
- import { lstatSync as lstatSync15, readFileSync as readFileSync13, readdirSync as readdirSync9, realpathSync as realpathSync4 } from "fs";
10779
- import { dirname as dirname12, isAbsolute as isAbsolute11, join as join23, resolve as resolve11, sep as sep5 } from "path";
10966
+ import { createHash as createHash13 } from "crypto";
10967
+ import { lstatSync as lstatSync17, readFileSync as readFileSync13, readdirSync as readdirSync9, realpathSync as realpathSync4 } from "fs";
10968
+ import { basename as basename8, dirname as dirname14, isAbsolute as isAbsolute14, join as join23, resolve as resolve14, sep as sep5 } from "path";
10780
10969
 
10781
10970
  // node_modules/yaml/dist/index.js
10782
10971
  var composer = require_composer();
@@ -10825,7 +11014,7 @@ var $visit = visit.visit;
10825
11014
  var $visitAsync = visit.visitAsync;
10826
11015
 
10827
11016
  // src/lib/agent-hermes.ts
10828
- import { dirname as dirname5, isAbsolute as isAbsolute3, join as join13, resolve as resolve4 } from "path";
11017
+ import { dirname as dirname5, isAbsolute as isAbsolute4, join as join13, resolve as resolve5 } from "path";
10829
11018
  import { lstatSync as lstatSync6, readFileSync as readFileSync9, opendirSync } from "fs";
10830
11019
  var HERMES_OPT_OUT = ".no-bundled-skills";
10831
11020
  var EVENTS = ["pre_llm_call", "pre_tool_call"];
@@ -10863,7 +11052,7 @@ function quoted(value) {
10863
11052
  return `'${value.replace(/'/g, `'\\''`)}'`;
10864
11053
  }
10865
11054
  function hermesHookDefinitions(supervisor) {
10866
- if (!isAbsolute3(supervisor.runtime) || !isAbsolute3(supervisor.path))
11055
+ if (!isAbsolute4(supervisor.runtime) || !isAbsolute4(supervisor.path))
10867
11056
  throw new Error("Expected absolute Hermes supervisor paths");
10868
11057
  const entry = (event) => ({ command: `${quoted(supervisor.runtime)} ${quoted(supervisor.path)} --event ${event}`, timeout: 15, ...event === "pre_tool_call" ? { fail_closed: true } : {} });
10869
11058
  return { pre_llm_call: entry("pre_llm_call"), pre_tool_call: entry("pre_tool_call") };
@@ -10899,14 +11088,14 @@ function assertHermesEnvironment(home) {
10899
11088
  throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
10900
11089
  }
10901
11090
  const selected = process.env.HERMES_HOME?.trim();
10902
- if (selected && (!isAbsolute3(selected) || resolve4(selected) !== join13(resolve4(home), ".hermes")))
11091
+ if (selected && (!isAbsolute4(selected) || resolve5(selected) !== join13(resolve5(home), ".hermes")))
10903
11092
  throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
10904
11093
  if (process.env.HERMES_ENABLE_PROJECT_PLUGINS?.trim() && !["0", "false", "no", "off"].includes(process.env.HERMES_ENABLE_PROJECT_PLUGINS.trim().toLowerCase()))
10905
11094
  throw new Error("NATIVE_SKILL_DRIFT: Hermes project plugins require a dedicated discovery review");
10906
11095
  }
10907
11096
  function assertNoHermesLegacyShadow(root) {
10908
11097
  let entries = 0;
10909
- for (let cursor = resolve4(root);; cursor = dirname5(cursor)) {
11098
+ for (let cursor = resolve5(root);; cursor = dirname5(cursor)) {
10910
11099
  if (lstatSync6(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
10911
11100
  throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes legacy discovery root");
10912
11101
  if (dirname5(cursor) === cursor)
@@ -10992,13 +11181,13 @@ if (failed) process.exitCode = 2;
10992
11181
  // src/lib/agent-discovery-directories.ts
10993
11182
  import { createHash as createHash5 } from "crypto";
10994
11183
  import { lstatSync as lstatSync7, opendirSync as opendirSync2 } from "fs";
10995
- import { dirname as dirname6, isAbsolute as isAbsolute4, join as join14, resolve as resolve5 } from "path";
11184
+ import { dirname as dirname6, isAbsolute as isAbsolute5, join as join14, resolve as resolve6 } from "path";
10996
11185
  function need(value, message) {
10997
11186
  if (!value)
10998
11187
  throw new Error(message);
10999
11188
  }
11000
11189
  function safe(path) {
11001
- need(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute4(path) && resolve5(path) === path && !path.includes("\x00"), "Expected a canonical absolute discovery directory path");
11190
+ need(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute5(path) && resolve6(path) === path && !path.includes("\x00"), "Expected a canonical absolute discovery directory path");
11002
11191
  for (let cursor = path;; cursor = dirname6(cursor)) {
11003
11192
  need(!lstatSync7(cursor, { throwIfNoEntry: false })?.isSymbolicLink(), "Refusing symlink native discovery directory");
11004
11193
  if (dirname6(cursor) === cursor)
@@ -11063,12 +11252,12 @@ function verifyDiscoveryDirectories(directories) {
11063
11252
  // src/lib/agent-discovery-bytes.ts
11064
11253
  import { createHash as createHash6 } from "crypto";
11065
11254
  import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync8, openSync as openSync3, readSync as readSync3 } from "fs";
11066
- import { dirname as dirname7, isAbsolute as isAbsolute5, resolve as resolve6 } from "path";
11255
+ import { dirname as dirname7, isAbsolute as isAbsolute6, resolve as resolve7 } from "path";
11067
11256
  function discoveryByteBudget() {
11068
11257
  return { remaining: AGENT_POLICY_LIMITS.discoveryRawTotalBytes };
11069
11258
  }
11070
11259
  function safe2(path) {
11071
- if (typeof path !== "string" || !isAbsolute5(path) || path !== resolve6(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
11260
+ if (typeof path !== "string" || !isAbsolute6(path) || path !== resolve7(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
11072
11261
  throw new Error("Expected a canonical absolute raw discovery path");
11073
11262
  for (let cursor = path;; cursor = dirname7(cursor)) {
11074
11263
  if (lstatSync8(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
@@ -11131,7 +11320,7 @@ function hashRawDiscoveryFile(path, budget, changes) {
11131
11320
  // src/lib/agent-discovery-path-bytes.ts
11132
11321
  import { createHash as createHash7 } from "crypto";
11133
11322
  import { lstatSync as lstatSync9, readlinkSync } from "fs";
11134
- import { dirname as dirname8, isAbsolute as isAbsolute6, join as join15, parse as parse2, resolve as resolve7, sep as sep3 } from "path";
11323
+ import { dirname as dirname8, isAbsolute as isAbsolute7, join as join15, parse as parse2, resolve as resolve8, sep as sep3 } from "path";
11135
11324
  function identity2(stat, mutable = false) {
11136
11325
  return {
11137
11326
  dev: String(stat.dev),
@@ -11143,7 +11332,7 @@ function identity2(stat, mutable = false) {
11143
11332
  };
11144
11333
  }
11145
11334
  function canonical(path) {
11146
- if (typeof path !== "string" || !isAbsolute6(path) || path !== resolve7(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00") || Buffer.from(path).toString("utf8") !== path)
11335
+ if (typeof path !== "string" || !isAbsolute7(path) || path !== resolve8(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00") || Buffer.from(path).toString("utf8") !== path)
11147
11336
  throw new Error("Expected a canonical absolute discovery path");
11148
11337
  }
11149
11338
  function trace(path) {
@@ -11192,11 +11381,11 @@ function trace(path) {
11192
11381
  if (!target || target.includes("\x00") || Buffer.byteLength(target) > AGENT_POLICY_LIMITS.pathCharacters || !after.isSymbolicLink() || JSON.stringify(identity2(stat, true)) !== JSON.stringify(identity2(after, true)))
11193
11382
  throw new Error("Discovery link changed during read");
11194
11383
  record2(candidate, { type: "link", target, ...identity2(stat, true) });
11195
- if (isAbsolute6(target)) {
11384
+ if (isAbsolute7(target)) {
11196
11385
  cursor = parse2(target).root;
11197
11386
  root();
11198
11387
  }
11199
- const remainder = isAbsolute6(target) ? target.slice(parse2(target).root.length) : target;
11388
+ const remainder = isAbsolute7(target) ? target.slice(parse2(target).root.length) : target;
11200
11389
  pending = [...remainder.split(sep3), ...pending];
11201
11390
  if (pending.length + steps > AGENT_POLICY_LIMITS.discoveryPathSteps)
11202
11391
  throw new Error("Discovery path step limit exceeded");
@@ -11244,15 +11433,15 @@ function hashDiscoveryPathFile(path, budget, changes) {
11244
11433
 
11245
11434
  // src/lib/plugin-discovery.ts
11246
11435
  import { closeSync as closeSync8, constants as constants7, fstatSync as fstatSync8, lstatSync as lstatSync14, openSync as openSync8, opendirSync as opendirSync4, readSync as readSync6 } from "fs";
11247
- import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute10, join as join21, resolve as resolve10 } from "path";
11436
+ import { basename as basename5, dirname as dirname11, isAbsolute as isAbsolute11, join as join21, resolve as resolve11 } from "path";
11248
11437
 
11249
11438
  // src/lib/plugin-admission.ts
11250
11439
  import { homedir as homedir2 } from "os";
11251
- import { isAbsolute as isAbsolute9, join as join20, resolve as resolve9 } from "path";
11440
+ import { isAbsolute as isAbsolute10, join as join20, resolve as resolve10 } from "path";
11252
11441
  // package.json
11253
11442
  var package_default = {
11254
11443
  name: "@hasna/skills",
11255
- version: "0.8.10",
11444
+ version: "0.8.12",
11256
11445
  description: "Skills library for AI coding agents",
11257
11446
  type: "module",
11258
11447
  bin: {
@@ -11314,10 +11503,11 @@ var package_default = {
11314
11503
  typecheck: "tsc --noEmit",
11315
11504
  "verify:release": "bun run scripts/release-guard.ts",
11316
11505
  "verify:consumer-types": "bun run scripts/consumer-types.ts",
11317
- prepare: "bun run build:js",
11318
- prepack: "bun run build && bun run verify:release && bun run verify:consumer-types",
11506
+ prepack: "bun run verify:generated && bun run verify:release && bun run verify:consumer-types",
11319
11507
  prepublishOnly: "bun run verify:producer && bun run typecheck && bun run test",
11320
- "verify:producer": "bun run scripts/release-dependencies.ts"
11508
+ "verify:producer": "bun run scripts/release-dependencies.ts",
11509
+ "verify:generated": "HASNA_SKILLS_RELEASE_ARTIFACT_TEST=1 bun test src/lib/release-artifacts.test.ts",
11510
+ "verify:consumer-credentials": "bun run scripts/consumer-credentials.ts"
11321
11511
  },
11322
11512
  keywords: [
11323
11513
  "skills",
@@ -11339,7 +11529,6 @@ var package_default = {
11339
11529
  license: "Apache-2.0",
11340
11530
  devDependencies: {
11341
11531
  "@hasna/contracts": "1.1.0",
11342
- "@hasna/secrets": "0.4.2",
11343
11532
  "@types/bun": "1.3.14",
11344
11533
  "@types/node": "25.2.3",
11345
11534
  "@types/react": "^18.2.0",
@@ -11352,6 +11541,7 @@ var package_default = {
11352
11541
  "@aws-sdk/client-ecs": "^3.1079.0",
11353
11542
  "@aws-sdk/client-s3": "^3.1079.0",
11354
11543
  "@hasna/events": "0.1.18",
11544
+ "@hasna/secrets": "0.4.2",
11355
11545
  "@modelcontextprotocol/sdk": "^1.26.0",
11356
11546
  chalk: "^5.3.0",
11357
11547
  commander: "^12.1.0",
@@ -11386,12 +11576,12 @@ import { spawnSync } from "child_process";
11386
11576
  import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
11387
11577
  import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
11388
11578
  import { hostname as osHostname } from "os";
11389
- import { isAbsolute as isAbsolute7, join as join22 } from "path";
11579
+ import { isAbsolute as isAbsolute8, join as join22 } from "path";
11390
11580
  import { createRequire } from "module";
11391
11581
  import { fileURLToPath as fileURLToPath2 } from "url";
11392
11582
  import { closeSync as closeSync4, fstatSync as fstatSync4, lstatSync as lstatSync10, openSync as openSync4, readFileSync as readFileSync10, realpathSync as realpathSync3, statSync as statSync7 } from "fs";
11393
11583
  import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
11394
- import { basename as basename3, dirname as dirname9, join as join16, relative as relative4, sep as sep4 } from "path";
11584
+ import { basename as basename4, dirname as dirname9, join as join16, relative as relative4, sep as sep4 } from "path";
11395
11585
  import { fileURLToPath, pathToFileURL } from "url";
11396
11586
  function envToken(name) {
11397
11587
  return name.toUpperCase().replace(/-/g, "_");
@@ -11499,7 +11689,7 @@ function entryPoint(directory) {
11499
11689
  function resolveInstalledSecrets(parentUrl) {
11500
11690
  let directory = dirname9(fileURLToPath(parentUrl));
11501
11691
  for (;; ) {
11502
- if (basename3(directory) !== "node_modules") {
11692
+ if (basename4(directory) !== "node_modules") {
11503
11693
  const candidate = join16(directory, "node_modules", PACKAGE);
11504
11694
  let absent = false;
11505
11695
  try {
@@ -11559,7 +11749,7 @@ function homeDir(env) {
11559
11749
  }
11560
11750
  function absoluteOverride(env, key) {
11561
11751
  const value = env[key]?.trim();
11562
- return value && isAbsolute7(value) ? value : null;
11752
+ return value && isAbsolute8(value) ? value : null;
11563
11753
  }
11564
11754
  function hasnaHomeDir(env) {
11565
11755
  const override = absoluteOverride(env, HASNA_HOME_ENV_KEY);
@@ -12338,10 +12528,16 @@ function isCredentialResolutionError(error) {
12338
12528
  function isSkillsFleetCredentialError(error) {
12339
12529
  return error instanceof SkillsFleetCredentialError || typeof error === "object" && error !== null && error.name === "SkillsFleetCredentialError";
12340
12530
  }
12341
- function asSkillsFleetCredentialError(error) {
12531
+ var REDACTED_VAULT_REFERENCE = "[redacted vault reference]";
12532
+ function redactVaultReference(message, vaultReference) {
12533
+ if (!vaultReference)
12534
+ return message;
12535
+ return message.split(vaultReference).join(REDACTED_VAULT_REFERENCE);
12536
+ }
12537
+ function asSkillsFleetCredentialError(error, vaultReference) {
12342
12538
  if (!isCredentialResolutionError(error))
12343
12539
  return null;
12344
- return new SkillsFleetCredentialError(error.message, "MISSING_API_CREDENTIAL");
12540
+ return new SkillsFleetCredentialError(redactVaultReference(error.message, vaultReference), "MISSING_API_CREDENTIAL");
12345
12541
  }
12346
12542
  function normalizeSkillsApiOrigin(apiUrl) {
12347
12543
  const url = new URL(apiUrl);
@@ -12526,7 +12722,7 @@ async function resolveSkillsConnection(env = process.env, options = {}) {
12526
12722
  try {
12527
12723
  completed = await completePointerCredential(SKILLS_APP, pointer, env);
12528
12724
  } catch (error) {
12529
- const translated = asSkillsFleetCredentialError(error);
12725
+ const translated = asSkillsFleetCredentialError(error, pointer.pointerVaultKey);
12530
12726
  if (translated)
12531
12727
  throw translated;
12532
12728
  throw error;
@@ -13407,11 +13603,18 @@ async function syncSelectionProfile(profileId, options = {}) {
13407
13603
  return { profile, receipt, changed, downloaded, stationReported };
13408
13604
  }
13409
13605
  async function resolveSelectionContext(profileId, options = {}) {
13410
- const session = options.sessionId ? readSkillSession(options.sessionId, options) : null;
13411
- const parentSession = !session && options.parentSessionId ? readSkillSession(options.parentSessionId, options) : null;
13606
+ const sessionState = options.sessionId ? readSkillSessionSnapshotIfExists(options.sessionId, options) : null;
13607
+ const session = sessionState?.receipt ?? null;
13608
+ const parentState = !session && options.parentSessionId ? readSkillSessionSnapshotIfExists(options.parentSessionId, options) : null;
13609
+ if (!session && options.parentSessionId && !parentState) {
13610
+ throw new SkillSelectionError("SESSION_PARENT_NOT_FOUND", "A child Skills session requires an existing parent receipt so its inherited pin can be bound exactly.");
13611
+ }
13612
+ const parentSession = parentState?.receipt ?? null;
13613
+ const sessionSnapshot = sessionState ? skillSessionSnapshotBinding(sessionState) : null;
13614
+ const parentSnapshot = parentState ? skillSessionSnapshotBinding(parentState) : undefined;
13412
13615
  const project = options.projectDir ? readProjectSelection(options.projectDir) : null;
13413
13616
  if (session && session.profile.profileId !== profileId || parentSession && parentSession.profile.profileId !== profileId || project && project.profile.profileId !== profileId) {
13414
- throw new SkillSelectionError("PROFILE_LOCK_MISMATCH", "The session or project is pinned to a different Skills profile. Select that profile or explicitly sync the new selection.");
13617
+ throw new SkillSelectionError("PROFILE_LOCK_MISMATCH", "The session or project is pinned to a different Skills profile. Select the pinned profile, or review skills sessions reconcile for an intentional session change and explicit project sync for a project change.");
13415
13618
  }
13416
13619
  if (options.cached) {
13417
13620
  const receipt2 = session ?? parentSession ?? project ?? readSelectionProfile(profileId, options);
@@ -13419,7 +13622,7 @@ async function resolveSelectionContext(profileId, options = {}) {
13419
13622
  throw new SkillSelectionError("CACHED_PROFILE_MISSING", "No verified Skills profile is cached; authenticate and sync it first.");
13420
13623
  assertFreshCachedProfile(receipt2, options);
13421
13624
  assertBinding(receipt2.profile, options);
13422
- return { receipt: receipt2, session, inheritedLoaded: parentSession?.loaded };
13625
+ return { receipt: receipt2, session, sessionSnapshot, parentSnapshot, inheritedLoaded: parentSession?.loaded };
13423
13626
  }
13424
13627
  const client = options.client ?? await createProfileClient();
13425
13628
  const current = await client.resolveProfile(profileId);
@@ -13432,7 +13635,7 @@ async function resolveSelectionContext(profileId, options = {}) {
13432
13635
  throw new SkillSelectionError("PROFILE_IDENTITY_MISMATCH", "The pinned selection belongs to another Skills authority or workspace.");
13433
13636
  }
13434
13637
  const receipt = locked ?? { schemaVersion: 1, verifiedAt: new Date((options.now ?? Date.now)()).toISOString(), profile: current };
13435
- return { receipt, session, client, inheritedLoaded: parentSession?.loaded };
13638
+ return { receipt, session, sessionSnapshot, parentSnapshot, client, inheritedLoaded: parentSession?.loaded };
13436
13639
  }
13437
13640
  function assertBinding(profile, options) {
13438
13641
  if (options.authority !== undefined && profile.authority !== options.authority || options.workspaceId !== undefined && profile.workspaceId !== options.workspaceId) {
@@ -13661,8 +13864,8 @@ function buildPluginProjection(entries) {
13661
13864
 
13662
13865
  // src/lib/plugin-projection-store.ts
13663
13866
  import { createHash as createHash10, randomUUID as randomUUID2 } from "crypto";
13664
- import { constants as constants6, closeSync as closeSync7, fchmodSync, fstatSync as fstatSync7, fsyncSync, linkSync, lstatSync as lstatSync13, mkdirSync as mkdirSync6, openSync as openSync7, opendirSync as opendirSync3, readSync as readSync5, renameSync as renameSync3, rmdirSync as rmdirSync2, rmSync as rmSync2, unlinkSync as unlinkSync3, writeSync as writeSync2 } from "fs";
13665
- import { dirname as dirname10, isAbsolute as isAbsolute8, join as join19, parse as parse4, resolve as resolve8 } from "path";
13867
+ import { constants as constants6, closeSync as closeSync7, fchmodSync, fstatSync as fstatSync7, fsyncSync as fsyncSync2, linkSync, lstatSync as lstatSync13, mkdirSync as mkdirSync6, openSync as openSync7, opendirSync as opendirSync3, readSync as readSync5, renameSync as renameSync3, rmdirSync as rmdirSync2, rmSync as rmSync2, unlinkSync as unlinkSync3, writeSync as writeSync2 } from "fs";
13868
+ import { dirname as dirname10, isAbsolute as isAbsolute9, join as join19, parse as parse4, resolve as resolve9 } from "path";
13666
13869
  var READ_FLAGS = constants6.O_RDONLY | constants6.O_NOFOLLOW | constants6.O_NONBLOCK;
13667
13870
  var WRITE_FLAGS = constants6.O_WRONLY | constants6.O_CREAT | constants6.O_EXCL | constants6.O_NOFOLLOW | constants6.O_NONBLOCK;
13668
13871
  var uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined;
@@ -13677,7 +13880,7 @@ function stat(path) {
13677
13880
  }
13678
13881
  }
13679
13882
  function canonical2(path) {
13680
- pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute8(path) && resolve8(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
13883
+ 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");
13681
13884
  }
13682
13885
  function owner(stats, system = false) {
13683
13886
  const current = uid();
@@ -13886,7 +14089,7 @@ function writeExclusive(path, bytes, mode) {
13886
14089
  pluginNeed(count > 0, "Plugin file write did not progress");
13887
14090
  offset += count;
13888
14091
  }
13889
- fsyncSync(fd);
14092
+ fsyncSync2(fd);
13890
14093
  } catch (error) {
13891
14094
  if (created) {
13892
14095
  same3(created, stat(path), true);
@@ -14060,7 +14263,7 @@ function validatePluginTarget(value) {
14060
14263
  for (const registration of value.registrations) {
14061
14264
  pluginKeys(registration, ["scope", "projectPath"]);
14062
14265
  pluginNeed(registration.scope === "user" || registration.scope === "project", "Unsupported plugin registration scope");
14063
- pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" && isAbsolute9(registration.projectPath) && resolve9(registration.projectPath) === registration.projectPath, "Plugin scope requires its exact project path");
14266
+ pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" && isAbsolute10(registration.projectPath) && resolve10(registration.projectPath) === registration.projectPath, "Plugin scope requires its exact project path");
14064
14267
  const key = JSON.stringify([registration.scope, registration.projectPath]);
14065
14268
  pluginNeed(!scopes.has(key), "Duplicate plugin registration scope");
14066
14269
  scopes.add(key);
@@ -14068,7 +14271,7 @@ function validatePluginTarget(value) {
14068
14271
  for (const key of ["native", "resolver"]) {
14069
14272
  pluginKeys(value[key], key === "native" ? ["version", "executable", "digest"] : ["executable", "digest"]);
14070
14273
  pluginText(value[key].executable);
14071
- pluginNeed(isAbsolute9(value[key].executable) && resolve9(value[key].executable) === value[key].executable, "Plugin executable must have a canonical absolute path");
14274
+ pluginNeed(isAbsolute10(value[key].executable) && resolve10(value[key].executable) === value[key].executable, "Plugin executable must have a canonical absolute path");
14072
14275
  pluginDigest(value[key].digest);
14073
14276
  }
14074
14277
  pluginNeed(["2.1.274", "2.1.276"].includes(value.native.version), "Plugin admission requires a certified Claude command-source runtime (2.1.274 or 2.1.276)");
@@ -14220,7 +14423,7 @@ function pluginResolverCommand(binding) {
14220
14423
  }
14221
14424
  function root(options) {
14222
14425
  const path = options.storeRoot ?? pluginAdmissionRoot();
14223
- pluginNeed(isAbsolute9(path) && resolve9(path) === path, "Invalid plugin admission store root");
14426
+ pluginNeed(isAbsolute10(path) && resolve10(path) === path, "Invalid plugin admission store root");
14224
14427
  return path;
14225
14428
  }
14226
14429
  function pluginBindingPath(storeRoot, id) {
@@ -14425,12 +14628,12 @@ function validateManagedPluginWitnesses(value) {
14425
14628
  pluginNeed(typeof item.bindingId === "string" && /^[a-f0-9]{64}$/.test(item.bindingId) && !seen.has(item.bindingId), "Invalid or duplicate managed plugin witness");
14426
14629
  seen.add(item.bindingId);
14427
14630
  pluginText(item.storeRoot);
14428
- pluginNeed(isAbsolute10(item.storeRoot) && resolve10(item.storeRoot) === item.storeRoot, "Invalid plugin receipt store path");
14631
+ pluginNeed(isAbsolute11(item.storeRoot) && resolve11(item.storeRoot) === item.storeRoot, "Invalid plugin receipt store path");
14429
14632
  }
14430
14633
  }
14431
14634
  function safe3(path) {
14432
14635
  pluginText(path);
14433
- pluginNeed(isAbsolute10(path) && resolve10(path) === path, "Invalid plugin registry path");
14636
+ pluginNeed(isAbsolute11(path) && resolve11(path) === path, "Invalid plugin registry path");
14434
14637
  for (let at = path;; at = dirname11(at)) {
14435
14638
  pluginNeed(!lstatSync14(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
14436
14639
  if (at === dirname11(at))
@@ -14499,7 +14702,7 @@ function hashManagedPluginRegistry(path, managed) {
14499
14702
  pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join21(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
14500
14703
  const approval = (producer) => {
14501
14704
  pluginText(producer);
14502
- const digest = `sha256:${basename4(producer)}`;
14705
+ const digest = `sha256:${basename5(producer)}`;
14503
14706
  const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest);
14504
14707
  pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
14505
14708
  if (!verified.has(producer)) {
@@ -14560,8 +14763,398 @@ function captureManagedPluginRegistry(path, managedPlugins) {
14560
14763
  return { path, hashMode: "claude-plugin-registry", managedPlugins, sha256: hashManagedPluginRegistry(path, managedPlugins) };
14561
14764
  }
14562
14765
 
14766
+ // src/lib/claude-marketplace-registry.ts
14767
+ import { createHash as createHash11 } from "crypto";
14768
+ import { closeSync as closeSync9, constants as constants8, fstatSync as fstatSync9, lstatSync as lstatSync15, openSync as openSync9, readSync as readSync7 } from "fs";
14769
+ import { basename as basename6, dirname as dirname12, isAbsolute as isAbsolute12, resolve as resolve12 } from "path";
14770
+ var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
14771
+ function need2(value, reason) {
14772
+ if (!value)
14773
+ throw new Error(`Claude marketplace registry ${reason}`);
14774
+ }
14775
+ function absolutePath(value) {
14776
+ need2(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");
14777
+ }
14778
+ var sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
14779
+ function parents(path) {
14780
+ const result = [];
14781
+ for (let at = dirname12(path);; at = dirname12(at)) {
14782
+ const stat2 = lstatSync15(at, { bigint: true });
14783
+ need2(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
14784
+ result.push([at, stat2]);
14785
+ if (at === dirname12(at))
14786
+ return result;
14787
+ }
14788
+ }
14789
+ function readRegistry2(path, budget) {
14790
+ absolutePath(path);
14791
+ need2(basename6(path) === "known_marketplaces.json", "requires known_marketplaces.json");
14792
+ need2(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
14793
+ const ancestors2 = parents(path), initial = lstatSync15(path, { bigint: true });
14794
+ need2(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
14795
+ need2(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
14796
+ const fd = openSync9(path, constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK);
14797
+ try {
14798
+ const opened = fstatSync9(fd, { bigint: true });
14799
+ need2(opened.isFile() && sameFile(initial, opened), "changed during open");
14800
+ const bytes = Buffer.alloc(Number(opened.size) + 1);
14801
+ let length = 0;
14802
+ while (length < bytes.length) {
14803
+ const count = readSync7(fd, bytes, length, bytes.length - length, null);
14804
+ if (!count)
14805
+ break;
14806
+ length += count;
14807
+ }
14808
+ const after = fstatSync9(fd, { bigint: true }), current = lstatSync15(path, { bigint: true });
14809
+ need2(current.isFile() && sameFile(opened, after) && sameFile(after, current) && BigInt(length) === opened.size, "changed during read");
14810
+ for (const [ancestor, before] of ancestors2) {
14811
+ const now = lstatSync15(ancestor, { bigint: true });
14812
+ need2(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
14813
+ }
14814
+ budget.remaining -= length;
14815
+ try {
14816
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
14817
+ } catch {
14818
+ throw new Error("Claude marketplace registry is not strict UTF-8");
14819
+ }
14820
+ } finally {
14821
+ closeSync9(fd);
14822
+ }
14823
+ }
14824
+ function parse5(text2) {
14825
+ let at = 0, nodes = 0;
14826
+ const numberPattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
14827
+ const space = () => {
14828
+ while (at < text2.length && /[\x20\t\r\n]/.test(text2[at]))
14829
+ at++;
14830
+ };
14831
+ function string() {
14832
+ need2(text2[at] === '"', "contains invalid JSON");
14833
+ const start = at++;
14834
+ while (at < text2.length) {
14835
+ const character = text2[at++];
14836
+ if (character === '"') {
14837
+ let value2;
14838
+ try {
14839
+ value2 = JSON.parse(text2.slice(start, at));
14840
+ } catch {
14841
+ throw new Error("Claude marketplace registry contains invalid JSON string");
14842
+ }
14843
+ need2(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
14844
+ return value2;
14845
+ }
14846
+ if (character === "\\")
14847
+ at++;
14848
+ }
14849
+ throw new Error("Claude marketplace registry contains an unterminated JSON string");
14850
+ }
14851
+ function value(depth) {
14852
+ need2(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
14853
+ space();
14854
+ const character = text2[at];
14855
+ if (character === '"')
14856
+ return { kind: "string", value: string() };
14857
+ if (character === "{" || character === "[") {
14858
+ at++;
14859
+ space();
14860
+ const object4 = character === "{", end = object4 ? "}" : "]", entries = [], items = [], keys = new Set;
14861
+ if (text2[at] !== end)
14862
+ while (true) {
14863
+ if (object4) {
14864
+ space();
14865
+ const key = string();
14866
+ need2(!keys.has(key), "contains a duplicate JSON key");
14867
+ keys.add(key);
14868
+ space();
14869
+ need2(text2[at++] === ":", "contains invalid JSON");
14870
+ entries.push([key, value(depth + 1)]);
14871
+ } else
14872
+ items.push(value(depth + 1));
14873
+ space();
14874
+ if (text2[at] === end)
14875
+ break;
14876
+ need2(text2[at++] === ",", "contains invalid JSON");
14877
+ }
14878
+ at++;
14879
+ return object4 ? { kind: "object", entries } : { kind: "array", items };
14880
+ }
14881
+ for (const literal of ["true", "false", "null"])
14882
+ if (text2.startsWith(literal, at)) {
14883
+ at += literal.length;
14884
+ return { kind: "literal", value: literal };
14885
+ }
14886
+ numberPattern.lastIndex = at;
14887
+ const number = numberPattern.exec(text2);
14888
+ need2(number, "contains invalid JSON");
14889
+ at += number[0].length;
14890
+ return { kind: "number", value: number[0] };
14891
+ }
14892
+ const root2 = value(0);
14893
+ space();
14894
+ need2(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
14895
+ need2(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
14896
+ return root2;
14897
+ }
14898
+ function exactKeys(value, keys) {
14899
+ return value?.kind === "object" && value.entries.length === keys.length && value.entries.every(([key]) => keys.includes(key));
14900
+ }
14901
+ var field = (object4, key) => object4.entries.find(([name]) => name === key)?.[1];
14902
+ function textField(value) {
14903
+ need2(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
14904
+ return value.value;
14905
+ }
14906
+ function normalizeRow(row) {
14907
+ if (!exactKeys(row, ["source", "installLocation", "lastUpdated"]))
14908
+ return;
14909
+ const source = field(row, "source");
14910
+ if (source?.kind !== "object")
14911
+ return;
14912
+ const type = field(source, "source");
14913
+ if (type?.kind !== "string" || !["github", "directory"].includes(type.value))
14914
+ return;
14915
+ const location = type.value === "github" ? "repo" : "path";
14916
+ if (!exactKeys(source, ["source", location]))
14917
+ return;
14918
+ const sourceLocation = textField(field(source, location));
14919
+ if (type.value === "directory")
14920
+ absolutePath(sourceLocation);
14921
+ absolutePath(textField(field(row, "installLocation")));
14922
+ const updated = textField(field(row, "lastUpdated"));
14923
+ need2(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
14924
+ const millis = Date.parse(updated);
14925
+ need2(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
14926
+ row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
14927
+ }
14928
+ function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
14929
+ const value = parse5(readRegistry2(path, budget));
14930
+ for (const [, row] of value.entries)
14931
+ normalizeRow(row);
14932
+ const sha2562 = createHash11("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
14933
+ return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
14934
+ }
14935
+
14936
+ // src/lib/claude-settings-witness.ts
14937
+ import { createHash as createHash12 } from "crypto";
14938
+ import { closeSync as closeSync10, constants as constants9, fstatSync as fstatSync10, lstatSync as lstatSync16, openSync as openSync10, readSync as readSync8 } from "fs";
14939
+ import { basename as basename7, dirname as dirname13, isAbsolute as isAbsolute13, resolve as resolve13 } from "path";
14940
+ var CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
14941
+ function need3(value, reason) {
14942
+ if (!value)
14943
+ throw new Error(`Claude settings witness ${reason}`);
14944
+ }
14945
+ function absolutePath2(value) {
14946
+ need3(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute13(value) && resolve13(value) === value, "requires a normalized absolute path");
14947
+ }
14948
+ var sameFile2 = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
14949
+ function parents2(path) {
14950
+ const result = [];
14951
+ for (let at = dirname13(path);; at = dirname13(at)) {
14952
+ const stat2 = lstatSync16(at, { bigint: true });
14953
+ need3(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
14954
+ result.push([at, stat2]);
14955
+ if (at === dirname13(at))
14956
+ return result;
14957
+ }
14958
+ }
14959
+ function readSettings(path, budget) {
14960
+ absolutePath2(path);
14961
+ need3(basename7(path) === "settings.json", "requires settings.json");
14962
+ need3(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
14963
+ const ancestors2 = parents2(path), initial = lstatSync16(path, { bigint: true });
14964
+ need3(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_SETTINGS_WITNESS_LIMITS.bytes), "is not a bounded regular file");
14965
+ need3(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
14966
+ const fd = openSync10(path, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
14967
+ try {
14968
+ const opened = fstatSync10(fd, { bigint: true });
14969
+ need3(opened.isFile() && sameFile2(initial, opened), "changed during open");
14970
+ const bytes = Buffer.alloc(Number(opened.size) + 1);
14971
+ let length = 0;
14972
+ while (length < bytes.length) {
14973
+ const count = readSync8(fd, bytes, length, bytes.length - length, null);
14974
+ if (!count)
14975
+ break;
14976
+ length += count;
14977
+ }
14978
+ const after = fstatSync10(fd, { bigint: true }), current = lstatSync16(path, { bigint: true });
14979
+ need3(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
14980
+ for (const [ancestor, before] of ancestors2) {
14981
+ const now = lstatSync16(ancestor, { bigint: true });
14982
+ need3(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
14983
+ }
14984
+ budget.remaining -= length;
14985
+ try {
14986
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes.subarray(0, length));
14987
+ } catch {
14988
+ throw new Error("Claude settings witness is not strict UTF-8");
14989
+ }
14990
+ } finally {
14991
+ closeSync10(fd);
14992
+ }
14993
+ }
14994
+ function parse6(text2) {
14995
+ let at = 0, nodes = 0;
14996
+ const numberPattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
14997
+ const space = () => {
14998
+ while (at < text2.length && /[\x20\t\r\n]/.test(text2[at]))
14999
+ at++;
15000
+ };
15001
+ function string() {
15002
+ need3(text2[at] === '"', "contains invalid JSON");
15003
+ const start = at++;
15004
+ while (at < text2.length) {
15005
+ const character = text2[at++];
15006
+ if (character === '"') {
15007
+ let value2;
15008
+ try {
15009
+ value2 = JSON.parse(text2.slice(start, at));
15010
+ } catch {
15011
+ throw new Error("Claude settings witness contains invalid JSON string");
15012
+ }
15013
+ need3(value2.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.stringCharacters, "exceeds its string limit");
15014
+ return value2;
15015
+ }
15016
+ if (character === "\\")
15017
+ at++;
15018
+ }
15019
+ throw new Error("Claude settings witness contains an unterminated JSON string");
15020
+ }
15021
+ function value(depth) {
15022
+ need3(depth <= CLAUDE_SETTINGS_WITNESS_LIMITS.depth && ++nodes <= CLAUDE_SETTINGS_WITNESS_LIMITS.nodes, "exceeds its nesting or node limit");
15023
+ space();
15024
+ const character = text2[at];
15025
+ if (character === '"')
15026
+ return { kind: "string", value: string() };
15027
+ if (character === "{" || character === "[") {
15028
+ at++;
15029
+ space();
15030
+ const object4 = character === "{", end = object4 ? "}" : "]", entries = [], items = [], keys = new Set;
15031
+ if (text2[at] !== end)
15032
+ while (true) {
15033
+ if (object4) {
15034
+ space();
15035
+ const key = string();
15036
+ need3(!keys.has(key), "contains a duplicate JSON key");
15037
+ keys.add(key);
15038
+ space();
15039
+ need3(text2[at++] === ":", "contains invalid JSON");
15040
+ entries.push([key, value(depth + 1)]);
15041
+ } else
15042
+ items.push(value(depth + 1));
15043
+ space();
15044
+ if (text2[at] === end)
15045
+ break;
15046
+ need3(text2[at++] === ",", "contains invalid JSON");
15047
+ }
15048
+ at++;
15049
+ return object4 ? { kind: "object", entries } : { kind: "array", items };
15050
+ }
15051
+ for (const literal of ["true", "false", "null"])
15052
+ if (text2.startsWith(literal, at)) {
15053
+ at += literal.length;
15054
+ return { kind: "literal", value: literal };
15055
+ }
15056
+ numberPattern.lastIndex = at;
15057
+ const number = numberPattern.exec(text2);
15058
+ need3(number, "contains invalid JSON");
15059
+ at += number[0].length;
15060
+ return { kind: "number", value: number[0] };
15061
+ }
15062
+ const root2 = value(0);
15063
+ space();
15064
+ need3(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
15065
+ need3(root2.entries.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.rows, "exceeds its settings field limit");
15066
+ return root2;
15067
+ }
15068
+ var BOOLEAN_PREFERENCES = new Set([
15069
+ "autoScrollEnabled",
15070
+ "axScreenReader",
15071
+ "emojiCompletionEnabled",
15072
+ "prefersReducedMotion",
15073
+ "showTurnDuration",
15074
+ "spinnerTipsEnabled",
15075
+ "syntaxHighlightingDisabled",
15076
+ "terminalProgressBarEnabled",
15077
+ "terminalTitleFromRename",
15078
+ "verbose",
15079
+ "wheelScrollAccelerationEnabled"
15080
+ ]);
15081
+ var ENUM_PREFERENCES = Object.freeze({
15082
+ editorMode: ["normal", "vim"],
15083
+ tui: ["default", "fullscreen"],
15084
+ viewMode: ["default", "verbose", "focus"]
15085
+ });
15086
+ var BUILTIN_MODEL_SELECTIONS = new Set([
15087
+ "default",
15088
+ "best",
15089
+ "fable",
15090
+ "fable[1m]",
15091
+ "sonnet",
15092
+ "sonnet[1m]",
15093
+ "opus",
15094
+ "opus[1m]",
15095
+ "haiku",
15096
+ "opusplan",
15097
+ "claude-fable-5-1",
15098
+ "claude-fable-5",
15099
+ "claude-fable-5[1m]",
15100
+ "claude-opus-5",
15101
+ "claude-sonnet-5",
15102
+ "claude-haiku-4-5-20251001",
15103
+ "claude-opus-4-6",
15104
+ "claude-sonnet-4-5",
15105
+ "claude-sonnet-4-5-20250929",
15106
+ "claude-opus-4-8",
15107
+ "claude-opus-4-8[1m]",
15108
+ "claude-opus-4-7",
15109
+ "claude-sonnet-4-6",
15110
+ "claude-opus-4-5-20251101",
15111
+ "claude-opus-4-5",
15112
+ "claude-haiku-4-5",
15113
+ "claude-fable-5-1[1m]",
15114
+ "claude-opus-5[1m]",
15115
+ "claude-opus-4-7[1m]",
15116
+ "claude-opus-4-6[1m]",
15117
+ "claude-sonnet-4-6[1m]"
15118
+ ]);
15119
+ function canonical3(value) {
15120
+ if (value.kind === "object")
15121
+ return { kind: "object", entries: value.entries.map(([key, child]) => [key, canonical3(child)]).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0) };
15122
+ if (value.kind === "array")
15123
+ return { kind: "array", items: value.items.map(canonical3) };
15124
+ return value;
15125
+ }
15126
+ function settingsDigest(text2) {
15127
+ const value = parse6(text2);
15128
+ value.entries = value.entries.filter(([key, child]) => {
15129
+ if (key === "model") {
15130
+ need3(child.kind === "string", "contains an invalid model selection");
15131
+ return !BUILTIN_MODEL_SELECTIONS.has(child.value);
15132
+ }
15133
+ if (BOOLEAN_PREFERENCES.has(key)) {
15134
+ need3(child.kind === "literal" && (child.value === "true" || child.value === "false"), "contains an invalid display preference");
15135
+ return false;
15136
+ }
15137
+ if (Object.hasOwn(ENUM_PREFERENCES, key)) {
15138
+ need3(child.kind === "string" && ENUM_PREFERENCES[key].includes(child.value), "contains an invalid display preference");
15139
+ return false;
15140
+ }
15141
+ return true;
15142
+ });
15143
+ return createHash12("sha256").update("hasna.skills.claude-settings.v1\x00").update(JSON.stringify(canonical3(value))).digest("hex");
15144
+ }
15145
+ function hashClaudeSettingsReplacement(text2, budget) {
15146
+ need3(typeof text2 === "string", "requires settings text");
15147
+ const bytes = Buffer.byteLength(text2);
15148
+ need3(bytes <= CLAUDE_SETTINGS_WITNESS_LIMITS.bytes && Number.isSafeInteger(budget.remaining) && budget.remaining >= bytes, "exceeds its byte limit");
15149
+ budget.remaining -= bytes;
15150
+ return settingsDigest(text2);
15151
+ }
15152
+ function captureClaudeSettings(path, budget = { remaining: 256 * 1024 * 1024 }) {
15153
+ return { path, hashMode: "claude-settings-v1", sha256: settingsDigest(readSettings(path, budget)) };
15154
+ }
15155
+
14563
15156
  // src/lib/agent-discovery.ts
14564
- var digest = (text2) => createHash11("sha256").update(text2).digest("hex");
15157
+ var digest = (text2) => createHash13("sha256").update(text2).digest("hex");
14565
15158
  function parseConfig(text2, path, toml = false) {
14566
15159
  try {
14567
15160
  const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
@@ -14573,12 +15166,12 @@ function parseConfig(text2, path, toml = false) {
14573
15166
  }
14574
15167
  }
14575
15168
  function safe4(path) {
14576
- if (!isAbsolute11(path) || path.includes("\x00"))
15169
+ if (!isAbsolute14(path) || path.includes("\x00"))
14577
15170
  throw new Error("Expected an absolute native discovery path");
14578
- for (let cursor = path;; cursor = dirname12(cursor)) {
14579
- if (lstatSync15(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
15171
+ for (let cursor = path;; cursor = dirname14(cursor)) {
15172
+ if (lstatSync17(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
14580
15173
  throw new Error(`Refusing symlink native discovery input: ${cursor}`);
14581
- if (dirname12(cursor) === cursor)
15174
+ if (dirname14(cursor) === cursor)
14582
15175
  break;
14583
15176
  }
14584
15177
  }
@@ -14586,7 +15179,7 @@ function read(path, changes) {
14586
15179
  safe4(path);
14587
15180
  if (changes?.has(path))
14588
15181
  return changes.get(path);
14589
- const stat2 = lstatSync15(path, { throwIfNoEntry: false });
15182
+ const stat2 = lstatSync17(path, { throwIfNoEntry: false });
14590
15183
  if (!stat2)
14591
15184
  return null;
14592
15185
  if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
@@ -14594,6 +15187,22 @@ function read(path, changes) {
14594
15187
  return readFileSync13(path, "utf8");
14595
15188
  }
14596
15189
  function projected(source, changes, budget = discoveryByteBudget()) {
15190
+ if (source.hashMode === "claude-settings-v1") {
15191
+ if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
15192
+ throw new Error("Claude settings witnesses require exact typed metadata");
15193
+ const current = captureClaudeSettings(source.path, budget).sha256;
15194
+ if (current !== source.sha256)
15195
+ throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
15196
+ return changes?.has(source.path) ? hashClaudeSettingsReplacement(changes.get(source.path), budget) : current;
15197
+ }
15198
+ if (source.hashMode === "claude-marketplace-registry") {
15199
+ if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256) || changes?.has(source.path))
15200
+ throw new Error("Claude marketplace witnesses require an exact reviewed registry without synthetic changes");
15201
+ const sha2562 = captureClaudeMarketplaceRegistry(source.path, budget).sha256;
15202
+ if (sha2562 !== source.sha256)
15203
+ throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
15204
+ return sha2562;
15205
+ }
14597
15206
  if (source.hashMode === "claude-plugin-registry") {
14598
15207
  if (source.format !== undefined || source.fields !== undefined || !source.managedPlugins || changes?.has(source.path))
14599
15208
  throw new Error("Managed plugin registry witnesses require verified native registrations");
@@ -14618,7 +15227,7 @@ function projected(source, changes, budget = discoveryByteBudget()) {
14618
15227
  const object4 = source.format === "yaml" ? parseHermesConfig(text2) : parseConfig(text2, source.path, source.format === "toml");
14619
15228
  if (!object4 || typeof object4 !== "object" || Array.isArray(object4))
14620
15229
  throw new Error("Expected native discovery configuration object");
14621
- return digest(JSON.stringify(Object.fromEntries((source.fields ?? []).map((field) => [field, object4[field] ?? null]))));
15230
+ return digest(JSON.stringify(Object.fromEntries((source.fields ?? []).map((field2) => [field2, object4[field2] ?? null]))));
14622
15231
  }
14623
15232
  function verifyAgentDiscovery(binding) {
14624
15233
  if (!binding || !Array.isArray(binding.sources) || !Array.isArray(binding.roots) || binding.sources.length > AGENT_POLICY_LIMITS.discoverySources || binding.roots.length > AGENT_POLICY_LIMITS.discoveryRoots)
@@ -14629,9 +15238,10 @@ function verifyAgentDiscovery(binding) {
14629
15238
  verifyDiscoveryDirectories(binding.directories);
14630
15239
  const budget = discoveryByteBudget();
14631
15240
  for (const source of binding.sources) {
15241
+ assertMarketplaceBinding(binding, source);
14632
15242
  if (source.hashMode === "claude-plugin-registry" && binding.agent !== "claude")
14633
15243
  throw new Error("Managed Claude registry witnesses cannot apply to another agent");
14634
- if (source.format !== undefined && (!["json", "toml", "yaml"].includes(source.format) || !Array.isArray(source.fields) || !source.fields.length || source.fields.length > 64 || source.fields.some((field) => typeof field !== "string" || !field)))
15244
+ if (source.format !== undefined && (!["json", "toml", "yaml"].includes(source.format) || !Array.isArray(source.fields) || !source.fields.length || source.fields.length > 64 || source.fields.some((field2) => typeof field2 !== "string" || !field2)))
14635
15245
  throw new Error("Invalid native discovery projection");
14636
15246
  if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
14637
15247
  throw new Error("Invalid native discovery digest");
@@ -14641,9 +15251,18 @@ function verifyAgentDiscovery(binding) {
14641
15251
  for (const root2 of binding.roots)
14642
15252
  safe4(root2);
14643
15253
  }
15254
+ function assertMarketplaceBinding(binding, source) {
15255
+ if (source.hashMode === "claude-settings-v1" && (binding.agent !== "claude" || binding.method !== "reviewed" || basename8(source.path) !== "settings.json"))
15256
+ throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
15257
+ if (source.hashMode === "claude-marketplace-registry" && (binding.agent !== "claude" || binding.method !== "reviewed"))
15258
+ throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
15259
+ }
14644
15260
  function rebindAgentDiscovery(binding, changes) {
14645
15261
  const budget = discoveryByteBudget();
14646
- return { ...binding, sources: binding.sources.map((source) => ({ ...source, sha256: projected(source, changes, budget) })) };
15262
+ return { ...binding, sources: binding.sources.map((source) => {
15263
+ assertMarketplaceBinding(binding, source);
15264
+ return { ...source, sha256: projected(source, changes, budget) };
15265
+ }) };
14647
15266
  }
14648
15267
  function captureDiscoveryByteSources(paths) {
14649
15268
  if (!Array.isArray(paths) || paths.length > AGENT_POLICY_LIMITS.discoverySources || new Set(paths).size !== paths.length)
@@ -14658,10 +15277,10 @@ function captureDiscoveryPathSources(paths) {
14658
15277
  return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget) }));
14659
15278
  }
14660
15279
  function resolveAgentDiscovery(options) {
14661
- const canonical3 = options.canonical ?? resolve11, home = resolve11(options.home), agent = options.agent;
15280
+ const canonical4 = options.canonical ?? resolve14, home = resolve14(options.home), agent = options.agent;
14662
15281
  const sources = [], roots = new Set, builtinNames = [];
14663
15282
  const witness = (path, format, fields) => {
14664
- const source = { path: canonical3(path), sha256: null, ...format ? { format, fields } : {} };
15283
+ const source = { path: canonical4(path), sha256: null, ...format ? { format, fields } : {} };
14665
15284
  source.sha256 = projected(source);
14666
15285
  sources.push(source);
14667
15286
  return read(source.path);
@@ -14689,7 +15308,7 @@ function resolveAgentDiscovery(options) {
14689
15308
  continue;
14690
15309
  if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
14691
15310
  unresolved("user-specific tilde expansion requires a separate discovery adapter");
14692
- const path = normalized === "~" ? home : normalized.startsWith("~/") ? join23(home, normalized.slice(2)) : resolve11(home, ".hermes", normalized);
15311
+ const path = normalized === "~" ? home : normalized.startsWith("~/") ? join23(home, normalized.slice(2)) : resolve14(home, ".hermes", normalized);
14693
15312
  safe4(path);
14694
15313
  roots.add(path);
14695
15314
  }
@@ -14698,21 +15317,21 @@ function resolveAgentDiscovery(options) {
14698
15317
  const executable = Bun.which("gemini");
14699
15318
  if (executable) {
14700
15319
  let packageRoot;
14701
- for (let directory = dirname12(realpathSync4(executable)), depth = 0;depth < 8; directory = dirname12(directory), depth++) {
15320
+ for (let directory = dirname14(realpathSync4(executable)), depth = 0;depth < 8; directory = dirname14(directory), depth++) {
14702
15321
  const path = join23(directory, "package.json"), raw = read(path);
14703
15322
  if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
14704
15323
  packageRoot = directory;
14705
15324
  witness(path);
14706
15325
  break;
14707
15326
  }
14708
- if (dirname12(directory) === directory)
15327
+ if (dirname14(directory) === directory)
14709
15328
  break;
14710
15329
  }
14711
15330
  if (!packageRoot)
14712
15331
  unresolved("installed Gemini package layout is not recognized");
14713
15332
  const builtinRoot = join23(packageRoot, "bundle/builtin");
14714
15333
  safe4(builtinRoot);
14715
- if (!lstatSync15(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
15334
+ if (!lstatSync17(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
14716
15335
  unresolved("installed Gemini builtin root is missing");
14717
15336
  for (const name of readdirSync9(builtinRoot).sort()) {
14718
15337
  const text2 = witness(join23(builtinRoot, name, "SKILL.md"));
@@ -14733,12 +15352,14 @@ function resolveAgentDiscovery(options) {
14733
15352
  if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
14734
15353
  throw new Error("Explicit discovery reviews require full source-file hashes");
14735
15354
  verifyAgentDiscovery(supplied);
14736
- if (!review.sources.some((source) => source.path === canonical3(configPath)))
15355
+ if (review.sources.some((source) => source.hashMode === "claude-settings-v1" && source.path !== canonical4(configPath)))
15356
+ throw new Error("Semantic settings witnesses must name the configured Claude configuration source");
15357
+ if (!review.sources.some((source) => source.path === canonical4(configPath)))
14737
15358
  throw new Error("Discovery review must include the agent configuration source");
14738
15359
  return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...sources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
14739
15360
  }
14740
15361
  function plugin(root2) {
14741
- root2 = canonical3(root2);
15362
+ root2 = canonical4(root2);
14742
15363
  safe4(root2);
14743
15364
  const manifestPath = join23(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
14744
15365
  const raw = witness(manifestPath);
@@ -14750,7 +15371,7 @@ function resolveAgentDiscovery(options) {
14750
15371
  unresolved("plugin hooks require a separate no-skill-injection review");
14751
15372
  const commands = join23(root2, "commands");
14752
15373
  safe4(commands);
14753
- if (agent === "claude" && (manifest.commands !== undefined || lstatSync15(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length))
15374
+ if (agent === "claude" && (manifest.commands !== undefined || lstatSync17(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length))
14754
15375
  unresolved("legacy command discovery requires retirement or a dedicated format adapter");
14755
15376
  const rootSkill = witness(join23(root2, "SKILL.md"));
14756
15377
  if (rootSkill !== null)
@@ -14761,18 +15382,18 @@ function resolveAgentDiscovery(options) {
14761
15382
  if (agent === "claude")
14762
15383
  roots.add(join23(root2, "skills"));
14763
15384
  for (const value of declared) {
14764
- const path = resolve11(root2, value);
15385
+ const path = resolve14(root2, value);
14765
15386
  if (path !== root2 && !path.startsWith(root2 + sep5))
14766
15387
  unresolved("plugin skill path escapes its root");
14767
- roots.add(path.endsWith(`${sep5}SKILL.md`) ? dirname12(path) : path);
15388
+ roots.add(path.endsWith(`${sep5}SKILL.md`) ? dirname14(path) : path);
14768
15389
  }
14769
15390
  }
14770
15391
  if (agent === "claude") {
14771
15392
  const registrations = witness(join23(home, ".claude/plugins/installed_plugins.json"));
14772
15393
  const installed = registrations === null ? {} : parseConfig(registrations, join23(home, ".claude/plugins/installed_plugins.json")).plugins;
14773
- const commands = canonical3(join23(home, ".claude/commands"));
15394
+ const commands = canonical4(join23(home, ".claude/commands"));
14774
15395
  safe4(commands);
14775
- if (lstatSync15(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length)
15396
+ if (lstatSync17(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length)
14776
15397
  unresolved("legacy home command discovery requires a dedicated format adapter");
14777
15398
  for (const [id, enabled] of Object.entries(config.enabledPlugins ?? {})) {
14778
15399
  if (enabled === false)
@@ -14792,7 +15413,7 @@ function resolveAgentDiscovery(options) {
14792
15413
  unresolved("unsupported plugin enablement");
14793
15414
  const split = id.lastIndexOf("@"), name = id.slice(0, split), marketplace = id.slice(split + 1);
14794
15415
  const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
14795
- if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute11(market.source))
15416
+ if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute14(market.source))
14796
15417
  unresolved("enabled plugin marketplace is not an explicit local source");
14797
15418
  const catalogText = witness(join23(market.source, ".agents/plugins/marketplace.json"));
14798
15419
  if (catalogText === null)
@@ -14800,7 +15421,7 @@ function resolveAgentDiscovery(options) {
14800
15421
  const catalog = parseConfig(catalogText, join23(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
14801
15422
  if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
14802
15423
  unresolved("plugin catalog source is missing or ambiguous");
14803
- plugin(resolve11(market.source, matches[0].source.path));
15424
+ plugin(resolve14(market.source, matches[0].source.path));
14804
15425
  }
14805
15426
  } else if (agent === "opencode") {
14806
15427
  if (witness(join23(home, ".config/opencode/opencode.jsonc")) !== null || witness(join23(home, ".config/opencode/config.json")) !== null)
@@ -14808,18 +15429,18 @@ function resolveAgentDiscovery(options) {
14808
15429
  if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
14809
15430
  unresolved("external plugin or remote skill source");
14810
15431
  for (const path of config.skills?.paths ?? []) {
14811
- if (typeof path !== "string" || !isAbsolute11(path))
15432
+ if (typeof path !== "string" || !isAbsolute14(path))
14812
15433
  unresolved("relative or malformed added skill root");
14813
15434
  roots.add(path);
14814
15435
  }
14815
15436
  const localPlugins = join23(home, ".config/opencode/plugins");
14816
15437
  safe4(localPlugins);
14817
- if (lstatSync15(localPlugins, { throwIfNoEntry: false }) && readdirSync9(localPlugins).some((name) => name !== "skills-cli.js"))
15438
+ if (lstatSync17(localPlugins, { throwIfNoEntry: false }) && readdirSync9(localPlugins).some((name) => name !== "skills-cli.js"))
14818
15439
  unresolved("additional local plugin hooks require review");
14819
15440
  } else if (agent === "hermes") {
14820
15441
  for (const directory of [join23(home, ".hermes/plugins"), join23(home, ".hermes/hermes-agent")]) {
14821
15442
  safe4(directory);
14822
- if (lstatSync15(directory, { throwIfNoEntry: false }))
15443
+ if (lstatSync17(directory, { throwIfNoEntry: false }))
14823
15444
  unresolved("installed runtime/plugin discovery requires reviewed source bindings");
14824
15445
  }
14825
15446
  if (Bun.which("hermes") || Object.keys(config.plugins ?? {}).length)
@@ -14829,11 +15450,11 @@ function resolveAgentDiscovery(options) {
14829
15450
  unresolved("custom extension or skill paths");
14830
15451
  const directory = join23(home, ".gemini/extensions");
14831
15452
  safe4(directory);
14832
- if (lstatSync15(directory, { throwIfNoEntry: false }))
15453
+ if (lstatSync17(directory, { throwIfNoEntry: false }))
14833
15454
  for (const name of readdirSync9(directory)) {
14834
15455
  const root2 = join23(directory, name);
14835
15456
  safe4(root2);
14836
- if (!lstatSync15(root2)?.isDirectory())
15457
+ if (!lstatSync17(root2)?.isDirectory())
14837
15458
  continue;
14838
15459
  const raw = witness(join23(root2, "gemini-extension.json"));
14839
15460
  if (raw === null)
@@ -14844,7 +15465,7 @@ function resolveAgentDiscovery(options) {
14844
15465
  roots.add(join23(root2, "skills"));
14845
15466
  }
14846
15467
  }
14847
- 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) => canonical3(path))) } : {} };
15468
+ 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))) } : {} };
14848
15469
  }
14849
15470
 
14850
15471
  // src/lib/agent-adapters.ts
@@ -14914,7 +15535,7 @@ export default async ({ directory }) => {
14914
15535
  }
14915
15536
 
14916
15537
  // src/lib/agent-codex.ts
14917
- import { isDeepStrictEqual } from "util";
15538
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
14918
15539
  var CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm;
14919
15540
  function assertCodexPathConfigEditable(text2) {
14920
15541
  const before = Bun.TOML.parse(text2);
@@ -14924,7 +15545,7 @@ function assertCodexPathConfigEditable(text2) {
14924
15545
  delete expected.skills.config;
14925
15546
  try {
14926
15547
  const withoutSections = Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
14927
- if (isDeepStrictEqual(withoutSections, expected))
15548
+ if (isDeepStrictEqual2(withoutSections, expected))
14928
15549
  return;
14929
15550
  } catch {}
14930
15551
  throw new Error("Codex skill path controls require ordinary [[skills.config]] TOML tables; preserve and convert inline or dotted config arrays before running skills hook install");
@@ -14962,7 +15583,7 @@ function disableCodexBundledSkills(text2) {
14962
15583
  const candidates = new Set;
14963
15584
  const consider = (candidate2) => {
14964
15585
  try {
14965
- if (isDeepStrictEqual(Bun.TOML.parse(candidate2), expected))
15586
+ if (isDeepStrictEqual2(Bun.TOML.parse(candidate2), expected))
14966
15587
  candidates.add(candidate2);
14967
15588
  } catch {}
14968
15589
  };
@@ -14988,7 +15609,7 @@ function disableCodexBundledSkills(text2) {
14988
15609
  }
14989
15610
 
14990
15611
  // src/lib/agent-integration.ts
14991
- var sha = (data) => createHash12("sha256").update(data).digest("hex");
15612
+ var sha = (data) => createHash14("sha256").update(data).digest("hex");
14992
15613
  var ROOTS = [
14993
15614
  ["claude", ".claude/skills"],
14994
15615
  ["codex", ".codex/skills"],
@@ -15007,11 +15628,11 @@ var ROOTS = [
15007
15628
  ["copilot", ".github/skills"]
15008
15629
  ];
15009
15630
  function assertSafePath(path) {
15010
- let cursor = resolve12(path);
15631
+ let cursor = resolve15(path);
15011
15632
  while (true) {
15012
- if (lstatSync16(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
15633
+ if (lstatSync18(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
15013
15634
  throw new Error(`Refusing symlink path: ${cursor}`);
15014
- const parent = dirname13(cursor);
15635
+ const parent = dirname15(cursor);
15015
15636
  if (parent === cursor)
15016
15637
  return;
15017
15638
  cursor = parent;
@@ -15020,16 +15641,16 @@ function assertSafePath(path) {
15020
15641
  function readRootAlias(home, agent) {
15021
15642
  assertSafePath(home);
15022
15643
  const realHome = realpathSync5(home), alias = join25(realHome, `.${agent}`);
15023
- const stat2 = lstatSync16(alias, { throwIfNoEntry: false, bigint: true });
15644
+ const stat2 = lstatSync18(alias, { throwIfNoEntry: false, bigint: true });
15024
15645
  if (!stat2?.isSymbolicLink())
15025
15646
  throw new Error(`Agent root alias changed after planning: ${alias}`);
15026
- const link = readlinkSync2(alias), targetPath = resolve12(dirname13(alias), link);
15647
+ const link = readlinkSync2(alias), targetPath = resolve15(dirname15(alias), link);
15027
15648
  assertSafePath(targetPath);
15028
- const targetStat = lstatSync16(targetPath, { throwIfNoEntry: false });
15649
+ const targetStat = lstatSync18(targetPath, { throwIfNoEntry: false });
15029
15650
  if (!targetStat?.isDirectory())
15030
15651
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
15031
15652
  const target = realpathSync5(targetPath), inside = relative5(realHome, target);
15032
- if (!inside || inside === ".." || inside.startsWith(`..${sep6}`) || isAbsolute12(inside))
15653
+ if (!inside || inside === ".." || inside.startsWith(`..${sep6}`) || isAbsolute15(inside))
15033
15654
  throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
15034
15655
  return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat2.dev}:${stat2.ino}:${stat2.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
15035
15656
  }
@@ -15038,14 +15659,14 @@ function rootAliases(home, allowed = false) {
15038
15659
  return [];
15039
15660
  const result = [];
15040
15661
  for (const agent of ["claude", "codex"])
15041
- if (lstatSync16(join25(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
15662
+ if (lstatSync18(join25(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
15042
15663
  result.push(readRootAlias(home, agent));
15043
15664
  if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep6) || other.target.startsWith(item.target + sep6))))
15044
15665
  throw new Error("Agent root alias targets must not overlap");
15045
15666
  return result;
15046
15667
  }
15047
15668
  function canonicalAgentPath(path, aliases) {
15048
- const absolute = resolve12(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep6));
15669
+ const absolute = resolve15(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep6));
15049
15670
  return binding ? join25(binding.target, relative5(binding.alias, absolute)) : absolute;
15050
15671
  }
15051
15672
  function recheckRootAliases(aliases) {
@@ -15059,35 +15680,35 @@ function recheckRootAliases(aliases) {
15059
15680
  }
15060
15681
  function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
15061
15682
  assertSafePath(path);
15062
- const before = lstatSync16(path);
15683
+ const before = lstatSync18(path);
15063
15684
  if (!before.isFile() || before.size > maximum)
15064
15685
  throw new Error("Unsupported or oversized native skill file");
15065
- const descriptor = openSync9(path, constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK);
15686
+ const descriptor = openSync11(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
15066
15687
  try {
15067
- const opened = fstatSync9(descriptor);
15688
+ const opened = fstatSync11(descriptor);
15068
15689
  if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size)
15069
15690
  throw new Error("Native skill file changed while reading");
15070
15691
  const bytes = Buffer.allocUnsafe(before.size + 1);
15071
15692
  let length = 0;
15072
15693
  while (length < bytes.length) {
15073
- const count = readSync7(descriptor, bytes, length, bytes.length - length, null);
15694
+ const count = readSync9(descriptor, bytes, length, bytes.length - length, null);
15074
15695
  if (!count)
15075
15696
  break;
15076
15697
  length += count;
15077
15698
  }
15078
- const after = fstatSync9(descriptor);
15699
+ const after = fstatSync11(descriptor);
15079
15700
  if (length !== before.size || after.size !== before.size || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs)
15080
15701
  throw new Error("Native skill file changed while reading");
15081
15702
  return bytes.subarray(0, length);
15082
15703
  } finally {
15083
- closeSync9(descriptor);
15704
+ closeSync11(descriptor);
15084
15705
  }
15085
15706
  }
15086
15707
  function treeHash(root2) {
15087
- const hash2 = createHash12("sha256");
15708
+ const hash2 = createHash14("sha256");
15088
15709
  let bytes = 0, files = 0;
15089
15710
  function visit2(path) {
15090
- const stat2 = lstatSync16(path);
15711
+ const stat2 = lstatSync18(path);
15091
15712
  if (stat2.isSymbolicLink())
15092
15713
  throw new Error(`Refusing symlink in skill: ${path}`);
15093
15714
  const rel = relative5(root2, path);
@@ -15113,11 +15734,11 @@ function treeHash(root2) {
15113
15734
  function projectAncestorDirectories(projects) {
15114
15735
  const directories = new Set;
15115
15736
  for (const project of projects) {
15116
- for (let path = resolve12(project), depth = 0;; depth++) {
15737
+ for (let path = resolve15(project), depth = 0;; depth++) {
15117
15738
  if (depth >= 100)
15118
15739
  throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
15119
15740
  directories.add(path);
15120
- const parent = dirname13(path);
15741
+ const parent = dirname15(path);
15121
15742
  if (parent === path)
15122
15743
  break;
15123
15744
  path = parent;
@@ -15144,10 +15765,10 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
15144
15765
  throw new Error("Native skill discovery metadata limit exceeded");
15145
15766
  }
15146
15767
  function emptySiblingCacheAlias(path, parent) {
15147
- const link = readlinkSync2(path), target = resolve12(parent, link);
15148
- if (dirname13(target) !== parent || (isAbsolute12(link) ? link !== target : dirname13(link) !== "."))
15768
+ const link = readlinkSync2(path), target = resolve15(parent, link);
15769
+ if (dirname15(target) !== parent || (isAbsolute15(link) ? link !== target : dirname15(link) !== "."))
15149
15770
  return false;
15150
- if (!lstatSync16(target, { throwIfNoEntry: false })?.isDirectory())
15771
+ if (!lstatSync18(target, { throwIfNoEntry: false })?.isDirectory())
15151
15772
  return false;
15152
15773
  const scan = cacheScans.get(target);
15153
15774
  return Boolean(scan?.complete && !scan.hasSkills && scan.entries <= maxAliasProofEntries);
@@ -15159,7 +15780,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
15159
15780
  assertSafePath(path);
15160
15781
  if (!existsSync11(path))
15161
15782
  return scan;
15162
- const stat2 = lstatSync16(path);
15783
+ const stat2 = lstatSync18(path);
15163
15784
  if (stat2.isFile())
15164
15785
  return scan;
15165
15786
  if (!stat2.isDirectory())
@@ -15178,7 +15799,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
15178
15799
  }
15179
15800
  const bridge = !vendor && isOwnedCliBridge(path, bridgePaths);
15180
15801
  const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep6));
15181
- entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(join25(home, ".codex", "skills", ".system"), aliases) + sep6) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve12(home) } : {}, ...rootAlias ? { rootAlias } : {} });
15802
+ entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(join25(home, ".codex", "skills", ".system"), aliases) + sep6) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve15(home) } : {}, ...rootAlias ? { rootAlias } : {} });
15182
15803
  return scan;
15183
15804
  }
15184
15805
  if (depth > (vendor ? 32 : 3))
@@ -15205,7 +15826,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
15205
15826
  if (isVendor && !options.includeVendor)
15206
15827
  continue;
15207
15828
  const child = join25(path, name);
15208
- if (isVendor && lstatSync16(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
15829
+ if (isVendor && lstatSync18(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
15209
15830
  const target = statSync8(child, { throwIfNoEntry: false });
15210
15831
  if (target?.isFile() || pluginCache && target?.isDirectory() && emptySiblingCacheAlias(child, path)) {
15211
15832
  scan.entries++;
@@ -15327,7 +15948,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
15327
15948
  function disableCodexSkills(text2, skills, aliases, bridgePath) {
15328
15949
  const bundled = disableCodexBundledSkills(text2);
15329
15950
  let result = bundled;
15330
- const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve12(bridgePath), resolve12(join25(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
15951
+ const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve15(bridgePath), resolve15(join25(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
15331
15952
  const previousConfig = Bun.TOML.parse(bundled).skills?.config;
15332
15953
  if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
15333
15954
  throw new Error("Codex skill controls require one path or name selector per [[skills.config]] entry; review ambiguous entries before running skills hook install");
@@ -15339,7 +15960,7 @@ function disableCodexSkills(text2, skills, aliases, bridgePath) {
15339
15960
  result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
15340
15961
  const parsed = Bun.TOML.parse(section);
15341
15962
  const declared = parsed.skills?.config?.[0];
15342
- if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve12(path), resolve12(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
15963
+ if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve15(path), resolve15(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
15343
15964
  return section;
15344
15965
  found = true;
15345
15966
  return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
@@ -15369,9 +15990,9 @@ function planAgentIntegration(options) {
15369
15990
  throw new Error("Invalid existing Skills bridge policy");
15370
15991
  if (policy.bridge?.agents !== undefined && (!Array.isArray(policy.bridge.agents) || policy.bridge.agents.some((agent) => !INTEGRATION_AGENTS.includes(agent))))
15371
15992
  throw new Error("Invalid existing bridge agent inventory");
15372
- for (const field of ["commands", "profiles"])
15373
- if (policy.bridge?.[field] !== undefined && (!policy.bridge[field] || typeof policy.bridge[field] !== "object" || Array.isArray(policy.bridge[field]) || Object.entries(policy.bridge[field]).some(([key, value]) => !INTEGRATION_AGENTS.includes(key) || typeof value !== "string" || !value || value.includes("\x00"))))
15374
- throw new Error(`Invalid existing bridge ${field} binding`);
15993
+ for (const field2 of ["commands", "profiles"])
15994
+ if (policy.bridge?.[field2] !== undefined && (!policy.bridge[field2] || typeof policy.bridge[field2] !== "object" || Array.isArray(policy.bridge[field2]) || Object.entries(policy.bridge[field2]).some(([key, value]) => !INTEGRATION_AGENTS.includes(key) || typeof value !== "string" || !value || value.includes("\x00"))))
15995
+ throw new Error(`Invalid existing bridge ${field2} binding`);
15375
15996
  const priorAgents = policy.bridge?.agents ?? [];
15376
15997
  const profileId = options.profileId ?? policy.profileId ?? "default";
15377
15998
  const validateProfile = (value) => {
@@ -15457,7 +16078,7 @@ function planAgentIntegration(options) {
15457
16078
  version: CLI_BRIDGE_VERSION,
15458
16079
  digest: CLI_BRIDGE_DIGEST,
15459
16080
  agents: [...new Set([...priorAgents, ...options.agents])].sort(),
15460
- home: resolve12(home),
16081
+ home: resolve15(home),
15461
16082
  includeVendor: true,
15462
16083
  ...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join25(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
15463
16084
  commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
@@ -15474,7 +16095,7 @@ function planAgentIntegration(options) {
15474
16095
  }
15475
16096
  function atomicWrite2(path, content) {
15476
16097
  assertSafePath(path);
15477
- mkdirSync7(dirname13(path), { recursive: true, mode: 448 });
16098
+ mkdirSync7(dirname15(path), { recursive: true, mode: 448 });
15478
16099
  const temporary = `${path}.skills-${randomUUID3()}`;
15479
16100
  try {
15480
16101
  writeFileSync5(temporary, content, { mode: 384, flag: "wx" });
@@ -15487,10 +16108,10 @@ function atomicWrite2(path, content) {
15487
16108
  function applyAgentIntegration(plan) {
15488
16109
  const policyPath = join25(plan.dataDir, "agent-policy.json");
15489
16110
  assertSafePath(policyPath);
15490
- const currentText = (path) => resolve12(path) === resolve12(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
16111
+ const currentText = (path) => resolve15(path) === resolve15(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
15491
16112
  readManagedSkillPolicySnapshot(plan.dataDir);
15492
16113
  for (const change of plan.changes)
15493
- if (resolve12(change.path) === resolve12(policyPath))
16114
+ if (resolve15(change.path) === resolve15(policyPath))
15494
16115
  parseManagedSkillPolicy(change.after);
15495
16116
  const aliases = plan.rootAliases ?? [];
15496
16117
  recheckRootAliases(aliases);
@@ -15520,7 +16141,7 @@ function applyAgentIntegration(plan) {
15520
16141
  recheckRootAliases(aliases);
15521
16142
  if (currentText(change.path) !== change.before)
15522
16143
  throw new Error(`Configuration changed after planning: ${change.path}`);
15523
- for (let directory = dirname13(change.path);!existsSync11(directory); directory = dirname13(directory))
16144
+ for (let directory = dirname15(change.path);!existsSync11(directory); directory = dirname15(directory))
15524
16145
  createdDirectories.add(directory);
15525
16146
  const after = change.after;
15526
16147
  atomicWrite2(change.path, after);
@@ -15560,11 +16181,11 @@ function applyAgentIntegration(plan) {
15560
16181
  }
15561
16182
  function syncArchiveDirectory(path) {
15562
16183
  assertSafePath(path);
15563
- const descriptor = openSync9(path, constants8.O_RDONLY | constants8.O_DIRECTORY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK);
16184
+ const descriptor = openSync11(path, constants10.O_RDONLY | constants10.O_DIRECTORY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
15564
16185
  try {
15565
- fsyncSync2(descriptor);
16186
+ fsyncSync3(descriptor);
15566
16187
  } finally {
15567
- closeSync9(descriptor);
16188
+ closeSync11(descriptor);
15568
16189
  }
15569
16190
  }
15570
16191
  function writeArchiveJournal(path, value) {
@@ -15572,18 +16193,18 @@ function writeArchiveJournal(path, value) {
15572
16193
  const temporary = `${path}.skills-${randomUUID3()}`;
15573
16194
  let descriptor;
15574
16195
  try {
15575
- descriptor = openSync9(temporary, constants8.O_WRONLY | constants8.O_CREAT | constants8.O_EXCL | constants8.O_NOFOLLOW, 384);
16196
+ descriptor = openSync11(temporary, constants10.O_WRONLY | constants10.O_CREAT | constants10.O_EXCL | constants10.O_NOFOLLOW, 384);
15576
16197
  writeFileSync5(descriptor, `${JSON.stringify(value)}
15577
16198
  `);
15578
- fsyncSync2(descriptor);
15579
- closeSync9(descriptor);
16199
+ fsyncSync3(descriptor);
16200
+ closeSync11(descriptor);
15580
16201
  descriptor = undefined;
15581
16202
  renameSync4(temporary, path);
15582
- syncArchiveDirectory(dirname13(path));
16203
+ syncArchiveDirectory(dirname15(path));
15583
16204
  } finally {
15584
16205
  if (descriptor !== undefined)
15585
- closeSync9(descriptor);
15586
- if (lstatSync16(temporary, { throwIfNoEntry: false }))
16206
+ closeSync11(descriptor);
16207
+ if (lstatSync18(temporary, { throwIfNoEntry: false }))
15587
16208
  unlinkSync4(temporary);
15588
16209
  }
15589
16210
  }
@@ -15616,7 +16237,7 @@ function archiveNativeSkills(inventory, options) {
15616
16237
  const moved = [];
15617
16238
  const verifyArchive = (entry) => {
15618
16239
  assertSafePath(entry.archive);
15619
- const stat2 = lstatSync16(entry.archive);
16240
+ const stat2 = lstatSync18(entry.archive);
15620
16241
  if (entry.discoveryOnly ? !stat2.isFile() : !stat2.isDirectory())
15621
16242
  throw new Error("Archive type changed");
15622
16243
  if ((entry.discoveryOnly ? sha(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
@@ -15624,14 +16245,14 @@ function archiveNativeSkills(inventory, options) {
15624
16245
  };
15625
16246
  assertSafePath(archiveRoot);
15626
16247
  const created = [];
15627
- for (let path = archiveRoot;!existsSync11(path); path = dirname13(path))
16248
+ for (let path = archiveRoot;!existsSync11(path); path = dirname15(path))
15628
16249
  created.push(path);
15629
16250
  mkdirSync7(archiveRoot, { recursive: true, mode: 448 });
15630
16251
  try {
15631
16252
  for (const directory of created)
15632
16253
  syncArchiveDirectory(directory);
15633
16254
  if (created.length)
15634
- syncArchiveDirectory(dirname13(created.at(-1)));
16255
+ syncArchiveDirectory(dirname15(created.at(-1)));
15635
16256
  writeArchiveJournal(receiptPath, journal);
15636
16257
  for (const [index, entry] of selected.entries()) {
15637
16258
  recheckRootAliases(aliases);
@@ -15643,7 +16264,7 @@ function archiveNativeSkills(inventory, options) {
15643
16264
  writeArchiveJournal(receiptPath, journal);
15644
16265
  renameSync4(move.source, move.archive);
15645
16266
  moved.push(move);
15646
- syncArchiveDirectory(dirname13(move.source));
16267
+ syncArchiveDirectory(dirname15(move.source));
15647
16268
  syncArchiveDirectory(archiveRoot);
15648
16269
  verifyArchive(move);
15649
16270
  move.status = "archived";
@@ -15664,7 +16285,7 @@ function archiveNativeSkills(inventory, options) {
15664
16285
  recheckRootAliases(aliases);
15665
16286
  assertSafePath(entry.source);
15666
16287
  assertSafePath(entry.archive);
15667
- if (lstatSync16(entry.source, { throwIfNoEntry: false })) {
16288
+ if (lstatSync18(entry.source, { throwIfNoEntry: false })) {
15668
16289
  entry.status = "recovery-required";
15669
16290
  entry.conflict = "source-occupied";
15670
16291
  continue;
@@ -15678,10 +16299,10 @@ function archiveNativeSkills(inventory, options) {
15678
16299
  }
15679
16300
  if (entry.discoveryOnly) {
15680
16301
  linkSync2(entry.archive, entry.source);
15681
- syncArchiveDirectory(dirname13(entry.source));
16302
+ syncArchiveDirectory(dirname15(entry.source));
15682
16303
  unlinkSync4(entry.archive);
15683
16304
  } else {
15684
- if (lstatSync16(entry.source, { throwIfNoEntry: false })) {
16305
+ if (lstatSync18(entry.source, { throwIfNoEntry: false })) {
15685
16306
  entry.status = "recovery-required";
15686
16307
  entry.conflict = "source-occupied";
15687
16308
  continue;
@@ -15689,7 +16310,7 @@ function archiveNativeSkills(inventory, options) {
15689
16310
  renameSync4(entry.archive, entry.source);
15690
16311
  }
15691
16312
  entry.status = "restored";
15692
- syncArchiveDirectory(dirname13(entry.source));
16313
+ syncArchiveDirectory(dirname15(entry.source));
15693
16314
  syncArchiveDirectory(archiveRoot);
15694
16315
  } catch {
15695
16316
  entry.status = "recovery-required";
@@ -15728,7 +16349,7 @@ function isSkillsOwnershipMarker(marker) {
15728
16349
  function hasSkillsOwnershipMarker(dir) {
15729
16350
  const path = join26(dir, SYNC_MARKER_FILE);
15730
16351
  try {
15731
- if (!lstatSync17(path).isFile())
16352
+ if (!lstatSync19(path).isFile())
15732
16353
  return false;
15733
16354
  const marker = JSON.parse(readFileSync15(path, "utf8"));
15734
16355
  return isSkillsOwnershipMarker(marker);
@@ -15954,9 +16575,9 @@ function writeManagedSkillDir(dir, skillMd, options) {
15954
16575
  const action = dirExists ? "update" : "create";
15955
16576
  if (options.dryRun)
15956
16577
  return { action, path: skillMdPath };
15957
- const parentDir = dirname14(dir);
16578
+ const parentDir = dirname16(dir);
15958
16579
  mkdirSync8(parentDir, { recursive: true });
15959
- const transactionDir = mkdtempSync2(join26(parentDir, `.hasna-skills-write-${basename5(dir)}-`));
16580
+ const transactionDir = mkdtempSync2(join26(parentDir, `.hasna-skills-write-${basename9(dir)}-`));
15960
16581
  const candidateDir = join26(transactionDir, "candidate");
15961
16582
  const backupDir = join26(transactionDir, "backup");
15962
16583
  const candidateSkillMdPath = join26(candidateDir, "SKILL.md");
@@ -16445,7 +17066,7 @@ function loadProjectConfigCompat(targetDir) {
16445
17066
  return loadProjectConfig(targetDir);
16446
17067
  }
16447
17068
  // src/lib/run-state.ts
16448
- import { createHash as createHash13, randomBytes } from "crypto";
17069
+ import { createHash as createHash15, randomBytes } from "crypto";
16449
17070
  import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync18, readdirSync as readdirSync12, statSync as statSync10, writeFileSync as writeFileSync8 } from "fs";
16450
17071
  import { extname, join as join29, relative as relative6 } from "path";
16451
17072
  function createSkillRun(params, targetDir = process.cwd()) {
@@ -16572,7 +17193,7 @@ function collectRunArtifacts(context) {
16572
17193
  artifacts.push({
16573
17194
  path: toProjectRelative(context.targetDir, path),
16574
17195
  mime: mimeForPath(path),
16575
- sha256: createHash13("sha256").update(bytes).digest("hex"),
17196
+ sha256: createHash15("sha256").update(bytes).digest("hex"),
16576
17197
  sizeBytes: stat2.size
16577
17198
  });
16578
17199
  }
@@ -16700,7 +17321,7 @@ __export(exports_external, {
16700
17321
  pipe: () => pipe,
16701
17322
  partialRecord: () => partialRecord,
16702
17323
  parseAsync: () => parseAsync2,
16703
- parse: () => parse7,
17324
+ parse: () => parse9,
16704
17325
  overwrite: () => _overwrite,
16705
17326
  output: () => output,
16706
17327
  optional: () => optional,
@@ -16920,7 +17541,7 @@ __export(exports_core2, {
16920
17541
  prettifyError: () => prettifyError,
16921
17542
  parseURLObject: () => parseURLObject,
16922
17543
  parseAsync: () => parseAsync,
16923
- parse: () => parse5,
17544
+ parse: () => parse7,
16924
17545
  meta: () => meta,
16925
17546
  mergeValues: () => mergeValues,
16926
17547
  memoizer: () => memoizer,
@@ -18374,7 +18995,7 @@ var _parse = (_Err) => {
18374
18995
  };
18375
18996
  return fn;
18376
18997
  };
18377
- var parse5 = /* @__PURE__ */ _parse($ZodRealError);
18998
+ var parse7 = /* @__PURE__ */ _parse($ZodRealError);
18378
18999
  var _parseAsync = (_Err) => {
18379
19000
  const fn = async (schema, value, _ctx, params) => {
18380
19001
  const ctx = _ctx ? { ..._ctx, async: true } : { async: true };
@@ -18445,18 +19066,18 @@ var validateAsync = async (schema, value, _ctx) => {
18445
19066
  return result.issues.length === 0;
18446
19067
  };
18447
19068
  var _encode = (_Err) => {
18448
- const parse6 = _parse(_Err);
19069
+ const parse8 = _parse(_Err);
18449
19070
  const fn = (schema, value, _ctx, _params) => {
18450
19071
  const ctx = _ctx ? { ..._ctx, direction: "backward" } : { direction: "backward" };
18451
- return parse6(schema, value, ctx, finalizeParams(fn, _params));
19072
+ return parse8(schema, value, ctx, finalizeParams(fn, _params));
18452
19073
  };
18453
19074
  return fn;
18454
19075
  };
18455
19076
  var encode = /* @__PURE__ */ _encode($ZodRealError);
18456
19077
  var _decode = (_Err) => {
18457
- const parse6 = _parse(_Err);
19078
+ const parse8 = _parse(_Err);
18458
19079
  const fn = (schema, value, _ctx, _params) => {
18459
- return parse6(schema, value, _ctx, finalizeParams(fn, _params));
19080
+ return parse8(schema, value, _ctx, finalizeParams(fn, _params));
18460
19081
  };
18461
19082
  return fn;
18462
19083
  };
@@ -20101,14 +20722,14 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
20101
20722
  const shape = zod.def.shape;
20102
20723
  const propValues = {};
20103
20724
  for (const key in shape) {
20104
- const field = shape[key]._zod;
20105
- if (field.values) {
20725
+ const field2 = shape[key]._zod;
20726
+ if (field2.values) {
20106
20727
  if (!Object.prototype.hasOwnProperty.call(propValues, key)) {
20107
20728
  assignProp(propValues, key, new Set);
20108
20729
  }
20109
- for (const v of field.values)
20730
+ for (const v of field2.values)
20110
20731
  propValues[key].add(v);
20111
- if (field.optin !== undefined)
20732
+ if (field2.optin !== undefined)
20112
20733
  propValues[key].add(undefined);
20113
20734
  }
20114
20735
  }
@@ -21379,10 +22000,10 @@ var $ZodFunction = /* @__PURE__ */ $constructor("$ZodFunction", (inst, def) => {
21379
22000
  throw new Error("implement() must be called with a function");
21380
22001
  }
21381
22002
  return Object.defineProperty(function(...args) {
21382
- const parsedArgs = inst._def.input ? parse5(inst._def.input, args) : args;
22003
+ const parsedArgs = inst._def.input ? parse7(inst._def.input, args) : args;
21383
22004
  const result = Reflect.apply(func, this, parsedArgs);
21384
22005
  if (inst._def.output) {
21385
- return parse5(inst._def.output, result);
22006
+ return parse7(inst._def.output, result);
21386
22007
  }
21387
22008
  return result;
21388
22009
  }, "_zod", { value: inst._zod, enumerable: false });
@@ -32301,11 +32922,11 @@ var objectProcessor = (schema, ctx, _json, params) => {
32301
32922
  }
32302
32923
  const allKeys = new Set(Object.keys(shape));
32303
32924
  const requiredKeys = new Set([...allKeys].filter((key) => {
32304
- const field = def.shape[key];
32925
+ const field2 = def.shape[key];
32305
32926
  if (ctx.io === "input") {
32306
- return inputOptin(field) === undefined;
32927
+ return inputOptin(field2) === undefined;
32307
32928
  } else {
32308
- return field._zod.optout === undefined;
32929
+ return field2._zod.optout === undefined;
32309
32930
  }
32310
32931
  }));
32311
32932
  if (requiredKeys.size > 0) {
@@ -33020,7 +33641,7 @@ var ZodRealError = /* @__PURE__ */ $constructor("ZodError", initializer2, undefi
33020
33641
  });
33021
33642
 
33022
33643
  // node_modules/zod/v4/classic/parse.js
33023
- var parse7 = /* @__PURE__ */ _parse(ZodRealError);
33644
+ var parse9 = /* @__PURE__ */ _parse(ZodRealError);
33024
33645
  var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
33025
33646
  var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
33026
33647
  var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
@@ -33156,7 +33777,7 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
33156
33777
  exports_util.own(this, "~standard", value);
33157
33778
  },
33158
33779
  parse: function _parse2(data, params) {
33159
- return parse7(this, data, params, { callee: _parse2 });
33780
+ return parse9(this, data, params, { callee: _parse2 });
33160
33781
  },
33161
33782
  parseAsync: async function _parseAsync2(data, params) {
33162
33783
  return await parseAsync2(this, data, params, { callee: _parseAsync2 });
@@ -35987,7 +36608,7 @@ function clone2(value) {
35987
36608
  return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
35988
36609
  }
35989
36610
  // src/lib/canonical-json.ts
35990
- import { createHash as createHash14 } from "crypto";
36611
+ import { createHash as createHash16 } from "crypto";
35991
36612
  function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
35992
36613
  return canonicalJsonAtDepth(value, maximumBytes, 64);
35993
36614
  }
@@ -36031,7 +36652,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
36031
36652
  return visit3(value, 0);
36032
36653
  }
36033
36654
  function canonicalJsonSha256(value, maximumBytes) {
36034
- return createHash14("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
36655
+ return createHash16("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
36035
36656
  }
36036
36657
 
36037
36658
  // src/lib/remote-recurring-contract.ts
@@ -36913,15 +37534,41 @@ function object5(value) {
36913
37534
  return value;
36914
37535
  }
36915
37536
 
37537
+ // src/lib/remote-credit-checkout.ts
37538
+ var creditCheckoutKeyPattern = /^[A-Za-z0-9._:-]{8,255}$/;
37539
+ function creditCheckoutRequestKey(value) {
37540
+ if (value === undefined)
37541
+ return crypto.randomUUID();
37542
+ if (typeof value !== "string" || !creditCheckoutKeyPattern.test(value))
37543
+ throw new Error("Checkout idempotency key must be 8-255 URL-safe characters");
37544
+ return value;
37545
+ }
37546
+ var creditCheckoutMessages = {
37547
+ CREDIT_CHECKOUT_UNCONFIRMED: "Checkout creation could not be confirmed. Keep the request idempotency key, inspect billing, and use the same key for an explicit retry on the same server, account and pack. No automatic retry was made.",
37548
+ CREDIT_CHECKOUT_IN_PROGRESS: "This checkout is still being created. Wait before explicitly retrying the same server, account and pack with the same request idempotency key. No automatic retry was made.",
37549
+ CREDIT_CHECKOUT_EXPIRED: "This checkout has expired. Inspect billing before deliberately starting a new purchase with a new request idempotency key. Do not retry this checkout automatically.",
37550
+ CREDIT_CHECKOUT_FULFILLED: "This checkout is already fulfilled. Inspect the account balance; do not create another checkout to recover this purchase."
37551
+ };
37552
+ function creditCheckoutFailure(value, status, key) {
37553
+ if (!value || typeof value !== "object" || Array.isArray(value))
37554
+ return { code: "CREDIT_CHECKOUT_UNCONFIRMED" };
37555
+ const row = value;
37556
+ if (row.requestIdempotencyKey !== undefined && row.requestIdempotencyKey !== key)
37557
+ return { code: "CREDIT_CHECKOUT_UNCONFIRMED" };
37558
+ const code = status === 409 && row.error === "credit checkout in_progress" ? "CREDIT_CHECKOUT_IN_PROGRESS" : status === 409 && row.error === "credit checkout expired" && row.requestIdempotencyKey === key ? "CREDIT_CHECKOUT_EXPIRED" : status === 409 && row.error === "credit checkout fulfilled" && row.requestIdempotencyKey === key ? "CREDIT_CHECKOUT_FULFILLED" : "CREDIT_CHECKOUT_UNCONFIRMED";
37559
+ const delay = row.retryAfterSeconds;
37560
+ return { code, ...code === "CREDIT_CHECKOUT_IN_PROGRESS" || status === 503 && row.error === "credit checkout creation unresolved" ? typeof delay === "number" && Number.isSafeInteger(delay) && delay > 0 && delay <= 3600 ? { retryAfterSeconds: delay } : {} : {} };
37561
+ }
37562
+
36916
37563
  // src/lib/remote-profile.ts
36917
- function customerNamePatch(input2, field) {
36918
- if (!isRecord4(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field))
37564
+ function customerNamePatch(input2, field2) {
37565
+ if (!isRecord4(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field2))
36919
37566
  throw new Error("Provide only the requested name field.");
36920
- const value = input2[field];
37567
+ const value = input2[field2];
36921
37568
  if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
36922
37569
  throw new Error("Use a name of 1\u2013100 characters without control characters or newlines.");
36923
37570
  }
36924
- return { [field]: value.trim() };
37571
+ return { [field2]: value.trim() };
36925
37572
  }
36926
37573
  function isRecord4(value) {
36927
37574
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -36966,10 +37613,10 @@ async function readQuoteUnavailableCode(response) {
36966
37613
  let timer;
36967
37614
  let deadlineExceeded = false;
36968
37615
  const expired = Symbol("quote body deadline");
36969
- const deadline = new Promise((resolve13) => {
37616
+ const deadline = new Promise((resolve16) => {
36970
37617
  timer = setTimeout(() => {
36971
37618
  deadlineExceeded = true;
36972
- resolve13(expired);
37619
+ resolve16(expired);
36973
37620
  reader.cancel().catch(() => {});
36974
37621
  }, 1500);
36975
37622
  });
@@ -37086,6 +37733,20 @@ class RemoteRequestError extends Error {
37086
37733
  }
37087
37734
  }
37088
37735
 
37736
+ class RemoteCreditCheckoutError extends RemoteRequestError {
37737
+ code;
37738
+ requestIdempotencyKey;
37739
+ retryAfterSeconds;
37740
+ constructor(code, status, requestIdempotencyKey, retryAfterSeconds) {
37741
+ super("/api/v1/billing/credits", status);
37742
+ this.code = code;
37743
+ this.requestIdempotencyKey = requestIdempotencyKey;
37744
+ this.retryAfterSeconds = retryAfterSeconds;
37745
+ this.name = "RemoteCreditCheckoutError";
37746
+ this.message = creditCheckoutMessages[code];
37747
+ }
37748
+ }
37749
+
37089
37750
  class RemoteQuoteUnavailableError extends RemoteRequestError {
37090
37751
  code;
37091
37752
  constructor(path, code) {
@@ -37574,14 +38235,43 @@ class RemoteSkillsClient {
37574
38235
  async listCreditPacks() {
37575
38236
  return parseRemoteCreditPacks(await (await this.requestNewRoute("/api/v1/billing/credits")).json());
37576
38237
  }
37577
- async createCreditCheckout(packId) {
38238
+ async createCreditCheckout(packId, options = {}) {
38239
+ const requestIdempotencyKey = creditCheckoutRequestKey(options.idempotencyKey);
37578
38240
  const packs = await this.listCreditPacks();
37579
38241
  if (!packs.some((pack) => pack.id === packId))
37580
38242
  throw new Error("Choose a credit pack returned by skills credits packs");
37581
- return parseRemoteCheckout(await (await this.requestNewRoute("/api/v1/billing/credits", {
37582
- method: "POST",
37583
- body: JSON.stringify({ packId })
37584
- })).json());
38243
+ let response;
38244
+ try {
38245
+ response = await this.request("/api/v1/billing/credits", {
38246
+ method: "POST",
38247
+ body: JSON.stringify({ packId, idempotencyKey: requestIdempotencyKey })
38248
+ });
38249
+ } catch {
38250
+ throw new RemoteCreditCheckoutError("CREDIT_CHECKOUT_UNCONFIRMED", 0, requestIdempotencyKey);
38251
+ }
38252
+ if (response.status === 404 || response.status === 405) {
38253
+ response.body?.cancel().catch(() => {});
38254
+ throw new RemoteRouteUnsupportedError("/api/v1/billing/credits", response.status, this.apiUrl);
38255
+ }
38256
+ let value;
38257
+ try {
38258
+ value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await readBoundedResponse(response, 4096)));
38259
+ } catch {
38260
+ throw new RemoteCreditCheckoutError("CREDIT_CHECKOUT_UNCONFIRMED", response.status, requestIdempotencyKey);
38261
+ }
38262
+ if (!response.ok) {
38263
+ const failure = creditCheckoutFailure(value, response.status, requestIdempotencyKey);
38264
+ throw new RemoteCreditCheckoutError(failure.code, response.status, requestIdempotencyKey, failure.retryAfterSeconds);
38265
+ }
38266
+ try {
38267
+ const checkout = parseRemoteCheckout(value);
38268
+ const echo = value.requestIdempotencyKey;
38269
+ if (echo !== undefined && echo !== requestIdempotencyKey)
38270
+ throw new Error("checkout request key changed");
38271
+ return { ...checkout, requestIdempotencyKey };
38272
+ } catch {
38273
+ throw new RemoteCreditCheckoutError("CREDIT_CHECKOUT_UNCONFIRMED", response.status, requestIdempotencyKey);
38274
+ }
37585
38275
  }
37586
38276
  async getUsage() {
37587
38277
  return this.arrayResponse("/api/v1/billing/usage");
@@ -37814,13 +38504,13 @@ class RemoteSkillsClient {
37814
38504
  return normalizeUpdatedSincePage(await response.json());
37815
38505
  }
37816
38506
  }
37817
- function requireOptionalString(record7, field) {
37818
- if (record7[field] === undefined)
38507
+ function requireOptionalString(record7, field2) {
38508
+ if (record7[field2] === undefined)
37819
38509
  return;
37820
- if (typeof record7[field] !== "string") {
37821
- throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
38510
+ if (typeof record7[field2] !== "string") {
38511
+ throw new Error(`Remote payload did not match the expected contract (${field2} must be a string when present)`);
37822
38512
  }
37823
- return record7[field];
38513
+ return record7[field2];
37824
38514
  }
37825
38515
  var INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
37826
38516
  function isVersionRecord(value) {
@@ -38154,10 +38844,10 @@ function recordScheduleRun(id, status, targetDir) {
38154
38844
  saveSchedules(data, targetDir);
38155
38845
  }
38156
38846
  // src/lib/revision.ts
38157
- import { createHash as createHash15 } from "crypto";
38847
+ import { createHash as createHash17 } from "crypto";
38158
38848
  var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
38159
38849
  function revisionIdOf(content) {
38160
- const canonical3 = JSON.stringify({
38850
+ const canonical4 = JSON.stringify({
38161
38851
  slug: content.slug,
38162
38852
  displayName: content.displayName,
38163
38853
  description: content.description,
@@ -38170,14 +38860,14 @@ function revisionIdOf(content) {
38170
38860
  bundleSha256: content.bundleSha256 ?? null,
38171
38861
  bundleByteSize: content.bundleByteSize ?? null
38172
38862
  });
38173
- return createHash15("sha256").update(canonical3).digest("hex");
38863
+ return createHash17("sha256").update(canonical4).digest("hex");
38174
38864
  }
38175
38865
  function revisionIdOfRecord(record7) {
38176
38866
  return revisionIdOf(record7);
38177
38867
  }
38178
38868
  // src/lib/pull.ts
38179
38869
  import { existsSync as existsSync17, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readFileSync as readFileSync20, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as writeFileSync10 } from "fs";
38180
- import { dirname as dirname15, join as join31 } from "path";
38870
+ import { dirname as dirname17, join as join31 } from "path";
38181
38871
 
38182
38872
  // src/lib/skill-bundles.ts
38183
38873
  import { createHmac, timingSafeEqual } from "crypto";
@@ -38519,7 +39209,7 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
38519
39209
  try {
38520
39210
  for (const entry of entries) {
38521
39211
  const destination = join31(staging, entry.path);
38522
- mkdirSync12(dirname15(destination), { recursive: true });
39212
+ mkdirSync12(dirname17(destination), { recursive: true });
38523
39213
  writeFileSync10(destination, entry.bytes, { mode: entry.mode });
38524
39214
  }
38525
39215
  writePullMarker(staging, {
@@ -38613,7 +39303,7 @@ function splitNameVersion(raw) {
38613
39303
  return version2 ? { name, version: version2 } : { name };
38614
39304
  }
38615
39305
  // src/lib/recurring-surface.ts
38616
- import { isAbsolute as isAbsolute13 } from "path";
39306
+ import { isAbsolute as isAbsolute16 } from "path";
38617
39307
 
38618
39308
  // src/lib/remote-invitation-recovery.ts
38619
39309
  class InvitationEmailInputError extends Error {
@@ -38711,7 +39401,7 @@ async function requestInvitationEmail(origin, action, input2) {
38711
39401
  }
38712
39402
 
38713
39403
  // src/lib/remote-private-publications.ts
38714
- import { createHash as createHash16 } from "crypto";
39404
+ import { createHash as createHash18 } from "crypto";
38715
39405
  var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
38716
39406
  var failures2 = {
38717
39407
  INVALID_REQUEST: [400, "The publication request is invalid."],
@@ -38758,7 +39448,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
38758
39448
  var record8 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
38759
39449
  var exact = (v, keys) => record8(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
38760
39450
  var date5 = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
38761
- var publicationSha256 = (bytes) => createHash16("sha256").update(bytes).digest("hex");
39451
+ var publicationSha256 = (bytes) => createHash18("sha256").update(bytes).digest("hex");
38762
39452
  function checkedPublicationDeclaration(value) {
38763
39453
  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)
38764
39454
  return bad();
@@ -39003,12 +39693,12 @@ class RemotePrivatePublicationsClient {
39003
39693
  previous = view;
39004
39694
  if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
39005
39695
  return view;
39006
- await new Promise((resolve13) => {
39696
+ await new Promise((resolve16) => {
39007
39697
  const timer = setTimeout(done, Math.min(1000, until - Date.now()));
39008
39698
  function done() {
39009
39699
  clearTimeout(timer);
39010
39700
  options.signal?.removeEventListener("abort", done);
39011
- resolve13();
39701
+ resolve16();
39012
39702
  }
39013
39703
  options.signal?.addEventListener("abort", done, { once: true });
39014
39704
  });
@@ -39272,7 +39962,7 @@ var stateSchema = exports_external.object({
39272
39962
  var uuid13 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
39273
39963
  var context = { userId: uuid13.optional(), membershipId: uuid13.optional() };
39274
39964
  var page2 = { limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() };
39275
- var directory = exports_external.string().max(4096).refine(isAbsolute13, "Use an absolute recovery directory");
39965
+ var directory = exports_external.string().max(4096).refine(isAbsolute16, "Use an absolute recovery directory");
39276
39966
  var email4 = exports_external.string().trim().toLowerCase().email().max(254);
39277
39967
  var code = exports_external.string().regex(/^\d{6}$/);
39278
39968
  var recurringSurfaceSchemas = {
@@ -39423,7 +40113,7 @@ function findSkillsParityForMcpTool(tool) {
39423
40113
  }
39424
40114
  // src/lib/registry-sync.ts
39425
40115
  import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "fs";
39426
- import { dirname as dirname16, relative as relative7 } from "path";
40116
+ import { dirname as dirname18, relative as relative7 } from "path";
39427
40117
  function createRegistrySyncArtifact(options = {}) {
39428
40118
  const profile = options.profile ?? "all";
39429
40119
  const includeDocs = options.includeDocs ?? true;
@@ -39480,7 +40170,7 @@ function createRegistrySyncArtifact(options = {}) {
39480
40170
  };
39481
40171
  }
39482
40172
  function writeRegistrySyncArtifact(path, artifact) {
39483
- mkdirSync13(dirname16(path), { recursive: true });
40173
+ mkdirSync13(dirname18(path), { recursive: true });
39484
40174
  writeFileSync11(path, `${JSON.stringify(artifact, null, 2)}
39485
40175
  `);
39486
40176
  }
@@ -39498,7 +40188,7 @@ var REMOTE_CUSTOMER_OPERATIONS = [
39498
40188
  { name: "list_remote_skills", title: "List Remote Skills", parameter: null, mutates: false, invoke: (client2) => client2.listSkills() },
39499
40189
  { name: "get_billing_status", title: "Get Billing Status", parameter: null, mutates: false, invoke: (client2) => client2.getBillingStatus() },
39500
40190
  { name: "list_credit_packs", title: "List Credit Packs", parameter: null, mutates: false, invoke: (client2) => client2.listCreditPacks() },
39501
- { name: "create_credit_checkout", title: "Create Credit Checkout", parameter: "pack_id", mutates: true, invoke: (client2, value) => client2.createCreditCheckout(value) },
40191
+ { name: "create_credit_checkout", title: "Create Credit Checkout", parameter: "pack_id", mutates: true, invoke: (client2, value, options) => client2.createCreditCheckout(value, options) },
39502
40192
  { name: "get_billing_usage", title: "Get Billing Usage", parameter: null, mutates: false, invoke: (client2) => client2.getUsage() },
39503
40193
  { name: "list_invoices", title: "List Invoices", parameter: null, mutates: false, invoke: (client2) => client2.listInvoices() },
39504
40194
  { name: "create_billing_checkout", title: "Create Billing Checkout", parameter: null, mutates: true, invoke: (client2) => client2.createBillingCheckout() },
@@ -40320,11 +41010,11 @@ var remoteCustomerContracts = REMOTE_CUSTOMER_OPERATIONS.map((operation) => ({
40320
41010
  name: operation.name,
40321
41011
  title: operation.title,
40322
41012
  description: `${operation.title} on the configured server; unavailable capabilities fail explicitly.`,
40323
- params: operation.parameter ? [operation.parameter] : [],
41013
+ params: operation.parameter ? [operation.parameter, ...operation.name === "create_credit_checkout" ? ["idempotency_key?"] : []] : [],
40324
41014
  category: "execution",
40325
41015
  sideEffects: operation.mutates ? "local-process-or-remote-run" : "none",
40326
41016
  stable: true,
40327
- inputSchema: objectSchema(operation.parameter ? { [operation.parameter]: stringSchema("Server resource identifier.") } : {}, operation.parameter ? [operation.parameter] : []),
41017
+ inputSchema: objectSchema(operation.parameter ? { [operation.parameter]: stringSchema("Server resource identifier."), ...operation.name === "create_credit_checkout" ? { idempotency_key: { type: "string", pattern: "^[A-Za-z0-9._:-]{8,255}$", description: "Caller-owned key retained before submission; reuse only for explicit recovery of the same server/account/pack." } } : {} } : {}, operation.parameter ? [operation.parameter] : []),
40328
41018
  outputSchema: { oneOf: [objectSchema({}, [], "Server response.", true), { type: "array", items: objectSchema({}, [], "Server record.", true) }] }
40329
41019
  }));
40330
41020
  remoteCustomerContracts.push({
@@ -40565,14 +41255,14 @@ function clone3(value) {
40565
41255
  }
40566
41256
  // src/lib/feedback.ts
40567
41257
  import { appendFileSync, existsSync as existsSync18, mkdirSync as mkdirSync14 } from "fs";
40568
- import { dirname as dirname17, join as join32 } from "path";
41258
+ import { dirname as dirname19, join as join32 } from "path";
40569
41259
  import { Database } from "bun:sqlite";
40570
41260
  function getFeedbackDbPath() {
40571
41261
  return join32(getDataDir(), "skills.db");
40572
41262
  }
40573
41263
  function getFeedbackDb() {
40574
41264
  const dbPath = getFeedbackDbPath();
40575
- const dir = dirname17(dbPath);
41265
+ const dir = dirname19(dbPath);
40576
41266
  if (!existsSync18(dir))
40577
41267
  mkdirSync14(dir, { recursive: true });
40578
41268
  const db = new Database(dbPath);
@@ -40601,7 +41291,7 @@ function saveFeedback(input2) {
40601
41291
  const category = input2.category ?? "general";
40602
41292
  if (isApiMode()) {
40603
41293
  const path = join32(getDataDir(), "feedback.jsonl");
40604
- const dir = dirname17(path);
41294
+ const dir = dirname19(path);
40605
41295
  if (!existsSync18(dir))
40606
41296
  mkdirSync14(dir, { recursive: true });
40607
41297
  appendFileSync(path, JSON.stringify({ message, category, email: input2.email ?? null, agent: input2.agent ?? null, version: input2.version ?? null, createdAt: new Date().toISOString() }) + `
@@ -40624,7 +41314,7 @@ function isApiMode(env = process.env) {
40624
41314
  }
40625
41315
  }
40626
41316
  // src/lib/native-storage.ts
40627
- import { createHash as createHash17, createHmac as createHmac2 } from "crypto";
41317
+ import { createHash as createHash19, createHmac as createHmac2 } from "crypto";
40628
41318
  import {
40629
41319
  existsSync as existsSync19,
40630
41320
  mkdirSync as mkdirSync15,
@@ -40633,7 +41323,7 @@ import {
40633
41323
  statSync as statSync11,
40634
41324
  writeFileSync as writeFileSync12
40635
41325
  } from "fs";
40636
- import { dirname as dirname18, join as join33, normalize as normalize3, relative as relative8, sep as sep7 } from "path";
41326
+ import { dirname as dirname20, join as join33, normalize as normalize3, relative as relative8, sep as sep7 } from "path";
40637
41327
  var SKILLS_STORAGE_TABLES = [
40638
41328
  "skills_sync_records",
40639
41329
  "skills_sync_cursors"
@@ -40800,7 +41490,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
40800
41490
  files.push({
40801
41491
  path: relativePath,
40802
41492
  sizeBytes: bytes.byteLength,
40803
- sha256: createHash17("sha256").update(bytes).digest("hex"),
41493
+ sha256: createHash19("sha256").update(bytes).digest("hex"),
40804
41494
  ...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
40805
41495
  });
40806
41496
  }
@@ -40819,18 +41509,18 @@ function importSkillsLocalSnapshot(snapshot, targetDir = process.cwd(), options
40819
41509
  skipped += 1;
40820
41510
  continue;
40821
41511
  }
40822
- const absolutePath = resolveSnapshotPath(targetDir, file2.path);
40823
- if (existsSync19(absolutePath) && !options.overwrite) {
41512
+ const absolutePath3 = resolveSnapshotPath(targetDir, file2.path);
41513
+ if (existsSync19(absolutePath3) && !options.overwrite) {
40824
41514
  skipped += 1;
40825
41515
  continue;
40826
41516
  }
40827
41517
  const bytes = Buffer.from(file2.contentBase64, "base64");
40828
- const hash4 = createHash17("sha256").update(bytes).digest("hex");
41518
+ const hash4 = createHash19("sha256").update(bytes).digest("hex");
40829
41519
  if (hash4 !== file2.sha256) {
40830
41520
  throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
40831
41521
  }
40832
- mkdirSync15(dirname18(absolutePath), { recursive: true });
40833
- writeFileSync12(absolutePath, bytes);
41522
+ mkdirSync15(dirname20(absolutePath3), { recursive: true });
41523
+ writeFileSync12(absolutePath3, bytes);
40834
41524
  written += 1;
40835
41525
  }
40836
41526
  return { written, skipped };
@@ -41157,7 +41847,7 @@ function toIsoString(value) {
41157
41847
  return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
41158
41848
  }
41159
41849
  function sha256Hex2(value) {
41160
- return createHash17("sha256").update(value).digest("hex");
41850
+ return createHash19("sha256").update(value).digest("hex");
41161
41851
  }
41162
41852
  function normalizeHeaders(headers) {
41163
41853
  const result = {};
@@ -41209,7 +41899,7 @@ function toArrayBuffer(bytes) {
41209
41899
  return buffer;
41210
41900
  }
41211
41901
  // src/lib/station-snapshot.ts
41212
- import { createHash as createHash18 } from "crypto";
41902
+ import { createHash as createHash20 } from "crypto";
41213
41903
  import {
41214
41904
  copyFileSync,
41215
41905
  mkdirSync as mkdirSync16,
@@ -41217,7 +41907,7 @@ import {
41217
41907
  statSync as statSync13,
41218
41908
  writeFileSync as writeFileSync13
41219
41909
  } from "fs";
41220
- import { dirname as dirname19, isAbsolute as isAbsolute14, relative as relative9, resolve as resolve13, sep as sep9 } from "path";
41910
+ import { dirname as dirname21, isAbsolute as isAbsolute17, relative as relative9, resolve as resolve16, sep as sep9 } from "path";
41221
41911
 
41222
41912
  // src/lib/portable-snapshot-filter.ts
41223
41913
  import { readdirSync as readdirSync14, statSync as statSync12 } from "fs";
@@ -41384,7 +42074,7 @@ function validateStationId(stationId) {
41384
42074
  }
41385
42075
  }
41386
42076
  function sha256File(filePath) {
41387
- return createHash18("sha256").update(readFileSync23(filePath)).digest("hex");
42077
+ return createHash20("sha256").update(readFileSync23(filePath)).digest("hex");
41388
42078
  }
41389
42079
  function scanHome(definition, homesRoot) {
41390
42080
  const homePath = homePathFor(definition, homesRoot);
@@ -41455,7 +42145,7 @@ function humanHomes(scanned) {
41455
42145
  }));
41456
42146
  }
41457
42147
  function writeStationSnapshot(options) {
41458
- const repoRoot = resolve13(options.repoRoot ?? process.cwd());
42148
+ const repoRoot = resolve16(options.repoRoot ?? process.cwd());
41459
42149
  const { scanned, plans, totalBytes } = planStationSnapshot(options);
41460
42150
  const manifestFiles = plans.map((plan) => ({
41461
42151
  relativePath: plan.source.relativePath,
@@ -41481,9 +42171,9 @@ function writeStationSnapshot(options) {
41481
42171
  const conflicts = [];
41482
42172
  const untouched = [];
41483
42173
  for (const plan of plans) {
41484
- const destination = resolve13(repoRoot, plan.destination);
42174
+ const destination = resolve16(repoRoot, plan.destination);
41485
42175
  const destinationRelative = relative9(repoRoot, destination);
41486
- if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep9) || isAbsolute14(destinationRelative)) {
42176
+ if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep9) || isAbsolute17(destinationRelative)) {
41487
42177
  throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
41488
42178
  }
41489
42179
  let existingDigest = null;
@@ -41504,8 +42194,8 @@ function writeStationSnapshot(options) {
41504
42194
  }
41505
42195
  let written = 0;
41506
42196
  for (const plan of untouched) {
41507
- const destination = resolve13(repoRoot, plan.destination);
41508
- mkdirSync16(dirname19(destination), { recursive: true });
42197
+ const destination = resolve16(repoRoot, plan.destination);
42198
+ mkdirSync16(dirname21(destination), { recursive: true });
41509
42199
  copyFileSync(plan.source.fullPath, destination);
41510
42200
  written += 1;
41511
42201
  }
@@ -41523,8 +42213,8 @@ function writeStationSnapshot(options) {
41523
42213
  },
41524
42214
  files: manifestFiles
41525
42215
  };
41526
- const manifestPath = resolve13(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
41527
- mkdirSync16(dirname19(manifestPath), { recursive: true });
42216
+ const manifestPath = resolve16(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
42217
+ mkdirSync16(dirname21(manifestPath), { recursive: true });
41528
42218
  writeFileSync13(manifestPath, `${JSON.stringify(manifest, null, 2)}
41529
42219
  `);
41530
42220
  return {
@@ -41535,7 +42225,7 @@ function writeStationSnapshot(options) {
41535
42225
  };
41536
42226
  }
41537
42227
  // src/lib/station-hydrate.ts
41538
- import { createHash as createHash19 } from "crypto";
42228
+ import { createHash as createHash21 } from "crypto";
41539
42229
  import {
41540
42230
  copyFileSync as copyFileSync2,
41541
42231
  mkdirSync as mkdirSync17,
@@ -41544,7 +42234,7 @@ import {
41544
42234
  statSync as statSync14,
41545
42235
  writeFileSync as writeFileSync14
41546
42236
  } from "fs";
41547
- import { dirname as dirname20, join as join35, resolve as resolve14, sep as sep10 } from "path";
42237
+ import { dirname as dirname22, join as join35, resolve as resolve17, sep as sep10 } from "path";
41548
42238
  var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
41549
42239
  var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
41550
42240
  var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
@@ -41741,12 +42431,12 @@ function skillSha256(skill) {
41741
42431
  return sha256File(skill.files[0].winner.fullPath);
41742
42432
  }
41743
42433
  const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
41744
- return createHash19("sha256").update(joined.sort().join(`
42434
+ return createHash21("sha256").update(joined.sort().join(`
41745
42435
  `)).digest("hex");
41746
42436
  }
41747
42437
  function writeStationHydration(options) {
41748
- const repoRoot = resolve14(options.repoRoot ?? process.cwd());
41749
- const cacheRoot = resolve14(options.cacheRoot ?? resolveCorpusRoot());
42438
+ const repoRoot = resolve17(options.repoRoot ?? process.cwd());
42439
+ const cacheRoot = resolve17(options.cacheRoot ?? resolveCorpusRoot());
41750
42440
  const plan = planStationHydration(options.stationId, repoRoot);
41751
42441
  const resultSkills = plan.winners.map((skill) => ({
41752
42442
  ident: skill.ident,
@@ -41800,7 +42490,7 @@ function writeStationHydration(options) {
41800
42490
  }
41801
42491
  let written = 0;
41802
42492
  for (const entry of toWrite) {
41803
- mkdirSync17(dirname20(entry.destination), { recursive: true });
42493
+ mkdirSync17(dirname22(entry.destination), { recursive: true });
41804
42494
  copyFileSync2(entry.fullPath, entry.destination);
41805
42495
  written += 1;
41806
42496
  }
@@ -41821,8 +42511,8 @@ function writeStationHydration(options) {
41821
42511
  },
41822
42512
  skills: resultSkills
41823
42513
  };
41824
- const hydrationManifestPath = join35(dirname20(cacheRoot), `hydration-${options.stationId}.json`);
41825
- mkdirSync17(dirname20(hydrationManifestPath), { recursive: true });
42514
+ const hydrationManifestPath = join35(dirname22(cacheRoot), `hydration-${options.stationId}.json`);
42515
+ mkdirSync17(dirname22(hydrationManifestPath), { recursive: true });
41826
42516
  writeFileSync14(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
41827
42517
  `);
41828
42518
  return {
@@ -41833,23 +42523,23 @@ function writeStationHydration(options) {
41833
42523
  };
41834
42524
  }
41835
42525
  // src/lib/private-publication-recovery.ts
41836
- import { constants as constants9, closeSync as closeSync10, fsyncSync as fsyncSync3, fstatSync as fstatSync10, lstatSync as lstatSync18, mkdirSync as mkdirSync18, openSync as openSync10, readSync as readSync8, realpathSync as realpathSync6, renameSync as renameSync7, unlinkSync as unlinkSync5, writeFileSync as writeFileSync15 } from "fs";
41837
- import { dirname as dirname21, isAbsolute as isAbsolute15, join as join36, resolve as resolve15 } from "path";
42526
+ import { constants as constants11, closeSync as closeSync12, fsyncSync as fsyncSync4, fstatSync as fstatSync12, lstatSync as lstatSync20, mkdirSync as mkdirSync18, openSync as openSync12, readSync as readSync10, realpathSync as realpathSync6, renameSync as renameSync7, unlinkSync as unlinkSync5, writeFileSync as writeFileSync15 } from "fs";
42527
+ import { dirname as dirname23, isAbsolute as isAbsolute18, join as join36, resolve as resolve18 } from "path";
41838
42528
  import { randomUUID as randomUUID4 } from "crypto";
41839
42529
  var fail2 = () => {
41840
42530
  throw new PrivatePublicationError("PUBLICATION_RECOVERY_INVALID", "The recovery directory is invalid or changed. Preserve it and inspect the existing intent; do not start a replacement automatically.");
41841
42531
  };
41842
42532
  function safeDirectory(directory2) {
41843
- if (!isAbsolute15(directory2) || directory2 !== resolve15(directory2) || realpathSync6(directory2) !== directory2)
42533
+ if (!isAbsolute18(directory2) || directory2 !== resolve18(directory2) || realpathSync6(directory2) !== directory2)
41844
42534
  return fail2();
41845
- for (let path = directory2;; path = dirname21(path)) {
41846
- const stat2 = lstatSync18(path);
42535
+ for (let path = directory2;; path = dirname23(path)) {
42536
+ const stat2 = lstatSync20(path);
41847
42537
  if (!stat2.isDirectory() || stat2.isSymbolicLink())
41848
42538
  return fail2();
41849
- if (path === dirname21(path))
42539
+ if (path === dirname23(path))
41850
42540
  break;
41851
42541
  }
41852
- const own2 = lstatSync18(directory2);
42542
+ const own2 = lstatSync20(directory2);
41853
42543
  if ((own2.mode & 63) !== 0 || process.getuid && own2.uid !== process.getuid())
41854
42544
  return fail2();
41855
42545
  return { dev: own2.dev, ino: own2.ino };
@@ -41861,35 +42551,35 @@ function unchangedDirectory(directory2, identity3) {
41861
42551
  }
41862
42552
  function readOwned(directory2, name, max) {
41863
42553
  const identity3 = safeDirectory(directory2), file2 = join36(directory2, name);
41864
- const fd = openSync10(file2, constants9.O_RDONLY | constants9.O_NOFOLLOW);
42554
+ const fd = openSync12(file2, constants11.O_RDONLY | constants11.O_NOFOLLOW);
41865
42555
  try {
41866
- const stat2 = fstatSync10(fd);
42556
+ const stat2 = fstatSync12(fd);
41867
42557
  if (!stat2.isFile() || stat2.nlink !== 1 || stat2.size > max || stat2.size < 1 || (stat2.mode & 63) !== 0 || process.getuid && stat2.uid !== process.getuid())
41868
42558
  return fail2();
41869
42559
  const buffer = Buffer.alloc(Math.min(max, stat2.size) + 1);
41870
42560
  let length = 0;
41871
42561
  while (length < buffer.length) {
41872
- const count = readSync8(fd, buffer, length, buffer.length - length, length);
42562
+ const count = readSync10(fd, buffer, length, buffer.length - length, length);
41873
42563
  if (count === 0)
41874
42564
  break;
41875
42565
  length += count;
41876
42566
  }
41877
- const bytes = buffer.subarray(0, length), after = fstatSync10(fd);
42567
+ const bytes = buffer.subarray(0, length), after = fstatSync12(fd);
41878
42568
  unchangedDirectory(directory2, identity3);
41879
42569
  if (bytes.length !== stat2.size || stat2.size !== after.size || stat2.mtimeMs !== after.mtimeMs || stat2.ctimeMs !== after.ctimeMs)
41880
42570
  return fail2();
41881
42571
  return bytes;
41882
42572
  } finally {
41883
- closeSync10(fd);
42573
+ closeSync12(fd);
41884
42574
  }
41885
42575
  }
41886
42576
  function writeOwned(directory2, name, bytes) {
41887
- const fd = openSync10(join36(directory2, name), constants9.O_WRONLY | constants9.O_CREAT | constants9.O_EXCL | constants9.O_NOFOLLOW, 384);
42577
+ const fd = openSync12(join36(directory2, name), constants11.O_WRONLY | constants11.O_CREAT | constants11.O_EXCL | constants11.O_NOFOLLOW, 384);
41888
42578
  try {
41889
42579
  writeFileSync15(fd, bytes);
41890
- fsyncSync3(fd);
42580
+ fsyncSync4(fd);
41891
42581
  } finally {
41892
- closeSync10(fd);
42582
+ closeSync12(fd);
41893
42583
  }
41894
42584
  }
41895
42585
  function save(directory2, value) {
@@ -41898,11 +42588,11 @@ function save(directory2, value) {
41898
42588
  `);
41899
42589
  unchangedDirectory(directory2, identity3);
41900
42590
  renameSync7(join36(directory2, temporary), join36(directory2, "receipt.json"));
41901
- const fd = openSync10(directory2, constants9.O_RDONLY | constants9.O_NOFOLLOW);
42591
+ const fd = openSync12(directory2, constants11.O_RDONLY | constants11.O_NOFOLLOW);
41902
42592
  try {
41903
- fsyncSync3(fd);
42593
+ fsyncSync4(fd);
41904
42594
  } finally {
41905
- closeSync10(fd);
42595
+ closeSync12(fd);
41906
42596
  }
41907
42597
  }
41908
42598
  function bind(client2, receipt) {
@@ -41913,20 +42603,20 @@ async function locked(directory2, action) {
41913
42603
  const identity3 = safeDirectory(directory2), file2 = join36(directory2, "operation.lock");
41914
42604
  let fd;
41915
42605
  try {
41916
- fd = openSync10(file2, constants9.O_WRONLY | constants9.O_CREAT | constants9.O_EXCL | constants9.O_NOFOLLOW, 384);
42606
+ fd = openSync12(file2, constants11.O_WRONLY | constants11.O_CREAT | constants11.O_EXCL | constants11.O_NOFOLLOW, 384);
41917
42607
  } catch {
41918
42608
  throw new PrivatePublicationError("PUBLICATION_RECOVERY_BUSY", "Another operation holds this recovery directory. If it crashed, confirm that process has stopped before explicitly removing operation.lock and resuming.");
41919
42609
  }
41920
- const lock = fstatSync10(fd);
42610
+ const lock = fstatSync12(fd);
41921
42611
  try {
41922
42612
  writeFileSync15(fd, JSON.stringify({ pid: process.pid }) + `
41923
42613
  `);
41924
- fsyncSync3(fd);
42614
+ fsyncSync4(fd);
41925
42615
  return await action();
41926
42616
  } finally {
41927
- closeSync10(fd);
42617
+ closeSync12(fd);
41928
42618
  unchangedDirectory(directory2, identity3);
41929
- const now = lstatSync18(file2);
42619
+ const now = lstatSync20(file2);
41930
42620
  if (now.dev !== lock.dev || now.ino !== lock.ino || !now.isFile() || now.isSymbolicLink())
41931
42621
  fail2();
41932
42622
  unlinkSync5(file2);
@@ -41978,15 +42668,15 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
41978
42668
  phase: "prepared",
41979
42669
  intent: null
41980
42670
  };
41981
- if (!isAbsolute15(recoveryDirectory) || resolve15(recoveryDirectory) !== recoveryDirectory || realpathSync6(dirname21(recoveryDirectory)) !== dirname21(recoveryDirectory))
42671
+ if (!isAbsolute18(recoveryDirectory) || resolve18(recoveryDirectory) !== recoveryDirectory || realpathSync6(dirname23(recoveryDirectory)) !== dirname23(recoveryDirectory))
41982
42672
  return fail2();
41983
42673
  mkdirSync18(recoveryDirectory, { mode: 448 });
41984
42674
  safeDirectory(recoveryDirectory);
41985
- const parent = openSync10(dirname21(recoveryDirectory), constants9.O_RDONLY | constants9.O_NOFOLLOW);
42675
+ const parent = openSync12(dirname23(recoveryDirectory), constants11.O_RDONLY | constants11.O_NOFOLLOW);
41986
42676
  try {
41987
- fsyncSync3(parent);
42677
+ fsyncSync4(parent);
41988
42678
  } finally {
41989
- closeSync10(parent);
42679
+ closeSync12(parent);
41990
42680
  }
41991
42681
  writeOwned(recoveryDirectory, "bundle.tgz", packed.bytes);
41992
42682
  save(recoveryDirectory, receipt);
@@ -42069,8 +42759,8 @@ async function inspectLocked(client2, directory2, cancel) {
42069
42759
  return privatePublicationResult(directory2, receipt);
42070
42760
  }
42071
42761
  // src/lib/skill-context.ts
42072
- import { createHash as createHash20 } from "crypto";
42073
- import { resolve as resolve16 } from "path";
42762
+ import { createHash as createHash22 } from "crypto";
42763
+ import { resolve as resolve19 } from "path";
42074
42764
  function words(value) {
42075
42765
  return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
42076
42766
  }
@@ -42105,7 +42795,7 @@ async function buildSkillContext(input2, options = {}) {
42105
42795
  const promptWords = words(prompt);
42106
42796
  const promptNames = new Set(prompt.toLowerCase().match(/[a-z0-9]+(?:-[a-z0-9]+)*/g) ?? []);
42107
42797
  const loaded = new Set(resolved.session?.loaded ?? []);
42108
- const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve16(input2.cwd)] : []];
42798
+ const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve19(input2.cwd)] : []];
42109
42799
  const candidates = profile.selections.map((selection) => {
42110
42800
  const key = selectionKey(selection);
42111
42801
  if (input2.restore && loaded.has(key))
@@ -42158,7 +42848,7 @@ ${content}`;
42158
42848
  `);
42159
42849
  const receipt = {
42160
42850
  schemaVersion: 1,
42161
- id: createHash20("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
42851
+ id: createHash22("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
42162
42852
  profileId: profile.profileId,
42163
42853
  profileRevision: profile.profileRevision,
42164
42854
  authority: profile.authority,
@@ -42170,9 +42860,105 @@ ${content}`;
42170
42860
  selections
42171
42861
  };
42172
42862
  if (sessionId)
42173
- writeSelectionJson(sessionReceiptPath(sessionId, options), { ...resolved.receipt, sessionId, loaded: [...loaded] });
42863
+ writeSkillSession({ ...resolved.receipt, sessionId, loaded: [...loaded] }, { current: resolved.sessionSnapshot, parent: resolved.parentSnapshot }, options);
42174
42864
  return { context: context2, selections, omitted, receipt };
42175
42865
  }
42866
+ // src/lib/session-reconciliation.ts
42867
+ import { createHash as createHash23 } from "crypto";
42868
+ var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
42869
+ var digest3 = (value) => createHash23("sha256").update(JSON.stringify(value)).digest("hex");
42870
+ function exactTimestamp(value) {
42871
+ const parsed = value === undefined ? Number.NaN : Date.parse(value);
42872
+ if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
42873
+ throw new SkillSelectionError("SESSION_PLAN_WINDOW_INVALID", "The reviewed plan must include its exact canonical issuedAt and expiresAt timestamps.");
42874
+ }
42875
+ return parsed;
42876
+ }
42877
+ function assertPlanWindow(issuedAt, expiresAt, now) {
42878
+ const issued = exactTimestamp(issuedAt), expires = exactTimestamp(expiresAt);
42879
+ if (expires - issued !== SESSION_RECONCILIATION_PLAN_TTL_MS) {
42880
+ throw new SkillSelectionError("SESSION_PLAN_WINDOW_INVALID", "A session reconciliation approval is valid for exactly five minutes and cannot be extended by the caller.");
42881
+ }
42882
+ if (now < issued || now >= expires) {
42883
+ throw new SkillSelectionError("SESSION_PLAN_EXPIRED", "The reviewed session reconciliation plan is not currently valid; inspect the receipt and prepare a new plan.");
42884
+ }
42885
+ }
42886
+ function inspectSkillSession(sessionId, options = {}) {
42887
+ const snapshot = readSkillSessionSnapshot(sessionId, options), { receipt } = snapshot;
42888
+ return {
42889
+ sessionId,
42890
+ path: snapshot.path,
42891
+ receiptSha256: snapshot.sha256,
42892
+ generation: snapshot.generation,
42893
+ verifiedAt: receipt.verifiedAt,
42894
+ authority: receipt.profile.authority,
42895
+ workspaceId: receipt.profile.workspaceId,
42896
+ profileId: receipt.profile.profileId,
42897
+ profileRevision: receipt.profile.profileRevision,
42898
+ selectionCount: receipt.profile.selections.length,
42899
+ loadedCount: receipt.loaded.length
42900
+ };
42901
+ }
42902
+ async function reconcileSkillSession(input2, options = {}) {
42903
+ if (![input2.fromProfile, input2.fromRevision, input2.selectionProfile, input2.profileRevision].every((value) => typeof value === "string" && value.trim()) || !/^[a-f0-9]{64}$/.test(input2.receiptSha256)) {
42904
+ throw new SkillSelectionError("INVALID_SESSION_RECONCILIATION", "Name the exact old receipt SHA256, old profile/revision and intended target profile/revision.");
42905
+ }
42906
+ if (input2.apply && (!input2.planDigest || !input2.planIssuedAt || !input2.planExpiresAt)) {
42907
+ throw new SkillSelectionError("SESSION_PLAN_REQUIRED", "Review the session reconciliation plan and provide its exact digest, issuedAt and expiresAt before applying it.");
42908
+ }
42909
+ if (!input2.apply && (input2.planDigest !== undefined || input2.planIssuedAt !== undefined || input2.planExpiresAt !== undefined)) {
42910
+ throw new SkillSelectionError("SESSION_PLAN_WINDOW_INVALID", "Plan timestamps are minted by the planner and may only be replayed with --apply.");
42911
+ }
42912
+ const now = (options.now ?? Date.now)();
42913
+ const issuedAt = input2.apply ? input2.planIssuedAt : new Date(now).toISOString();
42914
+ const expiresAt = input2.apply ? input2.planExpiresAt : new Date(now + SESSION_RECONCILIATION_PLAN_TTL_MS).toISOString();
42915
+ if (input2.apply)
42916
+ assertPlanWindow(issuedAt, expiresAt, now);
42917
+ const snapshot = readSkillSessionSnapshot(input2.sessionId, options), old = snapshot.receipt;
42918
+ if (snapshot.sha256 !== input2.receiptSha256 || old.profile.profileId !== input2.fromProfile || old.profile.profileRevision !== input2.fromRevision) {
42919
+ throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The current session receipt does not match the reviewed old bytes, profile and revision.");
42920
+ }
42921
+ const client2 = options.client ?? await createProfileClient();
42922
+ const target = structuredClone(await client2.resolveProfile(input2.selectionProfile));
42923
+ validateResolvedProfile(target, client2.authority);
42924
+ if (target.profileId !== input2.selectionProfile || target.profileRevision !== input2.profileRevision) {
42925
+ throw new SkillSelectionError("SESSION_TARGET_CHANGED", "The API selection profile does not match the intended target revision; review the current selection before proceeding.");
42926
+ }
42927
+ if (old.profile.authority !== target.authority || old.profile.workspaceId !== target.workspaceId) {
42928
+ throw new SkillSelectionError("PROFILE_IDENTITY_MISMATCH", "A session reconciliation cannot change its Skills authority or workspace.");
42929
+ }
42930
+ const targetKeys = new Set(target.selections.map(selectionKey));
42931
+ const loaded = old.loaded.filter((key) => targetKeys.has(key));
42932
+ const replacementGeneration = nextSkillSessionGeneration(snapshot.generation);
42933
+ const plan = {
42934
+ schemaVersion: 1,
42935
+ issuedAt,
42936
+ expiresAt,
42937
+ sessionId: input2.sessionId,
42938
+ sessionPath: snapshot.path,
42939
+ before: { receiptSha256: snapshot.sha256, generation: snapshot.generation, profileId: old.profile.profileId, profileRevision: old.profile.profileRevision, selectionCount: old.profile.selections.length },
42940
+ target: { authority: target.authority, workspaceId: target.workspaceId, profileId: target.profileId, profileRevision: target.profileRevision, profileSha256: digest3(target), selectionCount: target.selections.length },
42941
+ replacementGeneration,
42942
+ retainedLoadedCount: loaded.length,
42943
+ retiredLoadedCount: old.loaded.length - loaded.length,
42944
+ scope: "Only this receipt; existing child sessions, project locks, shared profiles, hooks and running processes are unchanged."
42945
+ };
42946
+ const planDigest = digest3(plan);
42947
+ if (input2.planDigest !== undefined && input2.planDigest !== planDigest)
42948
+ throw new SkillSelectionError("SESSION_PLAN_CHANGED", "The session reconciliation plan differs from the reviewed plan.");
42949
+ if (!input2.apply)
42950
+ return { applied: false, plan, planDigest, archivePath: undefined, receiptPath: undefined, beforeSha256: snapshot.sha256, afterSha256: undefined };
42951
+ assertPlanWindow(issuedAt, expiresAt, (options.now ?? Date.now)());
42952
+ const replacement = {
42953
+ ...old,
42954
+ generation: replacementGeneration,
42955
+ verifiedAt: new Date((options.now ?? Date.now)()).toISOString(),
42956
+ profile: target,
42957
+ loaded
42958
+ };
42959
+ const result = replaceSkillSession(skillSessionSnapshotBinding(snapshot), replacement, { ...plan, planDigest }, options, () => assertPlanWindow(issuedAt, expiresAt, (options.now ?? Date.now)()));
42960
+ return { applied: true, plan, planDigest, ...result };
42961
+ }
42176
42962
  export {
42177
42963
  writeStationSnapshot,
42178
42964
  writeStationHydration,
@@ -42237,6 +43023,7 @@ export {
42237
43023
  removeSchedule,
42238
43024
  removeManagedAgentSkill,
42239
43025
  recordScheduleRun,
43026
+ reconcileSkillSession,
42240
43027
  readPrivatePublicationRecovery,
42241
43028
  readPortableSkillManifest,
42242
43029
  pullSkills,
@@ -42292,6 +43079,7 @@ export {
42292
43079
  installSkillManifest,
42293
43080
  installSkillForAgent,
42294
43081
  installSkill,
43082
+ inspectSkillSession,
42295
43083
  inspectPrivatePublication,
42296
43084
  importSkillsLocalSnapshot,
42297
43085
  homePathFor,
@@ -42367,6 +43155,8 @@ export {
42367
43155
  captureDiscoveryPathSources,
42368
43156
  captureDiscoveryDirectories,
42369
43157
  captureDiscoveryByteSources,
43158
+ captureClaudeSettings,
43159
+ captureClaudeMarketplaceRegistry,
42370
43160
  canonicalizeManifest,
42371
43161
  buildSkillsS3ObjectUrl,
42372
43162
  buildSkillsApiUrl,
@@ -42430,6 +43220,7 @@ export {
42430
43220
  RemotePrivatePublicationsClient,
42431
43221
  RemoteInvitationEmailUnconfirmedError,
42432
43222
  RemoteInvitationEmailError,
43223
+ RemoteCreditCheckoutError,
42433
43224
  RemoteCreditApprovalError,
42434
43225
  RemoteCapabilityUnavailableError,
42435
43226
  REMOTE_SKILL_RUN_CONTRACT_VERSION,