@sellable/install 0.1.641 → 0.1.643

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.
@@ -43,6 +43,12 @@ import {
43
43
  observeProfileSoul,
44
44
  prepareSoulLockRoot,
45
45
  } from "../fly-soul-bridge.mjs";
46
+ import { proveDefaultProfileNativeSkills } from "../fly-skills-bridge.mjs";
47
+ import {
48
+ bindDefaultProfileProbeCron,
49
+ buildDefaultProfileProjection,
50
+ reconcileDefaultProfileBundle,
51
+ } from "../default-profile-reconciler.mjs";
46
52
  import {
47
53
  ensureHermesSnapshotCron,
48
54
  markHermesSnapshotDirty,
@@ -99,13 +105,67 @@ const BOOT_SESSION_ID = randomUUID();
99
105
  const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.20.0";
100
106
  const INSTALLER_PACKAGE =
101
107
  process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ??
102
- "@sellable/install@0.1.641";
108
+ "@sellable/install@0.1.643";
103
109
  const MCP_PACKAGE =
104
110
  process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.879";
111
+ const DEFAULT_PROFILE_BUNDLE_ROOT =
112
+ "/usr/local/share/sellable-agent/default-profile-bundle";
113
+ const DEFAULT_PROFILE_CRON_PYTHON = [
114
+ "import json,sys",
115
+ "from cron.jobs import create_job, update_job",
116
+ "value=json.loads(sys.argv[1])",
117
+ "job_id=value.pop('jobId', None)",
118
+ "enabled=value.pop('enabled')",
119
+ "result=update_job(job_id, {**value, 'enabled': enabled}) if job_id else create_job(**value, enabled=enabled)",
120
+ "print(json.dumps({'id': result.get('id') if result else None}))",
121
+ ].join("\n");
105
122
 
106
123
  const sha256 = (value) => createHash("sha256").update(value).digest("hex");
107
124
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
108
125
 
126
+ function customerDefaultProfileCronAdapter(profileId, profileRoot) {
127
+ const mutate = async ({ desired, spec, existing }) => {
128
+ const child = await run(
129
+ HERMES_PYTHON,
130
+ [
131
+ "-c",
132
+ DEFAULT_PROFILE_CRON_PYTHON,
133
+ JSON.stringify({
134
+ name: desired.name,
135
+ schedule: spec.schedule,
136
+ script: desired.script,
137
+ no_agent: true,
138
+ deliver: "local",
139
+ enabled: true,
140
+ ...(existing?.id ? { jobId: existing.id } : {}),
141
+ }),
142
+ ],
143
+ {
144
+ env: {
145
+ ...process.env,
146
+ HOME: DATA_ROOT,
147
+ HERMES_HOME: profileRoot,
148
+ HERMES_PROFILE: profileId,
149
+ NO_COLOR: "1",
150
+ },
151
+ timeoutMs: 120_000,
152
+ }
153
+ );
154
+ return {
155
+ ...child,
156
+ exitCode: child.status,
157
+ signal: null,
158
+ outputLimitExceeded: false,
159
+ };
160
+ };
161
+ return {
162
+ create: (input) => mutate(input),
163
+ update: (input) => mutate(input),
164
+ bind: (input) =>
165
+ bindDefaultProfileProbeCron({ ...input, bootId: BOOT_SESSION_ID }),
166
+ };
167
+ }
168
+
109
169
  function atomicFile(path, bytes, mode = 0o600) {
110
170
  mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
111
171
  const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
@@ -498,6 +558,12 @@ export function customerServingObservation(activeRuntime, exchange) {
498
558
  },
499
559
  productMcp: authority.productMcp,
500
560
  adminMcp: null,
561
+ ...(authority.bundleVersion === undefined
562
+ ? {}
563
+ : {
564
+ bundleVersion: authority.bundleVersion,
565
+ bundleDigest: authority.bundleDigest,
566
+ }),
501
567
  nativeGateway: true,
502
568
  customerRouting: true,
503
569
  });
