@sellable/install 0.1.651 → 0.1.653-wip.phase189.20260814.1

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.651
6
+ ARG INSTALLER_PACKAGE=@sellable/install@0.1.653-wip.phase189.20260814.1
7
7
  ARG MCP_PACKAGE=@sellable/mcp@0.1.880
8
8
  ARG INSTALLER_INTEGRITY
9
9
  ARG MCP_INTEGRITY=sha512-+Fo8aQlYduLaQMQff+ZA25LeUhWZ7I7UH5DECpND84LQ+rHSjKeKFj4Zs93bVHXwQCIy2OLbr75y4f4+CyT57w==
10
10
 
11
- RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.651" \
11
+ RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.653-wip.phase189.20260814.1" \
12
12
  && test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.880" \
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.651" \
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.651' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.880') 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.653-wip.phase189.20260814.1' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.880') 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.651', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.880', 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.653-wip.phase189.20260814.1', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.880', 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,7 +3,7 @@
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.651` is
6
+ `@sellable/install@0.1.653-wip.phase189.20260814.1` is
7
7
  published:
8
8
 
9
9
  ```sh
@@ -0,0 +1,24 @@
1
+ export const ADMIN_NATIVE_GATEWAY_MAX_LOCAL_RESTARTS = 3;
2
+
3
+ export function nativeGatewayRunning(gateway) {
4
+ const child = gateway?.child;
5
+ return Boolean(
6
+ child &&
7
+ child.exitCode === null &&
8
+ child.signalCode === null &&
9
+ child.killed !== true
10
+ );
11
+ }
12
+
13
+ export function nextAdminNativeGatewayRecovery({ attempts }) {
14
+ const completedAttempts = Number.isInteger(attempts)
15
+ ? Math.max(0, attempts)
16
+ : 0;
17
+ if (completedAttempts >= ADMIN_NATIVE_GATEWAY_MAX_LOCAL_RESTARTS) {
18
+ return {
19
+ action: "EXHAUSTED",
20
+ attempt: ADMIN_NATIVE_GATEWAY_MAX_LOCAL_RESTARTS,
21
+ };
22
+ }
23
+ return { action: "RESTART", attempt: completedAttempts + 1 };
24
+ }
@@ -148,8 +148,10 @@ RUN --mount=type=secret,id=mcp_trust_root,required=true \
148
148
  test "$(sha256sum /usr/local/share/sellable-agent/mcp-turn-trust-root.pem | cut -d ' ' -f 1)" = "${MCP_TRUST_ROOT_FINGERPRINT}"
149
149
 
150
150
  COPY fly-admin-image/admin-runtime.mjs /usr/local/lib/sellable-agent/admin-runtime/admin-runtime.mjs
151
+ COPY fly-admin-image/admin-runtime-phase-telemetry.mjs /usr/local/lib/sellable-agent/admin-runtime/admin-runtime-phase-telemetry.mjs
151
152
  COPY fly-admin-image/admin-state-generation.mjs /usr/local/lib/sellable-agent/admin-runtime/admin-state-generation.mjs
152
153
  COPY fly-admin-image/start-admin-runtime /usr/local/lib/sellable-agent/admin-runtime/start-admin-runtime
154
+ COPY admin-native-gateway-supervisor.mjs /usr/local/lib/sellable-agent/admin-native-gateway-supervisor.mjs
153
155
  COPY abandoned-runtime-scratch.mjs /usr/local/lib/sellable-agent/abandoned-runtime-scratch.mjs
154
156
  COPY mcp-context-proxy.mjs /usr/local/lib/sellable-agent/mcp-context-proxy.mjs
155
157
  COPY mcp-runtime-binding.mjs /usr/local/lib/sellable-agent/mcp-runtime-binding.mjs
@@ -237,6 +239,7 @@ RUN set -eux; \
237
239
  /usr/local/lib/sellable-agent/admin-runtime/admin-runtime.mjs \
238
240
  /usr/local/lib/sellable-agent/admin-runtime/admin-state-generation.mjs \
239
241
  /usr/local/lib/sellable-agent/admin-runtime/start-admin-runtime \
242
+ /usr/local/lib/sellable-agent/admin-native-gateway-supervisor.mjs \
240
243
  /usr/local/lib/sellable-agent/abandoned-runtime-scratch.mjs \
241
244
  /usr/local/lib/sellable-agent/mcp-context-proxy.mjs \
242
245
  /usr/local/lib/sellable-agent/mcp-runtime-binding.mjs \
@@ -0,0 +1,80 @@
1
+ const ADMIN_RUNTIME_PHASES = new Set([
2
+ "runtime_secrets",
3
+ "snapshot_restore",
4
+ "profile_settings_delivery",
5
+ "profile_stage",
6
+ "profile_live_merge_promote",
7
+ "profile_crons",
8
+ "default_profile_bundle",
9
+ "memory_snapshot",
10
+ "slack_serving_proof",
11
+ "native_gateway",
12
+ "serving_observation",
13
+ "operation_completion",
14
+ ]);
15
+
16
+ function phaseFailureCode(error) {
17
+ const code = error instanceof Error ? error.message : "";
18
+ return /^[A-Za-z0-9_:-]{1,160}$/.test(code)
19
+ ? code
20
+ : "admin_runtime_phase_failed";
21
+ }
22
+
23
+ export function createAdminRuntimePhaseTelemetry({
24
+ bootSessionId,
25
+ operationId,
26
+ now = () => Date.now(),
27
+ write = (entry) => process.stderr.write(`${JSON.stringify(entry)}\n`),
28
+ }) {
29
+ if (
30
+ typeof bootSessionId !== "string" ||
31
+ bootSessionId.length < 1 ||
32
+ typeof operationId !== "string" ||
33
+ operationId.length < 1
34
+ ) {
35
+ throw new Error("admin_runtime_phase_identity_rejected");
36
+ }
37
+
38
+ const run = async (phase, work) => {
39
+ if (!ADMIN_RUNTIME_PHASES.has(phase) || typeof work !== "function") {
40
+ throw new Error("admin_runtime_phase_rejected");
41
+ }
42
+ const startedAt = now();
43
+ write({
44
+ ok: true,
45
+ code: "admin_runtime_phase",
46
+ phase,
47
+ status: "started",
48
+ elapsedMs: 0,
49
+ bootSessionId,
50
+ operationId,
51
+ });
52
+ try {
53
+ const result = await work();
54
+ write({
55
+ ok: true,
56
+ code: "admin_runtime_phase",
57
+ phase,
58
+ status: "completed",
59
+ elapsedMs: Math.max(0, now() - startedAt),
60
+ bootSessionId,
61
+ operationId,
62
+ });
63
+ return result;
64
+ } catch (error) {
65
+ write({
66
+ ok: false,
67
+ code: "admin_runtime_phase",
68
+ phase,
69
+ status: "failed",
70
+ elapsedMs: Math.max(0, now() - startedAt),
71
+ bootSessionId,
72
+ operationId,
73
+ errorCode: phaseFailureCode(error),
74
+ });
75
+ throw error;
76
+ }
77
+ };
78
+
79
+ return { run };
80
+ }
@@ -28,6 +28,7 @@ import { createRequire } from "node:module";
28
28
  import { basename, dirname, join } from "node:path";
