@sellable/install 0.1.466 → 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.
- package/container/Dockerfile +4 -4
- package/container/README.md +2 -2
- package/lib/sellable-agent/fly-admin-image/admin-runtime.mjs +254 -30
- package/lib/sellable-agent/fly-customer-image/Dockerfile +1 -1
- package/lib/sellable-agent/fly-customer-image/customer-runtime.mjs +1 -1
- package/lib/sellable-agent/fly-customer-worker.mjs +12 -0
- package/lib/sellable-agent/host-bootstrap.mjs +1 -1
- package/lib/sellable-agent/host-worker.mjs +1 -1
- package/lib/sellable-agent/profile-materializer.mjs +1 -1
- package/lib/sellable-agent/provisioning-adapter.mjs +4 -4
- package/lib/sellable-agent/runtime-helper.mjs +1 -1
- package/package.json +1 -1
package/container/Dockerfile
CHANGED
|
@@ -3,12 +3,12 @@ FROM ${HERMES_IMAGE}
|
|
|
3
3
|
|
|
4
4
|
USER root
|
|
5
5
|
|
|
6
|
-
ARG INSTALLER_PACKAGE=@sellable/install@0.1.
|
|
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.
|
|
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.466" \
|
|
|
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.
|
|
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.
|
|
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
|
package/container/README.md
CHANGED
|
@@ -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.
|
|
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.
|
|
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`.
|
|
@@ -37,11 +37,15 @@ import {
|
|
|
37
37
|
promoteAdminProfileAtomically,
|
|
38
38
|
stageAdminProfile,
|
|
39
39
|
} from "../admin-artifact/node_modules/@sellable/admin-install/lib/hermes-admin-profile-factory.mjs";
|
|
40
|
-
import { parseFlyCustomerModelCredential } from "../admin-artifact/node_modules/@sellable/install/lib/sellable-agent/fly-customer-model.mjs";
|
|
41
40
|
import {
|
|
42
|
-
|
|
41
|
+
evaluateModelAuthReadback,
|
|
42
|
+
parseFlyCustomerModelCredential,
|
|
43
|
+
} from "../admin-artifact/node_modules/@sellable/install/lib/sellable-agent/fly-customer-model.mjs";
|
|
44
|
+
import {
|
|
45
|
+
buildAdminAgentServiceEnvironment,
|
|
43
46
|
buildNativeHermesLaunch,
|
|
44
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";
|
|
45
49
|
import sodium from "../admin-artifact/node_modules/libsodium-wrappers/dist/modules-esm/libsodium-wrappers.mjs";
|
|
46
50
|
import {
|
|
47
51
|
createFlyCustomerControlClient,
|
|
@@ -83,6 +87,7 @@ const RUNTIME_ROOT = join(DATA_ROOT, ".sellable-agent", "admin-runtime");
|
|
|
83
87
|
const DELIVERY_ROOT = join(RUNTIME_ROOT, "delivery");
|
|
84
88
|
const SECRET_ROOT = "/run/sellable-agent/admin-runtime";
|
|
85
89
|
const HERMES = "/opt/hermes/.venv/bin/hermes";
|
|
90
|
+
const HERMES_PYTHON = "/opt/hermes/.venv/bin/python";
|
|
86
91
|
const NODE = "/usr/local/bin/node";
|
|
87
92
|
const ADMIN_INSTALL_PACKAGE_ROOT = join(
|
|
88
93
|
ARTIFACT_ROOT,
|
|
@@ -891,10 +896,114 @@ async function consumeRuntimeSecrets({ claim, exchange, client }) {
|
|
|
891
896
|
return { mcpService, slackApp, slackBot, modelCodex };
|
|
892
897
|
}
|
|
893
898
|
|
|
894
|
-
function
|
|
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 }) {
|
|
895
922
|
privateDirectory(SECRET_ROOT);
|
|
923
|
+
const modelAuthority = claim?.runtimeContract?.model;
|
|
924
|
+
const modelEnvelope = claim?.secretEnvelopes?.modelCodex;
|
|
925
|
+
if (
|
|
926
|
+
modelAuthority?.provider !== "codex" ||
|
|
927
|
+
!Number.isSafeInteger(modelAuthority.credentialGeneration) ||
|
|
928
|
+
modelAuthority.credentialGeneration < 1 ||
|
|
929
|
+
!/^[a-f0-9]{16}$/.test(modelAuthority.credentialFingerprint ?? "") ||
|
|
930
|
+
modelAuthority.credentialGeneration !== modelEnvelope?.generation ||
|
|
931
|
+
modelAuthority.credentialFingerprint !== modelEnvelope?.fingerprint ||
|
|
932
|
+
!/^[A-Za-z0-9_-]{1,128}$/.test(claim?.operation?.profileId ?? "")
|
|
933
|
+
) {
|
|
934
|
+
throw new Error("admin_model_auth_identity_rejected");
|
|
935
|
+
}
|
|
896
936
|
const model = parseFlyCustomerModelCredential(secrets.modelCodex);
|
|
897
|
-
|
|
937
|
+
const rootAuthPath = join(DATA_ROOT, "auth.json");
|
|
938
|
+
const localAuthPath = join(
|
|
939
|
+
PROFILES_ROOT,
|
|
940
|
+
claim.operation.profileId,
|
|
941
|
+
"auth.json"
|
|
942
|
+
);
|
|
943
|
+
const identityPath = join(RUNTIME_ROOT, "model-auth-identity.json");
|
|
944
|
+
const validateAuthFile = (path) => {
|
|
945
|
+
const stat = lstatSync(path);
|
|
946
|
+
if (stat.isSymbolicLink() || !stat.isFile() || (stat.mode & 0o077) !== 0) {
|
|
947
|
+
throw new Error("admin_model_auth_store_rejected");
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
if (existsSync(localAuthPath)) {
|
|
951
|
+
validateAuthFile(localAuthPath);
|
|
952
|
+
rmSync(localAuthPath, { force: true });
|
|
953
|
+
}
|
|
954
|
+
let priorCredentialGeneration = null;
|
|
955
|
+
if (existsSync(identityPath)) {
|
|
956
|
+
validateAuthFile(identityPath);
|
|
957
|
+
let priorIdentity;
|
|
958
|
+
try {
|
|
959
|
+
priorIdentity = JSON.parse(readFileSync(identityPath, "utf8"));
|
|
960
|
+
} catch {
|
|
961
|
+
throw new Error("admin_model_auth_identity_rejected");
|
|
962
|
+
}
|
|
963
|
+
if (
|
|
964
|
+
priorIdentity?.schemaVersion !== "sellable-admin-model-auth/v1" ||
|
|
965
|
+
priorIdentity.provider !== "codex" ||
|
|
966
|
+
!Number.isSafeInteger(priorIdentity.credentialGeneration) ||
|
|
967
|
+
priorIdentity.credentialGeneration < 1 ||
|
|
968
|
+
!/^[a-f0-9]{16}$/.test(priorIdentity.credentialFingerprint ?? "") ||
|
|
969
|
+
Object.keys(priorIdentity).sort().join("\0") !==
|
|
970
|
+
[
|
|
971
|
+
"credentialFingerprint",
|
|
972
|
+
"credentialGeneration",
|
|
973
|
+
"provider",
|
|
974
|
+
"schemaVersion",
|
|
975
|
+
]
|
|
976
|
+
.sort()
|
|
977
|
+
.join("\0")
|
|
978
|
+
) {
|
|
979
|
+
throw new Error("admin_model_auth_identity_rejected");
|
|
980
|
+
}
|
|
981
|
+
priorCredentialGeneration = priorIdentity.credentialGeneration;
|
|
982
|
+
}
|
|
983
|
+
const replaceRootAuth =
|
|
984
|
+
priorCredentialGeneration === null ||
|
|
985
|
+
priorCredentialGeneration !== modelAuthority.credentialGeneration ||
|
|
986
|
+
!existsSync(rootAuthPath);
|
|
987
|
+
if (existsSync(rootAuthPath)) {
|
|
988
|
+
validateAuthFile(rootAuthPath);
|
|
989
|
+
if (replaceRootAuth) rmSync(rootAuthPath, { force: true });
|
|
990
|
+
}
|
|
991
|
+
if (replaceRootAuth) {
|
|
992
|
+
atomicPrivateFile(rootAuthPath, model.hermesRootAuthJson);
|
|
993
|
+
} else {
|
|
994
|
+
// Hermes may refresh the pooled OAuth record in place. Preserve that
|
|
995
|
+
// native state until the server advances the credential generation.
|
|
996
|
+
chmodSync(rootAuthPath, 0o600);
|
|
997
|
+
}
|
|
998
|
+
atomicPrivateFile(
|
|
999
|
+
identityPath,
|
|
1000
|
+
`${JSON.stringify({
|
|
1001
|
+
schemaVersion: "sellable-admin-model-auth/v1",
|
|
1002
|
+
provider: "codex",
|
|
1003
|
+
credentialGeneration: modelAuthority.credentialGeneration,
|
|
1004
|
+
credentialFingerprint: modelAuthority.credentialFingerprint,
|
|
1005
|
+
})}\n`
|
|
1006
|
+
);
|
|
898
1007
|
atomicPrivateFile(join(SECRET_ROOT, "slack-app"), `${secrets.slackApp}\n`);
|
|
899
1008
|
atomicPrivateFile(join(SECRET_ROOT, "slack-bot"), `${secrets.slackBot}\n`);
|
|
900
1009
|
const slackSecretFile = join(SECRET_ROOT, "profile-slack.env");
|
|
@@ -902,15 +1011,7 @@ function installRuntimeSecrets({ claim, secrets }) {
|
|
|
902
1011
|
slackSecretFile,
|
|
903
1012
|
`SLACK_BOT_TOKEN=${secrets.slackBot}\nSLACK_APP_TOKEN=${secrets.slackApp}\n`
|
|
904
1013
|
);
|
|
905
|
-
|
|
906
|
-
join(SECRET_ROOT, "gates.json"),
|
|
907
|
-
`${JSON.stringify({
|
|
908
|
-
workspaceId: claim.operation.workspaceId,
|
|
909
|
-
agentId: claim.operation.agentId,
|
|
910
|
-
revisionId: claim.operation.targetRevisionId,
|
|
911
|
-
fence: claim.operation.fence,
|
|
912
|
-
})}\n`
|
|
913
|
-
);
|
|
1014
|
+
const runtimeGates = writeAdminRuntimeGates(desiredLifecycle);
|
|
914
1015
|
const productConfigPath = join(SECRET_ROOT, "sellable-config.json");
|
|
915
1016
|
atomicPrivateFile(
|
|
916
1017
|
productConfigPath,
|
|
@@ -922,15 +1023,74 @@ function installRuntimeSecrets({ claim, secrets }) {
|
|
|
922
1023
|
})}\n`
|
|
923
1024
|
);
|
|
924
1025
|
return {
|
|
925
|
-
rootAuthPath
|
|
1026
|
+
rootAuthPath,
|
|
926
1027
|
productConfigPath,
|
|
1028
|
+
runtimeGates,
|
|
927
1029
|
slackSecretFile,
|
|
928
1030
|
};
|
|
929
1031
|
}
|
|
930
1032
|
|
|
1033
|
+
function proveAdminModelAuth(profile) {
|
|
1034
|
+
const environment = {
|
|
1035
|
+
...process.env,
|
|
1036
|
+
HOME: DATA_ROOT,
|
|
1037
|
+
HERMES_HOME: profile.profileHome,
|
|
1038
|
+
SELLABLE_HERMES_PROFILE_ID: profile.profile,
|
|
1039
|
+
NO_COLOR: "1",
|
|
1040
|
+
};
|
|
1041
|
+
delete environment.OPENAI_BASE_URL;
|
|
1042
|
+
delete environment.OPENAI_API_KEY;
|
|
1043
|
+
delete environment.OPENROUTER_API_KEY;
|
|
1044
|
+
const probe = spawnSync(
|
|
1045
|
+
HERMES_PYTHON,
|
|
1046
|
+
[
|
|
1047
|
+
"-c",
|
|
1048
|
+
[
|
|
1049
|
+
"import json",
|
|
1050
|
+
"from hermes_constants import get_default_hermes_root, get_hermes_home",
|
|
1051
|
+
"from hermes_cli.auth import resolve_codex_runtime_credentials",
|
|
1052
|
+
"value = resolve_codex_runtime_credentials(refresh_if_expiring=False)",
|
|
1053
|
+
'root_auth = get_default_hermes_root() / "auth.json"',
|
|
1054
|
+
'local_auth = get_hermes_home() / "auth.json"',
|
|
1055
|
+
'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}))',
|
|
1056
|
+
].join(";"),
|
|
1057
|
+
],
|
|
1058
|
+
{
|
|
1059
|
+
env: environment,
|
|
1060
|
+
encoding: "utf8",
|
|
1061
|
+
timeout: TIMEOUT_MS,
|
|
1062
|
+
maxBuffer: 64 * 1024,
|
|
1063
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1064
|
+
}
|
|
1065
|
+
);
|
|
1066
|
+
let readback = null;
|
|
1067
|
+
try {
|
|
1068
|
+
readback = JSON.parse(probe.stdout ?? "");
|
|
1069
|
+
} catch {
|
|
1070
|
+
readback = null;
|
|
1071
|
+
}
|
|
1072
|
+
const verdict = evaluateModelAuthReadback(readback);
|
|
1073
|
+
if (probe.status !== 0 || probe.error || !verdict.accepted) {
|
|
1074
|
+
throw new Error(
|
|
1075
|
+
`admin_model_auth_readback_rejected:${
|
|
1076
|
+
probe.status !== 0 || probe.error
|
|
1077
|
+
? "probe_failed"
|
|
1078
|
+
: (verdict.rejection ?? "unknown")
|
|
1079
|
+
}`
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
return Object.freeze({
|
|
1083
|
+
provider: readback.provider,
|
|
1084
|
+
source: readback.source,
|
|
1085
|
+
sourceClass: readback.source_class,
|
|
1086
|
+
authMode: readback.auth_mode,
|
|
1087
|
+
credentialPresent: readback.credential_present,
|
|
1088
|
+
localAuthEntryCount: readback.local_auth_entry_count,
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
|
|
931
1092
|
function clearRuntimeSecrets() {
|
|
932
1093
|
for (const name of [
|
|
933
|
-
"auth.json",
|
|
934
1094
|
"slack-app",
|
|
935
1095
|
"slack-bot",
|
|
936
1096
|
"profile-slack.env",
|
|
@@ -947,6 +1107,11 @@ function adminAgentServiceEnvironment(claim) {
|
|
|
947
1107
|
const bindings = claim?.runtimeContract?.slack?.selectedBindings;
|
|
948
1108
|
const toolInclude = service?.toolInclude;
|
|
949
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 ||
|
|
950
1115
|
!operation ||
|
|
951
1116
|
!service ||
|
|
952
1117
|
!/^[A-Za-z0-9_-]{1,128}$/.test(service.principalId ?? "") ||
|
|
@@ -965,7 +1130,8 @@ function adminAgentServiceEnvironment(claim) {
|
|
|
965
1130
|
) {
|
|
966
1131
|
throw new Error("admin_runtime_agent_service_environment_rejected");
|
|
967
1132
|
}
|
|
968
|
-
return
|
|
1133
|
+
return buildAdminAgentServiceEnvironment({
|
|
1134
|
+
agentKind: claim.agentKind,
|
|
969
1135
|
workspaceId: operation.workspaceId,
|
|
970
1136
|
agentId: operation.agentId,
|
|
971
1137
|
serviceCredentialGeneration: service.credentialGeneration,
|
|
@@ -1479,6 +1645,8 @@ function completionReceipt({
|
|
|
1479
1645
|
recovery,
|
|
1480
1646
|
observedLifecycle,
|
|
1481
1647
|
memorySnapshot,
|
|
1648
|
+
modelAuth,
|
|
1649
|
+
runtimeGates,
|
|
1482
1650
|
}) {
|
|
1483
1651
|
const identity = operationIdentity(claim, exchange);
|
|
1484
1652
|
const routeGenerationDigest = flyRuntimeDigest(
|
|
@@ -1517,7 +1685,9 @@ function completionReceipt({
|
|
|
1517
1685
|
package: sha256(JSON.stringify(packageSpecs())),
|
|
1518
1686
|
config: operation.profileContract.digest,
|
|
1519
1687
|
content: operation.profileContract.digest,
|
|
1520
|
-
process: sha256(
|
|
1688
|
+
process: sha256(
|
|
1689
|
+
JSON.stringify({ servers: profile.servers, modelAuth, runtimeGates })
|
|
1690
|
+
),
|
|
1521
1691
|
};
|
|
1522
1692
|
return {
|
|
1523
1693
|
...identity,
|
|
@@ -1538,6 +1708,8 @@ function completionReceipt({
|
|
|
1538
1708
|
{ code: "DUAL_MCP_PROVEN", status: "TRUE" },
|
|
1539
1709
|
{ code: "SETTINGS_GENERATION_PROVEN", status: "TRUE" },
|
|
1540
1710
|
{ code: "RECOVERY_GENERATION_PROVEN", status: "TRUE" },
|
|
1711
|
+
{ code: "MODEL_AUTH_PROVEN", status: "TRUE" },
|
|
1712
|
+
{ code: "RUNTIME_GATES_PROVEN", status: "TRUE" },
|
|
1541
1713
|
{ code: "SLACK_TRAFFIC_DISABLED", status: "TRUE" },
|
|
1542
1714
|
{ code: "NO_BOOT_INSTALL", status: "TRUE" },
|
|
1543
1715
|
],
|
|
@@ -1547,6 +1719,7 @@ function completionReceipt({
|
|
|
1547
1719
|
`hermes-memory-${
|
|
1548
1720
|
memorySnapshot.ok ? memorySnapshot.status : memorySnapshot.code
|
|
1549
1721
|
}`.toLowerCase(),
|
|
1722
|
+
`model-auth-${modelAuth.sourceClass}`,
|
|
1550
1723
|
],
|
|
1551
1724
|
adminRuntimeProof,
|
|
1552
1725
|
};
|
|
@@ -1753,23 +1926,24 @@ export async function runAdminCanary() {
|
|
|
1753
1926
|
for (const [name, value] of Object.entries(fixtureFiles)) {
|
|
1754
1927
|
atomicPrivateFile(join(sourceRoot, name), value);
|
|
1755
1928
|
}
|
|
1756
|
-
const rootAuthPath = join(
|
|
1929
|
+
const rootAuthPath = join(DATA_ROOT, "auth.json");
|
|
1757
1930
|
const productConfigPath = join(SECRET_ROOT, "canary-product-config.json");
|
|
1758
1931
|
rmSync(rootAuthPath, { force: true });
|
|
1759
1932
|
rmSync(productConfigPath, { force: true });
|
|
1760
|
-
const
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1933
|
+
const canaryModel = parseFlyCustomerModelCredential(
|
|
1934
|
+
JSON.stringify({
|
|
1935
|
+
OPENAI_API_KEY: null,
|
|
1936
|
+
auth_mode: "chatgpt",
|
|
1937
|
+
last_refresh: "2026-07-29T00:00:00.000Z",
|
|
1938
|
+
tokens: {
|
|
1939
|
+
access_token: "phase158-canary-oauth-access-token-placeholder",
|
|
1940
|
+
account_id: "phase158-canary-account",
|
|
1941
|
+
id_token: "phase158-canary-oauth-id-token-placeholder",
|
|
1942
|
+
refresh_token: "phase158-canary-oauth-refresh-token-placeholder",
|
|
1770
1943
|
},
|
|
1771
|
-
})
|
|
1944
|
+
})
|
|
1772
1945
|
);
|
|
1946
|
+
atomicPrivateFile(rootAuthPath, canaryModel.hermesRootAuthJson);
|
|
1773
1947
|
atomicPrivateFile(
|
|
1774
1948
|
productConfigPath,
|
|
1775
1949
|
`${JSON.stringify({
|
|
@@ -1779,6 +1953,33 @@ export async function runAdminCanary() {
|
|
|
1779
1953
|
workspaceId: fixtureWorkspaceId,
|
|
1780
1954
|
})}\n`
|
|
1781
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");
|
|
1782
1983
|
|
|
1783
1984
|
const template = JSON.parse(readFileSync(ADMIN_PROFILE_TEMPLATE_PATH));
|
|
1784
1985
|
const { contentDigest, ...templateContent } = template;
|
|
@@ -1935,7 +2136,9 @@ export async function runAdminCanary() {
|
|
|
1935
2136
|
sha256: sha256(soulBytes),
|
|
1936
2137
|
byteLength: soulBytes.byteLength,
|
|
1937
2138
|
},
|
|
2139
|
+
agentServiceEnv: agentServiceEnvironment,
|
|
1938
2140
|
});
|
|
2141
|
+
const modelAuth = proveAdminModelAuth(profile);
|
|
1939
2142
|
const observedSettings = settingsProof(
|
|
1940
2143
|
settingsRoot,
|
|
1941
2144
|
transition.settingsDigest
|
|
@@ -1956,6 +2159,20 @@ export async function runAdminCanary() {
|
|
|
1956
2159
|
profileFactory: "stageAdminProfile",
|
|
1957
2160
|
profileDigest: profileContract.digest,
|
|
1958
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
|
+
},
|
|
2175
|
+
modelAuth,
|
|
1959
2176
|
settings: observedSettings,
|
|
1960
2177
|
adminHome: profile.adminHome,
|
|
1961
2178
|
transition,
|
|
@@ -2159,8 +2376,12 @@ async function runAdminProduction() {
|
|
|
2159
2376
|
exchange,
|
|
2160
2377
|
client,
|
|
2161
2378
|
});
|
|
2162
|
-
const installed = installRuntimeSecrets({ claim, secrets });
|
|
2163
2379
|
const desiredLifecycle = desiredLifecycleForClaim(claim);
|
|
2380
|
+
const installed = installRuntimeSecrets({
|
|
2381
|
+
claim,
|
|
2382
|
+
secrets,
|
|
2383
|
+
desiredLifecycle,
|
|
2384
|
+
});
|
|
2164
2385
|
let operation = await pollDelivery({ identity, exchange });
|
|
2165
2386
|
let imported;
|
|
2166
2387
|
let profileContract;
|
|
@@ -2247,6 +2468,7 @@ async function runAdminProduction() {
|
|
|
2247
2468
|
slack: nativeSlackProfileInput(claim, installed),
|
|
2248
2469
|
agentServiceEnv: adminAgentServiceEnvironment(claim),
|
|
2249
2470
|
});
|
|
2471
|
+
const modelAuth = proveAdminModelAuth(profile);
|
|
2250
2472
|
const memorySnapshot = await flushHermesSnapshot({
|
|
2251
2473
|
client,
|
|
2252
2474
|
profileId: profile.profile,
|
|
@@ -2282,6 +2504,8 @@ async function runAdminProduction() {
|
|
|
2282
2504
|
recovery,
|
|
2283
2505
|
observedLifecycle: desiredLifecycle,
|
|
2284
2506
|
memorySnapshot,
|
|
2507
|
+
modelAuth,
|
|
2508
|
+
runtimeGates: installed.runtimeGates,
|
|
2285
2509
|
});
|
|
2286
2510
|
operationHeartbeat.assertHealthy();
|
|
2287
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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)) {
|