@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/bin/mcp.js CHANGED
@@ -20019,7 +20019,7 @@ var package_default;
20019
20019
  var init_package = __esm(() => {
20020
20020
  package_default = {
20021
20021
  name: "@hasna/skills",
20022
- version: "0.8.10",
20022
+ version: "0.8.12",
20023
20023
  description: "Skills library for AI coding agents",
20024
20024
  type: "module",
20025
20025
  bin: {
@@ -20081,10 +20081,11 @@ var init_package = __esm(() => {
20081
20081
  typecheck: "tsc --noEmit",
20082
20082
  "verify:release": "bun run scripts/release-guard.ts",
20083
20083
  "verify:consumer-types": "bun run scripts/consumer-types.ts",
20084
- prepare: "bun run build:js",
20085
- prepack: "bun run build && bun run verify:release && bun run verify:consumer-types",
20084
+ prepack: "bun run verify:generated && bun run verify:release && bun run verify:consumer-types",
20086
20085
  prepublishOnly: "bun run verify:producer && bun run typecheck && bun run test",
20087
- "verify:producer": "bun run scripts/release-dependencies.ts"
20086
+ "verify:producer": "bun run scripts/release-dependencies.ts",
20087
+ "verify:generated": "HASNA_SKILLS_RELEASE_ARTIFACT_TEST=1 bun test src/lib/release-artifacts.test.ts",
20088
+ "verify:consumer-credentials": "bun run scripts/consumer-credentials.ts"
20088
20089
  },
20089
20090
  keywords: [
20090
20091
  "skills",
@@ -20106,7 +20107,6 @@ var init_package = __esm(() => {
20106
20107
  license: "Apache-2.0",
20107
20108
  devDependencies: {
20108
20109
  "@hasna/contracts": "1.1.0",
20109
- "@hasna/secrets": "0.4.2",
20110
20110
  "@types/bun": "1.3.14",
20111
20111
  "@types/node": "25.2.3",
20112
20112
  "@types/react": "^18.2.0",
@@ -20119,6 +20119,7 @@ var init_package = __esm(() => {
20119
20119
  "@aws-sdk/client-ecs": "^3.1079.0",
20120
20120
  "@aws-sdk/client-s3": "^3.1079.0",
20121
20121
  "@hasna/events": "0.1.18",
20122
+ "@hasna/secrets": "0.4.2",
20122
20123
  "@modelcontextprotocol/sdk": "^1.26.0",
20123
20124
  chalk: "^5.3.0",
20124
20125
  commander: "^12.1.0",
@@ -21107,6 +21108,7 @@ __export(exports_fleet_credentials, {
21107
21108
  SKILLS_API_URL_ENV: () => SKILLS_API_URL_ENV,
21108
21109
  SKILLS_API_KEY_ENV_KEYS: () => SKILLS_API_KEY_ENV_KEYS,
21109
21110
  SKILLS_API_KEY_ENV: () => SKILLS_API_KEY_ENV,
21111
+ REDACTED_VAULT_REFERENCE: () => REDACTED_VAULT_REFERENCE,
21110
21112
  MissingSkillsFleetError: () => MissingSkillsFleetError
21111
21113
  });
21112
21114
  function isCredentialResolutionError(error61) {
@@ -21115,10 +21117,15 @@ function isCredentialResolutionError(error61) {
21115
21117
  function isSkillsFleetCredentialError(error61) {
21116
21118
  return error61 instanceof SkillsFleetCredentialError || typeof error61 === "object" && error61 !== null && error61.name === "SkillsFleetCredentialError";
21117
21119
  }
21118
- function asSkillsFleetCredentialError(error61) {
21120
+ function redactVaultReference(message, vaultReference) {
21121
+ if (!vaultReference)
21122
+ return message;
21123
+ return message.split(vaultReference).join(REDACTED_VAULT_REFERENCE);
21124
+ }
21125
+ function asSkillsFleetCredentialError(error61, vaultReference) {
21119
21126
  if (!isCredentialResolutionError(error61))
21120
21127
  return null;
21121
- return new SkillsFleetCredentialError(error61.message, "MISSING_API_CREDENTIAL");
21128
+ return new SkillsFleetCredentialError(redactVaultReference(error61.message, vaultReference), "MISSING_API_CREDENTIAL");
21122
21129
  }
21123
21130
  function normalizeSkillsApiOrigin(apiUrl) {
21124
21131
  const url2 = new URL(apiUrl);
@@ -21305,7 +21312,7 @@ async function resolveSkillsConnection(env = process.env, options = {}) {
21305
21312
  try {
21306
21313
  completed = await completePointerCredential(SKILLS_APP, pointer, env);
21307
21314
  } catch (error61) {
21308
- const translated = asSkillsFleetCredentialError(error61);
21315
+ const translated = asSkillsFleetCredentialError(error61, pointer.pointerVaultKey);
21309
21316
  if (translated)
21310
21317
  throw translated;
21311
21318
  throw error61;
@@ -21361,7 +21368,7 @@ function requireSkillsFleet(action = "This command", env = process.env, options
21361
21368
  return fleet;
21362
21369
  throw new MissingSkillsFleetError(action);
21363
21370
  }
21364
- var SKILLS_APP = "skills", ENV_KEYS, SKILLS_API_URL_ENV_KEYS, SKILLS_API_KEY_ENV_KEYS, SKILLS_API_URL_ENV, SKILLS_API_KEY_ENV, SkillsFleetCredentialError, localNoticePrinted = false, MissingSkillsFleetError;
21371
+ var SKILLS_APP = "skills", ENV_KEYS, SKILLS_API_URL_ENV_KEYS, SKILLS_API_KEY_ENV_KEYS, SKILLS_API_URL_ENV, SKILLS_API_KEY_ENV, SkillsFleetCredentialError, REDACTED_VAULT_REFERENCE = "[redacted vault reference]", localNoticePrinted = false, MissingSkillsFleetError;
21365
21372
  var init_fleet_credentials = __esm(() => {
21366
21373
  init_transport();
21367
21374
  init_instance_credentials();
@@ -39490,14 +39497,15 @@ function profileSelectionSnapshot(selections) {
39490
39497
  var MAX_PROFILE_SELECTIONS = 4096, MAX_PROFILE_DOCUMENT_BYTES, MAX_SKILL_SESSION_ID_CHARS = 256, PROFILE_SESSION_ENVELOPE_BYTES, MAX_RESOLVED_PROFILE_BYTES, LEGACY_PROFILE_SELECTIONS = 256, LEGACY_PROFILE_DOCUMENT_BYTES = 1e6;
39491
39498
  var init_profile_limits = __esm(() => {
39492
39499
  MAX_PROFILE_DOCUMENT_BYTES = 8 * 1024 * 1024;
39493
- PROFILE_SESSION_ENVELOPE_BYTES = MAX_PROFILE_SELECTIONS * 67 + MAX_SKILL_SESSION_ID_CHARS * 6 + 256;
39500
+ PROFILE_SESSION_ENVELOPE_BYTES = MAX_PROFILE_SELECTIONS * 67 + MAX_SKILL_SESSION_ID_CHARS * 12 + 512;
39494
39501
  MAX_RESOLVED_PROFILE_BYTES = MAX_PROFILE_DOCUMENT_BYTES - PROFILE_SESSION_ENVELOPE_BYTES;
39495
39502
  });
39496
39503
 
39497
39504
  // src/lib/selection-cache.ts
39498
39505
  import { createHash as createHash3, randomUUID } from "crypto";
39499
- import { closeSync as closeSync5, constants as constants2, fstatSync as fstatSync5, lstatSync as lstatSync5, mkdirSync as mkdirSync4, openSync as openSync5, readSync as readSync2, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
39506
+ import { closeSync as closeSync5, constants as constants2, fstatSync as fstatSync5, fsyncSync, lstatSync as lstatSync5, mkdirSync as mkdirSync4, openSync as openSync5, readSync as readSync2, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
39500
39507
  import { dirname as dirname5, join as join13, parse as parse6, resolve as resolve3 } from "path";
39508
+ import { isDeepStrictEqual } from "util";
39501
39509
  function selectionCacheRoot(options = {}) {
39502
39510
  return resolve3(options.cacheDir ?? join13(getDataDirReadOnly(), "selection-cache"));
39503
39511
  }
@@ -39601,12 +39609,21 @@ function writeSelectionJson(path, value) {
39601
39609
  throw new SkillSelectionError("RECEIPT_TOO_LARGE", "The Skills selection receipt exceeds its size limit.");
39602
39610
  atomicWrite(path, bytes, 384);
39603
39611
  }
39604
- function atomicWrite(path, bytes, mode) {
39612
+ function atomicWrite(path, bytes, mode, durable = false) {
39605
39613
  assertRegularPath(path, true);
39606
39614
  const temporary = join13(dirname5(path), `.selection-${randomUUID()}.tmp`);
39607
39615
  try {
39608
- writeFileSync4(temporary, bytes, { flag: "wx", mode });
39616
+ const fd = openSync5(temporary, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW, mode);
39617
+ try {
39618
+ writeFileSync4(fd, bytes);
39619
+ if (durable)
39620
+ fsyncSync(fd);
39621
+ } finally {
39622
+ closeSync5(fd);
39623
+ }
39609
39624
  renameSync2(temporary, path);
39625
+ if (durable)
39626
+ syncDirectory(dirname5(path));
39610
39627
  } finally {
39611
39628
  try {
39612
39629
  unlinkSync(temporary);
@@ -39719,16 +39736,130 @@ function sessionReceiptPath(sessionId, options = {}) {
39719
39736
  throw new SkillSelectionError("INVALID_SESSION", `A Skills session id must contain 1\u2013${MAX_SKILL_SESSION_ID_CHARS} characters.`);
39720
39737
  return join13(selectionCacheRoot(options), "sessions", `${hash2(sessionId)}.json`);
39721
39738
  }
39722
- function readSkillSession(sessionId, options = {}) {
39723
- const receipt = readSelectionJson(sessionReceiptPath(sessionId, options));
39724
- if (receipt) {
39725
- validateSelectionReceipt(receipt);
39726
- const keys = new Set(receipt.profile.selections.map(selectionKey));
39727
- 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))) {
39728
- throw new SkillSelectionError("INVALID_RECEIPT", "The Skills session receipt is invalid.");
39739
+ function skillSessionGeneration(receipt) {
39740
+ return receipt.generation ?? 0;
39741
+ }
39742
+ function validateSkillSessionBinding(binding) {
39743
+ 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)) {
39744
+ throw new SkillSelectionError("INVALID_RECEIPT", "The Skills session receipt contains an invalid parent snapshot binding.");
39745
+ }
39746
+ }
39747
+ function validateSkillSessionReceipt(receipt, sessionId) {
39748
+ validateSelectionReceipt(receipt);
39749
+ const keys = new Set(receipt.profile.selections.map(selectionKey));
39750
+ 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)) {
39751
+ throw new SkillSelectionError("INVALID_RECEIPT", "The Skills session receipt is invalid.");
39752
+ }
39753
+ if (receipt.parent) {
39754
+ validateSkillSessionBinding(receipt.parent);
39755
+ if (receipt.parent.sessionId === sessionId)
39756
+ throw new SkillSelectionError("INVALID_RECEIPT", "A Skills child session cannot name itself as its parent.");
39757
+ }
39758
+ }
39759
+ function readSkillSessionSnapshotIfExists(sessionId, options = {}) {
39760
+ const path = sessionReceiptPath(sessionId, options), bytes = readRegularFile(path, MAX_PROFILE_DOCUMENT_BYTES);
39761
+ if (!bytes)
39762
+ return null;
39763
+ let receipt;
39764
+ try {
39765
+ receipt = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
39766
+ } catch {
39767
+ throw new SkillSelectionError("INVALID_RECEIPT", "The Skills session receipt is unreadable.");
39768
+ }
39769
+ validateSkillSessionReceipt(receipt, sessionId);
39770
+ const sha256 = sha256Hex(bytes);
39771
+ return { path, bytes, receipt, sha256, sessionId, generation: skillSessionGeneration(receipt), receiptSha256: sha256 };
39772
+ }
39773
+ function skillSessionSnapshotBinding(snapshot) {
39774
+ return { sessionId: snapshot.sessionId, generation: snapshot.generation, receiptSha256: snapshot.receiptSha256 };
39775
+ }
39776
+ function withSessionWriteLocks(sessionIds, options, action) {
39777
+ const operationId = randomUUID();
39778
+ 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);
39779
+ const owned = [];
39780
+ const assertOwned = () => {
39781
+ for (const lock of owned) {
39782
+ const current = lstatSync5(lock.path, { throwIfNoEntry: false });
39783
+ if (!current?.isFile() || current.dev !== lock.dev || current.ino !== lock.ino) {
39784
+ throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "A session write lock changed during the operation; no replacement is permitted.");
39785
+ }
39786
+ }
39787
+ };
39788
+ try {
39789
+ for (const lock of locks) {
39790
+ assertRegularPath(lock.path, true);
39791
+ let fd;
39792
+ try {
39793
+ fd = openSync5(lock.path, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW, 384);
39794
+ } catch (error61) {
39795
+ if (error61.code === "EEXIST")
39796
+ 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.");
39797
+ throw error61;
39798
+ }
39799
+ const stat = fstatSync5(fd);
39800
+ owned.push({ path: lock.path, fd, dev: stat.dev, ino: stat.ino });
39801
+ writeFileSync4(fd, `${JSON.stringify({ schemaVersion: 1, operationId, sessionId: lock.sessionId, pid: process.pid })}
39802
+ `);
39803
+ }
39804
+ return action(assertOwned);
39805
+ } finally {
39806
+ for (const lock of owned.reverse()) {
39807
+ closeSync5(lock.fd);
39808
+ const current = lstatSync5(lock.path, { throwIfNoEntry: false });
39809
+ if (current?.isFile() && current.dev === lock.dev && current.ino === lock.ino)
39810
+ unlinkSync(lock.path);
39729
39811
  }
39730
39812
  }
39731
- return receipt;
39813
+ }
39814
+ function sameSnapshot(actual, expected) {
39815
+ return actual === null ? expected === null : expected !== null && actual.sessionId === expected.sessionId && actual.generation === expected.generation && actual.receiptSha256 === expected.receiptSha256;
39816
+ }
39817
+ function nextSkillSessionGeneration(generation) {
39818
+ if (!Number.isSafeInteger(generation) || generation < 0 || generation >= Number.MAX_SAFE_INTEGER) {
39819
+ throw new SkillSelectionError("SESSION_GENERATION_EXHAUSTED", "The Skills session generation cannot advance safely; preserve the receipt and review recovery before continuing.");
39820
+ }
39821
+ return generation + 1;
39822
+ }
39823
+ function parentChanged() {
39824
+ 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.");
39825
+ }
39826
+ function writeSkillSession(receipt, expected, options = {}) {
39827
+ validateSkillSessionReceipt(receipt, receipt.sessionId);
39828
+ if (expected.parent)
39829
+ validateSkillSessionBinding(expected.parent);
39830
+ if (expected.parent?.sessionId === receipt.sessionId)
39831
+ throw new SkillSelectionError("INVALID_RECEIPT", "A Skills child session cannot name itself as its parent.");
39832
+ const lockIds = expected.parent ? [receipt.sessionId, expected.parent.sessionId] : [receipt.sessionId];
39833
+ withSessionWriteLocks(lockIds, options, (assertOwned) => {
39834
+ const current = readSkillSessionSnapshotIfExists(receipt.sessionId, options);
39835
+ if (!sameSnapshot(current, expected.current) || current && !isDeepStrictEqual(current.receipt.profile, receipt.profile)) {
39836
+ throw new SkillSelectionError("SESSION_RECEIPT_CHANGED", "The Skills session receipt changed while context was loading; resolve context again without replacing the current pin.");
39837
+ }
39838
+ let parent = null;
39839
+ if (expected.parent) {
39840
+ parent = readSkillSessionSnapshotIfExists(expected.parent.sessionId, options);
39841
+ if (!sameSnapshot(parent, expected.parent) || !parent || !isDeepStrictEqual(parent.receipt.profile, receipt.profile))
39842
+ parentChanged();
39843
+ }
39844
+ const generation = nextSkillSessionGeneration(current?.generation ?? 0);
39845
+ const next = current ? { ...current.receipt, generation, loaded: [...new Set([...current.receipt.loaded, ...receipt.loaded])] } : { ...receipt, generation, ...expected.parent ? { parent: expected.parent } : {} };
39846
+ assertOwned();
39847
+ if (expected.parent) {
39848
+ parent = readSkillSessionSnapshotIfExists(expected.parent.sessionId, options);
39849
+ if (!sameSnapshot(parent, expected.parent))
39850
+ parentChanged();
39851
+ }
39852
+ writeSelectionJson(sessionReceiptPath(receipt.sessionId, options), next);
39853
+ });
39854
+ }
39855
+ function syncDirectory(path) {
39856
+ assertRegularPath(path);
39857
+ const fd = openSync5(path, constants2.O_RDONLY | constants2.O_DIRECTORY | constants2.O_NOFOLLOW);
39858
+ try {
39859
+ fsyncSync(fd);
39860
+ } finally {
39861
+ closeSync5(fd);
39862
+ }
39732
39863
  }
39733
39864
  var SkillSelectionError, MAX_CACHED_PROFILE_AGE_MS, SELECTION_LOCK_FILE = "selection.lock.json";
39734
39865
  var init_selection_cache = __esm(() => {
@@ -39748,6 +39879,7 @@ var init_selection_cache = __esm(() => {
39748
39879
  });
39749
39880
 
39750
39881
  // src/lib/agent-policy-limits.ts
39882
+ import { basename as basename4, isAbsolute as isAbsolute4, resolve as resolve4 } from "path";
39751
39883
  function object4(value) {
39752
39884
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
39753
39885
  }
@@ -39812,7 +39944,11 @@ function assertAgentPolicyCollections(policy) {
39812
39944
  text(source.path);
39813
39945
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
39814
39946
  if (source.hashMode !== undefined)
39815
- requireBound(["bytes", "path-bytes", "claude-plugin-registry"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
39947
+ 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));
39948
+ if (source.hashMode === "claude-settings-v1")
39949
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "settings.json");
39950
+ if (source.hashMode === "claude-marketplace-registry")
39951
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "known_marketplaces.json");
39816
39952
  if (source.hashMode === "claude-plugin-registry") {
39817
39953
  requireBound(agent === "claude");
39818
39954
  const managed = array3(source.managedPlugins, 64);
@@ -47400,7 +47536,7 @@ var init_plugin_projection = __esm(() => {
47400
47536
  });
47401
47537
 
47402
47538
  // src/lib/plugin-projection-store.ts
47403
- import { constants as constants5, closeSync as closeSync8, fchmodSync, fstatSync as fstatSync8, fsyncSync, linkSync, lstatSync as lstatSync8, mkdirSync as mkdirSync6, openSync as openSync8, opendirSync, readSync as readSync4, renameSync as renameSync3, rmdirSync as rmdirSync2, rmSync as rmSync2, unlinkSync as unlinkSync3, writeSync as writeSync2 } from "fs";
47539
+ import { constants as constants5, closeSync as closeSync8, fchmodSync, fstatSync as fstatSync8, fsyncSync as fsyncSync2, linkSync, lstatSync as lstatSync8, mkdirSync as mkdirSync6, openSync as openSync8, opendirSync, readSync as readSync4, renameSync as renameSync3, rmdirSync as rmdirSync2, rmSync as rmSync2, unlinkSync as unlinkSync3, writeSync as writeSync2 } from "fs";
47404
47540
  var READ_FLAGS, WRITE_FLAGS;
47405
47541
  var init_plugin_projection_store = __esm(() => {
47406
47542
  init_plugin_projection();
@@ -47452,6 +47588,69 @@ var init_plugin_discovery = __esm(() => {
47452
47588
  init_plugin_projection_store();
47453
47589
  });
47454
47590
 
47591
+ // src/lib/claude-marketplace-registry.ts
47592
+ var CLAUDE_MARKETPLACE_REGISTRY_LIMITS;
47593
+ var init_claude_marketplace_registry = __esm(() => {
47594
+ CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
47595
+ });
47596
+
47597
+ // src/lib/claude-settings-witness.ts
47598
+ var CLAUDE_SETTINGS_WITNESS_LIMITS, BOOLEAN_PREFERENCES, ENUM_PREFERENCES, BUILTIN_MODEL_SELECTIONS;
47599
+ var init_claude_settings_witness = __esm(() => {
47600
+ CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
47601
+ BOOLEAN_PREFERENCES = new Set([
47602
+ "autoScrollEnabled",
47603
+ "axScreenReader",
47604
+ "emojiCompletionEnabled",
47605
+ "prefersReducedMotion",
47606
+ "showTurnDuration",
47607
+ "spinnerTipsEnabled",
47608
+ "syntaxHighlightingDisabled",
47609
+ "terminalProgressBarEnabled",
47610
+ "terminalTitleFromRename",
47611
+ "verbose",
47612
+ "wheelScrollAccelerationEnabled"
47613
+ ]);
47614
+ ENUM_PREFERENCES = Object.freeze({
47615
+ editorMode: ["normal", "vim"],
47616
+ tui: ["default", "fullscreen"],
47617
+ viewMode: ["default", "verbose", "focus"]
47618
+ });
47619
+ BUILTIN_MODEL_SELECTIONS = new Set([
47620
+ "default",
47621
+ "best",
47622
+ "fable",
47623
+ "fable[1m]",
47624
+ "sonnet",
47625
+ "sonnet[1m]",
47626
+ "opus",
47627
+ "opus[1m]",
47628
+ "haiku",
47629
+ "opusplan",
47630
+ "claude-fable-5-1",
47631
+ "claude-fable-5",
47632
+ "claude-fable-5[1m]",
47633
+ "claude-opus-5",
47634
+ "claude-sonnet-5",
47635
+ "claude-haiku-4-5-20251001",
47636
+ "claude-opus-4-6",
47637
+ "claude-sonnet-4-5",
47638
+ "claude-sonnet-4-5-20250929",
47639
+ "claude-opus-4-8",
47640
+ "claude-opus-4-8[1m]",
47641
+ "claude-opus-4-7",
47642
+ "claude-sonnet-4-6",
47643
+ "claude-opus-4-5-20251101",
47644
+ "claude-opus-4-5",
47645
+ "claude-haiku-4-5",
47646
+ "claude-fable-5-1[1m]",
47647
+ "claude-opus-5[1m]",
47648
+ "claude-opus-4-7[1m]",
47649
+ "claude-opus-4-6[1m]",
47650
+ "claude-sonnet-4-6[1m]"
47651
+ ]);
47652
+ });
47653
+
47455
47654
  // src/lib/agent-discovery.ts
47456
47655
  var init_agent_discovery = __esm(() => {
47457
47656
  init_agent_policy_limits();
@@ -47461,6 +47660,8 @@ var init_agent_discovery = __esm(() => {
47461
47660
  init_agent_discovery_path_bytes();
47462
47661
  init_plugin_discovery();
47463
47662
  init_plugin_admission();
47663
+ init_claude_marketplace_registry();
47664
+ init_claude_settings_witness();
47464
47665
  init_agent_discovery_directories();
47465
47666
  });
47466
47667
 
@@ -48024,11 +48225,18 @@ var init_selected_document = __esm(() => {
48024
48225
 
48025
48226
  // src/lib/selection-resolver.ts
48026
48227
  async function resolveSelectionContext(profileId, options = {}) {
48027
- const session = options.sessionId ? readSkillSession(options.sessionId, options) : null;
48028
- const parentSession = !session && options.parentSessionId ? readSkillSession(options.parentSessionId, options) : null;
48228
+ const sessionState = options.sessionId ? readSkillSessionSnapshotIfExists(options.sessionId, options) : null;
48229
+ const session = sessionState?.receipt ?? null;
48230
+ const parentState = !session && options.parentSessionId ? readSkillSessionSnapshotIfExists(options.parentSessionId, options) : null;
48231
+ if (!session && options.parentSessionId && !parentState) {
48232
+ throw new SkillSelectionError("SESSION_PARENT_NOT_FOUND", "A child Skills session requires an existing parent receipt so its inherited pin can be bound exactly.");
48233
+ }
48234
+ const parentSession = parentState?.receipt ?? null;
48235
+ const sessionSnapshot = sessionState ? skillSessionSnapshotBinding(sessionState) : null;
48236
+ const parentSnapshot = parentState ? skillSessionSnapshotBinding(parentState) : undefined;
48029
48237
  const project = options.projectDir ? readProjectSelection(options.projectDir) : null;
48030
48238
  if (session && session.profile.profileId !== profileId || parentSession && parentSession.profile.profileId !== profileId || project && project.profile.profileId !== profileId) {
48031
- 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.");
48239
+ 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.");
48032
48240
  }
48033
48241
  if (options.cached) {
48034
48242
  const receipt2 = session ?? parentSession ?? project ?? readSelectionProfile(profileId, options);
@@ -48036,7 +48244,7 @@ async function resolveSelectionContext(profileId, options = {}) {
48036
48244
  throw new SkillSelectionError("CACHED_PROFILE_MISSING", "No verified Skills profile is cached; authenticate and sync it first.");
48037
48245
  assertFreshCachedProfile(receipt2, options);
48038
48246
  assertBinding(receipt2.profile, options);
48039
- return { receipt: receipt2, session, inheritedLoaded: parentSession?.loaded };
48247
+ return { receipt: receipt2, session, sessionSnapshot, parentSnapshot, inheritedLoaded: parentSession?.loaded };
48040
48248
  }
48041
48249
  const client = options.client ?? await createProfileClient();
48042
48250
  const current = await client.resolveProfile(profileId);
@@ -48049,7 +48257,7 @@ async function resolveSelectionContext(profileId, options = {}) {
48049
48257
  throw new SkillSelectionError("PROFILE_IDENTITY_MISMATCH", "The pinned selection belongs to another Skills authority or workspace.");
48050
48258
  }
48051
48259
  const receipt = locked ?? { schemaVersion: 1, verifiedAt: new Date((options.now ?? Date.now)()).toISOString(), profile: current };
48052
- return { receipt, session, client, inheritedLoaded: parentSession?.loaded };
48260
+ return { receipt, session, sessionSnapshot, parentSnapshot, client, inheritedLoaded: parentSession?.loaded };
48053
48261
  }
48054
48262
  function assertBinding(profile, options) {
48055
48263
  if (options.authority !== undefined && profile.authority !== options.authority || options.workspaceId !== undefined && profile.workspaceId !== options.workspaceId) {
@@ -48088,7 +48296,7 @@ async function loadSelectedSkill(spec, profileId, options = {}) {
48088
48296
  sessionId: options.sessionId,
48089
48297
  loaded: [...new Set([...context.session?.loaded ?? [], selectionKey(selection)])]
48090
48298
  };
48091
- writeSelectionJson(sessionReceiptPath(options.sessionId, options), session);
48299
+ writeSkillSession(session, { current: context.sessionSnapshot, parent: context.parentSnapshot }, options);
48092
48300
  }
48093
48301
  return { content, selection, receipt };
48094
48302
  }
@@ -48132,6 +48340,14 @@ var init_skill_context = __esm(() => {
48132
48340
  init_selected_document();
48133
48341
  });
48134
48342
 
48343
+ // src/lib/session-reconciliation.ts
48344
+ var SESSION_RECONCILIATION_PLAN_TTL_MS;
48345
+ var init_session_reconciliation = __esm(() => {
48346
+ init_profile_client();
48347
+ init_selection_cache();
48348
+ SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
48349
+ });
48350
+
48135
48351
  // src/cli/commands/context.ts
48136
48352
  function selectedProfileId(explicit) {
48137
48353
  return explicit ?? process.env.HASNA_SKILLS_SELECTION_PROFILE ?? readManagedSkillPolicy()?.profileId ?? "default";
@@ -48142,6 +48358,7 @@ var init_context = __esm(() => {
48142
48358
  init_selection_resolver();
48143
48359
  init_selection_cache();
48144
48360
  init_managed_policy();
48361
+ init_session_reconciliation();
48145
48362
  });
48146
48363
 
48147
48364
  // src/lib/remote-customer-operations.ts
@@ -48153,7 +48370,7 @@ var init_remote_customer_operations = __esm(() => {
48153
48370
  { name: "list_remote_skills", title: "List Remote Skills", parameter: null, mutates: false, invoke: (client) => client.listSkills() },
48154
48371
  { name: "get_billing_status", title: "Get Billing Status", parameter: null, mutates: false, invoke: (client) => client.getBillingStatus() },
48155
48372
  { name: "list_credit_packs", title: "List Credit Packs", parameter: null, mutates: false, invoke: (client) => client.listCreditPacks() },
48156
- { name: "create_credit_checkout", title: "Create Credit Checkout", parameter: "pack_id", mutates: true, invoke: (client, value) => client.createCreditCheckout(value) },
48373
+ { name: "create_credit_checkout", title: "Create Credit Checkout", parameter: "pack_id", mutates: true, invoke: (client, value, options) => client.createCreditCheckout(value, options) },
48157
48374
  { name: "get_billing_usage", title: "Get Billing Usage", parameter: null, mutates: false, invoke: (client) => client.getUsage() },
48158
48375
  { name: "list_invoices", title: "List Invoices", parameter: null, mutates: false, invoke: (client) => client.listInvoices() },
48159
48376
  { name: "create_billing_checkout", title: "Create Billing Checkout", parameter: null, mutates: true, invoke: (client) => client.createBillingCheckout() },
@@ -48961,13 +49178,13 @@ var init_remote_workspace_leave = __esm(() => {
48961
49178
 
48962
49179
  // src/lib/auth-store.ts
48963
49180
  import { chmodSync, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync15, renameSync as renameSync4, statSync as statSync8, unlinkSync as unlinkSync4, writeFileSync as writeFileSync6 } from "fs";
48964
- import { basename as basename4, dirname as dirname6, join as join19 } from "path";
49181
+ import { basename as basename5, dirname as dirname6, join as join19 } from "path";
48965
49182
  function getAuthFilePath(env = process.env) {
48966
49183
  return skillsCredentialFilePath(env);
48967
49184
  }
48968
49185
  function getIdentityFilePath(env = process.env) {
48969
49186
  const file2 = skillsCredentialFilePath(env);
48970
- return join19(dirname6(file2), basename4(file2).replace(/^credentials/, "identity") + ".json");
49187
+ return join19(dirname6(file2), basename5(file2).replace(/^credentials/, "identity") + ".json");
48971
49188
  }
48972
49189
  function getApiUrl(action, env = process.env, options = {}) {
48973
49190
  return requireSkillsApiOrigin(action, env, options);
@@ -49121,6 +49338,35 @@ var init_remote_account = __esm(() => {
49121
49338
  };
49122
49339
  });
49123
49340
 
49341
+ // src/lib/remote-credit-checkout.ts
49342
+ function creditCheckoutRequestKey(value) {
49343
+ if (value === undefined)
49344
+ return crypto.randomUUID();
49345
+ if (typeof value !== "string" || !creditCheckoutKeyPattern.test(value))
49346
+ throw new Error("Checkout idempotency key must be 8-255 URL-safe characters");
49347
+ return value;
49348
+ }
49349
+ function creditCheckoutFailure(value, status, key) {
49350
+ if (!value || typeof value !== "object" || Array.isArray(value))
49351
+ return { code: "CREDIT_CHECKOUT_UNCONFIRMED" };
49352
+ const row = value;
49353
+ if (row.requestIdempotencyKey !== undefined && row.requestIdempotencyKey !== key)
49354
+ return { code: "CREDIT_CHECKOUT_UNCONFIRMED" };
49355
+ 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";
49356
+ const delay = row.retryAfterSeconds;
49357
+ 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 } : {} : {} };
49358
+ }
49359
+ var creditCheckoutKeyPattern, creditCheckoutMessages;
49360
+ var init_remote_credit_checkout = __esm(() => {
49361
+ creditCheckoutKeyPattern = /^[A-Za-z0-9._:-]{8,255}$/;
49362
+ creditCheckoutMessages = {
49363
+ 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.",
49364
+ 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.",
49365
+ 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.",
49366
+ CREDIT_CHECKOUT_FULFILLED: "This checkout is already fulfilled. Inspect the account balance; do not create another checkout to recover this purchase."
49367
+ };
49368
+ });
49369
+
49124
49370
  // src/lib/remote-profile.ts
49125
49371
  function customerNamePatch(input2, field) {
49126
49372
  if (!isRecord4(input2) || Object.keys(input2).length !== 1 || !Object.hasOwn(input2, field))
@@ -49166,10 +49412,10 @@ async function readQuoteUnavailableCode(response) {
49166
49412
  let timer;
49167
49413
  let deadlineExceeded = false;
49168
49414
  const expired = Symbol("quote body deadline");
49169
- const deadline = new Promise((resolve4) => {
49415
+ const deadline = new Promise((resolve5) => {
49170
49416
  timer = setTimeout(() => {
49171
49417
  deadlineExceeded = true;
49172
- resolve4(expired);
49418
+ resolve5(expired);
49173
49419
  reader.cancel().catch(() => {});
49174
49420
  }, 1500);
49175
49421
  });
@@ -49284,6 +49530,7 @@ __export(exports_remote_client, {
49284
49530
  RemoteRouteUnsupportedError: () => RemoteRouteUnsupportedError,
49285
49531
  RemoteRequestError: () => RemoteRequestError,
49286
49532
  RemoteQuoteUnavailableError: () => RemoteQuoteUnavailableError,
49533
+ RemoteCreditCheckoutError: () => RemoteCreditCheckoutError,
49287
49534
  RemoteCapabilityUnavailableError: () => RemoteCapabilityUnavailableError
49288
49535
  });
49289
49536
 
@@ -49734,14 +49981,43 @@ class RemoteSkillsClient {
49734
49981
  async listCreditPacks() {
49735
49982
  return parseRemoteCreditPacks(await (await this.requestNewRoute("/api/v1/billing/credits")).json());
49736
49983
  }
49737
- async createCreditCheckout(packId) {
49984
+ async createCreditCheckout(packId, options = {}) {
49985
+ const requestIdempotencyKey = creditCheckoutRequestKey(options.idempotencyKey);
49738
49986
  const packs = await this.listCreditPacks();
49739
49987
  if (!packs.some((pack) => pack.id === packId))
49740
49988
  throw new Error("Choose a credit pack returned by skills credits packs");
49741
- return parseRemoteCheckout(await (await this.requestNewRoute("/api/v1/billing/credits", {
49742
- method: "POST",
49743
- body: JSON.stringify({ packId })
49744
- })).json());
49989
+ let response;
49990
+ try {
49991
+ response = await this.request("/api/v1/billing/credits", {
49992
+ method: "POST",
49993
+ body: JSON.stringify({ packId, idempotencyKey: requestIdempotencyKey })
49994
+ });
49995
+ } catch {
49996
+ throw new RemoteCreditCheckoutError("CREDIT_CHECKOUT_UNCONFIRMED", 0, requestIdempotencyKey);
49997
+ }
49998
+ if (response.status === 404 || response.status === 405) {
49999
+ response.body?.cancel().catch(() => {});
50000
+ throw new RemoteRouteUnsupportedError("/api/v1/billing/credits", response.status, this.apiUrl);
50001
+ }
50002
+ let value;
50003
+ try {
50004
+ value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await readBoundedResponse(response, 4096)));
50005
+ } catch {
50006
+ throw new RemoteCreditCheckoutError("CREDIT_CHECKOUT_UNCONFIRMED", response.status, requestIdempotencyKey);
50007
+ }
50008
+ if (!response.ok) {
50009
+ const failure = creditCheckoutFailure(value, response.status, requestIdempotencyKey);
50010
+ throw new RemoteCreditCheckoutError(failure.code, response.status, requestIdempotencyKey, failure.retryAfterSeconds);
50011
+ }
50012
+ try {
50013
+ const checkout = parseRemoteCheckout(value);
50014
+ const echo = value.requestIdempotencyKey;
50015
+ if (echo !== undefined && echo !== requestIdempotencyKey)
50016
+ throw new Error("checkout request key changed");
50017
+ return { ...checkout, requestIdempotencyKey };
50018
+ } catch {
50019
+ throw new RemoteCreditCheckoutError("CREDIT_CHECKOUT_UNCONFIRMED", response.status, requestIdempotencyKey);
50020
+ }
49745
50021
  }
49746
50022
  async getUsage() {
49747
50023
  return this.arrayResponse("/api/v1/billing/usage");
@@ -50111,7 +50387,7 @@ async function createRemoteSkillsClient(env = process.env) {
50111
50387
  function createRemoteSkillsClientReadOnly(env = process.env) {
50112
50388
  return createRemoteSkillsClient(env);
50113
50389
  }
50114
- var RemoteRouteUnsupportedError, RemoteRequestError, RemoteQuoteUnavailableError, RemoteWorkspaceMemberError, RemoteWorkspaceSelectionError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
50390
+ var RemoteRouteUnsupportedError, RemoteRequestError, RemoteCreditCheckoutError, RemoteQuoteUnavailableError, RemoteWorkspaceMemberError, RemoteWorkspaceSelectionError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
50115
50391
  var init_remote_client = __esm(() => {
50116
50392
  init_remote_recurring();
50117
50393
  init_remote_invitations();
@@ -50123,6 +50399,7 @@ var init_remote_client = __esm(() => {
50123
50399
  init_fleet_credentials();
50124
50400
  init_remote_account();
50125
50401
  init_remote_files();
50402
+ init_remote_credit_checkout();
50126
50403
  init_remote_quote_errors();
50127
50404
  init_remote_permissions();
50128
50405
  RemoteRouteUnsupportedError = class RemoteRouteUnsupportedError extends Error {
@@ -50147,6 +50424,19 @@ var init_remote_client = __esm(() => {
50147
50424
  this.name = "RemoteRequestError";
50148
50425
  }
50149
50426
  };
50427
+ RemoteCreditCheckoutError = class RemoteCreditCheckoutError extends RemoteRequestError {
50428
+ code;
50429
+ requestIdempotencyKey;
50430
+ retryAfterSeconds;
50431
+ constructor(code, status, requestIdempotencyKey, retryAfterSeconds) {
50432
+ super("/api/v1/billing/credits", status);
50433
+ this.code = code;
50434
+ this.requestIdempotencyKey = requestIdempotencyKey;
50435
+ this.retryAfterSeconds = retryAfterSeconds;
50436
+ this.name = "RemoteCreditCheckoutError";
50437
+ this.message = creditCheckoutMessages[code];
50438
+ }
50439
+ };
50150
50440
  RemoteQuoteUnavailableError = class RemoteQuoteUnavailableError extends RemoteRequestError {
50151
50441
  code;
50152
50442
  constructor(path, code) {
@@ -50533,12 +50823,12 @@ class RemotePrivatePublicationsClient {
50533
50823
  previous = view;
50534
50824
  if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
50535
50825
  return view;
50536
- await new Promise((resolve4) => {
50826
+ await new Promise((resolve5) => {
50537
50827
  const timer = setTimeout(done, Math.min(1000, until - Date.now()));
50538
50828
  function done() {
50539
50829
  clearTimeout(timer);
50540
50830
  options.signal?.removeEventListener("abort", done);
50541
- resolve4();
50831
+ resolve5();
50542
50832
  }
50543
50833
  options.signal?.addEventListener("abort", done, { once: true });
50544
50834
  });
@@ -51058,8 +51348,8 @@ var init_recurring_customer = __esm(() => {
51058
51348
  });
51059
51349
 
51060
51350
  // src/lib/recurring-recovery.ts
51061
- import { constants as constants7, closeSync as closeSync10, fsyncSync as fsyncSync2, fstatSync as fstatSync10, lstatSync as lstatSync10, mkdirSync as mkdirSync10, openSync as openSync10, readSync as readSync5, realpathSync as realpathSync4, renameSync as renameSync6, unlinkSync as unlinkSync5, writeFileSync as writeFileSync8 } from "fs";
51062
- import { dirname as dirname7, isAbsolute as isAbsolute4, join as join20, resolve as resolve4 } from "path";
51351
+ import { constants as constants7, closeSync as closeSync10, fsyncSync as fsyncSync3, fstatSync as fstatSync10, lstatSync as lstatSync10, mkdirSync as mkdirSync10, openSync as openSync10, readSync as readSync5, realpathSync as realpathSync4, renameSync as renameSync6, unlinkSync as unlinkSync5, writeFileSync as writeFileSync8 } from "fs";
51352
+ import { dirname as dirname7, isAbsolute as isAbsolute5, join as join20, resolve as resolve5 } from "path";
51063
51353
  import { randomUUID as randomUUID2 } from "crypto";
51064
51354
 
51065
51355
  class RecoveryOwner {
@@ -51067,7 +51357,7 @@ class RecoveryOwner {
51067
51357
  parents = [];
51068
51358
  constructor(directory) {
51069
51359
  this.directory = directory;
51070
- if (!isAbsolute4(directory) || resolve4(directory) !== directory || realpathSync4(directory) !== directory)
51360
+ if (!isAbsolute5(directory) || resolve5(directory) !== directory || realpathSync4(directory) !== directory)
51071
51361
  invalid5();
51072
51362
  for (let path = directory;; path = dirname7(path)) {
51073
51363
  const stat2 = lstatSync10(path);
@@ -51122,7 +51412,7 @@ class RecoveryOwner {
51122
51412
  const fd = openSync10(join20(this.directory, name), constants7.O_WRONLY | constants7.O_CREAT | constants7.O_EXCL | constants7.O_NOFOLLOW, 384);
51123
51413
  try {
51124
51414
  writeFileSync8(fd, bytes);
51125
- fsyncSync2(fd);
51415
+ fsyncSync3(fd);
51126
51416
  } finally {
51127
51417
  closeSync10(fd);
51128
51418
  }
@@ -51133,7 +51423,7 @@ class RecoveryOwner {
51133
51423
  this.assert();
51134
51424
  const fd = openSync10(this.directory, constants7.O_RDONLY | constants7.O_NOFOLLOW);
51135
51425
  try {
51136
- fsyncSync2(fd);
51426
+ fsyncSync3(fd);
51137
51427
  } finally {
51138
51428
  closeSync10(fd);
51139
51429
  }
@@ -51160,7 +51450,7 @@ class RecoveryOwner {
51160
51450
  const owner = fstatSync10(fd);
51161
51451
  try {
51162
51452
  writeFileSync8(fd, JSON.stringify({ pid: process.pid }));
51163
- fsyncSync2(fd);
51453
+ fsyncSync3(fd);
51164
51454
  this.sync();
51165
51455
  return await action();
51166
51456
  } finally {
@@ -51202,7 +51492,7 @@ function base(customer, organizationId) {
51202
51492
  };
51203
51493
  }
51204
51494
  function create(directory, intent) {
51205
- if (!isAbsolute4(directory) || resolve4(directory) !== directory || realpathSync4(dirname7(directory)) !== dirname7(directory))
51495
+ if (!isAbsolute5(directory) || resolve5(directory) !== directory || realpathSync4(dirname7(directory)) !== dirname7(directory))
51206
51496
  invalid5();
51207
51497
  mkdirSync10(directory, { mode: 448 });
51208
51498
  const owner = new RecoveryOwner(directory), checked = intentSchema.parse(intent), intentSha256 = canonicalJsonSha256(checked);
@@ -51365,7 +51655,7 @@ var init_recurring_recovery = __esm(() => {
51365
51655
  });
51366
51656
 
51367
51657
  // src/lib/recurring-surface.ts
51368
- import { isAbsolute as isAbsolute5 } from "path";
51658
+ import { isAbsolute as isAbsolute6 } from "path";
51369
51659
  function recurringMcpSchema(action) {
51370
51660
  return action === "activate" ? recurringSurfaceSchemas.activate.extend({ code, confirm: exports_external.literal(true) }) : recurringSurfaceSchemas[action];
51371
51661
  }
@@ -51470,7 +51760,7 @@ var init_recurring_surface = __esm(() => {
51470
51760
  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}$/);
51471
51761
  context = { userId: uuid13.optional(), membershipId: uuid13.optional() };
51472
51762
  page2 = { limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() };
51473
- directory = exports_external.string().max(4096).refine(isAbsolute5, "Use an absolute recovery directory");
51763
+ directory = exports_external.string().max(4096).refine(isAbsolute6, "Use an absolute recovery directory");
51474
51764
  email4 = exports_external.string().trim().toLowerCase().email().max(254);
51475
51765
  code = exports_external.string().regex(/^\d{6}$/);
51476
51766
  recurringSurfaceSchemas = {
@@ -52439,11 +52729,11 @@ var init_mcp_contracts = __esm(() => {
52439
52729
  name: operation.name,
52440
52730
  title: operation.title,
52441
52731
  description: `${operation.title} on the configured server; unavailable capabilities fail explicitly.`,
52442
- params: operation.parameter ? [operation.parameter] : [],
52732
+ params: operation.parameter ? [operation.parameter, ...operation.name === "create_credit_checkout" ? ["idempotency_key?"] : []] : [],
52443
52733
  category: "execution",
52444
52734
  sideEffects: operation.mutates ? "local-process-or-remote-run" : "none",
52445
52735
  stable: true,
52446
- inputSchema: objectSchema(operation.parameter ? { [operation.parameter]: stringSchema("Server resource identifier.") } : {}, operation.parameter ? [operation.parameter] : []),
52736
+ 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] : []),
52447
52737
  outputSchema: { oneOf: [objectSchema({}, [], "Server response.", true), { type: "array", items: objectSchema({}, [], "Server record.", true) }] }
52448
52738
  }));
52449
52739
  remoteCustomerContracts.push({
@@ -53535,7 +53825,7 @@ import { closeSync as closeSync11, fstatSync as fstatSync11, openSync as openSyn
53535
53825
  import { O_NOFOLLOW as O_NOFOLLOW3, O_NONBLOCK as O_NONBLOCK3, O_RDONLY as O_RDONLY3 } from "constants";
53536
53826
  import { createRequire as createRequire2 } from "module";
53537
53827
  import { hostname as osHostname2 } from "os";
53538
- import { isAbsolute as isAbsolute6, join as join22 } from "path";
53828
+ import { isAbsolute as isAbsolute7, join as join22 } from "path";
53539
53829
  import { createRequire as createRequire22 } from "module";
53540
53830
  function envToken2(name) {
53541
53831
  return name.toUpperCase().replace(/-/g, "_");
@@ -53559,7 +53849,7 @@ function homeDir2(env) {
53559
53849
  }
53560
53850
  function absoluteOverride2(env, key) {
53561
53851
  const value = env[key]?.trim();
53562
- return value && isAbsolute6(value) ? value : null;
53852
+ return value && isAbsolute7(value) ? value : null;
53563
53853
  }
53564
53854
  function hasnaHomeDir2(env) {
53565
53855
  const override = absoluteOverride2(env, HASNA_HOME_ENV_KEY2);
@@ -54696,7 +54986,7 @@ function createSecretsClientFromEnv(env = process.env, overrides = {}) {
54696
54986
  });
54697
54987
  return new SecretsClient({ ...clientOverrides, baseUrl, apiKey });
54698
54988
  }
54699
- var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res), CREDENTIAL_PROFILE_ENV_KEY2 = "HASNA_PROFILE", CredentialResolutionError2, CredentialFileUnsafeError2, HASNA_HOME_ENV_KEY2 = "HASNA_HOME", HASNA_CONFIG_HOME_ENV_KEY2 = "HASNA_CONFIG_HOME", KEYCHAIN_STATION_ENV_KEY2 = "HASNA_STATION", HASNA_HOME_DIR2 = ".hasna", CONFIG_SUBDIR2 = "config", CREDENTIALS_FILE2 = "credentials", KEYCHAIN_SECURITY_BIN2 = "/usr/bin/security", KEYCHAIN_SERVICE_PREFIX2 = "hasna.credentials", KEYCHAIN_ITEM_NOT_FOUND_STATUS2 = 44, KEYCHAIN_SPAWN_TIMEOUT_MS2 = 1e4, MAX_CREDENTIAL_FILE_BYTES2, SAFE_APP_SLUG2, SAFE_PROFILE2, ILLEGAL_IN_HEADER_VALUE2, VAULT_POINTER_SHAPE2, CREDENTIAL_SHAPED_KEY2, INSPECT_CUSTOM2, CREDENTIAL_SEAL2, CALLER_SUPPLIED_CREDENTIAL_PROVIDER_SOURCE = "caller-supplied CredentialProvider", AMBIENT_ENVIRONMENT2, SECRETS_PACKAGE_SPECIFIER, requireSecretsSdk2, DEFAULT_FLEET_GATEWAY_ORIGIN2 = "https://api.hasna.com", DEFAULT_AUTHORITY_SOURCE = "default", ASCII_CONTROL_PATTERN2, DNS_LABEL_PATTERN2, ClientTransportConfigurationError2, HasnaHttpError, DEFAULT_RETRY_STATUSES, IDEMPOTENT_METHODS2, AUTHORITY_OVERRIDE_HEADERS2, defaultSleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)), init_transport2, MAX_CREDENTIAL_FILE_BYTES22, INSPECT_CUSTOM22, CREDENTIAL_SEAL22, AMBIENT_ENVIRONMENT22, SECRETS_PACKAGE_SPECIFIER2, requireSecretsSdk22, IDEMPOTENT_METHODS22, AUTHORITY_OVERRIDE_HEADERS22, init_storage, PATHS_RESOLVER_APP_SLUG_RE2, SQLITE_SIDECAR_FILES, LEGACY_SERVICE_FILES, init_data_dir, SecretsTestIsolationError, TEST_ENTRYPOINT_RE, init_test_isolation, init_client, ApiError;
54989
+ var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res), CREDENTIAL_PROFILE_ENV_KEY2 = "HASNA_PROFILE", CredentialResolutionError2, CredentialFileUnsafeError2, HASNA_HOME_ENV_KEY2 = "HASNA_HOME", HASNA_CONFIG_HOME_ENV_KEY2 = "HASNA_CONFIG_HOME", KEYCHAIN_STATION_ENV_KEY2 = "HASNA_STATION", HASNA_HOME_DIR2 = ".hasna", CONFIG_SUBDIR2 = "config", CREDENTIALS_FILE2 = "credentials", KEYCHAIN_SECURITY_BIN2 = "/usr/bin/security", KEYCHAIN_SERVICE_PREFIX2 = "hasna.credentials", KEYCHAIN_ITEM_NOT_FOUND_STATUS2 = 44, KEYCHAIN_SPAWN_TIMEOUT_MS2 = 1e4, MAX_CREDENTIAL_FILE_BYTES2, SAFE_APP_SLUG2, SAFE_PROFILE2, ILLEGAL_IN_HEADER_VALUE2, VAULT_POINTER_SHAPE2, CREDENTIAL_SHAPED_KEY2, INSPECT_CUSTOM2, CREDENTIAL_SEAL2, CALLER_SUPPLIED_CREDENTIAL_PROVIDER_SOURCE = "caller-supplied CredentialProvider", AMBIENT_ENVIRONMENT2, SECRETS_PACKAGE_SPECIFIER, requireSecretsSdk2, DEFAULT_FLEET_GATEWAY_ORIGIN2 = "https://api.hasna.com", DEFAULT_AUTHORITY_SOURCE = "default", ASCII_CONTROL_PATTERN2, DNS_LABEL_PATTERN2, ClientTransportConfigurationError2, HasnaHttpError, DEFAULT_RETRY_STATUSES, IDEMPOTENT_METHODS2, AUTHORITY_OVERRIDE_HEADERS2, defaultSleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)), init_transport2, MAX_CREDENTIAL_FILE_BYTES22, INSPECT_CUSTOM22, CREDENTIAL_SEAL22, AMBIENT_ENVIRONMENT22, SECRETS_PACKAGE_SPECIFIER2, requireSecretsSdk22, IDEMPOTENT_METHODS22, AUTHORITY_OVERRIDE_HEADERS22, init_storage, PATHS_RESOLVER_APP_SLUG_RE2, SQLITE_SIDECAR_FILES, LEGACY_SERVICE_FILES, init_data_dir, SecretsTestIsolationError, TEST_ENTRYPOINT_RE, init_test_isolation, init_client, ApiError;
54700
54990
  var init_sdk = __esm(() => {
54701
54991
  init_transport2 = __esm2(() => {
54702
54992
  CredentialResolutionError2 = class CredentialResolutionError3 extends Error {
@@ -55011,7 +55301,7 @@ var init_execution_grants = __esm(() => {
55011
55301
  });
55012
55302
 
55013
55303
  // src/lib/execution-grant-client.ts
55014
- import { isDeepStrictEqual } from "util";
55304
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
55015
55305
  function id(value) {
55016
55306
  if (!grantIdentifier(value))
55017
55307
  throw new Error("Invalid execution grant profile or revision identifier.");
@@ -55054,7 +55344,7 @@ async function resolveExecutionGrant(input2) {
55054
55344
  if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
55055
55345
  throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
55056
55346
  }
55057
- if (!isDeepStrictEqual(value.bindings.selection, snapshot.selection) || !isDeepStrictEqual(value.bindings.consumer, snapshot.consumer)) {
55347
+ if (!isDeepStrictEqual2(value.bindings.selection, snapshot.selection) || !isDeepStrictEqual2(value.bindings.consumer, snapshot.consumer)) {
55058
55348
  throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
55059
55349
  }
55060
55350
  return value;
@@ -55076,7 +55366,7 @@ __export(exports_selected_run, {
55076
55366
  import { createHash as createHash9 } from "crypto";
55077
55367
  import { spawn } from "child_process";
55078
55368
  import { mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync10, realpathSync as realpathSync6 } from "fs";
55079
- import { dirname as dirname8, join as join23, resolve as resolve5 } from "path";
55369
+ import { dirname as dirname8, join as join23, resolve as resolve6 } from "path";
55080
55370
  import { tmpdir, hostname as stationHostname2 } from "os";
55081
55371
  async function resolveSelectedRun(spec, profileId, options = {}) {
55082
55372
  const context2 = await resolveSelectionContext(profileId, options);
@@ -55169,7 +55459,7 @@ async function executeSelectedLocal(selected, options = {}) {
55169
55459
  writeFileSync10(destination, entry.bytes, { flag: "wx", mode: entry.mode & 511 });
55170
55460
  }
55171
55461
  env.SKILLS_INPUT_JSON = input2;
55172
- env.SKILLS_WORKSPACE_DIR = resolve5(options.cwd ?? process.cwd());
55462
+ env.SKILLS_WORKSPACE_DIR = resolve6(options.cwd ?? process.cwd());
55173
55463
  env.SKILLS_RUN_DIR = runDirectory;
55174
55464
  if (runtime.version) {
55175
55465
  const version2 = await subprocess([executable, ...runtimeName === "bun" ? ["--no-env-file"] : [], "--version"], runDirectory, env, "", Math.min(timeoutMs, 5000));
@@ -56930,11 +57220,11 @@ var init_private_publication_customer = __esm(() => {
56930
57220
  });
56931
57221
 
56932
57222
  // src/lib/private-publication-recovery.ts
56933
- import { constants as constants9, closeSync as closeSync13, fsyncSync as fsyncSync3, fstatSync as fstatSync13, lstatSync as lstatSync11, mkdirSync as mkdirSync16, openSync as openSync13, readSync as readSync7, realpathSync as realpathSync7, renameSync as renameSync7, unlinkSync as unlinkSync6, writeFileSync as writeFileSync13 } from "fs";
56934
- import { dirname as dirname11, isAbsolute as isAbsolute7, join as join28, resolve as resolve6 } from "path";
57223
+ import { constants as constants9, closeSync as closeSync13, fsyncSync as fsyncSync4, fstatSync as fstatSync13, lstatSync as lstatSync11, mkdirSync as mkdirSync16, openSync as openSync13, readSync as readSync7, realpathSync as realpathSync7, renameSync as renameSync7, unlinkSync as unlinkSync6, writeFileSync as writeFileSync13 } from "fs";
57224
+ import { dirname as dirname11, isAbsolute as isAbsolute8, join as join28, resolve as resolve7 } from "path";
56935
57225
  import { randomUUID as randomUUID3 } from "crypto";
56936
57226
  function safeDirectory(directory2) {
56937
- if (!isAbsolute7(directory2) || directory2 !== resolve6(directory2) || realpathSync7(directory2) !== directory2)
57227
+ if (!isAbsolute8(directory2) || directory2 !== resolve7(directory2) || realpathSync7(directory2) !== directory2)
56938
57228
  return fail2();
56939
57229
  for (let path = directory2;; path = dirname11(path)) {
56940
57230
  const stat2 = lstatSync11(path);
@@ -56981,7 +57271,7 @@ function writeOwned(directory2, name, bytes) {
56981
57271
  const fd = openSync13(join28(directory2, name), constants9.O_WRONLY | constants9.O_CREAT | constants9.O_EXCL | constants9.O_NOFOLLOW, 384);
56982
57272
  try {
56983
57273
  writeFileSync13(fd, bytes);
56984
- fsyncSync3(fd);
57274
+ fsyncSync4(fd);
56985
57275
  } finally {
56986
57276
  closeSync13(fd);
56987
57277
  }
@@ -56994,7 +57284,7 @@ function save(directory2, value) {
56994
57284
  renameSync7(join28(directory2, temporary), join28(directory2, "receipt.json"));
56995
57285
  const fd = openSync13(directory2, constants9.O_RDONLY | constants9.O_NOFOLLOW);
56996
57286
  try {
56997
- fsyncSync3(fd);
57287
+ fsyncSync4(fd);
56998
57288
  } finally {
56999
57289
  closeSync13(fd);
57000
57290
  }
@@ -57015,7 +57305,7 @@ async function locked(directory2, action) {
57015
57305
  try {
57016
57306
  writeFileSync13(fd, JSON.stringify({ pid: process.pid }) + `
57017
57307
  `);
57018
- fsyncSync3(fd);
57308
+ fsyncSync4(fd);
57019
57309
  return await action();
57020
57310
  } finally {
57021
57311
  closeSync13(fd);
@@ -57072,13 +57362,13 @@ async function preparePrivatePublication(client, sourceDirectory, recoveryDirect
57072
57362
  phase: "prepared",
57073
57363
  intent: null
57074
57364
  };
57075
- if (!isAbsolute7(recoveryDirectory) || resolve6(recoveryDirectory) !== recoveryDirectory || realpathSync7(dirname11(recoveryDirectory)) !== dirname11(recoveryDirectory))
57365
+ if (!isAbsolute8(recoveryDirectory) || resolve7(recoveryDirectory) !== recoveryDirectory || realpathSync7(dirname11(recoveryDirectory)) !== dirname11(recoveryDirectory))
57076
57366
  return fail2();
57077
57367
  mkdirSync16(recoveryDirectory, { mode: 448 });
57078
57368
  safeDirectory(recoveryDirectory);
57079
57369
  const parent = openSync13(dirname11(recoveryDirectory), constants9.O_RDONLY | constants9.O_NOFOLLOW);
57080
57370
  try {
57081
- fsyncSync3(parent);
57371
+ fsyncSync4(parent);
57082
57372
  } finally {
57083
57373
  closeSync13(parent);
57084
57374
  }
@@ -57173,10 +57463,10 @@ var init_private_publication_recovery = __esm(() => {
57173
57463
  });
57174
57464
 
57175
57465
  // src/mcp/private-publication-tools.ts
57176
- import { isAbsolute as isAbsolute8 } from "path";
57466
+ import { isAbsolute as isAbsolute9 } from "path";
57177
57467
  function registerPrivatePublicationTools(server) {
57178
57468
  const uuid9 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
57179
- const directory2 = exports_external.string().max(4096).refine(isAbsolute8, "Use an absolute local directory");
57469
+ const directory2 = exports_external.string().max(4096).refine(isAbsolute9, "Use an absolute local directory");
57180
57470
  const verification2 = { email: exports_external.string().email().max(254), code: exports_external.string().regex(/^\d{6}$/), userId: uuid9, membershipId: uuid9, recoveryDirectory: directory2 };
57181
57471
  const description = "Manage a private source publication using fresh workspace-bound verification and a host-local recovery directory. Publishing requires explicit UUID current-version comparison and upload consent. Resume reuses immutable saved bytes and intent; an uncertain PUT is finalized for server verification, never uploaded twice. Sessions and signed URLs are not returned or persisted. Private execution is unavailable. Host request history may contain the supplied verification code.";
57182
57472
  const handler = (action) => async (value) => {
@@ -57331,6 +57621,14 @@ function registerRemoteCustomerTools(server) {
57331
57621
  });
57332
57622
  }
57333
57623
  for (const operation of REMOTE_CUSTOMER_OPERATIONS) {
57624
+ if (operation.name === "create_credit_checkout") {
57625
+ server.registerTool(operation.name, {
57626
+ title: operation.title,
57627
+ description: "Create one external checkout link on the selected Skills server. Retain an idempotency_key before calling and reuse it only for explicit recovery on the same server, account and pack. No automatic retries; payment requires customer confirmation.",
57628
+ inputSchema: { pack_id: exports_external.string().min(1), idempotency_key: exports_external.string().regex(/^[A-Za-z0-9._:-]{8,255}$/).optional() }
57629
+ }, ({ pack_id, idempotency_key }) => callRemote((client) => operation.invoke(client, pack_id, { idempotencyKey: idempotency_key })));
57630
+ continue;
57631
+ }
57334
57632
  const inputSchema = {};
57335
57633
  if (operation.parameter)
57336
57634
  inputSchema[operation.parameter] = exports_external.string().min(1);
@@ -57407,6 +57705,15 @@ async function callRemote(action) {
57407
57705
  return mcpError("AUTH_REQUIRED", "Configure a Skills API and sign in with skills auth login");
57408
57706
  return mcpJson(await action(client));
57409
57707
  } catch (error61) {
57708
+ if (error61 instanceof RemoteCreditCheckoutError) {
57709
+ return { ...mcpJson({
57710
+ code: error61.code,
57711
+ message: error61.message,
57712
+ status: error61.status,
57713
+ requestIdempotencyKey: error61.requestIdempotencyKey,
57714
+ ...error61.retryAfterSeconds === undefined ? {} : { retryAfterSeconds: error61.retryAfterSeconds }
57715
+ }), isError: true };
57716
+ }
57410
57717
  if (error61 instanceof RemoteCapabilityUnavailableError) {
57411
57718
  return { ...mcpJson({ code: error61.code, message: error61.message, status: error61.status }), isError: true };
57412
57719
  }
@@ -57871,7 +58178,7 @@ var RequestError, nonJoinedHeaders, validHeaderName, isHttpWhitespace = (code2)
57871
58178
  request2[bodyBufferKey] = buffered;
57872
58179
  return Promise.resolve(buffered);
57873
58180
  }
57874
- const promise3 = new Promise((resolve7, reject) => {
58181
+ const promise3 = new Promise((resolve8, reject) => {
57875
58182
  const chunks = [];
57876
58183
  let settled = false;
57877
58184
  const finish = (callback) => {
@@ -57893,7 +58200,7 @@ var RequestError, nonJoinedHeaders, validHeaderName, isHttpWhitespace = (code2)
57893
58200
  reject(error61 ?? normalizeAbortError(request2, incoming));
57894
58201
  else {
57895
58202
  request2[bodyBufferKey] = recovered;
57896
- resolve7(recovered);
58203
+ resolve8(recovered);
57897
58204
  }
57898
58205
  });
57899
58206
  return true;
@@ -57905,7 +58212,7 @@ var RequestError, nonJoinedHeaders, validHeaderName, isHttpWhitespace = (code2)
57905
58212
  finish(() => {
57906
58213
  const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
57907
58214
  request2[bodyBufferKey] = buffer;
57908
- resolve7(buffer);
58215
+ resolve8(buffer);
57909
58216
  });
57910
58217
  };
57911
58218
  const onError = (error61) => {
@@ -58179,7 +58486,7 @@ var RequestError, nonJoinedHeaders, validHeaderName, isHttpWhitespace = (code2)
58179
58486
  });
58180
58487
  if (!chunk) {
58181
58488
  if (i === 1) {
58182
- await new Promise((resolve7) => setTimeout(resolve7));
58489
+ await new Promise((resolve8) => setTimeout(resolve8));
58183
58490
  maxReadCount = 3;
58184
58491
  continue;
58185
58492
  }
@@ -59409,9 +59716,9 @@ data:
59409
59716
  const initRequest = messages.find((m) => isInitializeRequest(m));
59410
59717
  const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
59411
59718
  if (this._enableJsonResponse) {
59412
- return new Promise((resolve7) => {
59719
+ return new Promise((resolve8) => {
59413
59720
  this._streamMapping.set(streamId, {
59414
- resolveJson: resolve7,
59721
+ resolveJson: resolve8,
59415
59722
  cleanup: () => {
59416
59723
  this._streamMapping.delete(streamId);
59417
59724
  }
@@ -59780,19 +60087,19 @@ async function connectMcpForNode() {
59780
60087
  return { server: server2, transport };
59781
60088
  }
59782
60089
  function readBody(req) {
59783
- return new Promise((resolve7, reject) => {
60090
+ return new Promise((resolve8, reject) => {
59784
60091
  const chunks = [];
59785
60092
  req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
59786
60093
  req.on("end", () => {
59787
60094
  const raw = Buffer.concat(chunks).toString("utf-8");
59788
60095
  if (!raw.trim()) {
59789
- resolve7(undefined);
60096
+ resolve8(undefined);
59790
60097
  return;
59791
60098
  }
59792
60099
  try {
59793
- resolve7(JSON.parse(raw));
60100
+ resolve8(JSON.parse(raw));
59794
60101
  } catch {
59795
- resolve7(undefined);
60102
+ resolve8(undefined);
59796
60103
  }
59797
60104
  });
59798
60105
  req.on("error", reject);
@@ -59834,9 +60141,9 @@ async function startSkillsMcpHttpServer(options = {}) {
59834
60141
  }
59835
60142
  }
59836
60143
  });
59837
- await new Promise((resolve7, reject) => {
60144
+ await new Promise((resolve8, reject) => {
59838
60145
  httpServer.once("error", reject);
59839
- httpServer.listen(port, hostname3, () => resolve7());
60146
+ httpServer.listen(port, hostname3, () => resolve8());
59840
60147
  });
59841
60148
  const address = httpServer.address();
59842
60149
  const listenPort = typeof address === "object" && address ? address.port : port;