@@ -533,6 +599,11 @@ function startOperationHeartbeat({ claim, exchange, client }) {
533
599
  assertHealthy() {
534
600
  if (failure) throw failure;
535
601
  },
602
+ async reattest() {
603
+ beat();
604
+ await inFlight;
605
+ if (failure) throw failure;
606
+ },
536
607
  async stop() {
537
608
  stopped = true;
538
609
  clearInterval(timer);
@@ -2631,6 +2702,8 @@ function completionReceipt({
2631
2702
  proof,
2632
2703
  memorySnapshot,
2633
2704
  observedSoulRawSha256,
2705
+ activeProjection,
2706
+ nativeDefaultProfileProof,
2634
2707
  }) {
2635
2708
  const identity = operationIdentity(claim, exchange);
2636
2709
  const observedLifecycle =
@@ -2651,6 +2724,12 @@ function completionReceipt({
2651
2724
  secretDisposition: "INSTALLED",
2652
2725
  observedLifecycle,
2653
2726
  observedRevisionId: desired.revisionId,
2727
+ ...(activeProjection
2728
+ ? {
2729
+ bundleVersion: activeProjection.bundleVersion,
2730
+ bundleDigest: activeProjection.bundleDigest,
2731
+ }
2732
+ : {}),
2654
2733
  signerGeneration: exchange.keyGeneration,
2655
2734
  signedDigest: flyRuntimeDigest({
2656
2735
  ...identity,
@@ -2680,6 +2759,11 @@ function completionReceipt({
2680
2759
  },
2681
2760
  { code: "NO_BOOT_INSTALL", status: "TRUE" },
2682
2761
  { code: "DEDICATED_SLACK_APP", status: "TRUE" },
2762
+ {
2763
+ code: "DEFAULT_PROFILE_NATIVE_PROOF",
2764
+ status:
2765
+ nativeDefaultProfileProof.status === "PROVEN" ? "TRUE" : "UNKNOWN",
2766
+ },
2683
2767
  ],
2684
2768
  errorClass: "NONE",
2685
2769
  auditIds: [
@@ -2697,9 +2781,10 @@ async function flushHermesSnapshot({
2697
2781
  profileId,
2698
2782
  profileRoot,
2699
2783
  operationIdentity,
2784
+ forceDirty = false,
2700
2785
  }) {
2701
2786
  try {
2702
- markHermesSnapshotDirty(profileRoot);
2787
+ if (forceDirty) markHermesSnapshotDirty(profileRoot);
2703
2788
  return await reconcileHermesSnapshot({
2704
2789
  profileId,
2705
2790
  profileRoot,
@@ -2773,7 +2858,13 @@ function runtimeModelHealthReporter({ identity, exchange, runtimeConfig }) {
2773
2858
  });
2774
2859
  }
2775
2860
 
2776
- async function processOperation({ claim, exchange, client, identity }) {
2861
+ async function processOperation({
2862
+ claim,
2863
+ exchange,
2864
+ client,
2865
+ identity,
2866
+ reattestAuthority,
2867
+ }) {
2777
2868
  const trustRootPem = readFileSync(TRUST_ROOT, "utf8");
2778
2869
  const { desired, toolInclude } = compileDesired(claim, trustRootPem);
2779
2870
  mkdirSync(PROFILES_ROOT, { recursive: true, mode: 0o700 });
@@ -2812,6 +2903,28 @@ async function processOperation({ claim, exchange, client, identity }) {
2812
2903
  profileRoot,
2813
2904
  });
2814
2905
  if (!pristineProfile.ok) throw new Error(pristineProfile.code);
2906
+ const activeProjection = buildDefaultProfileProjection({
2907
+ kind: "CUSTOMER",
2908
+ claim,
2909
+ });
2910
+ const reconciledDefaultProfile = await reconcileDefaultProfileBundle({
2911
+ activeProjection,
2912
+ materializerReceipt: pristineProfile.manifest,
2913
+ profileRoot,
2914
+ bundleRoot: DEFAULT_PROFILE_BUNDLE_ROOT,
2915
+ dataRoot: DATA_ROOT,
2916
+ reattestAuthority: async () => {
2917
+ await reattestAuthority();
2918
+ return activeProjection;
2919
+ },
2920
+ cronAdapter: customerDefaultProfileCronAdapter(
2921
+ desired.profileId,
2922
+ profileRoot
2923
+ ),
2924
+ });
2925
+ if (!reconciledDefaultProfile.ok)
2926
+ throw new Error(reconciledDefaultProfile.code);
2927
+ const defaultProfile = reconciledDefaultProfile;
2815
2928
  installInternalAdminRuntimeSettings({ desired, profileRoot });
2816
2929
  const runtimeConfig = writeRuntimeHermesConfig({
2817
2930
  desired,
@@ -2835,6 +2948,10 @@ async function processOperation({ claim, exchange, client, identity }) {
2835
2948
  hermesCli: HERMES,
2836
2949
  });
2837
2950
  if (!memoryCron.ok) throw new Error(memoryCron.code);
2951
+ const materializerChanged =
2952
+ materialized.status !== "REUSED" || memoryCron.status !== "REUSED";
2953
+ if (materializerChanged || defaultProfile.changed)
2954
+ markHermesSnapshotDirty(profileRoot);
2838
2955
  const memorySnapshot = await flushHermesSnapshot({
2839
2956
  client,
2840
2957
  profileId: desired.profileId,
@@ -2851,6 +2968,13 @@ async function processOperation({ claim, exchange, client, identity }) {
2851
2968
  secrets,
2852
2969
  profileDigest: pristineProfile.manifest.profileDigest,
2853
2970
  });
2971
+ const nativeDefaultProfileProof = proof.gatewayReadback?.running
2972
+ ? proveDefaultProfileNativeSkills({
2973
+ actions: defaultProfile.actions,
2974
+ profileId: desired.profileId,
2975
+ dataRoot: DATA_ROOT,
2976
+ })
2977
+ : { status: "EMPTY", digest: sha256("[]") };
2854
2978
  const receipt = completionReceipt({
2855
2979
  claim,
2856
2980
  exchange,
@@ -2858,6 +2982,8 @@ async function processOperation({ claim, exchange, client, identity }) {
2858
2982
  proof,
2859
2983
  memorySnapshot,
2860
2984
  observedSoulRawSha256,
2985
+ activeProjection,
2986
+ nativeDefaultProfileProof,
2861
2987
  });
2862
2988
  const candidateRuntime = {
2863
2989
  claim,
@@ -3133,6 +3259,7 @@ async function main() {
3133
3259
  profileId: claim.operation.profileId,
3134
3260
  profileRoot: join(PROFILES_ROOT, claim.operation.profileId),
3135
3261
  operationIdentity: snapshotIdentity,
3262
+ forceDirty: true,
3136
3263
  });
3137
3264
  if (!backup.ok || backup.status !== "UPLOADED") {
3138
3265
  throw new Error(
@@ -3176,6 +3303,7 @@ async function main() {
3176
3303
  exchange,
3177
3304
  client,
3178
3305
  identity,
3306
+ reattestAuthority: () => operationHeartbeat.reattest(),
3179
3307
  });
3180
3308
  soulClaimTracker = createSoulClaimObservationTracker({
3181
3309
  runtimeId: RUNTIME_ID,
@@ -17,6 +17,21 @@ import { parseFlyCustomerModelCredential } from "./fly-customer-model.mjs";
17
17
 
18
18
  const SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
19
19
  const SHA256 = /^[a-f0-9]{64}$/;
20
+
21
+ function imageBundleIdentity(env) {
22
+ const rawVersion = env?.SELLABLE_AGENT_DEFAULT_PROFILE_BUNDLE_VERSION;
23
+ const bundleDigest = env?.SELLABLE_AGENT_DEFAULT_PROFILE_BUNDLE_DIGEST;
24
+ if (rawVersion === undefined && bundleDigest === undefined) return null;
25
+ const bundleVersion = Number(rawVersion);
26
+ if (
27
+ !Number.isSafeInteger(bundleVersion) ||
28
+ bundleVersion < 1 ||
29
+ !SHA256.test(bundleDigest ?? "")
30
+ ) {
31
+ throw new Error("runtime_serving_observation_rejected");
32
+ }
33
+ return Object.freeze({ bundleVersion, bundleDigest });
34
+ }
20
35
  function sha256(value) {
21
36
  return createHash("sha256").update(value).digest("hex");
22
37
  }
@@ -38,7 +53,8 @@ export function isOperationBoundBackupClaim(claim) {
38
53
  }
39
54
 
40
55
  // CUSTOMER and ADMIN runtimes share this one serving-observation builder.
41
- export function buildRuntimeServingObservation(input = {}) {
56
+ export function buildRuntimeServingObservation(input = {}, env = process.env) {
57
+ const bundleIdentity = imageBundleIdentity(env);
42
58
  const keys = [
43
59
  "scope",
44
60
  "kind",
@@ -60,6 +76,7 @@ export function buildRuntimeServingObservation(input = {}) {
60
76
  "adminMcp",
61
77
  "nativeGateway",
62
78
  "customerRouting",
79
+ ...(bundleIdentity ? ["bundleVersion", "bundleDigest"] : []),
63
80
  ];
64
81
  const identity = (value) => typeof value === "string" && SAFE_ID.test(value);
65
82
  const mcp = (value, name) =>
@@ -77,7 +94,7 @@ export function buildRuntimeServingObservation(input = {}) {
77
94
  mcp(input.adminMcp, "sellable-admin-mcp") &&
78
95
  input.customerRouting === false;
79
96
  if (
80
- !exactKeys(input, keys) ||
97
+ !exactKeys({ ...input, ...(bundleIdentity ?? {}) }, keys) ||
81
98
  !["ASSIGNED", "CANDIDATE"].includes(input.scope) ||
82
99
  !keys
83
100
  .slice(2, 13)
@@ -106,6 +123,7 @@ export function buildRuntimeServingObservation(input = {}) {
106
123
  return Object.freeze({
107
124
  schemaVersion: "sellable-agent-serving-observation/v2",
108
125
  ...input,
126
+ ...(bundleIdentity ?? {}),
109
127
  });
110
128
  }
111
129
 
@@ -23,6 +23,21 @@ import { Upload as TusUpload } from "tus-js-client";
23
23
 
24
24
  const execFileAsync = promisify(execFile);
25
25
  const SHA256 = /^[a-f0-9]{64}$/;
26
+
27
+ function imageBundleIdentity(env) {
28
+ const rawVersion = env?.SELLABLE_AGENT_DEFAULT_PROFILE_BUNDLE_VERSION;
29
+ const bundleDigest = env?.SELLABLE_AGENT_DEFAULT_PROFILE_BUNDLE_DIGEST;
30
+ if (rawVersion === undefined && bundleDigest === undefined) return null;
31
+ const bundleVersion = Number(rawVersion);
32
+ if (
33
+ !Number.isSafeInteger(bundleVersion) ||
34
+ bundleVersion < 1 ||
35
+ !SHA256.test(bundleDigest ?? "")
36
+ ) {
37
+ throw new Error("runtime_bundle_identity_rejected");
38
+ }
39
+ return Object.freeze({ bundleVersion, bundleDigest });
40
+ }
26
41
  const SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
27
42
  const FLY_OIDC_AUTHORIZATION_SCHEME = "FlyOIDC";
28
43
 
@@ -906,11 +921,13 @@ export function createFlyCustomerControlClient({
906
921
  origin,
907
922
  identity,
908
923
  exchange,
924
+ env = process.env,
909
925
  fetchImpl = fetch,
910
926
  retryDelayMs = 150,
911
927
  tusUploadImpl = uploadSupabaseSignedTus,
912
928
  }) {
913
929
  const endpoint = exactHttpsOrigin(origin);
930
+ const bundleIdentity = imageBundleIdentity(env);
914
931
  if (
915
932
  !Number.isSafeInteger(retryDelayMs) ||
916
933
  retryDelayMs < 0 ||
@@ -919,6 +936,28 @@ export function createFlyCustomerControlClient({
919
936
  throw new Error("worker_control_retry_delay_invalid");
920
937
  }
921
938
  const post = async (verb, path, body, { replaySafe = false } = {}) => {
939
+ const presentedBundle =
940
+ body && typeof body === "object" && !Array.isArray(body)
941
+ ? imageBundleIdentity({
942
+ SELLABLE_AGENT_DEFAULT_PROFILE_BUNDLE_VERSION:
943
+ body.bundleVersion === undefined
944
+ ? undefined
945
+ : String(body.bundleVersion),
946
+ SELLABLE_AGENT_DEFAULT_PROFILE_BUNDLE_DIGEST: body.bundleDigest,
947
+ })
948
+ : null;
949
+ if (
950
+ presentedBundle &&
951
+ bundleIdentity &&
952
+ (presentedBundle.bundleVersion !== bundleIdentity.bundleVersion ||
953
+ presentedBundle.bundleDigest !== bundleIdentity.bundleDigest)
954
+ ) {
955
+ throw new Error("runtime_bundle_identity_rejected");
956
+ }
957
+ const signedBody =
958
+ verb === "complete" && bundleIdentity && !presentedBundle
959
+ ? { ...body, ...bundleIdentity }
960
+ : body;
922
961
  const maxAttempts = replaySafe ? 3 : 1;
923
962
  for (let attempt = 1; ; attempt += 1) {
924
963
  try {
@@ -926,7 +965,7 @@ export function createFlyCustomerControlClient({
926
965
  identity,
927
966
  exchange,
928
967
  verb,
929
- body,
968
+ body: signedBody,
930
969
  now: new Date(),
931
970
  });
932
971
  let response;
@@ -934,7 +973,7 @@ export function createFlyCustomerControlClient({
934
973
  response = await fetchImpl(`${endpoint}${path}`, {
935
974
  method: "POST",
936
975
  headers: { authorization, "content-type": "application/json" },
937
- body: JSON.stringify(body),
976
+ body: JSON.stringify(signedBody),
938
977
  signal: AbortSignal.timeout(20_000),
939
978
  });
940
979
  } catch {
@@ -352,7 +352,7 @@ function publicInventory(inventory) {
352
352
  }
353
353
 
354
354
  const NATIVE_SCRIPT = String.raw`
355
- import json, os, sys
355
+ import hashlib, json, os, sys
356
356
  from pathlib import Path
357
357
  request = json.loads(sys.stdin.read())
358
358
  from tools.skills_tool import _find_all_skills, skill_view
@@ -360,6 +360,13 @@ from tools.skill_manager_tool import _find_skill, _create_skill, _edit_skill, _d
360
360
  from hermes_cli.config import load_config
361
361
  from hermes_cli.skills_config import get_disabled_skills, save_disabled_skills
362
362
 
363
+ def native_view(read_name, read_file=None):
364
+ viewed = skill_view(read_name, file_path=read_file, preprocess=False) if read_file else skill_view(read_name, preprocess=False)
365
+ content = viewed.get("content") if isinstance(viewed, dict) else viewed
366
+ success = viewed.get("success") is True if isinstance(viewed, dict) else isinstance(viewed, str)
367
+ name = str(viewed.get("name") or "") if isinstance(viewed, dict) else read_name
368
+ return {"success": success, "name": name, "content": content, "contentSha256": hashlib.sha256(content.encode("utf-8")).hexdigest() if isinstance(content, str) else None}
369
+
363
370
  def inventory(read_name=None, read_file=None):
364
371
  rows = _find_all_skills(skip_disabled=True)
365
372
  config = load_config()
@@ -381,14 +388,14 @@ def inventory(read_name=None, read_file=None):
381
388
  result.append(item)
382
389
  viewed = None
383
390
  if read_name:
384
- viewed = skill_view(read_name, file_path=read_file) if read_file else skill_view(read_name)
391
+ viewed = native_view(read_name, read_file)
385
392
  return {"complete": True, "skills": result, "disabledNames": disabled, "disabledConfig": json.dumps(disabled, separators=(",", ":")), "nativeView": viewed}
386
393
 
387
394
  op = request.get("operation")
388
395
  if op == "enumerate":
389
396
  output = inventory(request.get("readName"), request.get("readFile"))
390
397
  elif op == "read":
391
- output = {"nativeView": skill_view(request["readName"], file_path=request.get("readFile")) if request.get("readFile") else skill_view(request["readName"])}
398
+ output = {"nativeView": native_view(request["readName"], request.get("readFile"))}
392
399
  elif op == "scan":
393
400
  from tools.skills_guard import scan_skill, should_allow_install
394
401
  # Phase 170 production receipt skills_apply_failed_rolled_back proved that
@@ -538,6 +545,18 @@ function findSkill(inventory, id, { editable = false } = {}) {
538
545
  return skill;
539
546
  }
540
547
 
548
+ function assertNativeView(proof, name, content, code) {
549
+ if (
550
+ proof?.success !== true ||
551
+ proof.name !== name ||
552
+ typeof proof.content !== "string" ||
553
+ proof.content !== content ||
554
+ proof.contentSha256 !== sha256(Buffer.from(content, "utf8"))
555
+ ) {
556
+ fail(code);
557
+ }
558
+ }
559
+
541
560
  function backupTree(skillRoot) {
542
561
  const snapshot = [];
543
562
  const walk = (directory) => {
@@ -622,8 +641,12 @@ function executeLocked(request, paths, adapter, options) {
622
641
  readName: skill.name,
623
642
  readFile: relativePath === "SKILL.md" ? undefined : relativePath,
624
643
  });
625
- if (typeof proof.nativeView !== "string")
626
- fail("skills_native_readback_failed");
644
+ assertNativeView(
645
+ proof.nativeView,
646
+ skill.name,
647
+ content,
648
+ "skills_native_readback_failed"
649
+ );
627
650
  atomicProof(paths, before);
628
651
  return {
629
652
  ok: true,
@@ -788,8 +811,7 @@ function executeLocked(request, paths, adapter, options) {
788
811
  const after = normalizeEnumeration(afterNative, paths, options.limits);
789
812
  if (
790
813
  !after.complete ||
791
- (request.operation !== "delete" &&
792
- typeof afterNative.nativeView !== "string")
814
+ (request.operation !== "delete" && !afterNative.nativeView)
793
815
  )
794
816
  fail("skills_readback_failed");
795
817
  const afterSkill =
@@ -798,6 +820,14 @@ function executeLocked(request, paths, adapter, options) {
798
820
  request.operation === "delete" ? afterSkill !== null : afterSkill === null
799
821
  )
800
822
  fail("skills_readback_failed");
823
+ if (afterSkill) {
824
+ assertNativeView(
825
+ afterNative.nativeView,
826
+ afterSkill.name,
827
+ afterSkill.manifestContent,
828
+ "skills_readback_failed"
829
+ );
830
+ }
801
831
  if (
802
832
  (request.operation === "edit" &&
803
833
  afterSkill.fileSha256 !==
@@ -933,6 +963,32 @@ export function executeExternalSkillsBridgeOperation(
933
963
  });
934
964
  }
935
965
 
966
+ export function proveDefaultProfileNativeSkills({
967
+ actions,
968
+ profileId,
969
+ dataRoot = SOUL_BRIDGE_DATA_ROOT,
970
+ } = {}) {
971
+ const managed = (actions ?? []).filter((action) => action.type === "skill");
972
+ if (managed.length === 0)
973
+ return { status: "EMPTY", digest: sha256("[]") };
974
+ const inventory = executeExternalSkillsBridgeOperation(
975
+ { operation: "list" },
976
+ { dataRoot, callerProfileId: profileId }
977
+ );
978
+ const proofs = managed.map((action) => {
979
+ const skill = inventory.skills.find((entry) => entry.name === action.id);
980
+ if (!skill) fail("default_profile_native_skill_missing");
981
+ const viewed = executeExternalSkillsBridgeOperation(
982
+ { operation: "read", skillId: skill.id },
983
+ { dataRoot, callerProfileId: profileId }
984
+ );
985
+ if (!viewed.ok || viewed.skill?.name !== action.id)
986
+ fail("default_profile_native_skill_readback_rejected");
987
+ return { id: action.id, fileSha256: viewed.fileSha256 };
988
+ });
989
+ return { status: "PROVEN", digest: sha256(stableJson(proofs)) };
990
+ }
991
+
936
992
  export function computeSkillsConfigIdentity(profileRoot) {
937
993
  if (!isAbsolute(profileRoot) || !existsSync(profileRoot))
938
994
  fail("skills_profile_root_rejected");
@@ -38,7 +38,7 @@ import { installAgentHostServices } from "./service-installer.mjs";
38
38
  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
- const INSTALLER_PACKAGE = "@sellable/install@0.1.641";
41
+ const INSTALLER_PACKAGE = "@sellable/install@0.1.643";
42
42
  const MCP_PACKAGE = "@sellable/mcp@0.1.879";
43
43
  const HERMES_VERSION = "0.18.0";
44
44
  const SHA256 = /^[a-f0-9]{64}$/;
@@ -1547,7 +1547,7 @@ export function compileClaimToProfileDesired(activeClaim, config) {
1547
1547
  pinned.hermesCli !== "hermes" ||
1548
1548
  pinned.hermesVersion !== "0.18.0" ||
1549
1549
  pinned.installerPackage !==
1550
- "@sellable/install@0.1.641" ||
1550
+ "@sellable/install@0.1.643" ||
1551
1551
  pinned.mcpPackage !== "@sellable/mcp@0.1.879" ||
1552
1552
  !Array.isArray(policy.toolInclude) ||
1553
1553
  policy.toolInclude.length === 0 ||
@@ -283,7 +283,7 @@ function validateDesired(desired) {
283
283
  desired.hermesCli !== "hermes" ||
284
284
  desired.hermesVersion !== "0.18.0" ||
285
285
  desired.installerPackage !==
286
- "@sellable/install@0.1.641" ||
286
+ "@sellable/install@0.1.643" ||
287
287
  desired.mcpPackage !== "@sellable/mcp@0.1.879" ||
288
288
  !Array.isArray(desired.toolInclude) ||
289
289
  desired.toolInclude.length === 0 ||
@@ -26,7 +26,7 @@ import { deriveContainedProfileId } from "./profile-materializer.mjs";
26
26
 
27
27
  export const PROVISIONING_ACTION = "PROVISION_HERMES_PROFILE";
28
28
  export const PINNED_INSTALL_PACKAGE =
29
- "@sellable/install@0.1.641";
29
+ "@sellable/install@0.1.643";
30
30
  export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.879";
31
31
  const PINNED_INSTALL_VERSION = PINNED_INSTALL_PACKAGE.slice(
32
32
  PINNED_INSTALL_PACKAGE.lastIndexOf("@") + 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.641",
3
+ "version": "0.1.643",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code, Codex, and Hermes",
6
6
  "bin": {