@sellable/install 0.1.789 → 0.1.790
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 +1 -1
- package/lib/sellable-agent/fly-customer-image/Dockerfile +1 -1
- package/lib/sellable-agent/fly-customer-image/customer-runtime.mjs +27 -1
- package/lib/sellable-agent/fly-customer-image/hermes-runs-client.mjs +75 -1
- 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 +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.790
|
|
7
7
|
ARG MCP_PACKAGE=@sellable/mcp@0.1.955
|
|
8
8
|
ARG INSTALLER_INTEGRITY
|
|
9
9
|
ARG MCP_INTEGRITY=sha512-OsyazlSe75wU6O1jAqOutTTt3K+rP5epwQKZEDWyl+qz0wxstVwTbq3FFEGniwr+svVOX3GWET8koIqeDyanow==
|
|
10
10
|
|
|
11
|
-
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.
|
|
11
|
+
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.790" \
|
|
12
12
|
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.955" \
|
|
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.789" \
|
|
|
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.790' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.955') 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.790', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.955', 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,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.
|
|
6
|
+
`@sellable/install@0.1.790` is
|
|
7
7
|
published:
|
|
8
8
|
|
|
9
9
|
```sh
|
|
@@ -8,7 +8,7 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright
|
|
|
8
8
|
|
|
9
9
|
ARG MCP_PACKAGE=@sellable/mcp@0.1.955
|
|
10
10
|
ARG MCP_INTEGRITY=sha512-OsyazlSe75wU6O1jAqOutTTt3K+rP5epwQKZEDWyl+qz0wxstVwTbq3FFEGniwr+svVOX3GWET8koIqeDyanow==
|
|
11
|
-
ARG INSTALLER_PACKAGE=@sellable/install@0.1.
|
|
11
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.790
|
|
12
12
|
ARG ADMIN_MCP_PACKAGE=@sellable/admin-mcp@0.1.202
|
|
13
13
|
ARG ADMIN_MCP_INTEGRITY=sha512-ym3aye/0vQ8tfULJlKz9JFOxvIQsl1G+n3np+TTgK/tecwNblBo/j3AkTnRqH9iomKzPbZzP+n4tEQsvsU8ByA==
|
|
14
14
|
ARG TUS_JS_CLIENT_VERSION=4.3.1
|
|
@@ -95,6 +95,7 @@ import {
|
|
|
95
95
|
import {
|
|
96
96
|
createHermesRunsClient,
|
|
97
97
|
HERMES_RUNS_FIXTURE_IDENTITY,
|
|
98
|
+
validateEndpointModelSelection,
|
|
98
99
|
validateOpenerOutputContract,
|
|
99
100
|
} from "./hermes-runs-client.mjs";
|
|
100
101
|
import {
|
|
@@ -123,7 +124,7 @@ const RUNTIME_GENERATION = Number(
|
|
|
123
124
|
const BOOT_SESSION_ID = randomUUID();
|
|
124
125
|
const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.21.5";
|
|
125
126
|
const INSTALLER_PACKAGE =
|
|
126
|
-
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.
|
|
127
|
+
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.790";
|
|
127
128
|
const MCP_PACKAGE =
|
|
128
129
|
process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.955";
|
|
129
130
|
export const ENDPOINT_MAX_CONCURRENT_RUNS = 10;
|
|
@@ -1071,6 +1072,16 @@ function endpointEventEnvelope(event, ordinal) {
|
|
|
1071
1072
|
outputDigest: event.outputDigest ?? null,
|
|
1072
1073
|
errorDigest: event.errorDigest ?? null,
|
|
1073
1074
|
evidenceDigest: event.evidence?.digest ?? null,
|
|
1075
|
+
...(event.servedRuntime || event.usage
|
|
1076
|
+
? {
|
|
1077
|
+
servedProvider: event.servedRuntime?.provider ?? null,
|
|
1078
|
+
servedModel: event.servedRuntime?.model ?? null,
|
|
1079
|
+
inputTokens: event.usage?.inputTokens ?? null,
|
|
1080
|
+
outputTokens: event.usage?.outputTokens ?? null,
|
|
1081
|
+
cacheReadTokens: event.usage?.cacheReadTokens ?? null,
|
|
1082
|
+
cacheWriteTokens: event.usage?.cacheWriteTokens ?? null,
|
|
1083
|
+
}
|
|
1084
|
+
: {}),
|
|
1074
1085
|
...(/^writer-[123]$|^editor$/.test(event.stage ?? "")
|
|
1075
1086
|
? {
|
|
1076
1087
|
stage: event.stage,
|
|
@@ -1887,6 +1898,11 @@ export function createCustomerEndpointRelay(options = {}) {
|
|
|
1887
1898
|
|
|
1888
1899
|
const runClaim = async (claim, outerSignal) => {
|
|
1889
1900
|
const identity = endpointClaimIdentity(claim);
|
|
1901
|
+
// A caller-named model rides on the run; absent means the machine default.
|
|
1902
|
+
const modelSelection =
|
|
1903
|
+
claim?.run?.modelSelection === undefined
|
|
1904
|
+
? null
|
|
1905
|
+
: validateEndpointModelSelection(claim.run.modelSelection);
|
|
1890
1906
|
const activeRuntime = getActiveRuntime();
|
|
1891
1907
|
if (!endpointRuntimeCurrent(claim, activeRuntime, runtimeGeneration)) {
|
|
1892
1908
|
return { ok: false, code: "customer_endpoint_generation_fenced" };
|
|
@@ -2031,6 +2047,15 @@ export function createCustomerEndpointRelay(options = {}) {
|
|
|
2031
2047
|
held.committedSequence = sequences.at(-1);
|
|
2032
2048
|
};
|
|
2033
2049
|
try {
|
|
2050
|
+
if (usesOpenerDraftStrategy(claim) && modelSelection) {
|
|
2051
|
+
// The three-draft stages are pinned to the Codex output-format path in
|
|
2052
|
+
// the Hermes bridge; refuse rather than silently serve the default.
|
|
2053
|
+
await complete(claim, identity, {
|
|
2054
|
+
attemptStatus: "FAILED",
|
|
2055
|
+
status: "FAILED",
|
|
2056
|
+
});
|
|
2057
|
+
return { ok: false, code: "OPENER_STAGE_MODEL_SELECTION_UNSUPPORTED" };
|
|
2058
|
+
}
|
|
2034
2059
|
if (usesOpenerDraftStrategy(claim)) {
|
|
2035
2060
|
// Phase 206: writers are private native sessions within this one fenced
|
|
2036
2061
|
// claim. Only the editor's validated result reaches durable completion.
|
|
@@ -2126,6 +2151,7 @@ export function createCustomerEndpointRelay(options = {}) {
|
|
|
2126
2151
|
claim.run.conversationHistory
|
|
2127
2152
|
),
|
|
2128
2153
|
correlation: `${claim.attempt.conversationId}:${claim.attempt.runId}:${claim.attempt.id}`,
|
|
2154
|
+
...(modelSelection ? { modelSelection } : {}),
|
|
2129
2155
|
};
|
|
2130
2156
|
while (!submitted) {
|
|
2131
2157
|
try {
|
|
@@ -200,6 +200,39 @@ function canonicalHistory(value) {
|
|
|
200
200
|
return result;
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
// The models a caller may name for one endpoint run. The server checks the
|
|
204
|
+
// same list before it hands the run over; the runtime checks it again here so
|
|
205
|
+
// a run can never reach Hermes with a model that was not reviewed.
|
|
206
|
+
export const ENDPOINT_MODEL_SELECTION_ALLOWLIST = Object.freeze({
|
|
207
|
+
openrouter: Object.freeze([
|
|
208
|
+
"anthropic/claude-opus-5.5",
|
|
209
|
+
"anthropic/claude-sonnet-5.5",
|
|
210
|
+
]),
|
|
211
|
+
});
|
|
212
|
+
const ENDPOINT_MODEL_REASONING_EFFORTS = Object.freeze([
|
|
213
|
+
"low",
|
|
214
|
+
"medium",
|
|
215
|
+
"high",
|
|
216
|
+
"xhigh",
|
|
217
|
+
]);
|
|
218
|
+
|
|
219
|
+
export function validateEndpointModelSelection(value) {
|
|
220
|
+
if (
|
|
221
|
+
!exactObject(value) ||
|
|
222
|
+
Object.keys(value).sort().join(",") !== "model,provider,reasoningEffort" ||
|
|
223
|
+
!Object.hasOwn(ENDPOINT_MODEL_SELECTION_ALLOWLIST, value.provider) ||
|
|
224
|
+
!ENDPOINT_MODEL_SELECTION_ALLOWLIST[value.provider].includes(value.model) ||
|
|
225
|
+
!ENDPOINT_MODEL_REASONING_EFFORTS.includes(value.reasoningEffort)
|
|
226
|
+
) {
|
|
227
|
+
throw new Error("hermes_runs_model_selection_rejected");
|
|
228
|
+
}
|
|
229
|
+
return Object.freeze({
|
|
230
|
+
provider: value.provider,
|
|
231
|
+
model: value.model,
|
|
232
|
+
reasoningEffort: value.reasoningEffort,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
203
236
|
export function validateOpenerOutputContract(value) {
|
|
204
237
|
const schema = value?.schema;
|
|
205
238
|
if (
|
|
@@ -232,7 +265,7 @@ function compileCreateBody(input, openerStage = false) {
|
|
|
232
265
|
"input",
|
|
233
266
|
"instructions",
|
|
234
267
|
"correlation",
|
|
235
|
-
...(openerStage ? ["outputContract"] : []),
|
|
268
|
+
...(openerStage ? ["outputContract"] : ["modelSelection"]),
|
|
236
269
|
];
|
|
237
270
|
if (Object.keys(input).some((key) => !allowed.includes(key))) {
|
|
238
271
|
throw new Error("hermes_runs_submit_rejected");
|
|
@@ -254,11 +287,24 @@ function compileCreateBody(input, openerStage = false) {
|
|
|
254
287
|
if (openerStage && history.length !== 0) {
|
|
255
288
|
throw new Error("hermes_opener_stage_history_rejected");
|
|
256
289
|
}
|
|
290
|
+
const modelSelection =
|
|
291
|
+
input.modelSelection === undefined
|
|
292
|
+
? null
|
|
293
|
+
: validateEndpointModelSelection(input.modelSelection);
|
|
257
294
|
return Object.freeze({
|
|
258
295
|
input: compiledInput,
|
|
259
296
|
instructions,
|
|
260
297
|
conversation_history: history,
|
|
261
298
|
session_id: `${openerStage ? "sellable_opener_stage" : "sellable_endpoint"}_${sha256(correlation).slice(0, 32)}`,
|
|
299
|
+
// Hermes 0.21.5 honours a per-run provider and model on /v1/runs and
|
|
300
|
+
// resolves the provider's credential from the machine's own pool.
|
|
301
|
+
...(modelSelection === null
|
|
302
|
+
? {}
|
|
303
|
+
: {
|
|
304
|
+
provider: modelSelection.provider,
|
|
305
|
+
model: modelSelection.model,
|
|
306
|
+
model_options: { reasoning_effort: modelSelection.reasoningEffort },
|
|
307
|
+
}),
|
|
262
308
|
...(input.outputContract === undefined
|
|
263
309
|
? {}
|
|
264
310
|
: {
|
|
@@ -287,6 +333,30 @@ async function readBounded(response, maximum = MAX_RESPONSE_BYTES) {
|
|
|
287
333
|
}
|
|
288
334
|
}
|
|
289
335
|
|
|
336
|
+
const SERVED_RUNTIME_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
|
|
337
|
+
|
|
338
|
+
// The provider and model that actually served a run (a fallback can differ
|
|
339
|
+
// from the request) and its token counts, so cost and model are auditable.
|
|
340
|
+
function servedRuntime(value) {
|
|
341
|
+
return exactObject(value) &&
|
|
342
|
+
SERVED_RUNTIME_ID.test(value.provider ?? "") &&
|
|
343
|
+
SERVED_RUNTIME_ID.test(value.model ?? "")
|
|
344
|
+
? Object.freeze({ provider: value.provider, model: value.model })
|
|
345
|
+
: null;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function runUsage(value) {
|
|
349
|
+
if (!exactObject(value)) return null;
|
|
350
|
+
const count = (key) =>
|
|
351
|
+
Number.isSafeInteger(value[key]) && value[key] >= 0 ? value[key] : null;
|
|
352
|
+
return Object.freeze({
|
|
353
|
+
inputTokens: count("input_tokens"),
|
|
354
|
+
outputTokens: count("output_tokens"),
|
|
355
|
+
cacheReadTokens: count("cache_read_tokens"),
|
|
356
|
+
cacheWriteTokens: count("cache_write_tokens"),
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
290
360
|
function ensureRunId(value) {
|
|
291
361
|
if (!RUN_ID.test(value ?? "")) throw new Error("hermes_runs_run_id_rejected");
|
|
292
362
|
return value;
|
|
@@ -340,6 +410,8 @@ function nativeStatus(value, expectedRunId) {
|
|
|
340
410
|
classification: "COMPLETED",
|
|
341
411
|
output,
|
|
342
412
|
outputDigest: sha256(stableJson(output)),
|
|
413
|
+
servedRuntime: servedRuntime(value.runtime),
|
|
414
|
+
usage: runUsage(value.usage),
|
|
343
415
|
});
|
|
344
416
|
}
|
|
345
417
|
if (status === "failed") {
|
|
@@ -439,6 +511,8 @@ export function sanitizeHermesRunEvent(value, options = {}) {
|
|
|
439
511
|
output: value.output,
|
|
440
512
|
outputDigest: sha256(stableJson(value.output)),
|
|
441
513
|
usageDigest: sha256(stableJson(value.usage)),
|
|
514
|
+
servedRuntime: servedRuntime(value.runtime),
|
|
515
|
+
usage: runUsage(value.usage),
|
|
442
516
|
});
|
|
443
517
|
}
|
|
444
518
|
if (value.event === "run.failed") {
|
|
@@ -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.790";
|
|
42
42
|
const MCP_PACKAGE = "@sellable/mcp@0.1.955";
|
|
43
43
|
const HERMES_VERSION = "0.18.0";
|
|
44
44
|
const SHA256 = /^[a-f0-9]{64}$/;
|
|
@@ -1546,7 +1546,7 @@ export function compileClaimToProfileDesired(activeClaim, config) {
|
|
|
1546
1546
|
]) ||
|
|
1547
1547
|
pinned.hermesCli !== "hermes" ||
|
|
1548
1548
|
pinned.hermesVersion !== "0.18.0" ||
|
|
1549
|
-
pinned.installerPackage !== "@sellable/install@0.1.
|
|
1549
|
+
pinned.installerPackage !== "@sellable/install@0.1.790" ||
|
|
1550
1550
|
pinned.mcpPackage !== "@sellable/mcp@0.1.955" ||
|
|
1551
1551
|
!Array.isArray(policy.toolInclude) ||
|
|
1552
1552
|
policy.toolInclude.length === 0 ||
|
|
@@ -312,7 +312,7 @@ function validateDesired(desired) {
|
|
|
312
312
|
desired.serviceCredentialGeneration < 1 ||
|
|
313
313
|
desired.hermesCli !== "hermes" ||
|
|
314
314
|
desired.hermesVersion !== "0.18.0" ||
|
|
315
|
-
desired.installerPackage !== "@sellable/install@0.1.
|
|
315
|
+
desired.installerPackage !== "@sellable/install@0.1.790" ||
|
|
316
316
|
desired.mcpPackage !== "@sellable/mcp@0.1.955" ||
|
|
317
317
|
!Array.isArray(desired.toolInclude) ||
|
|
318
318
|
desired.toolInclude.length === 0 ||
|
|
@@ -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.790";
|
|
29
29
|
export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.955";
|
|
30
30
|
const PINNED_INSTALL_VERSION = PINNED_INSTALL_PACKAGE.slice(
|
|
31
31
|
PINNED_INSTALL_PACKAGE.lastIndexOf("@") + 1
|