@sellable/install 0.1.484 → 0.1.486-wip.phase169.20260804.2

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.
@@ -39,8 +39,12 @@ import {
39
39
  } from "../admin-artifact/node_modules/@sellable/admin-install/lib/hermes-admin-profile-factory.mjs";
40
40
  import {
41
41
  evaluateModelAuthReadback,
42
- parseFlyCustomerModelCredential,
43
- } from "../admin-artifact/node_modules/@sellable/install/lib/sellable-agent/fly-customer-model.mjs";
42
+ modelAuthConfigOverlay,
43
+ parseModelAuthRuntimeCredential,
44
+ readModelAuthMaterialization,
45
+ reconcileHermesModelAuth,
46
+ } from "../model-auth-reconciler.mjs";
47
+ import { createModelHealthReporter } from "../model-health-reporter.mjs";
44
48
  import {
45
49
  buildAdminAgentServiceEnvironment,
46
50
  buildNativeHermesLaunch,
@@ -571,10 +575,40 @@ function profilePackageReceipt(
571
575
  };
572
576
  }
573
577
 
578
+ function installAdminModelConfig(profileRoot, modelConfig) {
579
+ const path = join(profileRoot, "config.yaml");
580
+ const stat = lstatSync(path);
581
+ if (stat.isSymbolicLink() || !stat.isFile()) {
582
+ throw new Error("admin_model_config_rejected");
583
+ }
584
+ const document = parseDocument(readFileSync(path, "utf8"));
585
+ if (document.errors.length > 0) {
586
+ throw new Error("admin_model_config_rejected");
587
+ }
588
+ document.set("model", modelConfig.model);
589
+ document.set(
590
+ "credential_pool_strategies",
591
+ modelConfig.credential_pool_strategies
592
+ );
593
+ document.set("fallback_providers", modelConfig.fallback_providers);
594
+ atomicPrivateFile(path, document.toString());
595
+ const readback = parseDocument(readFileSync(path, "utf8")).toJS();
596
+ if (
597
+ canonicalJson(readback.model) !== canonicalJson(modelConfig.model) ||
598
+ canonicalJson(readback.credential_pool_strategies) !==
599
+ canonicalJson(modelConfig.credential_pool_strategies) ||
600
+ canonicalJson(readback.fallback_providers) !==
601
+ canonicalJson(modelConfig.fallback_providers)
602
+ ) {
603
+ throw new Error("admin_model_config_rejected");
604
+ }
605
+ }
606
+
574
607
  async function materializeAdminProfile({
575
608
  profileContract,
576
609
  settingsRoot,
577
610
  rootAuthPath,
611
+ modelConfig,
578
612
  productConfigPath,
579
613
  soul,
580
614
  slack = null,
@@ -659,6 +693,7 @@ async function materializeAdminProfile({
659
693
  return { ok: true, ...proof };
660
694
  },
661
695
  });
696
+ installAdminModelConfig(stageRoot, modelConfig);
662
697
  mergeHermesState(profileHome, stageRoot);
663
698
  installAdminHermesMemoryHook(stageRoot);
664
699
  stageAdminRuntimeTree(stageRoot);
@@ -924,25 +959,43 @@ function installRuntimeSecrets({ claim, secrets, desiredLifecycle }) {
924
959
  privateDirectory(SECRET_ROOT);
925
960
  const modelAuthority = claim?.runtimeContract?.model;
926
961
  const modelEnvelope = claim?.secretEnvelopes?.modelCodex;
962
+ const modelTuple = claim?.adminRuntimeGeneration?.tuple;
927
963
  if (
928
964
  modelAuthority?.provider !== "codex" ||
929
- !Number.isSafeInteger(modelAuthority.credentialGeneration) ||
930
- modelAuthority.credentialGeneration < 1 ||
965
+ !/^[A-Za-z0-9_.:-]{1,256}$/.test(modelAuthority.activationId ?? "") ||
966
+ !Number.isSafeInteger(modelAuthority.bundleGeneration) ||
967
+ modelAuthority.bundleGeneration < 1 ||
968
+ !/^[a-f0-9]{64}$/.test(modelAuthority.policyHash ?? "") ||
931
969
  !/^[a-f0-9]{16}$/.test(modelAuthority.credentialFingerprint ?? "") ||
932
- modelAuthority.credentialGeneration !== modelEnvelope?.generation ||
970
+ modelAuthority.bundleGeneration !== modelEnvelope?.generation ||
933
971
  modelAuthority.credentialFingerprint !== modelEnvelope?.fingerprint ||
972
+ modelEnvelope?.activationId !== modelAuthority.activationId ||
973
+ modelEnvelope?.bundleGeneration !== modelAuthority.bundleGeneration ||
974
+ modelEnvelope?.policyHash !== modelAuthority.policyHash ||
975
+ modelTuple?.modelAuthActivationId !== modelAuthority.activationId ||
976
+ modelTuple?.modelAuthBundleGeneration !== modelAuthority.bundleGeneration ||
977
+ modelTuple?.modelAuthPolicyHash !== modelAuthority.policyHash ||
934
978
  !/^[A-Za-z0-9_-]{1,128}$/.test(claim?.operation?.profileId ?? "")
935
979
  ) {
936
980
  throw new Error("admin_model_auth_identity_rejected");
937
981
  }
938
- const model = parseFlyCustomerModelCredential(secrets.modelCodex);
982
+ const model = parseModelAuthRuntimeCredential(secrets.modelCodex);
983
+ if (
984
+ model.activationId !== modelAuthority.activationId ||
985
+ model.bundleGeneration !== modelAuthority.bundleGeneration ||
986
+ model.policyHash !== modelAuthority.policyHash
987
+ ) {
988
+ throw new Error("admin_model_auth_identity_rejected");
989
+ }
939
990
  const rootAuthPath = join(DATA_ROOT, "auth.json");
991
+ const markerPath = join(RUNTIME_ROOT, "model-auth-source.json");
940
992
  const localAuthPath = join(
941
993
  PROFILES_ROOT,
942
994
  claim.operation.profileId,
943
995
  "auth.json"
944
996
  );
945
997
  const identityPath = join(RUNTIME_ROOT, "model-auth-identity.json");
998
+ const modelBundlePath = join(SECRET_ROOT, "model-auth-runtime-v2.json");
946
999
  const validateAuthFile = (path) => {
947
1000
  const stat = lstatSync(path);
948
1001
  if (stat.isSymbolicLink() || !stat.isFile() || (stat.mode & 0o077) !== 0) {
@@ -953,57 +1006,32 @@ function installRuntimeSecrets({ claim, secrets, desiredLifecycle }) {
953
1006
  validateAuthFile(localAuthPath);
954
1007
  rmSync(localAuthPath, { force: true });
955
1008
  }
956
- let priorCredentialGeneration = null;
957
- if (existsSync(identityPath)) {
958
- validateAuthFile(identityPath);
959
- let priorIdentity;
960
- try {
961
- priorIdentity = JSON.parse(readFileSync(identityPath, "utf8"));
962
- } catch {
963
- throw new Error("admin_model_auth_identity_rejected");
964
- }
965
- if (
966
- priorIdentity?.schemaVersion !== "sellable-admin-model-auth/v1" ||
967
- priorIdentity.provider !== "codex" ||
968
- !Number.isSafeInteger(priorIdentity.credentialGeneration) ||
969
- priorIdentity.credentialGeneration < 1 ||
970
- !/^[a-f0-9]{16}$/.test(priorIdentity.credentialFingerprint ?? "") ||
971
- Object.keys(priorIdentity).sort().join("\0") !==
972
- [
973
- "credentialFingerprint",
974
- "credentialGeneration",
975
- "provider",
976
- "schemaVersion",
977
- ]
978
- .sort()
979
- .join("\0")
980
- ) {
981
- throw new Error("admin_model_auth_identity_rejected");
982
- }
983
- priorCredentialGeneration = priorIdentity.credentialGeneration;
984
- }
985
- const replaceRootAuth =
986
- priorCredentialGeneration === null ||
987
- priorCredentialGeneration !== modelAuthority.credentialGeneration ||
988
- !existsSync(rootAuthPath);
989
- if (existsSync(rootAuthPath)) {
990
- validateAuthFile(rootAuthPath);
991
- if (replaceRootAuth) rmSync(rootAuthPath, { force: true });
992
- }
993
- if (replaceRootAuth) {
994
- atomicPrivateFile(rootAuthPath, model.hermesRootAuthJson);
995
- } else {
996
- // Hermes may refresh the pooled OAuth record in place. Preserve that
997
- // native state until the server advances the credential generation.
998
- chmodSync(rootAuthPath, 0o600);
1009
+ atomicPrivateFile(modelBundlePath, model.authJson);
1010
+ const modelAuthState = reconcileHermesModelAuth({
1011
+ authStorePath: rootAuthPath,
1012
+ markerPath,
1013
+ credential: model,
1014
+ lifecycle: desiredLifecycle,
1015
+ });
1016
+ const profileAuthPath =
1017
+ desiredLifecycle === "ON"
1018
+ ? rootAuthPath
1019
+ : join(SECRET_ROOT, "model-auth-pending-start.json");
1020
+ if (desiredLifecycle === "OFF") {
1021
+ atomicPrivateFile(profileAuthPath, model.hermesRootAuthJson);
999
1022
  }
1000
1023
  atomicPrivateFile(
1001
1024
  identityPath,
1002
1025
  `${JSON.stringify({
1003
- schemaVersion: "sellable-admin-model-auth/v1",
1026
+ schemaVersion: "sellable-admin-model-auth/v2",
1004
1027
  provider: "codex",
1005
- credentialGeneration: modelAuthority.credentialGeneration,
1028
+ activationId: modelAuthority.activationId,
1029
+ bundleGeneration: modelAuthority.bundleGeneration,
1030
+ policyHash: modelAuthority.policyHash,
1006
1031
  credentialFingerprint: modelAuthority.credentialFingerprint,
1032
+ materializationStatus: modelAuthState.materialized
1033
+ ? "current"
1034
+ : "pending_start",
1007
1035
  })}\n`
1008
1036
  );
1009
1037
  atomicPrivateFile(join(SECRET_ROOT, "slack-app"), `${secrets.slackApp}\n`);
@@ -1026,13 +1054,70 @@ function installRuntimeSecrets({ claim, secrets, desiredLifecycle }) {
1026
1054
  );
1027
1055
  return {
1028
1056
  rootAuthPath,
1057
+ profileAuthPath,
1058
+ markerPath,
1059
+ model,
1060
+ modelAuthState,
1061
+ modelConfig: modelAuthConfigOverlay(model),
1029
1062
  productConfigPath,
1030
1063
  runtimeGates,
1031
1064
  slackSecretFile,
1032
1065
  };
1033
1066
  }
1034
1067
 
1035
- function proveAdminModelAuth(profile) {
1068
+ function pendingAdminModelAuthReadback(model) {
1069
+ return Object.freeze({
1070
+ schemaVersion: "sellable-agent-model-auth-readback/v2",
1071
+ activationId: model.activationId,
1072
+ bundleGeneration: model.bundleGeneration,
1073
+ policyHash: model.policyHash,
1074
+ provider: model.provider,
1075
+ model: model.model,
1076
+ fallbackDepth: model.value.openrouter.models.length,
1077
+ desiredStatus: "converged",
1078
+ materializedStatus: "pending_start",
1079
+ slots: Object.freeze(
1080
+ model.identity.slots.map((slot) =>
1081
+ Object.freeze({
1082
+ slotId: slot.slotId,
1083
+ grantFingerprint: slot.grantFingerprint,
1084
+ expiresAt: new Date(slot.expiresAt).toISOString(),
1085
+ state: "ok",
1086
+ lastErrorReason: null,
1087
+ lastErrorResetAt: null,
1088
+ })
1089
+ )
1090
+ ),
1091
+ openrouter: Object.freeze({
1092
+ keyFingerprint: model.identity.openrouterKeyFingerprint,
1093
+ state: "ok",
1094
+ }),
1095
+ });
1096
+ }
1097
+
1098
+ function proveAdminModelAuth(profile, installed, desiredLifecycle) {
1099
+ if (desiredLifecycle === "OFF") {
1100
+ return Object.freeze({
1101
+ ...pendingAdminModelAuthReadback(installed.model),
1102
+ source: "desired_converged_materialization_pending_start",
1103
+ sourceClass: "access_only_pool_pending_start",
1104
+ runtimeRefreshOwned: false,
1105
+ });
1106
+ }
1107
+ const materialization = readModelAuthMaterialization({
1108
+ authStorePath: installed.rootAuthPath,
1109
+ markerPath: installed.markerPath,
1110
+ });
1111
+ const verdict = evaluateModelAuthReadback(materialization, {
1112
+ activationId: installed.model.activationId,
1113
+ bundleGeneration: installed.model.bundleGeneration,
1114
+ policyHash: installed.model.policyHash,
1115
+ });
1116
+ if (!verdict.accepted) {
1117
+ throw new Error(
1118
+ `admin_model_auth_readback_rejected:${verdict.rejection ?? "unknown"}`
1119
+ );
1120
+ }
1036
1121
  const environment = {
1037
1122
  ...process.env,
1038
1123
  HOME: DATA_ROOT,
@@ -1049,12 +1134,10 @@ function proveAdminModelAuth(profile) {
1049
1134
  "-c",
1050
1135
  [
1051
1136
  "import json",
1052
- "from hermes_constants import get_default_hermes_root, get_hermes_home",
1053
- "from hermes_cli.auth import resolve_codex_runtime_credentials",
1054
- "value = resolve_codex_runtime_credentials(refresh_if_expiring=False)",
1055
- 'root_auth = get_default_hermes_root() / "auth.json"',
1056
- 'local_auth = get_hermes_home() / "auth.json"',
1057
- 'print(json.dumps({"provider": value.get("provider"), "source": value.get("source"), "source_class": "root_fallback" if root_auth.is_file() and not local_auth.exists() else "unexpected", "auth_mode": value.get("auth_mode"), "credential_present": bool(value.get("api_key")), "local_auth_entry_count": 0 if not local_auth.exists() else 1}))',
1137
+ "from agent.credential_pool import load_pool",
1138
+ 'pool = load_pool("openai-codex")',
1139
+ "entry = pool.select()",
1140
+ 'print(json.dumps({"provider": "openai-codex", "source": getattr(entry, "source", None), "source_class": "access_only_pool", "credential_id": getattr(entry, "id", None), "credential_present": bool(getattr(entry, "access_token", None)), "runtime_refresh_owned": bool(getattr(entry, "refresh_token", None))}))',
1058
1141
  ].join(";"),
1059
1142
  ],
1060
1143
  {
@@ -1071,23 +1154,26 @@ function proveAdminModelAuth(profile) {
1071
1154
  } catch {
1072
1155
  readback = null;
1073
1156
  }
1074
- const verdict = evaluateModelAuthReadback(readback);
1075
- if (probe.status !== 0 || probe.error || !verdict.accepted) {
1076
- throw new Error(
1077
- `admin_model_auth_readback_rejected:${
1078
- probe.status !== 0 || probe.error
1079
- ? "probe_failed"
1080
- : (verdict.rejection ?? "unknown")
1081
- }`
1082
- );
1157
+ if (
1158
+ probe.status !== 0 ||
1159
+ probe.error ||
1160
+ readback?.provider !== "openai-codex" ||
1161
+ readback?.source !== "manual:device_code" ||
1162
+ readback?.source_class !== "access_only_pool" ||
1163
+ !["dittto-primary", "kaigo-secondary"].includes(
1164
+ readback?.credential_id
1165
+ ) ||
1166
+ readback?.credential_present !== true ||
1167
+ readback?.runtime_refresh_owned !== false
1168
+ ) {
1169
+ throw new Error("access_only_snapshot_infeasible");
1083
1170
  }
1084
1171
  return Object.freeze({
1085
- provider: readback.provider,
1172
+ ...materialization,
1086
1173
  source: readback.source,
1087
1174
  sourceClass: readback.source_class,
1088
- authMode: readback.auth_mode,
1089
- credentialPresent: readback.credential_present,
1090
- localAuthEntryCount: readback.local_auth_entry_count,
1175
+ selectedCredentialId: readback.credential_id,
1176
+ runtimeRefreshOwned: readback.runtime_refresh_owned,
1091
1177
  });
1092
1178
  }
1093
1179
 
@@ -1738,6 +1824,12 @@ function completionReceipt({
1738
1824
  ciphertextDigest: recovery.ciphertextDigest,
1739
1825
  locatorDigest: sha256(recovery.locator),
1740
1826
  },
1827
+ modelAuth: {
1828
+ activationId: modelAuth.activationId,
1829
+ bundleGeneration: modelAuth.bundleGeneration,
1830
+ policyHash: modelAuth.policyHash,
1831
+ materializedStatus: modelAuth.materializedStatus,
1832
+ },
1741
1833
  servers: profile.servers,
1742
1834
  traffic: {
1743
1835
  slackAccepted: false,
@@ -1772,7 +1864,13 @@ function completionReceipt({
1772
1864
  { code: "DUAL_MCP_PROVEN", status: "TRUE" },
1773
1865
  { code: "SETTINGS_GENERATION_PROVEN", status: "TRUE" },
1774
1866
  { code: "RECOVERY_GENERATION_PROVEN", status: "TRUE" },
1775
- { code: "MODEL_AUTH_PROVEN", status: "TRUE" },
1867
+ {
1868
+ code:
1869
+ modelAuth.materializedStatus === "current"
1870
+ ? "MODEL_AUTH_PROVEN"
1871
+ : "MODEL_AUTH_DESIRED_PENDING_START",
1872
+ status: "TRUE",
1873
+ },
1776
1874
  { code: "RUNTIME_GATES_PROVEN", status: "TRUE" },
1777
1875
  { code: "SLACK_TRAFFIC_DISABLED", status: "TRUE" },
1778
1876
  { code: "NO_BOOT_INSTALL", status: "TRUE" },
@@ -1818,6 +1916,47 @@ async function flushHermesSnapshot({ client, profileId, profileRoot }) {
1818
1916
  }
1819
1917
  }
1820
1918
 
1919
+ function adminModelHealthReporter({ identity, exchange, installed, claim }) {
1920
+ return createModelHealthReporter({
1921
+ origin: ORIGIN,
1922
+ identity: {
1923
+ kind: "ADMIN",
1924
+ runtimeGeneration: RUNTIME_GENERATION,
1925
+ workloadKeyHash: exchange.publicKeyHash,
1926
+ machineId: exchange.workerId,
1927
+ adminRuntimeGenerationId: claim.adminRuntimeGeneration.id,
1928
+ adminRuntimeGenerationFence: claim.adminRuntimeGeneration.fence,
1929
+ },
1930
+ observe: () =>
1931
+ readModelAuthMaterialization({
1932
+ authStorePath: installed.rootAuthPath,
1933
+ markerPath: installed.markerPath,
1934
+ }),
1935
+ authorize: (body) =>
1936
+ signedWorkerAuthorization({
1937
+ identity,
1938
+ exchange,
1939
+ verb: "model-health",
1940
+ body,
1941
+ now: new Date(),
1942
+ unbound: true,
1943
+ }),
1944
+ onError: (error) => {
1945
+ process.stderr.write(
1946
+ `${JSON.stringify({
1947
+ ok: false,
1948
+ code:
1949
+ error instanceof Error
1950
+ ? error.message
1951
+ : "model_health_report_failed",
1952
+ })}\n`
1953
+ );
1954
+ },
1955
+ allowHttpLoopback: process.env.SELLABLE_AGENT_ALLOW_TEST_OIDC === "1",
1956
+ sequencePath: join(RUNTIME_ROOT, "model-health-sequence.json"),
1957
+ });
1958
+ }
1959
+
1821
1960
  function desiredLifecycleForClaim(claim) {
1822
1961
  switch (claim.operation.action) {
1823
1962
  case "INSTANTIATE":
@@ -1996,23 +2135,64 @@ export async function runAdminCanary() {
1996
2135
  atomicPrivateFile(join(sourceRoot, name), value);
1997
2136
  }
1998
2137
  const rootAuthPath = join(DATA_ROOT, "auth.json");
2138
+ const modelMarkerPath = join(canaryRoot, "model-auth-source.json");
1999
2139
  const productConfigPath = join(SECRET_ROOT, "canary-product-config.json");
2000
2140
  rmSync(rootAuthPath, { force: true });
2141
+ rmSync(modelMarkerPath, { force: true });
2001
2142
  rmSync(productConfigPath, { force: true });
2002
- const canaryModel = parseFlyCustomerModelCredential(
2143
+ const canaryExpiresAt = new Date(Date.now() + 60 * 60 * 1000);
2144
+ const canaryJwt = (subject) =>
2145
+ [
2146
+ Buffer.from(JSON.stringify({ alg: "none", typ: "JWT" })).toString(
2147
+ "base64url"
2148
+ ),
2149
+ Buffer.from(
2150
+ JSON.stringify({ sub: subject, exp: Math.floor(canaryExpiresAt / 1000) })
2151
+ ).toString("base64url"),
2152
+ "phase158-canary-signature-placeholder",
2153
+ ].join(".");
2154
+ const canaryModel = parseModelAuthRuntimeCredential(
2003
2155
  JSON.stringify({
2004
- OPENAI_API_KEY: null,
2005
- auth_mode: "chatgpt",
2006
- last_refresh: "2026-07-29T00:00:00.000Z",
2007
- tokens: {
2008
- access_token: "phase158-canary-oauth-access-token-placeholder",
2009
- account_id: "phase158-canary-account",
2010
- id_token: "phase158-canary-oauth-id-token-placeholder",
2011
- refresh_token: "phase158-canary-oauth-refresh-token-placeholder",
2156
+ schemaVersion: "sellable-agent-model-auth-runtime/v2",
2157
+ activationId: "phase158-canary-model-auth",
2158
+ bundleGeneration: 1,
2159
+ policyHash: "a".repeat(64),
2160
+ credentialPoolStrategy: "fill_first",
2161
+ codex: [
2162
+ {
2163
+ slotId: "dittto-primary",
2164
+ priority: 1,
2165
+ accessToken: canaryJwt("phase158-canary-dittto"),
2166
+ accountId: "phase158-canary-dittto",
2167
+ expiresAt: canaryExpiresAt.toISOString(),
2168
+ grantFingerprint: "a".repeat(16),
2169
+ },
2170
+ {
2171
+ slotId: "kaigo-secondary",
2172
+ priority: 2,
2173
+ accessToken: canaryJwt("phase158-canary-kaigo"),
2174
+ accountId: "phase158-canary-kaigo",
2175
+ expiresAt: canaryExpiresAt.toISOString(),
2176
+ grantFingerprint: "b".repeat(16),
2177
+ },
2178
+ ],
2179
+ openrouter: {
2180
+ apiKey: "phase158-canary-openrouter-placeholder",
2181
+ keyFingerprint: "c".repeat(16),
2182
+ models: [
2183
+ "moonshotai/kimi-k3",
2184
+ "~deepseek/deepseek-v4-flash-latest",
2185
+ "z-ai/glm-5.2",
2186
+ ],
2012
2187
  },
2013
2188
  })
2014
2189
  );
2015
- atomicPrivateFile(rootAuthPath, canaryModel.hermesRootAuthJson);
2190
+ const canaryModelAuthState = reconcileHermesModelAuth({
2191
+ authStorePath: rootAuthPath,
2192
+ markerPath: modelMarkerPath,
2193
+ credential: canaryModel,
2194
+ lifecycle: "ON",
2195
+ });
2016
2196
  atomicPrivateFile(
2017
2197
  productConfigPath,
2018
2198
  `${JSON.stringify({
@@ -2199,6 +2379,7 @@ export async function runAdminCanary() {
2199
2379
  profileContract,
2200
2380
  settingsRoot,
2201
2381
  rootAuthPath,
2382
+ modelConfig: modelAuthConfigOverlay(canaryModel),
2202
2383
  productConfigPath,
2203
2384
  soul: {
2204
2385
  bytes: soulBytes,
@@ -2207,7 +2388,16 @@ export async function runAdminCanary() {
2207
2388
  },
2208
2389
  agentServiceEnv: agentServiceEnvironment,
2209
2390
  });
2210
- const modelAuth = proveAdminModelAuth(profile);
2391
+ const modelAuth = proveAdminModelAuth(
2392
+ profile,
2393
+ {
2394
+ rootAuthPath,
2395
+ markerPath: modelMarkerPath,
2396
+ model: canaryModel,
2397
+ modelAuthState: canaryModelAuthState,
2398
+ },
2399
+ "ON"
2400
+ );
2211
2401
  const attachmentHandoff = proveAdminAttachmentHandoff(profile);
2212
2402
  const observedSettings = settingsProof(
2213
2403
  settingsRoot,
@@ -2357,6 +2547,7 @@ async function runAdminProduction() {
2357
2547
  const controller = new AbortController();
2358
2548
  let activeGateway = null;
2359
2549
  let activeLaunch = null;
2550
+ let activeModelHealthReporter = null;
2360
2551
  let shutdownMemorySnapshot = null;
2361
2552
  const beginShutdownSnapshot = () => {
2362
2553
  if (!activeLaunch || shutdownMemorySnapshot) return;
@@ -2409,6 +2600,27 @@ async function runAdminProduction() {
2409
2600
  });
2410
2601
  exchange = renewed.exchange;
2411
2602
  client = renewed.client;
2603
+ await activeModelHealthReporter?.stop();
2604
+ activeModelHealthReporter = null;
2605
+ if (activeLaunch) {
2606
+ activeModelHealthReporter = adminModelHealthReporter({
2607
+ identity,
2608
+ exchange,
2609
+ installed: activeLaunch.installed,
2610
+ claim: activeLaunch.claim,
2611
+ });
2612
+ activeModelHealthReporter.start("periodic").catch((reportError) => {
2613
+ process.stderr.write(
2614
+ `${JSON.stringify({
2615
+ ok: false,
2616
+ code:
2617
+ reportError instanceof Error
2618
+ ? reportError.message
2619
+ : "model_health_report_failed",
2620
+ })}\n`
2621
+ );
2622
+ });
2623
+ }
2412
2624
  failures = 0;
2413
2625
  process.stderr.write(
2414
2626
  `${JSON.stringify({
@@ -2466,6 +2678,8 @@ async function runAdminProduction() {
2466
2678
  exchange,
2467
2679
  client,
2468
2680
  });
2681
+ await activeModelHealthReporter?.stop();
2682
+ activeModelHealthReporter = null;
2469
2683
  await stopNativeGateway(activeGateway);
2470
2684
  activeGateway = null;
2471
2685
  activeLaunch = null;
@@ -2561,13 +2775,18 @@ async function runAdminProduction() {
2561
2775
  const profile = await materializeAdminProfile({
2562
2776
  profileContract,
2563
2777
  settingsRoot: imported.receipt.generationRoot,
2564
- rootAuthPath: installed.rootAuthPath,
2778
+ rootAuthPath: installed.profileAuthPath,
2779
+ modelConfig: installed.modelConfig,
2565
2780
  productConfigPath: installed.productConfigPath,
2566
2781
  soul: exactSoul(claim),
2567
2782
  slack: nativeSlackProfileInput(claim, installed),
2568
2783
  agentServiceEnv: adminAgentServiceEnvironment(claim),
2569
2784
  });
2570
- const modelAuth = proveAdminModelAuth(profile);
2785
+ const modelAuth = proveAdminModelAuth(
2786
+ profile,
2787
+ installed,
2788
+ desiredLifecycle
2789
+ );
2571
2790
  const memorySnapshot = await flushHermesSnapshot({
2572
2791
  client,
2573
2792
  profileId: profile.profile,
@@ -2608,7 +2827,7 @@ async function runAdminProduction() {
2608
2827
  });
2609
2828
  operationHeartbeat.assertHealthy();
2610
2829
  const candidateLaunch =
2611
- desiredLifecycle === "ON" ? { profile, claim } : null;
2830
+ desiredLifecycle === "ON" ? { profile, claim, installed } : null;
2612
2831
  let candidateGateway = null;
2613
2832
  try {
2614
2833
  if (candidateLaunch) {
@@ -2631,6 +2850,27 @@ async function runAdminProduction() {
2631
2850
  if (desiredLifecycle === "ON" && !activeLaunch) {
2632
2851
  throw new Error("admin_native_gateway_completion_rejected");
2633
2852
  }
2853
+ if (activeLaunch) {
2854
+ activeModelHealthReporter = adminModelHealthReporter({
2855
+ identity,
2856
+ exchange,
2857
+ installed: activeLaunch.installed,
2858
+ claim: activeLaunch.claim,
2859
+ });
2860
+ try {
2861
+ await activeModelHealthReporter.start("materialized");
2862
+ } catch (error) {
2863
+ process.stderr.write(
2864
+ `${JSON.stringify({
2865
+ ok: false,
2866
+ code:
2867
+ error instanceof Error
2868
+ ? error.message
2869
+ : "model_health_report_failed",
2870
+ })}\n`
2871
+ );
2872
+ }
2873
+ }
2634
2874
  await operationHeartbeat.stop();
2635
2875
  operationHeartbeat = null;
2636
2876
  failures = 0;
@@ -2663,6 +2903,7 @@ async function runAdminProduction() {
2663
2903
  })}\n`
2664
2904
  );
2665
2905
  }
2906
+ await activeModelHealthReporter?.stop();
2666
2907
  await stopNativeGateway(activeGateway);
2667
2908
  clearRuntimeSecrets();
2668
2909
  }
@@ -5,7 +5,7 @@ USER root
5
5
 
6
6
  ARG MCP_PACKAGE=@sellable/mcp@0.1.805
7
7
  ARG MCP_INTEGRITY=sha512-ULMGwWalb14SaMgxLeeiOWjavXpnONhdD8kAkvp7rJFdiL7s30VwXX+1j/aNwijiWXqqNQ1wcsXUPqk4eWpslg==
8
- ARG INSTALLER_PACKAGE=@sellable/install@0.1.484
8
+ ARG INSTALLER_PACKAGE=@sellable/install@0.1.486-wip.phase169.20260804.2
9
9
  ARG TUS_JS_CLIENT_VERSION=4.3.1
10
10
  ARG HERMES_VERSION=0.20.0
11
11
  ARG MCP_TRUST_ROOT_B64
@@ -56,7 +56,7 @@ RUN test -n "${MCP_TRUST_ROOT_B64}" \
56
56
  /etc/cont-init.d/02-reconcile-profiles \
57
57
  && npm cache clean --force
58
58
 
59
- COPY mcp-context-proxy.mjs fly-customer-model.mjs fly-customer-worker.mjs profile-materializer.mjs hermes-bridge.mjs hermes-memory-snapshot.mjs fly-runtime-identity.mjs \
59
+ COPY mcp-context-proxy.mjs fly-customer-model.mjs fly-customer-worker.mjs profile-materializer.mjs model-auth-reconciler.mjs model-health-reporter.mjs hermes-bridge.mjs hermes-memory-snapshot.mjs fly-runtime-identity.mjs \
60
60
  /usr/local/lib/sellable-agent/
61
61
  COPY hermes-memory-dirty.mjs /opt/sellable/bin/hermes-memory-dirty
62
62
  COPY hermes-memory-reconcile.sh /opt/sellable/share/hermes-memory-reconcile.sh