29
29
  import { createInterface } from "node:readline";
30
30
 
31
+ import { pruneAbandonedRuntimeScratch } from "../abandoned-runtime-scratch.mjs";
31
32
  import {
32
33
  buildAdminSettingsManifest,
33
34
  encodeAdminSettingsBundle,
@@ -45,6 +46,15 @@ import {
45
46
  isOperationBoundBackupClaim,
46
47
  } from "../admin-artifact/node_modules/@sellable/install/lib/sellable-agent/fly-customer-worker.mjs";
47
48
  import sodium from "../admin-artifact/node_modules/libsodium-wrappers/dist/modules-esm/libsodium-wrappers.mjs";
49
+ import {
50
+ nativeGatewayRunning,
51
+ nextAdminNativeGatewayRecovery,
52
+ } from "../admin-native-gateway-supervisor.mjs";
53
+ import {
54
+ bindDefaultProfileProbeCron,
55
+ buildDefaultProfileProjection,
56
+ reconcileDefaultProfileBundle,
57
+ } from "../default-profile-reconciler.mjs";
48
58
  import {
49
59
  SELLABLE_BROWSER_TOOLSET_DEFAULT_MARKER,
50
60
  sellableSlackToolsetsForMaterialization,
@@ -63,18 +73,13 @@ import {
63
73
  renewFlyAdminControlSession,
64
74
  signedWorkerAuthorization,
65
75
  } from "../fly-runtime-identity.mjs";
76
+ import { proveDefaultProfileNativeSkills } from "../fly-skills-bridge.mjs";
66
77
  import {
67
78
  executeExternalSoulBridgeOperation,
68
79
  observeProfileSoul,
69
80
  prepareSoulLockRoot,
70
81
  withProfileSoulLock,
71
82
  } from "../fly-soul-bridge.mjs";
72
- import { proveDefaultProfileNativeSkills } from "../fly-skills-bridge.mjs";
73
- import {
74
- bindDefaultProfileProbeCron,
75
- buildDefaultProfileProjection,
76
- reconcileDefaultProfileBundle,
77
- } from "../default-profile-reconciler.mjs";
78
83
  import {
79
84
  HERMES_SNAPSHOT_HOOKS,
80
85
  HERMES_SNAPSHOT_HOOK_ALLOWLIST_BYTES,
@@ -98,12 +103,12 @@ import {
98
103
  reconcileHermesModelAuth,
99
104
  } from "../model-auth-reconciler.mjs";
100
105
  import { createModelHealthReporter } from "../model-health-reporter.mjs";
106
+ import { createAdminRuntimePhaseTelemetry } from "./admin-runtime-phase-telemetry.mjs";
101
107
  import {
102
108
  completeAdminStateReceiptRecovery,
103
109
  prepareAdminStateGeneration,
104
110
  recoverAdminStateReceiptMismatch,
105
111
  } from "./admin-state-generation.mjs";
106
- import { pruneAbandonedRuntimeScratch } from "../abandoned-runtime-scratch.mjs";
107
112
 
108
113
  export const ADMIN_RUNTIME_SETTINGS_DIRECTORY =
109
114
  "/run/sellable-agent/admin-settings";
@@ -268,7 +273,10 @@ function adminMaterializerReceipt(profileId, profileRoot) {
268
273
  fileHashes: Object.fromEntries(
269
274
  paths
270
275
  .filter((relative) => existsSync(join(profileRoot, relative)))
271
- .map((relative) => [relative, sha256(readFileSync(join(profileRoot, relative)))])
276
+ .map((relative) => [
277
+ relative,
278
+ sha256(readFileSync(join(profileRoot, relative))),
279
+ ])
272
280
  ),
273
281
  };
274
282
  }
@@ -997,6 +1005,7 @@ async function materializeAdminProfile({
997
1005
  soul,
998
1006
  slack = null,
999
1007
  agentServiceEnv = {},
1008
+ runPhase = async (_phase, work) => work(),
1000
1009
  }) {
1001
1010
  const profileHome = profileContract.profileHome;
1002
1011
  const profile = basename(profileHome);
@@ -1023,91 +1032,99 @@ async function materializeAdminProfile({
1023
1032
  const layout = adminHomeLayout(profileHome);
1024
1033
  // stageAdminProfile canonicalizes these two paths before staging, so the
1025
1034
  // Admin home working copies must exist before the factory runs.
1026
- deliverAdminSettings(settingsRoot, layout);
1035
+ await runPhase("profile_settings_delivery", async () => {
1036
+ deliverAdminSettings(settingsRoot, layout);
1037
+ });
1027
1038
  const serverProofs = {};
1028
- const stageWithAuthority = async (stagedAuthority) => {
1029
- const stage = await stageAdminProfile({
1030
- mode: slack ? "direct_soul_native_slack" : "direct_soul",
1031
- lifecycleAuthorized: slack ? true : undefined,
1032
- ephemeralProductConfig: Boolean(slack),
1033
- ephemeralSlackSecrets: Boolean(slack),
1034
- profile,
1035
- profilesRoot: PROFILES_ROOT,
1036
- stageRoot,
1037
- finalProfileRoot: profileHome,
1038
- rootAuthPath,
1039
- canonicalAdminConfigPath: layout.configPath,
1040
- canonicalAdminStatePath: layout.statePath,
1041
- productConfigPath,
1042
- model: "gpt-5.6-sol",
1043
- reasoningEffort: "high",
1044
- soul: stagedAuthority,
1045
- ...(slack
1046
- ? {
1047
- slackSecretFile: slack.secretFile,
1048
- teamId: slack.teamId,
1049
- primaryChannel: slack.primaryChannel,
1050
- secondaryReadChannelIds: slack.secondaryReadChannelIds,
1051
- allowedSlackUserIds: slack.allowedSlackUserIds,
1052
- slackApp: slack.slackApp,
1039
+ const stageWithAuthority = async (stagedAuthority) =>
1040
+ runPhase("profile_stage", async () => {
1041
+ const stage = await stageAdminProfile({
1042
+ mode: slack ? "direct_soul_native_slack" : "direct_soul",
1043
+ lifecycleAuthorized: slack ? true : undefined,
1044
+ ephemeralProductConfig: Boolean(slack),
1045
+ ephemeralSlackSecrets: Boolean(slack),
1046
+ profile,
1047
+ profilesRoot: PROFILES_ROOT,
1048
+ stageRoot,
1049
+ finalProfileRoot: profileHome,
1050
+ rootAuthPath,
1051
+ canonicalAdminConfigPath: layout.configPath,
1052
+ canonicalAdminStatePath: layout.statePath,
1053
+ productConfigPath,
1054
+ model: "gpt-5.6-sol",
1055
+ reasoningEffort: "high",
1056
+ soul: stagedAuthority,
1057
+ ...(slack
1058
+ ? {
1059
+ slackSecretFile: slack.secretFile,
1060
+ teamId: slack.teamId,
1061
+ primaryChannel: slack.primaryChannel,
1062
+ secondaryReadChannelIds: slack.secondaryReadChannelIds,
1063
+ allowedSlackUserIds: slack.allowedSlackUserIds,
1064
+ slackApp: slack.slackApp,
1065
+ }
1066
+ : {}),
1067
+ skillsRoot: join(ADMIN_INSTALL_ASSET_ROOT, "mcp", "skills"),
1068
+ installRoot: layout.adminRoot,
1069
+ adminHome: layout.adminHome,
1070
+ installAdmin: async () =>
1071
+ profilePackageReceipt(
1072
+ profileContract,
1073
+ "sellable-admin",
1074
+ "mcp",
1075
+ layout
1076
+ ),
1077
+ installProduct: async () =>
1078
+ profilePackageReceipt(
1079
+ profileContract,
1080
+ "sellable",
1081
+ "mcp",
1082
+ layout,
1083
+ agentServiceEnv
1084
+ ),
1085
+ verifyMcp: async ({ kind, profileRoot, config }) => {
1086
+ const alias = kind === "admin" ? "sellable-admin" : "sellable";
1087
+ const server = config.mcp_servers[alias];
1088
+ const inventoryProof =
1089
+ alias === "sellable" ? productMcpInventoryProofEnvironment() : null;
1090
+ let proof;
1091
+ try {
1092
+ proof = await inspectMcp({
1093
+ alias,
1094
+ command: server.command,
1095
+ args: server.args,
1096
+ env: {
1097
+ HOME: profileRoot,
1098
+ HERMES_HOME: profileRoot,
1099
+ ...adminToolEnv(layout),
1100
+ ...agentServiceEnv,
1101
+ SELLABLE_CONFIG_PATH: productConfigPath,
1102
+ ...(server.env ?? {}),
1103
+ ...(inventoryProof?.env ?? {}),
1104
+ },
1105
+ packageProof: profileContract.packages[alias].mcp,
1106
+ });
1107
+ } finally {
1108
+ if (inventoryProof) {
1109
+ rmSync(inventoryProof.root, { recursive: true, force: true });
1110
+ }
1053
1111
  }
1054
- : {}),
1055
- skillsRoot: join(ADMIN_INSTALL_ASSET_ROOT, "mcp", "skills"),
1056
- installRoot: layout.adminRoot,
1057
- adminHome: layout.adminHome,
1058
- installAdmin: async () =>
1059
- profilePackageReceipt(profileContract, "sellable-admin", "mcp", layout),
1060
- installProduct: async () =>
1061
- profilePackageReceipt(
1062
- profileContract,
1063
- "sellable",
1064
- "mcp",
1065
- layout,
1066
- agentServiceEnv
1067
- ),
1068
- verifyMcp: async ({ kind, profileRoot, config }) => {
1069
- const alias = kind === "admin" ? "sellable-admin" : "sellable";
1070
- const server = config.mcp_servers[alias];
1071
- const inventoryProof =
1072
- alias === "sellable" ? productMcpInventoryProofEnvironment() : null;
1073
- let proof;
1074
- try {
1075
- proof = await inspectMcp({
1076
- alias,
1077
- command: server.command,
1078
- args: server.args,
1079
- env: {
1080
- HOME: profileRoot,
1081
- HERMES_HOME: profileRoot,
1082
- ...adminToolEnv(layout),
1083
- ...agentServiceEnv,
1084
- SELLABLE_CONFIG_PATH: productConfigPath,
1085
- ...(server.env ?? {}),
1086
- ...(inventoryProof?.env ?? {}),
1087
- },
1088
- packageProof: profileContract.packages[alias].mcp,
1089
- });
1090
- } finally {
1091
- if (inventoryProof) {
1092
- rmSync(inventoryProof.root, { recursive: true, force: true });
1112
+ if (
1113
+ JSON.stringify(proof.tools) !==
1114
+ JSON.stringify(profileContract.tools[alias])
1115
+ ) {
1116
+ throw new Error("admin_runtime_tool_inventory_mismatch");
1093
1117
  }
1094
- }
1095
- if (
1096
- JSON.stringify(proof.tools) !==
1097
- JSON.stringify(profileContract.tools[alias])
1098
- ) {
1099
- throw new Error("admin_runtime_tool_inventory_mismatch");
1100
- }
1101
- serverProofs[alias] = proof;
1102
- return { ok: true, ...proof };
1103
- },
1118
+ serverProofs[alias] = proof;
1119
+ return { ok: true, ...proof };
1120
+ },
1121
+ });
1122
+ if (slack) installWritableAdminSlackChannelPolicy(stageRoot, slack);
1123
+ installAdminModelConfig(stageRoot, modelConfig);
1124
+ installAdminHermesMemoryHook(stageRoot);
1125
+ stageAdminRuntimeTree(stageRoot);
1126
+ return stage;
1104
1127
  });
1105
- if (slack) installWritableAdminSlackChannelPolicy(stageRoot, slack);
1106
- installAdminModelConfig(stageRoot, modelConfig);
1107
- installAdminHermesMemoryHook(stageRoot);
1108
- stageAdminRuntimeTree(stageRoot);
1109
- return stage;
1110
- };
1111
1128
 
1112
1129
  let stagedAuthority = withProfileSoulLock(profile, readAuthoritativeSoul, {
1113
1130
  dataRoot: DATA_ROOT,
@@ -1116,79 +1133,83 @@ async function materializeAdminProfile({
1116
1133
  let promoted = null;
1117
1134
  for (let attempt = 0; attempt < 2 && !promoted; attempt += 1) {
1118
1135
  stage = await stageWithAuthority(stagedAuthority);
1119
- const committed = await withProfileSoulLock(
1120
- profile,
1121
- async () => {
1122
- const finalAuthority = readAuthoritativeSoul();
1123
- if (
1124
- finalAuthority.sha256 !== stagedAuthority.sha256 ||
1125
- !finalAuthority.bytes.equals(stagedAuthority.bytes)
1126
- ) {
1127
- rmSync(stageRoot, { recursive: true, force: true });
1128
- return { status: "RESTAGE", stagedAuthority: finalAuthority };
1129
- }
1130
- const liveSkillsIdentity = adminPreservedSkillsIdentity(profileHome);
1131
- if (liveSkillsIdentity !== null) {
1132
- replaceHermesState(
1133
- join(profileHome, "skills"),
1134
- join(stageRoot, "skills"),
1135
- {
1136
- preserveTargetTopLevelNames: [...ADMIN_SEALED_SKILL_DIRECTORIES],
1137
- }
1138
- );
1139
- }
1140
- mergeHermesState(profileHome, stageRoot, {
1141
- skipSourceTopLevelNames:
1142
- liveSkillsIdentity === null ? [] : ["skills"],
1143
- });
1144
- mergeAdminLiveSkillsConfig(profileHome, stageRoot);
1145
- installAdminBrowserToolsetDefault(profileHome, stageRoot);
1146
- if (
1147
- liveSkillsIdentity !== null &&
1148
- adminPreservedSkillsIdentity(stageRoot) !== liveSkillsIdentity
1149
- ) {
1150
- rmSync(stageRoot, { recursive: true, force: true });
1151
- return { status: "RESTAGE", stagedAuthority: finalAuthority };
1152
- }
1153
- const stagedSoulPath = join(stageRoot, "SOUL.md");
1154
- const stagedSoul = lstatSync(stagedSoulPath);
1155
- const stagedSoulBytes = readFileSync(stagedSoulPath);
1156
- if (
1157
- stagedSoul.isSymbolicLink() ||
1158
- !stagedSoul.isFile() ||
1159
- !stagedSoulBytes.equals(finalAuthority.bytes)
1160
- ) {
1161
- throw new Error("admin_runtime_staged_soul_rejected");
1162
- }
1163
- const promotion = await promoteAdminProfileAtomically({
1164
- profile,
1165
- currentRoot: profileHome,
1166
- stagedRoot: stageRoot,
1167
- backupRoot,
1168
- });
1169
- const finalSoulPath = join(profileHome, "SOUL.md");
1170
- const finalSoul = lstatSync(finalSoulPath);
1171
- const finalSoulBytes = readFileSync(finalSoulPath);
1172
- if (
1173
- finalSoul.isSymbolicLink() ||
1174
- !finalSoul.isFile() ||
1175
- finalSoul.uid !== (process.getuid?.() ?? finalSoul.uid) ||
1176
- finalSoul.gid !== (process.getgid?.() ?? finalSoul.gid) ||
1177
- !finalSoulBytes.equals(finalAuthority.bytes)
1178
- ) {
1179
- throw new Error("admin_runtime_live_soul_readback_rejected");
1180
- }
1181
- chmodSync(finalSoulPath, 0o600);
1182
- if ((lstatSync(finalSoulPath).mode & 0o777) !== 0o600) {
1183
- throw new Error("admin_runtime_live_soul_mode_rejected");
1184
- }
1185
- return {
1186
- status: "PROMOTED",
1187
- promotion,
1188
- observedSoulRawSha256: sha256(finalSoulBytes),
1189
- };
1190
- },
1191
- { dataRoot: DATA_ROOT }
1136
+ const committed = await runPhase("profile_live_merge_promote", async () =>
1137
+ withProfileSoulLock(
1138
+ profile,
1139
+ async () => {
1140
+ const finalAuthority = readAuthoritativeSoul();
1141
+ if (
1142
+ finalAuthority.sha256 !== stagedAuthority.sha256 ||
1143
+ !finalAuthority.bytes.equals(stagedAuthority.bytes)
1144
+ ) {
1145
+ rmSync(stageRoot, { recursive: true, force: true });
1146
+ return { status: "RESTAGE", stagedAuthority: finalAuthority };
1147
+ }
1148
+ const liveSkillsIdentity = adminPreservedSkillsIdentity(profileHome);
1149
+ if (liveSkillsIdentity !== null) {
1150
+ replaceHermesState(
1151
+ join(profileHome, "skills"),
1152
+ join(stageRoot, "skills"),
1153
+ {
1154
+ preserveTargetTopLevelNames: [
1155
+ ...ADMIN_SEALED_SKILL_DIRECTORIES,
1156
+ ],
1157
+ }
1158
+ );
1159
+ }
1160
+ mergeHermesState(profileHome, stageRoot, {
1161
+ skipSourceTopLevelNames:
1162
+ liveSkillsIdentity === null ? [] : ["skills"],
1163
+ });
1164
+ mergeAdminLiveSkillsConfig(profileHome, stageRoot);
1165
+ installAdminBrowserToolsetDefault(profileHome, stageRoot);
1166
+ if (
1167
+ liveSkillsIdentity !== null &&
1168
+ adminPreservedSkillsIdentity(stageRoot) !== liveSkillsIdentity
1169
+ ) {
1170
+ rmSync(stageRoot, { recursive: true, force: true });
1171
+ return { status: "RESTAGE", stagedAuthority: finalAuthority };
1172
+ }
1173
+ const stagedSoulPath = join(stageRoot, "SOUL.md");
1174
+ const stagedSoul = lstatSync(stagedSoulPath);
1175
+ const stagedSoulBytes = readFileSync(stagedSoulPath);
1176
+ if (
1177
+ stagedSoul.isSymbolicLink() ||
1178
+ !stagedSoul.isFile() ||
1179
+ !stagedSoulBytes.equals(finalAuthority.bytes)
1180
+ ) {
1181
+ throw new Error("admin_runtime_staged_soul_rejected");
1182
+ }
1183
+ const promotion = await promoteAdminProfileAtomically({
1184
+ profile,
1185
+ currentRoot: profileHome,
1186
+ stagedRoot: stageRoot,
1187
+ backupRoot,
1188
+ });
1189
+ const finalSoulPath = join(profileHome, "SOUL.md");
1190
+ const finalSoul = lstatSync(finalSoulPath);
1191
+ const finalSoulBytes = readFileSync(finalSoulPath);
1192
+ if (
1193
+ finalSoul.isSymbolicLink() ||
1194
+ !finalSoul.isFile() ||
1195
+ finalSoul.uid !== (process.getuid?.() ?? finalSoul.uid) ||
1196
+ finalSoul.gid !== (process.getgid?.() ?? finalSoul.gid) ||
1197
+ !finalSoulBytes.equals(finalAuthority.bytes)
1198
+ ) {
1199
+ throw new Error("admin_runtime_live_soul_readback_rejected");
1200
+ }
1201
+ chmodSync(finalSoulPath, 0o600);
1202
+ if ((lstatSync(finalSoulPath).mode & 0o777) !== 0o600) {
1203
+ throw new Error("admin_runtime_live_soul_mode_rejected");
1204
+ }
1205
+ return {
1206
+ status: "PROMOTED",
1207
+ promotion,
1208
+ observedSoulRawSha256: sha256(finalSoulBytes),
1209
+ };
1210
+ },
1211
+ { dataRoot: DATA_ROOT }
1212
+ )
1192
1213
  );
1193
1214
  if (committed.status === "PROMOTED") {
1194
1215
  promoted = committed;
@@ -1201,20 +1222,27 @@ async function materializeAdminProfile({
1201
1222
  }
1202
1223
  // The installer bakes absolute paths into every generated wrapper, so bin/
1203
1224
  // is generated once the promoted Admin home is at its final path.
1204
- const adminHome = installAdminHome({ settingsRoot, layout });
1205
- const memoryCron = await ensureHermesSnapshotCron({
1206
- profileId: profile,
1207
- profileRoot: profileHome,
1208
- hermesRoot: DATA_ROOT,
1209
- hermesCli: HERMES,
1210
- });
1211
- if (!memoryCron.ok) throw new Error(memoryCron.code);
1212
- const opportunitySequencesCron = await ensureHermesOpportunitySequencesCron({
1213
- profileId: profile,
1214
- profileRoot: profileHome,
1215
- hermesRoot: DATA_ROOT,
1216
- statePath: layout.statePath,
1217
- });
1225
+ const { adminHome, memoryCron, opportunitySequencesCron } = await runPhase(
1226
+ "profile_crons",
1227
+ async () => {
1228
+ const adminHome = installAdminHome({ settingsRoot, layout });
1229
+ const memoryCron = await ensureHermesSnapshotCron({
1230
+ profileId: profile,
1231
+ profileRoot: profileHome,
1232
+ hermesRoot: DATA_ROOT,
1233
+ hermesCli: HERMES,
1234
+ });
1235
+ if (!memoryCron.ok) throw new Error(memoryCron.code);
1236
+ const opportunitySequencesCron =
1237
+ await ensureHermesOpportunitySequencesCron({
1238
+ profileId: profile,
1239
+ profileRoot: profileHome,
1240
+ hermesRoot: DATA_ROOT,
1241
+ statePath: layout.statePath,
1242
+ });
1243
+ return { adminHome, memoryCron, opportunitySequencesCron };
1244
+ }
1245
+ );
1218
1246
  return {
1219
1247
  stage,
1220
1248
  promotion: promoted.promotion,
@@ -1453,8 +1481,7 @@ function operationSnapshotIdentity(claim, exchange) {
1453
1481
  function adminServingObservation(activeLaunch, activeGateway, exchange) {
1454
1482
  try {
1455
1483
  const authority = activeLaunch?.claim?.runtimeAuthority;
1456
- const running =
1457
- activeGateway?.child?.exitCode === null && !activeGateway.child.killed;
1484
+ const running = nativeGatewayRunning(activeGateway);
1458
1485
  const productMcp = activeLaunch?.profile?.servers?.find(
1459
1486
  (server) => server.alias === "sellable"
1460
1487
  );
@@ -2813,7 +2840,7 @@ async function startNativeGateway({ profile, claim }) {
2813
2840
  }
2814
2841
 
2815
2842
  async function stopNativeGateway(gateway) {
2816
- if (!gateway?.child || gateway.child.exitCode !== null) return;
2843
+ if (!nativeGatewayRunning(gateway)) return;
2817
2844
  gateway.child.kill("SIGTERM");
2818
2845
  const stopped = await Promise.race([
2819
2846
  gateway.closed.then(() => true),
@@ -2825,6 +2852,100 @@ async function stopNativeGateway(gateway) {
2825
2852
  }
2826
2853
  }
2827
2854
 
2855
+ async function recoverUnexpectedNativeGateway({
2856
+ activeLaunch,
2857
+ activeGateway,
2858
+ activeModelHealthReporter,
2859
+ identity,
2860
+ exchange,
2861
+ attempts,
2862
+ }) {
2863
+ const recovery = nextAdminNativeGatewayRecovery({ attempts });
2864
+ await activeModelHealthReporter?.stop();
2865
+ await stopNativeGateway(activeGateway);
2866
+ if (recovery.action === "EXHAUSTED") {
2867
+ process.stderr.write(
2868
+ `${JSON.stringify({
2869
+ ok: false,
2870
+ code: "admin_native_gateway_restart_exhausted",
2871
+ attempts: recovery.attempt,
2872
+ manualRetryAvailable: true,
2873
+ })}\n`
2874
+ );
2875
+ return {
2876
+ activeGateway: null,
2877
+ activeModelHealthReporter: null,
2878
+ attempts: recovery.attempt,
2879
+ exhausted: true,
2880
+ };
2881
+ }
2882
+ let candidateGateway = null;
2883
+ try {
2884
+ process.stderr.write(
2885
+ `${JSON.stringify({
2886
+ ok: false,
2887
+ code: "admin_native_gateway_exit_detected",
2888
+ attempt: recovery.attempt,
2889
+ })}\n`
2890
+ );
2891
+ candidateGateway = await startNativeGateway(activeLaunch);
2892
+ if (!nativeGatewayRunning(candidateGateway)) {
2893
+ throw new Error("admin_native_gateway_local_restart_failed");
2894
+ }
2895
+ const nextModelHealthReporter = adminModelHealthReporter({
2896
+ identity,
2897
+ exchange,
2898
+ installed: activeLaunch.installed,
2899
+ claim: activeLaunch.claim,
2900
+ });
2901
+ try {
2902
+ await nextModelHealthReporter.start("gateway-recovered");
2903
+ } catch (error) {
2904
+ process.stderr.write(
2905
+ `${JSON.stringify({
2906
+ ok: false,
2907
+ code:
2908
+ error instanceof Error
2909
+ ? error.message
2910
+ : "model_health_report_failed",
2911
+ })}\n`
2912
+ );
2913
+ }
2914
+ process.stderr.write(
2915
+ `${JSON.stringify({
2916
+ ok: true,
2917
+ code: "admin_native_gateway_local_restart",
2918
+ attempt: recovery.attempt,
2919
+ processIdentity: `gateway-${candidateGateway.child.pid}-${activeLaunch.claim.operation.fence}`,
2920
+ })}\n`
2921
+ );
2922
+ return {
2923
+ activeGateway: candidateGateway,
2924
+ activeModelHealthReporter: nextModelHealthReporter,
2925
+ attempts: recovery.attempt,
2926
+ exhausted: false,
2927
+ };
2928
+ } catch (error) {
2929
+ await stopNativeGateway(candidateGateway);
2930
+ process.stderr.write(
2931
+ `${JSON.stringify({
2932
+ ok: false,
2933
+ code:
2934
+ error instanceof Error
2935
+ ? error.message
2936
+ : "admin_native_gateway_local_restart_failed",
2937
+ attempt: recovery.attempt,
2938
+ })}\n`
2939
+ );
2940
+ return {
2941
+ activeGateway: null,
2942
+ activeModelHealthReporter: null,
2943
+ attempts: recovery.attempt,
2944
+ exhausted: false,
2945
+ };
2946
+ }
2947
+ }
2948
+
2828
2949
  export async function runAdminCanary() {
2829
2950
  const step = process.env.SELLABLE_AGENT_ADMIN_CANARY_STEP;
2830
2951
  const recoverySecretPath =
@@ -3379,6 +3500,8 @@ async function runAdminProduction() {
3379
3500
  process.once("SIGTERM", stop);
3380
3501
  let operationHeartbeat = null;
3381
3502
  let soulClaimTracker = null;
3503
+ let gatewayRestartAttempts = 0;
3504
+ let gatewayRestartExhaustionReported = false;
3382
3505
  let failures = 0;
3383
3506
  while (!controller.signal.aborted) {
3384
3507
  let claimedOperation = null;
@@ -3454,6 +3577,23 @@ async function runAdminProduction() {
3454
3577
  if (controller.signal.aborted) break;
3455
3578
  if (!claim) {
3456
3579
  if (activeLaunch) {
3580
+ if (
3581
+ !nativeGatewayRunning(activeGateway) &&
3582
+ !gatewayRestartExhaustionReported
3583
+ ) {
3584
+ const recovery = await recoverUnexpectedNativeGateway({
3585
+ activeLaunch,
3586
+ activeGateway,
3587
+ activeModelHealthReporter,
3588
+ identity,
3589
+ exchange,
3590
+ attempts: gatewayRestartAttempts,
3591
+ });
3592
+ activeGateway = recovery.activeGateway;
3593
+ activeModelHealthReporter = recovery.activeModelHealthReporter;
3594
+ gatewayRestartAttempts = recovery.attempts;
3595
+ gatewayRestartExhaustionReported = recovery.exhausted;
3596
+ }
3457
3597
  await reconcileHermesSnapshot({
3458
3598
  profileId: activeLaunch.profile.profile,
3459
3599
  profileRoot: activeLaunch.profile.profileHome,
@@ -3521,6 +3661,10 @@ async function runAdminProduction() {
3521
3661
  exchange,
3522
3662
  client,
3523
3663
  });
3664
+ const phaseTelemetry = createAdminRuntimePhaseTelemetry({
3665
+ bootSessionId: BOOT_SESSION_ID,
3666
+ operationId: claim.operation.id,
3667
+ });
3524
3668
  await activeModelHealthReporter?.stop();
3525
3669
  activeModelHealthReporter = null;
3526
3670
  await stopNativeGateway(activeGateway);
@@ -3594,31 +3738,42 @@ async function runAdminProduction() {
3594
3738
  // Authority is fully decidable from the claim and durable settings
3595
3739
  // metadata. Refuse it before consuming any lease secret so the failure
3596
3740
  // can be reported as exact and retryable instead of uncertain.
3597
- const secrets = await consumeRuntimeSecrets({
3598
- claim,
3599
- exchange,
3600
- client,
3601
- });
3602
- claimSecretsConsumed = true;
3603
3741
  const desiredLifecycle = desiredLifecycleForClaim(claim);
3604
- const installed = installRuntimeSecrets({
3605
- claim,
3606
- secrets,
3607
- desiredLifecycle,
3608
- });
3742
+ const { secrets, installed } = await phaseTelemetry.run(
3743
+ "runtime_secrets",
3744
+ async () => {
3745
+ const secrets = await consumeRuntimeSecrets({
3746
+ claim,
3747
+ exchange,
3748
+ client,
3749
+ });
3750
+ claimSecretsConsumed = true;
3751
+ const installed = installRuntimeSecrets({
3752
+ claim,
3753
+ secrets,
3754
+ desiredLifecycle,
3755
+ });
3756
+ return { secrets, installed };
3757
+ }
3758
+ );
3609
3759
  const proofOperation = operation ?? {
3610
3760
  runtimeGenerationId: claim.adminRuntimeGeneration.id,
3611
3761
  generationFence: claim.adminRuntimeGeneration.fence,
3612
3762
  profileContract,
3613
3763
  };
3614
- const memoryRestore = await restoreLatestHermesSnapshot({
3615
- profileId: basename(profileContract.profileHome),
3616
- profileRoot: profileContract.profileHome,
3617
- hermesRoot: DATA_ROOT,
3618
- hermesVersion: "0.20.0",
3619
- fetchLatest: () => client.getMemorySnapshot(),
3620
- });
3764
+ const memoryRestore = await phaseTelemetry.run(
3765
+ "snapshot_restore",
3766
+ async () =>
3767
+ restoreLatestHermesSnapshot({
3768
+ profileId: basename(profileContract.profileHome),
3769
+ profileRoot: profileContract.profileHome,
3770
+ hermesRoot: DATA_ROOT,
3771
+ hermesVersion: "0.20.0",
3772
+ fetchLatest: () => client.getMemorySnapshot(),
3773
+ })
3774
+ );
3621
3775
  if (!memoryRestore.ok) throw new Error(memoryRestore.code);
3776
+ await operationHeartbeat.reattest();
3622
3777
  const legacyLightfieldVenv = quarantineLegacyLightfieldVenv({
3623
3778
  profileId: basename(profileContract.profileHome),
3624
3779
  profileRoot: profileContract.profileHome,
@@ -3646,7 +3801,9 @@ async function runAdminProduction() {
3646
3801
  soul: exactSoul(claim),
3647
3802
  slack: nativeSlackProfileInput(claim, installed),
3648
3803
  agentServiceEnv: adminAgentServiceEnvironment(claim),
3804
+ runPhase: phaseTelemetry.run,
3649
3805
  });
3806
+ await operationHeartbeat.reattest();
3650
3807
  const materializerReceipt = adminMaterializerReceipt(
3651
3808
  profile.profile,
3652
3809
  profile.profileHome
@@ -3655,21 +3812,25 @@ async function runAdminProduction() {
3655
3812
  kind: "ADMIN",
3656
3813
  claim,
3657
3814
  });
3658
- const reconciledDefaultProfile = await reconcileDefaultProfileBundle({
3659
- activeProjection,
3660
- materializerReceipt,
3661
- profileRoot: profile.profileHome,
3662
- bundleRoot: DEFAULT_PROFILE_BUNDLE_ROOT,
3663
- dataRoot: DATA_ROOT,
3664
- reattestAuthority: async () => {
3665
- await operationHeartbeat.reattest();
3666
- return activeProjection;
3667
- },
3668
- cronAdapter: adminDefaultProfileCronAdapter(
3669
- profile.profile,
3670
- profile.profileHome
3671
- ),
3672
- });
3815
+ const reconciledDefaultProfile = await phaseTelemetry.run(
3816
+ "default_profile_bundle",
3817
+ async () =>
3818
+ reconcileDefaultProfileBundle({
3819
+ activeProjection,
3820
+ materializerReceipt,
3821
+ profileRoot: profile.profileHome,
3822
+ bundleRoot: DEFAULT_PROFILE_BUNDLE_ROOT,
3823
+ dataRoot: DATA_ROOT,
3824
+ reattestAuthority: async () => {
3825
+ await operationHeartbeat.reattest();
3826
+ return activeProjection;
3827
+ },
3828
+ cronAdapter: adminDefaultProfileCronAdapter(
3829
+ profile.profile,
3830
+ profile.profileHome
3831
+ ),
3832
+ })
3833
+ );
3673
3834
  if (!reconciledDefaultProfile.ok)
3674
3835
  throw new Error(reconciledDefaultProfile.code);
3675
3836
  const defaultProfile = reconciledDefaultProfile;
@@ -3692,11 +3853,16 @@ async function runAdminProduction() {
3692
3853
  profile.opportunitySequencesCron.status !== "REUSED";
3693
3854
  if (materializerChanged || defaultProfile.changed)
3694
3855
  markHermesSnapshotDirty(profile.profileHome);
3695
- const memorySnapshot = await flushHermesSnapshot({
3696
- client,
3697
- profileId: profile.profile,
3698
- profileRoot: profile.profileHome,
3699
- });
3856
+ const memorySnapshot = await phaseTelemetry.run(
3857
+ "memory_snapshot",
3858
+ async () =>
3859
+ flushHermesSnapshot({
3860
+ client,
3861
+ profileId: profile.profile,
3862
+ profileRoot: profile.profileHome,
3863
+ })
3864
+ );
3865
+ await operationHeartbeat.reattest();
3700
3866
  const settings = settingsProof(
3701
3867
  imported.receipt.generationRoot,
3702
3868
  imported.receipt.settingsDigest
@@ -3719,10 +3885,14 @@ async function runAdminProduction() {
3719
3885
  });
3720
3886
  }
3721
3887
  operationHeartbeat.assertHealthy();
3722
- const slackProof =
3723
- desiredLifecycle === "ON"
3724
- ? await proveAdminSlackServing(claim, secrets)
3725
- : false;
3888
+ const slackProof = await phaseTelemetry.run(
3889
+ "slack_serving_proof",
3890
+ async () =>
3891
+ desiredLifecycle === "ON"
3892
+ ? proveAdminSlackServing(claim, secrets)
3893
+ : false
3894
+ );
3895
+ await operationHeartbeat.reattest();
3726
3896
  const candidateLaunch =
3727
3897
  desiredLifecycle === "ON"
3728
3898
  ? { profile, claim, installed, slackProof }
@@ -3730,7 +3900,11 @@ async function runAdminProduction() {
3730
3900
  let candidateGateway = null;
3731
3901
  try {
3732
3902
  if (candidateLaunch) {
3733
- candidateGateway = await startNativeGateway(candidateLaunch);
3903
+ candidateGateway = await phaseTelemetry.run(
3904
+ "native_gateway",
3905
+ async () => startNativeGateway(candidateLaunch)
3906
+ );
3907
+ await operationHeartbeat.reattest();
3734
3908
  }
3735
3909
  const nativeDefaultProfileProof = candidateGateway
3736
3910
  ? proveDefaultProfileNativeSkills({
@@ -3752,26 +3926,31 @@ async function runAdminProduction() {
3752
3926
  activeProjection,
3753
3927
  nativeDefaultProfileProof,
3754
3928
  });
3755
- if (claim.runtimeAuthority && candidateLaunch && candidateGateway) {
3756
- const servingObservation = adminServingObservation(
3757
- candidateLaunch,
3758
- candidateGateway,
3759
- exchange
3760
- );
3761
- if (!servingObservation) {
3762
- throw new Error("admin_serving_observation_unavailable");
3763
- }
3764
- const serving = await client.claimTick({
3765
- leaseSeconds: 60,
3766
- servingObservation,
3767
- });
3768
- if (serving?.servingObservationStatus !== "accepted") {
3769
- throw new Error(
3770
- `admin_serving_observation_rejected:${serving?.servingObservationCode ?? serving?.servingObservationStatus ?? "unknown"}`
3929
+ await phaseTelemetry.run("serving_observation", async () => {
3930
+ if (claim.runtimeAuthority && candidateLaunch && candidateGateway) {
3931
+ const servingObservation = adminServingObservation(
3932
+ candidateLaunch,
3933
+ candidateGateway,
3934
+ exchange
3771
3935
  );
3936
+ if (!servingObservation) {
3937
+ throw new Error("admin_serving_observation_unavailable");
3938
+ }
3939
+ const serving = await client.claimTick({
3940
+ leaseSeconds: 60,
3941
+ servingObservation,
3942
+ });
3943
+ if (serving?.servingObservationStatus !== "accepted") {
3944
+ throw new Error(
3945
+ `admin_serving_observation_rejected:${serving?.servingObservationCode ?? serving?.servingObservationStatus ?? "unknown"}`
3946
+ );
3947
+ }
3772
3948
  }
3773
- }
3774
- const completed = await client.complete(receipt);
3949
+ });
3950
+ const completed = await phaseTelemetry.run(
3951
+ "operation_completion",
3952
+ async () => client.complete(receipt)
3953
+ );
3775
3954
  if (completed?.accepted !== true) {
3776
3955
  throw new Error("admin_runtime_completion_rejected");
3777
3956
  }
@@ -3818,6 +3997,8 @@ async function runAdminProduction() {
3818
3997
  }
3819
3998
  await operationHeartbeat.stop();
3820
3999
  operationHeartbeat = null;
4000
+ gatewayRestartAttempts = 0;
4001
+ gatewayRestartExhaustionReported = false;
3821
4002
  failures = 0;
3822
4003
  } catch (error) {
3823
4004
  try {
@@ -8,7 +8,7 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright
8
8
 
9
9
  ARG MCP_PACKAGE=@sellable/mcp@0.1.880
10
10
  ARG MCP_INTEGRITY=sha512-+Fo8aQlYduLaQMQff+ZA25LeUhWZ7I7UH5DECpND84LQ+rHSjKeKFj4Zs93bVHXwQCIy2OLbr75y4f4+CyT57w==
11
- ARG INSTALLER_PACKAGE=@sellable/install@0.1.651
11
+ ARG INSTALLER_PACKAGE=@sellable/install@0.1.653-wip.phase189.20260814.1
12
12
  ARG ADMIN_MCP_PACKAGE=@sellable/admin-mcp@0.1.186-wip.phase158.20260802034727
13
13
  ARG ADMIN_MCP_INTEGRITY=sha512-qYNew5wd2IfgVcp1UYOG9QvpF/8Mx19YJ5HQtMKzvSfUf3evePjtaKsoXLf2C/GUJ5QlLlHJo84VWOI3V01+9A==
14
14
  ARG TUS_JS_CLIENT_VERSION=4.3.1
@@ -105,7 +105,7 @@ const BOOT_SESSION_ID = randomUUID();
105
105
  const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.20.0";
106
106
  const INSTALLER_PACKAGE =
107
107
  process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ??
108
- "@sellable/install@0.1.651";
108
+ "@sellable/install@0.1.653-wip.phase189.20260814.1";
109
109
  const MCP_PACKAGE =
110
110
  process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.880";
111
111
  const DEFAULT_PROFILE_BUNDLE_ROOT =
@@ -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.651";
41
+ const INSTALLER_PACKAGE = "@sellable/install@0.1.653-wip.phase189.20260814.1";
42
42
  const MCP_PACKAGE = "@sellable/mcp@0.1.880";
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.651" ||
1550
+ "@sellable/install@0.1.653-wip.phase189.20260814.1" ||
1551
1551
  pinned.mcpPackage !== "@sellable/mcp@0.1.880" ||
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.651" ||
286
+ "@sellable/install@0.1.653-wip.phase189.20260814.1" ||
287
287
  desired.mcpPackage !== "@sellable/mcp@0.1.880" ||
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.651";
29
+ "@sellable/install@0.1.653-wip.phase189.20260814.1";
30
30
  export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.880";
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.651",
3
+ "version": "0.1.653-wip.phase189.20260814.1",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code, Codex, and Hermes",
6
6
  "bin": {