@sellable/install 0.1.467 → 0.1.468

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.
@@ -3,12 +3,12 @@ FROM ${HERMES_IMAGE}
3
3
 
4
4
  USER root
5
5
 
6
- ARG INSTALLER_PACKAGE=@sellable/install@0.1.467
6
+ ARG INSTALLER_PACKAGE=@sellable/install@0.1.468
7
7
  ARG MCP_PACKAGE=@sellable/mcp@0.1.760-wip.phase143.20260727100513
8
8
  ARG INSTALLER_INTEGRITY
9
9
  ARG MCP_INTEGRITY=sha512-muNXHuRnLTfsnW+MQUvK+p0VBZETDKeevr70iRliXyqE3/SLrZQJLL1laJzMo+qiUs9jMXMHdKOQfQqLiwJvtg==
10
10
 
11
- RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.467" \
11
+ RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.468" \
12
12
  && test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.760-wip.phase143.20260727100513" \
13
13
  && test -n "${INSTALLER_INTEGRITY}" \
14
14
  && test "$(npm view "${INSTALLER_PACKAGE}" dist.integrity)" = "${INSTALLER_INTEGRITY}" \
@@ -23,9 +23,9 @@ RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.467" \
23
23
  && mkdir -p /opt/sellable-agent/install-root /opt/sellable-agent/mcp-root \
24
24
  && npm install --prefix /opt/sellable-agent/install-root --omit=dev --ignore-scripts --no-audit --no-fund "${INSTALLER_PACKAGE}" \
25
25
  && npm install --prefix /opt/sellable-agent/mcp-root --omit=dev --ignore-scripts --no-audit --no-fund "${MCP_PACKAGE}" \
26
- && node --input-type=module -e "import { readFileSync } from 'node:fs'; const install = JSON.parse(readFileSync('/opt/sellable-agent/install-root/node_modules/@sellable/install/package.json')); const mcp = JSON.parse(readFileSync('/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp/package.json')); if (install.name !== '@sellable/install' || install.version !== '0.1.467' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.760-wip.phase143.20260727100513') throw new Error('package identity rejected');" \
26
+ && node --input-type=module -e "import { readFileSync } from 'node:fs'; const install = JSON.parse(readFileSync('/opt/sellable-agent/install-root/node_modules/@sellable/install/package.json')); const mcp = JSON.parse(readFileSync('/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp/package.json')); if (install.name !== '@sellable/install' || install.version !== '0.1.468' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.760-wip.phase143.20260727100513') throw new Error('package identity rejected');" \
27
27
  && node --input-type=module -e "import { buildExternalRuntimeClosure } from '/opt/sellable-agent/install-root/node_modules/@sellable/install/lib/sellable-agent/external-runtime-builder.mjs'; const built = buildExternalRuntimeClosure({ mcpPackageRoot: '/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp', mcpNodeModulesRoot: '/opt/sellable-agent/mcp-root/node_modules', hermesSourceRoot: '/opt/hermes', ownerUid: 0, ownerGid: 0 }); if (!built.closureDigest) throw new Error('runtime closure rejected');" \
28
- && node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.467', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.760-wip.phase143.20260727100513', mcpIntegrity: process.argv[2] }; writeFileSync('/opt/sellable-agent/release.json', JSON.stringify(release) + '\\n', { mode: 0o444 }); chmodSync('/opt/sellable-agent/release.json', 0o444);" "${INSTALLER_INTEGRITY}" "${MCP_INTEGRITY}" \
28
+ && node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.468', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.760-wip.phase143.20260727100513', mcpIntegrity: process.argv[2] }; writeFileSync('/opt/sellable-agent/release.json', JSON.stringify(release) + '\\n', { mode: 0o444 }); chmodSync('/opt/sellable-agent/release.json', 0o444);" "${INSTALLER_INTEGRITY}" "${MCP_INTEGRITY}" \
29
29
  && npm cache clean --force
30
30
 
31
31
  COPY entrypoint.sh /usr/local/bin/sellable-agent-container-entrypoint
@@ -3,13 +3,13 @@
3
3
  This image is a dedicated Agent worker/runtime boundary beside an existing Hermes dashboard. It never replaces or reconfigures the dashboard container.
4
4
 
5
5
  Build from this directory after
6
- `@sellable/install@0.1.467` is
6
+ `@sellable/install@0.1.468` is
7
7
  published:
8
8
 
