@sellable/install 0.1.579 → 0.1.581

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.
@@ -1596,6 +1596,113 @@ export function patchHermesSourceFile(patchKind, source) {
1596
1596
  return patched;
1597
1597
  }
1598
1598
 
1599
+ if (patchKind === "skills-cache-freshness-v020") {
1600
+ let patched = replaceExactlyOnce(
1601
+ source,
1602
+ " sig.append((str(d), m))",
1603
+ [
1604
+ " skill_files = []",
1605
+ " for root, dirs, files in os.walk(d, followlinks=False):",
1606
+ " dirs[:] = sorted(",
1607
+ " name for name in dirs",
1608
+ " if not os.path.islink(os.path.join(root, name))",
1609
+ " )",
1610
+ ' if "SKILL.md" not in files:',
1611
+ " continue",
1612
+ ' skill_path = os.path.join(root, "SKILL.md")',
1613
+ " try:",
1614
+ " skill_stat = os.stat(skill_path, follow_symlinks=False)",
1615
+ " skill_files.append(",
1616
+ " (skill_path, skill_stat.st_mtime_ns, skill_stat.st_size)",
1617
+ " )",
1618
+ " except OSError:",
1619
+ " continue",
1620
+ " sig.append((str(d), m, tuple(skill_files)))",
1621
+ ].join("\n"),
1622
+ "v0.20 live SKILL.md signature"
1623
+ );
1624
+ patched = replaceExactlyOnce(
1625
+ patched,
1626
+ " disabled = set() if skip_disabled else _get_disabled_skill_names()",
1627
+ [
1628
+ " disabled = _get_disabled_skill_names()",
1629
+ " filtered_disabled = set() if skip_disabled else disabled",
1630
+ ].join("\n"),
1631
+ "v0.20 shared disabled signature"
1632
+ );
1633
+ patched = replaceExactlyOnce(
1634
+ patched,
1635
+ " if name in disabled:\n continue",
1636
+ " if name in filtered_disabled:\n continue",
1637
+ "v0.20 filtered disabled behavior"
1638
+ );
1639
+ patched = replaceExactlyOnce(
1640
+ patched,
1641
+ " skills = []\n seen_names: set = set()",
1642
+ [
1643
+ " # A signature change invalidates both filtered and unfiltered",
1644
+ " # variants together; neither waits for the 30-second TTL.",
1645
+ " if cached is not None and cached[0] != signature:",
1646
+ " _SKILLS_CACHE.clear()",
1647
+ "",
1648
+ " skills = []",
1649
+ " seen_names: set = set()",
1650
+ ].join("\n"),
1651
+ "v0.20 both-variant cache invalidation"
1652
+ );
1653
+ return patched;
1654
+ }
1655
+
1656
+ if (patchKind === "skills-prompt-freshness-v020") {
1657
+ let patched = replaceExactlyOnce(
1658
+ source,
1659
+ "_SKILLS_PROMPT_CACHE_LOCK = threading.Lock()",
1660
+ [
1661
+ "_SKILLS_PROMPT_CACHE_LOCK = threading.Lock()",
1662
+ "_SELLABLE_AGENT_SKILLS_PROMPT_SIGNATURE = None",
1663
+ ].join("\n"),
1664
+ "v0.20 prompt signature state"
1665
+ );
1666
+ patched = replaceExactlyOnce(
1667
+ patched,
1668
+ " disabled = get_disabled_skill_names(_platform_hint or None)\n cache_key = (",
1669
+ [
1670
+ " disabled = get_disabled_skill_names(_platform_hint or None)",
1671
+ " signature_roots = [skills_dir, *external_dirs]",
1672
+ " live_signature = tuple(",
1673
+ " (",
1674
+ " str(root),",
1675
+ " tuple(",
1676
+ " (path, tuple(values))",
1677
+ " for path, values in sorted(_build_skills_manifest(root).items())",
1678
+ " ),",
1679
+ " )",
1680
+ " for root in signature_roots",
1681
+ " if root.exists()",
1682
+ " )",
1683
+ " cache_key = (",
1684
+ ].join("\n"),
1685
+ "v0.20 prompt live signature"
1686
+ );
1687
+ patched = replaceExactlyOnce(
1688
+ patched,
1689
+ " tuple(sorted(compact_categories or ())),\n )\n with _SKILLS_PROMPT_CACHE_LOCK:\n cached = _SKILLS_PROMPT_CACHE.get(cache_key)",
1690
+ [
1691
+ " tuple(sorted(compact_categories or ())),",
1692
+ " live_signature,",
1693
+ " )",
1694
+ " global _SELLABLE_AGENT_SKILLS_PROMPT_SIGNATURE",
1695
+ " with _SKILLS_PROMPT_CACHE_LOCK:",
1696
+ " if _SELLABLE_AGENT_SKILLS_PROMPT_SIGNATURE != live_signature:",
1697
+ " _SKILLS_PROMPT_CACHE.clear()",
1698
+ " _SELLABLE_AGENT_SKILLS_PROMPT_SIGNATURE = live_signature",
1699
+ " cached = _SKILLS_PROMPT_CACHE.get(cache_key)",
1700
+ ].join("\n"),
1701
+ "v0.20 prompt cache invalidation"
1702
+ );
1703
+ return patched;
1704
+ }
1705
+
1599
1706
  throw new HermesAgentBridgeError(
1600
1707
  "HERMES_BRIDGE_UNKNOWN_PATCH_KIND",
1601
1708
  `Unknown Hermes bridge patch kind: ${patchKind}`,
@@ -1820,7 +1927,7 @@ export const HERMES_AGENT_BRIDGE_DEDICATED_V020_CONTRACT = normalizeContract({
1820
1927
  sourceSha256:
1821
1928
  "f9d50eec7b98819fa91b8ce640c8a32eff0726e0f349832763852bd82bbbb958",
1822
1929
  patchedSha256:
1823
- "0622b793e72967f16688b059dbecc22ecd91b78623792be0af37a2cdeb19404b",
1930
+ "4b0bcc47a07a43402958bbd09524483f8adb370bcbfa6ffa99b63b4ca5b35ff1",
1824
1931
  },
1825
1932
  {
1826
1933
  path: "gateway/run.py",
@@ -1836,7 +1943,7 @@ export const HERMES_AGENT_BRIDGE_DEDICATED_V020_CONTRACT = normalizeContract({
1836
1943
  sourceSha256:
1837
1944
  "a0e136367b64007d7b49ea006ab0aa7dcc66b12134b512a463a03bd69fb8a90c",
1838
1945
  patchedSha256:
1839
- "670d05a11edf0f9174e84e6b8803ffc1f2522e138898a6f86cbfaff7e0e3e299",
1946
+ "5f19a22c832db2e93b3da0fe9ea62606968233aa964318f8f81b0ae3dc7c8a13",
1840
1947
  },
1841
1948
  {
1842
1949
  path: "hermes_cli/web_routers/tools.py",
@@ -1852,7 +1959,23 @@ export const HERMES_AGENT_BRIDGE_DEDICATED_V020_CONTRACT = normalizeContract({
1852
1959
  sourceSha256:
1853
1960
  "aba4c2b9c8691ccc86518cfa10dcd92e55d7b2103c1c6807f54ddf58919f3f48",
1854
1961
  patchedSha256:
1855
- "90c98514a8c6c4078dacdd67a92cf749bd174ec2c074226163c6cb07ca3df59f",
1962
+ "8302998c684a290eae1dd8c34f93c790ced764340d72d52995021b15625f0acb",
1963
+ },
1964
+ {
1965
+ path: "tools/skills_tool.py",
1966
+ patchKind: "skills-cache-freshness-v020",
1967
+ sourceSha256:
1968
+ "6cfe456c8872c91309083694290e5e2cbdb64a1b2f4ac9c2cb38973fab2228ab",
1969
+ patchedSha256:
1970
+ "9a1b1417879d9aac6d5b6e1713237193009e2d67dbc691db35ef5940e94f9397",
1971
+ },
1972
+ {
1973
+ path: "agent/prompt_builder.py",
1974
+ patchKind: "skills-prompt-freshness-v020",
1975
+ sourceSha256:
1976
+ "2a8a8b2b6c0ff830349f68b5c2f26bf2c9871ae03caf516cff3628c00b5769c7",
1977
+ patchedSha256:
1978
+ "4a55eb6dd4e79d35fca9fd02544444576c892ca02dfebb0c97954156cc470fdb",
1856
1979
  },
1857
1980
  ],
1858
1981
  });
@@ -39,8 +39,8 @@ const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
39
39
  const VERSION = "sellable-agent-host-bootstrap/v1";
40
40
  const RECEIPT_VERSION = "sellable-agent-host-registration/v1";
41
41
  const INSTALLER_PACKAGE =
42
- "@sellable/install@0.1.577";
43
- const MCP_PACKAGE = "@sellable/mcp@0.1.858";
42
+ "@sellable/install@0.1.581";
43
+ const MCP_PACKAGE = "@sellable/mcp@0.1.859";
44
44
  const HERMES_VERSION = "0.18.0";
45
45
  const SHA256 = /^[a-f0-9]{64}$/;
46
46
  const SHA256_16 = /^[a-f0-9]{16}$/;
@@ -29,6 +29,7 @@ import {
29
29
  relative,
30
30
  resolve,
31
31
  } from "node:path";
32
+ import { readSkillsProofObservation } from "./fly-skills-bridge.mjs";
32
33
  import {
33
34
  commitRetainedProfilePromotion,
34
35
  computeAgentProfileDigest,
@@ -77,6 +78,11 @@ const SECRET_PATTERN =
77
78
  /sat_[A-Za-z0-9_-]+|xox[bap]-[A-Za-z0-9-]+|asec\.v1\.|authorization[_-]?code|access[_-]?token|refresh[_-]?token/i;
78
79
  const STATE_FILE = "worker-state.json";
79
80
 
81
+ export function attachSkillsProofObservation(observed, profileRoot) {
82
+ const skills = readSkillsProofObservation(profileRoot);
83
+ return skills ? { ...observed, skills } : observed;
84
+ }
85
+
80
86
  function stableJson(value) {
81
87
  if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
82
88
  if (value && typeof value === "object") {
@@ -1540,10 +1546,8 @@ export function compileClaimToProfileDesired(activeClaim, config) {
1540
1546
  ]) ||
1541
1547
  pinned.hermesCli !== "hermes" ||
1542
1548
  pinned.hermesVersion !== "0.18.0" ||
1543
- pinned.installerPackage !==
1544
- "@sellable/install@0.1.577" ||
1545
- pinned.mcpPackage !==
1546
- "@sellable/mcp@0.1.858" ||
1549
+ pinned.installerPackage !== "@sellable/install@0.1.581" ||
1550
+ pinned.mcpPackage !== "@sellable/mcp@0.1.859" ||
1547
1551
  !Array.isArray(policy.toolInclude) ||
1548
1552
  policy.toolInclude.length === 0 ||
1549
1553
  !Number.isSafeInteger(slack.generation) ||
@@ -2348,7 +2352,10 @@ export function createLocalPinnedActionPorts(config, dependencies = {}) {
2348
2352
  observed.process.uid !== config.runtimeUid
2349
2353
  )
2350
2354
  throw new Error("runtime observation identity rejected");
2351
- return observed;
2355
+ return attachSkillsProofObservation(
2356
+ observed,
2357
+ join(realpathSync(config.profilesRoot), context.profileId)
2358
+ );
2352
2359
  };
2353
2360
  const ensureRefreshTarget = async () => {
2354
2361
  if (refreshPrepared) return;
@@ -201,10 +201,8 @@ function validateDesired(desired) {
201
201
  desired.serviceCredentialGeneration < 1 ||
202
202
  desired.hermesCli !== "hermes" ||
203
203
  desired.hermesVersion !== "0.18.0" ||
204
- desired.installerPackage !==
205
- "@sellable/install@0.1.577" ||
206
- desired.mcpPackage !==
207
- "@sellable/mcp@0.1.858" ||
204
+ desired.installerPackage !== "@sellable/install@0.1.581" ||
205
+ desired.mcpPackage !== "@sellable/mcp@0.1.859" ||
208
206
  !Array.isArray(desired.toolInclude) ||
209
207
  desired.toolInclude.length === 0 ||
210
208
  desired.toolInclude.some((tool) => !/^[a-z][a-z0-9_]{0,127}$/.test(tool)) ||
@@ -408,6 +406,113 @@ function configFor(desired, profileId) {
408
406
  };
409
407
  }
410
408
 
409
+ const LIVE_SKILLS_CONFIG_KEYS = Object.freeze([
410
+ "disabled",
411
+ "platform_disabled",
412
+ ]);
413
+
414
+ function profileConfigWithoutLiveSkills(value) {
415
+ const normalized = structuredClone(value);
416
+ if (
417
+ normalized?.skills &&
418
+ typeof normalized.skills === "object" &&
419
+ !Array.isArray(normalized.skills)
420
+ ) {
421
+ for (const key of LIVE_SKILLS_CONFIG_KEYS) delete normalized.skills[key];
422
+ if (Object.keys(normalized.skills).length === 0) delete normalized.skills;
423
+ }
424
+ return normalized;
425
+ }
426
+
427
+ function normalizedProfileConfigBytes(pathValue) {
428
+ const value = JSON.parse(readFileSync(pathValue, "utf8"));
429
+ return Buffer.from(
430
+ `${JSON.stringify(profileConfigWithoutLiveSkills(value), null, 2)}\n`,
431
+ "utf8"
432
+ );
433
+ }
434
+
435
+ function mergeLiveSkillsConfig(sourceRoot, targetRoot) {
436
+ const sourcePath = join(sourceRoot, "config.yaml");
437
+ const targetPath = join(targetRoot, "config.yaml");
438
+ if (!existsSync(sourcePath) || !existsSync(targetPath)) return;
439
+ const source = JSON.parse(readFileSync(sourcePath, "utf8"));
440
+ const target = JSON.parse(readFileSync(targetPath, "utf8"));
441
+ const sourceSkills = source?.skills;
442
+ if (
443
+ !sourceSkills ||
444
+ typeof sourceSkills !== "object" ||
445
+ Array.isArray(sourceSkills)
446
+ ) {
447
+ return;
448
+ }
449
+ const preserved = Object.fromEntries(
450
+ LIVE_SKILLS_CONFIG_KEYS.filter((key) =>
451
+ Object.prototype.hasOwnProperty.call(sourceSkills, key)
452
+ ).map((key) => [key, structuredClone(sourceSkills[key])])
453
+ );
454
+ if (Object.keys(preserved).length === 0) return;
455
+ target.skills = {
456
+ ...(target.skills &&
457
+ typeof target.skills === "object" &&
458
+ !Array.isArray(target.skills)
459
+ ? target.skills
460
+ : {}),
461
+ ...preserved,
462
+ };
463
+ atomicWrite(
464
+ targetPath,
465
+ `${JSON.stringify(target, null, 2)}\n`,
466
+ lstatSync(targetPath).mode & 0o777
467
+ );
468
+ }
469
+
470
+ function mergeLiveSkillsState(sourceRoot, targetRoot) {
471
+ if (!existsSync(sourceRoot)) return;
472
+ const sourceSkills = join(sourceRoot, "skills");
473
+ const targetSkills = join(targetRoot, "skills");
474
+ if (existsSync(sourceSkills)) mergeHermesState(sourceSkills, targetSkills);
475
+ mergeLiveSkillsConfig(sourceRoot, targetRoot);
476
+ }
477
+
478
+ function preservedSkillsConfigIdentity(profileRoot) {
479
+ if (!existsSync(profileRoot)) return null;
480
+ const entries = [];
481
+ const skillsRoot = join(profileRoot, "skills");
482
+ const walk = (directory, relativePath = "") => {
483
+ for (const name of readdirSync(directory).sort()) {
484
+ const nextRelative = relativePath ? `${relativePath}/${name}` : name;
485
+ if (nextRelative === "sellable") continue;
486
+ const pathValue = join(directory, name);
487
+ const link = lstatSync(pathValue);
488
+ if (link.isSymbolicLink())
489
+ throw new Error("live skills symlink rejected");
490
+ if (link.isDirectory()) walk(pathValue, nextRelative);
491
+ else if (link.isFile()) {
492
+ entries.push({
493
+ path: nextRelative,
494
+ sha256: sha256(readFileSync(pathValue)),
495
+ });
496
+ } else throw new Error("live skills entry rejected");
497
+ }
498
+ };
499
+ if (existsSync(skillsRoot)) walk(skillsRoot);
500
+ const configPath = join(profileRoot, "config.yaml");
501
+ let disabled = {};
502
+ if (existsSync(configPath)) {
503
+ const config = JSON.parse(readFileSync(configPath, "utf8"));
504
+ const skills = config?.skills;
505
+ if (skills && typeof skills === "object" && !Array.isArray(skills)) {
506
+ disabled = Object.fromEntries(
507
+ LIVE_SKILLS_CONFIG_KEYS.filter((key) =>
508
+ Object.prototype.hasOwnProperty.call(skills, key)
509
+ ).map((key) => [key, skills[key]])
510
+ );
511
+ }
512
+ }
513
+ return sha256(stableJson({ disabled, entries }));
514
+ }
515
+
411
516
  function mcpFor(desired, profileId) {
412
517
  return {
413
518
  package: desired.mcpPackage,
@@ -616,10 +721,8 @@ function validateRuntimeOwnership(observed, runtimeIdentity) {
616
721
  "skills/sellable",
617
722
  "skills/sellable/SKILL.md",
618
723
  ].sort();
619
- if (
620
- stableJson(observed.entries.map((entry) => entry.path).sort()) !==
621
- stableJson(expectedPaths)
622
- ) {
724
+ const observedPaths = observed.entries.map((entry) => entry.path);
725
+ if (expectedPaths.some((pathValue) => !observedPaths.includes(pathValue))) {
623
726
  return false;
624
727
  }
625
728
  return observed.entries.every(
@@ -644,9 +747,16 @@ export function validateAgentProfileBundle({ profileRoot, desired }) {
644
747
  return { ok: false, code: "profile_path_invalid" };
645
748
  }
646
749
  const foundInventory = inventory(profileRoot);
750
+ const serverOwned = [...AGENT_PROFILE_INVENTORY].sort();
647
751
  if (
648
- JSON.stringify(foundInventory) !==
649
- JSON.stringify([...AGENT_PROFILE_INVENTORY].sort())
752
+ serverOwned.some(
753
+ (logicalPath) => !foundInventory.includes(logicalPath)
754
+ ) ||
755
+ foundInventory.some(
756
+ (logicalPath) =>
757
+ !serverOwned.includes(logicalPath) &&
758
+ !logicalPath.startsWith("skills/")
759
+ )
650
760
  ) {
651
761
  return { ok: false, code: "profile_inventory_open" };
652
762
  }
@@ -667,7 +777,11 @@ export function validateAgentProfileBundle({ profileRoot, desired }) {
667
777
  .sort()
668
778
  .map((logicalPath) => [
669
779
  logicalPath,
670
- sha256(readFileSync(join(profileRoot, logicalPath))),
780
+ sha256(
781
+ logicalPath === "config.yaml"
782
+ ? normalizedProfileConfigBytes(join(profileRoot, logicalPath))
783
+ : readFileSync(join(profileRoot, logicalPath))
784
+ ),
671
785
  ])
672
786
  );
673
787
  if (stableJson(manifest.fileHashes) !== stableJson(expectedHashes)) {
@@ -686,7 +800,8 @@ export function validateAgentProfileBundle({ profileRoot, desired }) {
686
800
  readFileSync(join(profileRoot, "sellable", "mcp.json"), "utf8")
687
801
  );
688
802
  if (
689
- stableJson(config) !== stableJson(configFor(desired, manifest.profileId))
803
+ stableJson(profileConfigWithoutLiveSkills(config)) !==
804
+ stableJson(configFor(desired, manifest.profileId))
690
805
  ) {
691
806
  return { ok: false, code: "config_drift" };
692
807
  }
@@ -1625,7 +1740,7 @@ async function rollback({
1625
1740
  return outcome;
1626
1741
  }
1627
1742
 
1628
- export async function materializeAgentProfile(input = {}) {
1743
+ async function materializeAgentProfileLocked(input = {}) {
1629
1744
  const valid = validateDesired(input.desired);
1630
1745
  if (!valid.ok)
1631
1746
  return { ok: false, status: "DESIRED_REJECTED", code: valid.code };
@@ -1688,7 +1803,15 @@ export async function materializeAgentProfile(input = {}) {
1688
1803
  revisionId: desired.revisionId,
1689
1804
  fence: desired.fence,
1690
1805
  });
1806
+ const liveSkillsIdentity = preservedSkillsConfigIdentity(profileRoot);
1691
1807
  renderStagedProfile(stagingRoot, desired);
1808
+ mergeLiveSkillsState(profileRoot, stagingRoot);
1809
+ if (
1810
+ liveSkillsIdentity !== null &&
1811
+ preservedSkillsConfigIdentity(stagingRoot) !== liveSkillsIdentity
1812
+ ) {
1813
+ throw new Error("live skills identity changed during staging");
1814
+ }
1692
1815
  const staged = validateAgentProfileBundle({
1693
1816
  profileRoot: stagingRoot,
1694
1817
  desired,
@@ -2016,6 +2139,26 @@ export async function materializeAgentProfile(input = {}) {
2016
2139
  }
2017
2140
  }
2018
2141
 
2142
+ export async function materializeAgentProfile(input = {}) {
2143
+ const valid = validateDesired(input.desired);
2144
+ const root = traversableProfilesRoot(input.profilesRoot);
2145
+ if (!valid.ok || !root) return materializeAgentProfileLocked(input);
2146
+ const profileId = deriveContainedProfileId(
2147
+ input.desired.workspaceId,
2148
+ input.desired.agentId,
2149
+ input.desired.hostId
2150
+ );
2151
+ try {
2152
+ return await withProfileSoulLock(
2153
+ profileId,
2154
+ () => materializeAgentProfileLocked(input),
2155
+ { dataRoot: basename(root) === "profiles" ? dirname(root) : root }
2156
+ );
2157
+ } catch {
2158
+ return { ok: false, status: "LOCK_REJECTED", profileId };
2159
+ }
2160
+ }
2161
+
2019
2162
  export const NATIVE_SLACK_CUSTOMER_PROFILE_INVENTORY = Object.freeze([
2020
2163
  "agent-profile.json",
2021
2164
  "config.yaml",
@@ -2326,7 +2469,11 @@ export function validateNativeSlackCustomerProfile({ desired, profileRoot }) {
2326
2469
  link.isSymbolicLink() ||
2327
2470
  !link.isFile() ||
2328
2471
  (link.mode & 0o022) !== 0 ||
2329
- !readFileSync(pathValue).equals(Buffer.from(expected))
2472
+ !(logicalPath === "config.yaml"
2473
+ ? normalizedProfileConfigBytes(pathValue).equals(
2474
+ Buffer.from(expected)
2475
+ )
2476
+ : readFileSync(pathValue).equals(Buffer.from(expected)))
2330
2477
  ) {
2331
2478
  return { ok: false, code: "native_slack_profile_file_drift" };
2332
2479
  }
@@ -2393,6 +2540,7 @@ export function materializeNativeSlackCustomerProfile({
2393
2540
  return { ok: false, code: "native_slack_profile_path_rejected" };
2394
2541
  }
2395
2542
  let soulBytes = Buffer.from(desired.soul, "utf8");
2543
+ const liveSkillsIdentity = preservedSkillsConfigIdentity(activeRoot);
2396
2544
  if (existsSync(activeRoot)) {
2397
2545
  const soulPath = join(activeRoot, "SOUL.md");
2398
2546
  if (existsSync(soulPath)) {
@@ -2409,6 +2557,13 @@ export function materializeNativeSlackCustomerProfile({
2409
2557
  soulBytes
2410
2558
  );
2411
2559
  mergeHermesState(activeRoot, stagingRoot);
2560
+ mergeLiveSkillsConfig(activeRoot, stagingRoot);
2561
+ if (
2562
+ liveSkillsIdentity !== null &&
2563
+ preservedSkillsConfigIdentity(stagingRoot) !== liveSkillsIdentity
2564
+ ) {
2565
+ throw new Error("live skills identity changed during staging");
2566
+ }
2412
2567
  const staged = validateNativeSlackCustomerProfile({
2413
2568
  desired,
2414
2569
  profileRoot: stagingRoot,
@@ -25,10 +25,8 @@ import { fileURLToPath } from "node:url";
25
25
  import { deriveContainedProfileId } from "./profile-materializer.mjs";
26
26
 
27
27
  export const PROVISIONING_ACTION = "PROVISION_HERMES_PROFILE";
28
- export const PINNED_INSTALL_PACKAGE =
29
- "@sellable/install@0.1.577";
30
- export const PINNED_MCP_PACKAGE =
31
- "@sellable/mcp@0.1.858";
28
+ export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.581";
29
+ export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.859";
32
30
 
33
31
  export function deriveAgentProfileId(workspaceId, agentId, hostId) {
34
32
  return deriveContainedProfileId(workspaceId, agentId, hostId);
@@ -613,8 +611,7 @@ function inspectProvisionedProfile({
613
611
  !matchesReadback(manifest, request) ||
614
612
  manifest.serviceCredentialReference !== serviceCredentialReference ||
615
613
  manifest.installerPackage !== PINNED_INSTALL_PACKAGE ||
616
- manifest.installerVersion !==
617
- "0.1.577" ||
614
+ manifest.installerVersion !== "0.1.581" ||
618
615
  manifest.mcpPackage !== PINNED_MCP_PACKAGE ||
619
616
  manifest.credentialKeyVersion !== credentialKeyVersion ||
620
617
  !/^[a-f0-9]{16}$/.test(manifest.credentialFingerprint) ||
@@ -828,7 +825,7 @@ function successReceipt({
828
825
  subject: `agent-profile:${observed.profileId}`,
829
826
  cli: {
830
827
  installPackage: PINNED_INSTALL_PACKAGE,
831
- installVersion: "0.1.577",
828
+ installVersion: "0.1.581",
832
829
  mcpPackage: PINNED_MCP_PACKAGE,
833
830
  command: "hermes profile bootstrap",
834
831
  },
@@ -876,7 +873,7 @@ function failure(code, stages, extra = {}, request = null) {
876
873
  : "agent-profile:unbound",
877
874
  cli: {
878
875
  installPackage: PINNED_INSTALL_PACKAGE,
879
- installVersion: "0.1.577",
876
+ installVersion: "0.1.581",
880
877
  mcpPackage: PINNED_MCP_PACKAGE,
881
878
  command: "hermes profile bootstrap",
882
879
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.579",
3
+ "version": "0.1.581",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code, Codex, and Hermes",
6
6
  "bin": {