9
9
  ```sh
10
10
  docker build --platform linux/amd64 \
11
11
  --build-arg INSTALLER_INTEGRITY='sha512-…' \
12
- --tag sellable-agent-host:0.1.467 .
12
+ --tag sellable-agent-host:0.1.468 .
13
13
  ```
14
14
 
15
15
  Create a private `bootstrap/` directory owned by root with mode `0700`. It must contain `bootstrap.json`, `provider-auth`, and `slack-app`, each owned by root with mode `0600`. `bootstrap.json` contains only non-secret identities, hashes, package integrities, and the control-plane URL. The other two files contain the provider JSON and global Slack app token whose hashes are pinned by `bootstrap.json`.
@@ -42,9 +42,10 @@ import {
42
42
  parseFlyCustomerModelCredential,
43
43
  } from "../admin-artifact/node_modules/@sellable/install/lib/sellable-agent/fly-customer-model.mjs";
44
44
  import {
45
- buildAgentServiceEnvironment,
45
+ buildAdminAgentServiceEnvironment,
46
46
  buildNativeHermesLaunch,
47
47
  } from "../admin-artifact/node_modules/@sellable/install/lib/sellable-agent/fly-customer-worker.mjs";
48
+ import { expectedRuntimeGates } from "../admin-artifact/node_modules/@sellable/install/lib/sellable-agent/runtime-helper.mjs";
48
49
  import sodium from "../admin-artifact/node_modules/libsodium-wrappers/dist/modules-esm/libsodium-wrappers.mjs";
49
50
  import {
50
51
  createFlyCustomerControlClient,
@@ -895,7 +896,29 @@ async function consumeRuntimeSecrets({ claim, exchange, client }) {
895
896
  return { mcpService, slackApp, slackBot, modelCodex };
896
897
  }
897
898
 
898
- function installRuntimeSecrets({ claim, secrets }) {
899
+ function writeAdminRuntimeGates(desiredLifecycle) {
900
+ const gates = expectedRuntimeGates(desiredLifecycle);
901
+ const path = join(SECRET_ROOT, "gates.json");
902
+ atomicPrivateFile(path, `${JSON.stringify(gates)}\n`);
903
+ const stat = lstatSync(path);
904
+ let observed;
905
+ try {
906
+ observed = JSON.parse(readFileSync(path, "utf8"));
907
+ } catch {
908
+ throw new Error("admin_runtime_gates_rejected");
909
+ }
910
+ if (
911
+ stat.isSymbolicLink() ||
912
+ !stat.isFile() ||
913
+ (stat.mode & 0o777) !== 0o600 ||
914
+ canonicalJson(observed) !== canonicalJson(gates)
915
+ ) {
916
+ throw new Error("admin_runtime_gates_rejected");
917
+ }
918
+ return Object.freeze({ ...observed });
919
+ }
920
+
921
+ function installRuntimeSecrets({ claim, secrets, desiredLifecycle }) {
899
922
  privateDirectory(SECRET_ROOT);
900
923
  const modelAuthority = claim?.runtimeContract?.model;
901
924
  const modelEnvelope = claim?.secretEnvelopes?.modelCodex;
@@ -988,15 +1011,7 @@ function installRuntimeSecrets({ claim, secrets }) {
988
1011
  slackSecretFile,
989
1012
  `SLACK_BOT_TOKEN=${secrets.slackBot}\nSLACK_APP_TOKEN=${secrets.slackApp}\n`
990
1013
  );
991
- atomicPrivateFile(
992
- join(SECRET_ROOT, "gates.json"),
993
- `${JSON.stringify({
994
- workspaceId: claim.operation.workspaceId,
995
- agentId: claim.operation.agentId,
996
- revisionId: claim.operation.targetRevisionId,
997
- fence: claim.operation.fence,
998
- })}\n`
999
- );
1014
+ const runtimeGates = writeAdminRuntimeGates(desiredLifecycle);
1000
1015
  const productConfigPath = join(SECRET_ROOT, "sellable-config.json");
1001
1016
  atomicPrivateFile(
1002
1017
  productConfigPath,
@@ -1010,6 +1025,7 @@ function installRuntimeSecrets({ claim, secrets }) {
1010
1025
  return {
1011
1026
  rootAuthPath,
1012
1027
  productConfigPath,
1028
+ runtimeGates,
1013
1029
  slackSecretFile,
1014
1030
  };
1015
1031
  }
@@ -1091,6 +1107,11 @@ function adminAgentServiceEnvironment(claim) {
1091
1107
  const bindings = claim?.runtimeContract?.slack?.selectedBindings;
1092
1108
  const toolInclude = service?.toolInclude;
1093
1109
  if (
1110
+ claim?.agentKind !== "ADMIN" ||
1111
+ claim?.adminRuntimeGeneration?.agentKind !== "ADMIN" ||
1112
+ claim.adminRuntimeGeneration.workspaceId !== operation?.workspaceId ||
1113
+ claim.adminRuntimeGeneration.agentId !== operation?.agentId ||
1114
+ claim.adminRuntimeGeneration.fence !== operation?.fence ||
1094
1115
  !operation ||
1095
1116
  !service ||
1096
1117
  !/^[A-Za-z0-9_-]{1,128}$/.test(service.principalId ?? "") ||
@@ -1109,7 +1130,8 @@ function adminAgentServiceEnvironment(claim) {
1109
1130
  ) {
1110
1131
  throw new Error("admin_runtime_agent_service_environment_rejected");
1111
1132
  }
1112
- return buildAgentServiceEnvironment({
1133
+ return buildAdminAgentServiceEnvironment({
1134
+ agentKind: claim.agentKind,
1113
1135
  workspaceId: operation.workspaceId,
1114
1136
  agentId: operation.agentId,
1115
1137
  serviceCredentialGeneration: service.credentialGeneration,
@@ -1624,6 +1646,7 @@ function completionReceipt({
1624
1646
  observedLifecycle,
1625
1647
  memorySnapshot,
1626
1648
  modelAuth,
1649
+ runtimeGates,
1627
1650
  }) {
1628
1651
  const identity = operationIdentity(claim, exchange);
1629
1652
  const routeGenerationDigest = flyRuntimeDigest(
@@ -1662,7 +1685,9 @@ function completionReceipt({
1662
1685
  package: sha256(JSON.stringify(packageSpecs())),
1663
1686
  config: operation.profileContract.digest,
1664
1687
  content: operation.profileContract.digest,
1665
- process: sha256(JSON.stringify({ servers: profile.servers, modelAuth })),
1688
+ process: sha256(
1689
+ JSON.stringify({ servers: profile.servers, modelAuth, runtimeGates })
1690
+ ),
1666
1691
  };
1667
1692
  return {
1668
1693
  ...identity,
@@ -1684,6 +1709,7 @@ function completionReceipt({
1684
1709
  { code: "SETTINGS_GENERATION_PROVEN", status: "TRUE" },
1685
1710
  { code: "RECOVERY_GENERATION_PROVEN", status: "TRUE" },
1686
1711
  { code: "MODEL_AUTH_PROVEN", status: "TRUE" },
1712
+ { code: "RUNTIME_GATES_PROVEN", status: "TRUE" },
1687
1713
  { code: "SLACK_TRAFFIC_DISABLED", status: "TRUE" },
1688
1714
  { code: "NO_BOOT_INSTALL", status: "TRUE" },
1689
1715
  ],
@@ -1927,6 +1953,33 @@ export async function runAdminCanary() {
1927
1953
  workspaceId: fixtureWorkspaceId,
1928
1954
  })}\n`
1929
1955
  );
1956
+ const canaryClaim = {
1957
+ agentKind: "ADMIN",
1958
+ adminRuntimeGeneration: {
1959
+ agentKind: "ADMIN",
1960
+ workspaceId: fixtureWorkspaceId,
1961
+ agentId: fixtureAgentId,
1962
+ fence: "phase158-canary-fence-a",
1963
+ },
1964
+ operation: {
1965
+ workspaceId: fixtureWorkspaceId,
1966
+ agentId: fixtureAgentId,
1967
+ fence: "phase158-canary-fence-a",
1968
+ },
1969
+ runtimeContract: {
1970
+ service: {
1971
+ principalId: "phase158-canary-principal",
1972
+ policyHash: "a".repeat(64),
1973
+ toolInclude: ["get_workspace"],
1974
+ credentialGeneration: 1,
1975
+ },
1976
+ slack: {
1977
+ selectedBindings: [{ channelId: "C_PHASE158_CANARY" }],
1978
+ },
1979
+ },
1980
+ };
1981
+ const agentServiceEnvironment = adminAgentServiceEnvironment(canaryClaim);
1982
+ const runtimeGates = writeAdminRuntimeGates("ON");
1930
1983
 
1931
1984
  const template = JSON.parse(readFileSync(ADMIN_PROFILE_TEMPLATE_PATH));
1932
1985
  const { contentDigest, ...templateContent } = template;
@@ -2083,6 +2136,7 @@ export async function runAdminCanary() {
2083
2136
  sha256: sha256(soulBytes),
2084
2137
  byteLength: soulBytes.byteLength,
2085
2138
  },
2139
+ agentServiceEnv: agentServiceEnvironment,
2086
2140
  });
2087
2141
  const modelAuth = proveAdminModelAuth(profile);
2088
2142
  const observedSettings = settingsProof(
@@ -2105,6 +2159,19 @@ export async function runAdminCanary() {
2105
2159
  profileFactory: "stageAdminProfile",
2106
2160
  profileDigest: profileContract.digest,
2107
2161
  servers: profile.servers,
2162
+ runtimeGates,
2163
+ workspaceAuthority: {
2164
+ agentKind: canaryClaim.agentKind,
2165
+ workspaceLockPresent:
2166
+ Object.prototype.hasOwnProperty.call(
2167
+ agentServiceEnvironment,
2168
+ "SELLABLE_LOCK_WORKSPACE_ID"
2169
+ ) ||
2170
+ Object.prototype.hasOwnProperty.call(
2171
+ agentServiceEnvironment,
2172
+ "SELLABLE_REQUIRE_WORKSPACE_LOCK"
2173
+ ),
2174
+ },
2108
2175
  modelAuth,
2109
2176
  settings: observedSettings,
2110
2177
  adminHome: profile.adminHome,
@@ -2309,8 +2376,12 @@ async function runAdminProduction() {
2309
2376
  exchange,
2310
2377
  client,
2311
2378
  });
2312
- const installed = installRuntimeSecrets({ claim, secrets });
2313
2379
  const desiredLifecycle = desiredLifecycleForClaim(claim);
2380
+ const installed = installRuntimeSecrets({
2381
+ claim,
2382
+ secrets,
2383
+ desiredLifecycle,
2384
+ });
2314
2385
  let operation = await pollDelivery({ identity, exchange });
2315
2386
  let imported;
2316
2387
  let profileContract;
@@ -2434,6 +2505,7 @@ async function runAdminProduction() {
2434
2505
  observedLifecycle: desiredLifecycle,
2435
2506
  memorySnapshot,
2436
2507
  modelAuth,
2508
+ runtimeGates: installed.runtimeGates,
2437
2509
  });
2438
2510
  operationHeartbeat.assertHealthy();
2439
2511
  const candidateLaunch =
@@ -5,7 +5,7 @@ USER root
5
5
 
6
6
  ARG MCP_PACKAGE=@sellable/mcp@0.1.789-wip.phase159.20260729223403
7
7
  ARG MCP_INTEGRITY=sha512-KoheQcAQe/c8xzhF3R1ls6nQd11bcQqM3ykOUlzzNX5kykO7rO8RR2KsOvomwMv2Nn5VzvZZx6rGrle/6woKfw==
8
- ARG INSTALLER_PACKAGE=@sellable/install@0.1.467
8
+ ARG INSTALLER_PACKAGE=@sellable/install@0.1.468
9
9
  ARG TUS_JS_CLIENT_VERSION=4.3.1
10
10
  ARG HERMES_VERSION=0.19.0
11
11
  ARG MCP_TRUST_ROOT_B64
@@ -69,7 +69,7 @@ const RUNTIME_GENERATION = Number(
69
69
  const BOOT_SESSION_ID = randomUUID();
70
70
  const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.19.0";
71
71
  const INSTALLER_PACKAGE =
72
- process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.467";
72
+ process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.468";
73
73
  const MCP_PACKAGE =
74
74
  process.env.SELLABLE_AGENT_MCP_PACKAGE ??
75
75
  "@sellable/mcp@0.1.789-wip.phase159.20260729223403";
@@ -66,6 +66,18 @@ export function buildAgentServiceEnvironment({
66
66
  });
67
67
  }
68
68
 
69
+ export function buildAdminAgentServiceEnvironment({ agentKind, ...input }) {
70
+ if (agentKind !== "ADMIN") {
71
+ throw new Error("admin_agent_service_environment_rejected");
72
+ }
73
+ const {
74
+ SELLABLE_LOCK_WORKSPACE_ID: _workspaceLock,
75
+ SELLABLE_REQUIRE_WORKSPACE_LOCK: _workspaceLockRequired,
76
+ ...environment
77
+ } = buildAgentServiceEnvironment(input);
78
+ return Object.freeze(environment);
79
+ }
80
+
69
81
  function exactKeys(value, keys) {
70
82
  return Boolean(
71
83
  value &&
@@ -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.467";
41
+ const INSTALLER_PACKAGE = "@sellable/install@0.1.468";
42
42
  const MCP_PACKAGE = "@sellable/mcp@0.1.760-wip.phase143.20260727100513";
43
43
  const HERMES_VERSION = "0.18.0";
44
44
  const SHA256 = /^[a-f0-9]{64}$/;
@@ -1540,7 +1540,7 @@ export function compileClaimToProfileDesired(activeClaim, config) {
1540
1540
  ]) ||
1541
1541
  pinned.hermesCli !== "hermes" ||
1542
1542
  pinned.hermesVersion !== "0.18.0" ||
1543
- pinned.installerPackage !== "@sellable/install@0.1.467" ||
1543
+ pinned.installerPackage !== "@sellable/install@0.1.468" ||
1544
1544
  pinned.mcpPackage !==
1545
1545
  "@sellable/mcp@0.1.760-wip.phase143.20260727100513" ||
1546
1546
  !Array.isArray(policy.toolInclude) ||
@@ -197,7 +197,7 @@ function validateDesired(desired) {
197
197
  desired.serviceCredentialGeneration < 1 ||
198
198
  desired.hermesCli !== "hermes" ||
199
199
  desired.hermesVersion !== "0.18.0" ||
200
- desired.installerPackage !== "@sellable/install@0.1.467" ||
200
+ desired.installerPackage !== "@sellable/install@0.1.468" ||
201
201
  desired.mcpPackage !==
202
202
  "@sellable/mcp@0.1.760-wip.phase143.20260727100513" ||
203
203
  !Array.isArray(desired.toolInclude) ||
@@ -25,7 +25,7 @@ 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 = "@sellable/install@0.1.467";
28
+ export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.468";
29
29
  export const PINNED_MCP_PACKAGE =
30
30
  "@sellable/mcp@0.1.760-wip.phase143.20260727100513";
31
31
 
@@ -612,7 +612,7 @@ function inspectProvisionedProfile({
612
612
  !matchesReadback(manifest, request) ||
613
613
  manifest.serviceCredentialReference !== serviceCredentialReference ||
614
614
  manifest.installerPackage !== PINNED_INSTALL_PACKAGE ||
615
- manifest.installerVersion !== "0.1.467" ||
615
+ manifest.installerVersion !== "0.1.468" ||
616
616
  manifest.mcpPackage !== PINNED_MCP_PACKAGE ||
617
617
  manifest.credentialKeyVersion !== credentialKeyVersion ||
618
618
  !/^[a-f0-9]{16}$/.test(manifest.credentialFingerprint) ||
@@ -826,7 +826,7 @@ function successReceipt({
826
826
  subject: `agent-profile:${observed.profileId}`,
827
827
  cli: {
828
828
  installPackage: PINNED_INSTALL_PACKAGE,
829
- installVersion: "0.1.467",
829
+ installVersion: "0.1.468",
830
830
  mcpPackage: PINNED_MCP_PACKAGE,
831
831
  command: "hermes profile bootstrap",
832
832
  },
@@ -874,7 +874,7 @@ function failure(code, stages, extra = {}, request = null) {
874
874
  : "agent-profile:unbound",
875
875
  cli: {
876
876
  installPackage: PINNED_INSTALL_PACKAGE,
877
- installVersion: "0.1.467",
877
+ installVersion: "0.1.468",
878
878
  mcpPackage: PINNED_MCP_PACKAGE,
879
879
  command: "hermes profile bootstrap",
880
880
  },
@@ -504,7 +504,7 @@ function runtimeServiceEnvironmentValues({ profile, profileId, manifest, target,
504
504
  };
505
505
  }
506
506
 
507
- function expectedRuntimeGates(lifecycle) {
507
+ export function expectedRuntimeGates(lifecycle) {
508
508
  if (lifecycle === "ON") return { lifecycle, dispatch: "ENABLED", route: "OPEN", output: "OPEN" };
509
509
  if (lifecycle === "OFF") return { lifecycle, dispatch: "FENCED", route: "OPEN", output: "OPEN" };
510
510
  if (["ARCHIVING", "ARCHIVED"].includes(lifecycle)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.467",
3
+ "version": "0.1.468",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code, Codex, and Hermes",
6
6
  "bin": {