@sellable/install 0.1.721 → 0.1.722-wip.hermes.20260909.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.
- package/container/Dockerfile +4 -4
- package/container/README.md +1 -1
- package/lib/sellable-agent/fly-admin-image/Dockerfile +2 -2
- package/lib/sellable-agent/fly-admin-image/admin-runtime.mjs +5 -5
- package/lib/sellable-agent/fly-customer-image/Dockerfile +5 -5
- package/lib/sellable-agent/fly-customer-image/customer-runtime.mjs +3 -4
- package/lib/sellable-agent/fly-customer-image/hermes-runs-client.mjs +34 -10
- package/lib/sellable-agent/fly-runtime-identity.mjs +2 -4
- package/lib/sellable-agent/hermes-bridge.mjs +210 -6
- package/lib/sellable-agent/hermes-memory-snapshot.mjs +1 -1
- package/lib/sellable-agent/host-bootstrap.mjs +1 -1
- package/lib/sellable-agent/host-worker.mjs +1 -2
- package/lib/sellable-agent/profile-materializer.mjs +2 -3
- package/lib/sellable-agent/provisioning-adapter.mjs +1 -2
- 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.722-wip.hermes.20260909.1
|
|
7
7
|
ARG MCP_PACKAGE=@sellable/mcp@0.1.915
|
|
8
8
|
ARG INSTALLER_INTEGRITY
|
|
9
9
|
ARG MCP_INTEGRITY=sha512-+WdyO3WxQ9U7fHMvxBojPRDIZun/INy9mKOz/A1IcBFEWXFkpPooT0yMUzY1qCYcewTM1AUxP3p2QOtsxRIYQA==
|
|
10
10
|
|
|
11
|
-
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.
|
|
11
|
+
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.722-wip.hermes.20260909.1" \
|
|
12
12
|
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.915" \
|
|
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.721" \
|
|
|
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.722-wip.hermes.20260909.1' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.915') 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.722-wip.hermes.20260909.1', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.915', 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.722-wip.hermes.20260909.1` is
|
|
7
7
|
published:
|
|
8
8
|
|
|
9
9
|
```sh
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# syntax=docker/dockerfile:1.7
|
|
2
|
-
ARG HERMES_IMAGE=nousresearch/hermes-agent@sha256:
|
|
2
|
+
ARG HERMES_IMAGE=nousresearch/hermes-agent@sha256:d58e024542f9de5f9de16a4a668f6728c417ce3341ee5e8419479492de16c432
|
|
3
3
|
ARG SLACK_PP_GO_IMAGE=golang@sha256:386d475a660466863d9f8c766fec64d7fdad3edac2c6a05020c09534d71edb4b
|
|
4
4
|
|
|
5
5
|
FROM ${SLACK_PP_GO_IMAGE} AS slack-pp-cli-builder
|
|
@@ -202,7 +202,7 @@ RUN set -eux; \
|
|
|
202
202
|
'const fs=require("fs"); const manifest=JSON.parse(fs.readFileSync("/usr/local/share/sellable-agent/default-profile-bundle/manifest.json")); if(String(manifest.bundleVersion)!==process.argv[1] || manifest.bundleDigest!==process.argv[2]) process.exit(1);' \
|
|
203
203
|
"${DEFAULT_PROFILE_BUNDLE_VERSION}" "${DEFAULT_PROFILE_BUNDLE_DIGEST}" \
|
|
204
204
|
&& node --input-type=module -e \
|
|
205
|
-
'import { installHermesAgentBridge,
|
|
205
|
+
'import { installHermesAgentBridge, HERMES_AGENT_BRIDGE_DEDICATED_V021_CONTRACT } from "/usr/local/lib/sellable-agent/hermes-bridge.mjs"; installHermesAgentBridge({ sourceRoot: "/opt/hermes", contract: HERMES_AGENT_BRIDGE_DEDICATED_V021_CONTRACT });' \
|
|
206
206
|
&& node --input-type=module -e \
|
|
207
207
|
'await import("/usr/local/lib/sellable-agent/fly-runtime-identity.mjs");' \
|
|
208
208
|
&& node --input-type=module -e \
|
|
@@ -2883,7 +2883,7 @@ async function flushHermesSnapshot({
|
|
|
2883
2883
|
profileId,
|
|
2884
2884
|
profileRoot,
|
|
2885
2885
|
hermesRoot: DATA_ROOT,
|
|
2886
|
-
hermesVersion: "0.
|
|
2886
|
+
hermesVersion: "0.21.1",
|
|
2887
2887
|
debounceMs: 0,
|
|
2888
2888
|
operationIdentity,
|
|
2889
2889
|
upload: ({
|
|
@@ -2900,7 +2900,7 @@ async function flushHermesSnapshot({
|
|
|
2900
2900
|
{
|
|
2901
2901
|
archiveDigest,
|
|
2902
2902
|
archiveBytes,
|
|
2903
|
-
hermesVersion: "0.
|
|
2903
|
+
hermesVersion: "0.21.1",
|
|
2904
2904
|
sourceProfileId,
|
|
2905
2905
|
...v2,
|
|
2906
2906
|
},
|
|
@@ -3843,13 +3843,13 @@ async function runAdminProduction() {
|
|
|
3843
3843
|
profileId: activeLaunch.profile.profile,
|
|
3844
3844
|
profileRoot: activeLaunch.profile.profileHome,
|
|
3845
3845
|
hermesRoot: DATA_ROOT,
|
|
3846
|
-
hermesVersion: "0.
|
|
3846
|
+
hermesVersion: "0.21.1",
|
|
3847
3847
|
upload: ({ archiveDigest, archiveBytes, sourceProfileId, bytes }) =>
|
|
3848
3848
|
client.putMemorySnapshot(
|
|
3849
3849
|
{
|
|
3850
3850
|
archiveDigest,
|
|
3851
3851
|
archiveBytes,
|
|
3852
|
-
hermesVersion: "0.
|
|
3852
|
+
hermesVersion: "0.21.1",
|
|
3853
3853
|
sourceProfileId,
|
|
3854
3854
|
},
|
|
3855
3855
|
bytes
|
|
@@ -4019,7 +4019,7 @@ async function runAdminProduction() {
|
|
|
4019
4019
|
profileId: basename(profileContract.profileHome),
|
|
4020
4020
|
profileRoot: profileContract.profileHome,
|
|
4021
4021
|
hermesRoot: DATA_ROOT,
|
|
4022
|
-
hermesVersion: "0.
|
|
4022
|
+
hermesVersion: "0.21.1",
|
|
4023
4023
|
fetchLatest: () => client.getMemorySnapshot(),
|
|
4024
4024
|
})
|
|
4025
4025
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# syntax=docker/dockerfile:1.7
|
|
2
|
-
ARG HERMES_IMAGE=nousresearch/hermes-agent@sha256:
|
|
2
|
+
ARG HERMES_IMAGE=nousresearch/hermes-agent@sha256:d58e024542f9de5f9de16a4a668f6728c417ce3341ee5e8419479492de16c432
|
|
3
3
|
FROM ${HERMES_IMAGE}
|
|
4
4
|
|
|
5
5
|
USER root
|
|
@@ -8,12 +8,12 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright
|
|
|
8
8
|
|
|
9
9
|
ARG MCP_PACKAGE=@sellable/mcp@0.1.915
|
|
10
10
|
ARG MCP_INTEGRITY=sha512-+WdyO3WxQ9U7fHMvxBojPRDIZun/INy9mKOz/A1IcBFEWXFkpPooT0yMUzY1qCYcewTM1AUxP3p2QOtsxRIYQA==
|
|
11
|
-
ARG INSTALLER_PACKAGE=@sellable/install@0.1.
|
|
11
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.722-wip.hermes.20260909.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
|
|
15
15
|
ARG YAML_VERSION=2.9.0
|
|
16
|
-
ARG HERMES_VERSION=0.
|
|
16
|
+
ARG HERMES_VERSION=0.21.1
|
|
17
17
|
ARG AGENT_BROWSER_VERSION=0.26.0
|
|
18
18
|
ARG TIRITH_VERSION=0.3.3
|
|
19
19
|
ARG PLAYWRIGHT_VERSION=1.62.1
|
|
@@ -54,7 +54,7 @@ RUN --mount=type=secret,id=mcp_trust_root,required=true \
|
|
|
54
54
|
&& ln -s /usr/local/lib/sellable-agent/external-runtime/playwright/node_modules/.bin/playwright \
|
|
55
55
|
/usr/local/bin/playwright \
|
|
56
56
|
&& test "$(playwright --version)" = "Version ${PLAYWRIGHT_VERSION}" \
|
|
57
|
-
&& playwright install chromium \
|
|
57
|
+
&& playwright install --with-deps chromium \
|
|
58
58
|
&& test -d "/opt/hermes/.playwright/chromium-${PLAYWRIGHT_BROWSER_REVISION}" \
|
|
59
59
|
&& test -d "/opt/hermes/.playwright/chromium_headless_shell-${PLAYWRIGHT_BROWSER_REVISION}" \
|
|
60
60
|
&& ln -sf /usr/local/lib/sellable-agent/node_modules/.bin/agent-browser /usr/local/bin/agent-browser \
|
|
@@ -107,7 +107,7 @@ RUN set -eux; \
|
|
|
107
107
|
'const fs=require("fs"); const manifest=JSON.parse(fs.readFileSync("/usr/local/share/sellable-agent/default-profile-bundle/manifest.json")); if(String(manifest.bundleVersion)!==process.argv[1] || manifest.bundleDigest!==process.argv[2]) process.exit(1);' \
|
|
108
108
|
"${DEFAULT_PROFILE_BUNDLE_VERSION}" "${DEFAULT_PROFILE_BUNDLE_DIGEST}" \
|
|
109
109
|
&& node --input-type=module -e \
|
|
110
|
-
'import { installHermesAgentBridge,
|
|
110
|
+
'import { installHermesAgentBridge, HERMES_AGENT_BRIDGE_DEDICATED_V021_CONTRACT } from "/usr/local/lib/sellable-agent/hermes-bridge.mjs"; installHermesAgentBridge({ sourceRoot: "/opt/hermes", contract: HERMES_AGENT_BRIDGE_DEDICATED_V021_CONTRACT });' \
|
|
111
111
|
&& node --input-type=module -e \
|
|
112
112
|
'await import("/usr/local/lib/sellable-agent/fly-runtime-identity.mjs");' \
|
|
113
113
|
&& node --input-type=module -e \
|
|
@@ -105,12 +105,11 @@ const RUNTIME_GENERATION = Number(
|
|
|
105
105
|
"0"
|
|
106
106
|
);
|
|
107
107
|
const BOOT_SESSION_ID = randomUUID();
|
|
108
|
-
const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.
|
|
108
|
+
const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.21.1";
|
|
109
109
|
const INSTALLER_PACKAGE =
|
|
110
|
-
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.
|
|
110
|
+
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.722-wip.hermes.20260909.1";
|
|
111
111
|
const MCP_PACKAGE =
|
|
112
|
-
process.env.SELLABLE_AGENT_MCP_PACKAGE ??
|
|
113
|
-
"@sellable/mcp@0.1.915";
|
|
112
|
+
process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.915";
|
|
114
113
|
export const ENDPOINT_MAX_CONCURRENT_RUNS = 10;
|
|
115
114
|
const DEFAULT_PROFILE_BUNDLE_ROOT =
|
|
116
115
|
"/usr/local/share/sellable-agent/default-profile-bundle";
|
|
@@ -14,13 +14,13 @@ const MAX_HISTORY_ITEMS = 64;
|
|
|
14
14
|
const MAX_EVENT_BYTES = 128 * 1024;
|
|
15
15
|
const MAX_CANONICAL_OUTPUT_BYTES = 48 * 1024;
|
|
16
16
|
const EXACT_FIXTURE_DIGEST =
|
|
17
|
-
"
|
|
17
|
+
"51c75f1c639cd02920be8524cb35242be244d6fff277aff646035a2f0a66b312";
|
|
18
18
|
|
|
19
19
|
export const HERMES_RUNS_FIXTURE_IDENTITY = Object.freeze({
|
|
20
20
|
schemaVersion: "sellable-hermes-api-capabilities/v1",
|
|
21
21
|
image:
|
|
22
|
-
"nousresearch/hermes-agent@sha256:
|
|
23
|
-
hermesVersion: "0.
|
|
22
|
+
"nousresearch/hermes-agent@sha256:d58e024542f9de5f9de16a4a668f6728c417ce3341ee5e8419479492de16c432",
|
|
23
|
+
hermesVersion: "0.21.1",
|
|
24
24
|
fixtureDigest: EXACT_FIXTURE_DIGEST,
|
|
25
25
|
origin: LOOPBACK_ORIGIN,
|
|
26
26
|
auth: "bearer",
|
|
@@ -68,6 +68,10 @@ function exactObject(value) {
|
|
|
68
68
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
function approvalResolved(value) {
|
|
72
|
+
return value === true || (Number.isSafeInteger(value) && value > 0);
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
function normalizedTimestamp(value) {
|
|
72
76
|
if (
|
|
73
77
|
typeof value === "string" &&
|
|
@@ -76,7 +80,7 @@ function normalizedTimestamp(value) {
|
|
|
76
80
|
) {
|
|
77
81
|
return value;
|
|
78
82
|
}
|
|
79
|
-
// Hermes 0.
|
|
83
|
+
// Hermes 0.21.1's live /v1/runs implementation emits time.time() seconds,
|
|
80
84
|
// even though the captured fixture examples use ISO strings. Normalize that
|
|
81
85
|
// observed wire shape so known tool events do not collapse to UNKNOWN.
|
|
82
86
|
if (
|
|
@@ -254,7 +258,10 @@ function nativeStatus(value, expectedRunId) {
|
|
|
254
258
|
if (!exactObject(value) || value.run_id !== expectedRunId) {
|
|
255
259
|
return evidence("NATIVE_STATUS_UNKNOWN", value);
|
|
256
260
|
}
|
|
257
|
-
const status =
|
|
261
|
+
const status =
|
|
262
|
+
value.status === "waiting_for_approval"
|
|
263
|
+
? "awaiting_approval"
|
|
264
|
+
: value.status;
|
|
258
265
|
if (
|
|
259
266
|
![
|
|
260
267
|
"started",
|
|
@@ -370,7 +377,10 @@ export function sanitizeHermesRunEvent(value, options = {}) {
|
|
|
370
377
|
});
|
|
371
378
|
}
|
|
372
379
|
if (value.event === "approval.responded") {
|
|
373
|
-
if (
|
|
380
|
+
if (
|
|
381
|
+
!["once", "deny"].includes(value.choice) ||
|
|
382
|
+
!approvalResolved(value.resolved)
|
|
383
|
+
) {
|
|
374
384
|
return evidence("NATIVE_EVENT_UNKNOWN", value, { ordinal });
|
|
375
385
|
}
|
|
376
386
|
return Object.freeze({
|
|
@@ -513,7 +523,7 @@ export function createHermesRunsClient(options = {}) {
|
|
|
513
523
|
throw transportFailure("SUBMIT_AMBIGUOUS", error);
|
|
514
524
|
}
|
|
515
525
|
const value = await readBounded(response);
|
|
516
|
-
// Hermes 0.
|
|
526
|
+
// Hermes 0.21.1 emits 429 for its concurrency limiter and 503 while the
|
|
517
527
|
// gateway is draining. Both responses occur before it allocates a run,
|
|
518
528
|
// so retrying the identical request cannot duplicate a native attempt.
|
|
519
529
|
if (response.status === 429 || response.status === 503) {
|
|
@@ -522,8 +532,12 @@ export function createHermesRunsClient(options = {}) {
|
|
|
522
532
|
if (
|
|
523
533
|
response.status !== 202 ||
|
|
524
534
|
!exactObject(value) ||
|
|
525
|
-
stableJson(
|
|
526
|
-
|
|
535
|
+
stableJson(
|
|
536
|
+
Object.keys(value)
|
|
537
|
+
.filter((key) => key !== "replayed")
|
|
538
|
+
.sort()
|
|
539
|
+
) !== stableJson(["run_id", "status"]) ||
|
|
540
|
+
(value.replayed !== undefined && value.replayed !== false) ||
|
|
527
541
|
value.status !== "started" ||
|
|
528
542
|
!RUN_ID.test(value.run_id ?? "")
|
|
529
543
|
) {
|
|
@@ -671,7 +685,7 @@ export function createHermesRunsClient(options = {}) {
|
|
|
671
685
|
stableJson(["choice", "object", "resolved", "run_id"]) ||
|
|
672
686
|
value.run_id !== runId ||
|
|
673
687
|
value.choice !== choice ||
|
|
674
|
-
value.resolved
|
|
688
|
+
!approvalResolved(value.resolved)
|
|
675
689
|
) {
|
|
676
690
|
return evidence("NATIVE_APPROVAL_UNCERTAIN", value, {
|
|
677
691
|
nativeStatus: response.status,
|
|
@@ -699,6 +713,16 @@ export function createHermesRunsClient(options = {}) {
|
|
|
699
713
|
});
|
|
700
714
|
}
|
|
701
715
|
const value = await readBounded(response);
|
|
716
|
+
// Hermes 0.21 returns the existing terminal status when stop races
|
|
717
|
+
// completion. Preserve that result; a failed/completed run is not cancelled.
|
|
718
|
+
if (
|
|
719
|
+
response.status === 200 &&
|
|
720
|
+
exactObject(value) &&
|
|
721
|
+
value.run_id === runId &&
|
|
722
|
+
["completed", "failed", "cancelled"].includes(value.status)
|
|
723
|
+
) {
|
|
724
|
+
return nativeStatus(value, runId);
|
|
725
|
+
}
|
|
702
726
|
if (
|
|
703
727
|
response.status !== 200 ||
|
|
704
728
|
!exactObject(value) ||
|
|
@@ -1029,7 +1029,7 @@ export function createFlyCustomerControlClient({
|
|
|
1029
1029
|
Number.isSafeInteger(value?.archiveBytes) &&
|
|
1030
1030
|
value.archiveBytes > 0 &&
|
|
1031
1031
|
value.archiveBytes <= 512 * 1024 * 1024 &&
|
|
1032
|
-
value.hermesVersion === "0.
|
|
1032
|
+
value.hermesVersion === "0.21.1" &&
|
|
1033
1033
|
/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(value?.sourceProfileId ?? "");
|
|
1034
1034
|
const boundedSnapshotIdentity = (value) =>
|
|
1035
1035
|
/^[A-Za-z0-9][A-Za-z0-9_./:-]{0,255}$/.test(value ?? "");
|
|
@@ -1141,9 +1141,7 @@ export function createFlyCustomerControlClient({
|
|
|
1141
1141
|
operationId: expected.operationId,
|
|
1142
1142
|
}
|
|
1143
1143
|
: { schemaVersion: "sellable-hermes-snapshot-get/v1" };
|
|
1144
|
-
const search = isV2
|
|
1145
|
-
? `?${new URLSearchParams(body).toString()}`
|
|
1146
|
-
: "";
|
|
1144
|
+
const search = isV2 ? `?${new URLSearchParams(body).toString()}` : "";
|
|
1147
1145
|
const result = await memorySnapshotControl(
|
|
1148
1146
|
"GET",
|
|
1149
1147
|
body,
|
|
@@ -1167,6 +1167,129 @@ export function patchHermesSourceFile(patchKind, source) {
|
|
|
1167
1167
|
);
|
|
1168
1168
|
}
|
|
1169
1169
|
|
|
1170
|
+
// Hermes 0.21 split configuration, MCP dispatch, and turn execution into
|
|
1171
|
+
// separate modules. Keep the historical contracts intact for older images.
|
|
1172
|
+
if (patchKind === "gateway-config-dedicated-v021") {
|
|
1173
|
+
return replaceExactlyOnce(
|
|
1174
|
+
source,
|
|
1175
|
+
"getenv = _getenv_str\n",
|
|
1176
|
+
`${SECRET_FD_HELPER}
|
|
1177
|
+
def getenv(name, default=""):
|
|
1178
|
+
if name == "SLACK_BOT_TOKEN" and os.getenv("SELLABLE_AGENT_RUNTIME", "") == "1":
|
|
1179
|
+
return _sellable_agent_read_slack_secret(9, "SLACK_BOT_TOKEN", "xoxb-")
|
|
1180
|
+
return _getenv_str(name, default)
|
|
1181
|
+
`,
|
|
1182
|
+
"v0.21 dedicated config credential reader"
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
if (patchKind === "slack-adapter-dedicated-v021") {
|
|
1187
|
+
let patched = replaceExactlyOnce(
|
|
1188
|
+
source,
|
|
1189
|
+
"logger = logging.getLogger(__name__)\n",
|
|
1190
|
+
`logger = logging.getLogger(__name__)\n${SECRET_FD_HELPER}${SLACK_DEDICATED_TEST_API_HELPER}${SLACK_MESSAGE_UNFURL_FILE_HELPER}`,
|
|
1191
|
+
"v0.21 dedicated Slack helpers"
|
|
1192
|
+
);
|
|
1193
|
+
patched = replaceExactlyOnce(
|
|
1194
|
+
patched,
|
|
1195
|
+
' app_token = get_secret("SLACK_APP_TOKEN")',
|
|
1196
|
+
' app_token = (_sellable_agent_read_slack_secret(8, "SLACK_APP_TOKEN", "xapp-")\n if os.getenv("SELLABLE_AGENT_RUNTIME", "") == "1"\n else get_secret("SLACK_APP_TOKEN"))',
|
|
1197
|
+
"v0.21 dedicated app credential"
|
|
1198
|
+
);
|
|
1199
|
+
patched = replaceExactlyOnce(
|
|
1200
|
+
patched,
|
|
1201
|
+
" client = AsyncWebClient(token=token, user_agent_prefix=_HERMES_SLACK_USER_AGENT_PREFIX)",
|
|
1202
|
+
" client = AsyncWebClient(token=token, user_agent_prefix=_HERMES_SLACK_USER_AGENT_PREFIX,\n **_sellable_agent_slack_api_kwargs())",
|
|
1203
|
+
"v0.21 Slack API client"
|
|
1204
|
+
);
|
|
1205
|
+
patched = replaceExactlyOnce(
|
|
1206
|
+
patched,
|
|
1207
|
+
" present = tokens_file.exists()",
|
|
1208
|
+
' present = tokens_file.exists() and os.getenv("SELLABLE_AGENT_RUNTIME", "") != "1"',
|
|
1209
|
+
"v0.21 OAuth file isolation"
|
|
1210
|
+
);
|
|
1211
|
+
patched = replaceExactlyOnce(
|
|
1212
|
+
patched,
|
|
1213
|
+
" scope_id=str(team_id) if team_id else None,\n # Workflow/app posts",
|
|
1214
|
+
" scope_id=str(team_id) if team_id else None,\n message_id=ts,\n # Workflow/app posts",
|
|
1215
|
+
"v0.21 Slack source identity"
|
|
1216
|
+
);
|
|
1217
|
+
return replaceExactlyOnce(
|
|
1218
|
+
patched,
|
|
1219
|
+
' for f in event.get("files", []):',
|
|
1220
|
+
[
|
|
1221
|
+
' files = list(event.get("files") or [])',
|
|
1222
|
+
" files.extend(await _sellable_agent_recover_slack_message_unfurl_files(",
|
|
1223
|
+
' self, event.get("attachments") or [], channel_id, team_id, files))',
|
|
1224
|
+
" for f in files:",
|
|
1225
|
+
].join("\n"),
|
|
1226
|
+
"v0.21 unfurled file recovery"
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
if (patchKind === "mcp-tool-endpoint-v021") {
|
|
1231
|
+
let patched = replaceExactlyOnce(
|
|
1232
|
+
source,
|
|
1233
|
+
'logger = logging.getLogger("tools.mcp_tool")\n',
|
|
1234
|
+
`logger = logging.getLogger("tools.mcp_tool")\nimport os\n${MCP_ENDPOINT_META_HELPER_OPTIONAL_SESSION}`,
|
|
1235
|
+
"v0.21 MCP metadata helper"
|
|
1236
|
+
);
|
|
1237
|
+
return replaceExactlyOnce(
|
|
1238
|
+
patched,
|
|
1239
|
+
" _call_coro = server.session.call_tool(tool_name, arguments=args)",
|
|
1240
|
+
[
|
|
1241
|
+
" sellable_meta = _sellable_agent_call_meta()",
|
|
1242
|
+
' call_kwargs = {"arguments": args}',
|
|
1243
|
+
" if sellable_meta is not None:",
|
|
1244
|
+
' call_kwargs["meta"] = sellable_meta',
|
|
1245
|
+
" _call_coro = server.session.call_tool(tool_name, **call_kwargs)",
|
|
1246
|
+
].join("\n"),
|
|
1247
|
+
"v0.21 MCP call metadata"
|
|
1248
|
+
);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
if (patchKind === "gateway-session-v021") {
|
|
1252
|
+
return replaceExactlyOnce(
|
|
1253
|
+
source,
|
|
1254
|
+
" session_key=context.session_key,\n",
|
|
1255
|
+
` # ${HERMES_AGENT_BRIDGE_MARKER}: durable conversation identity.
|
|
1256
|
+
session_key=context.session_key,
|
|
1257
|
+
session_id=context.session_id,
|
|
1258
|
+
`,
|
|
1259
|
+
"v0.21 durable session identity"
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
if (patchKind === "gateway-provider-preflight-v021") {
|
|
1264
|
+
let patched = replaceExactlyOnce(
|
|
1265
|
+
source,
|
|
1266
|
+
'logger = logging.getLogger("gateway.run")\n',
|
|
1267
|
+
`logger = logging.getLogger("gateway.run")\nimport os\nimport concurrent.futures\n${V020_PROVIDER_PREFLIGHT_HELPER}`,
|
|
1268
|
+
"v0.21 provider helper"
|
|
1269
|
+
);
|
|
1270
|
+
return replaceExactlyOnce(
|
|
1271
|
+
patched,
|
|
1272
|
+
" max_iterations = _current_max_iterations()\n",
|
|
1273
|
+
" max_iterations = _current_max_iterations()\n _sellable_agent_preflight_model_providers()\n",
|
|
1274
|
+
"v0.21 provider preflight"
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
if (patchKind === "turn-context-v021") {
|
|
1279
|
+
let patched = replaceExactlyOnce(
|
|
1280
|
+
source,
|
|
1281
|
+
"logger = logging.getLogger(__name__)\n",
|
|
1282
|
+
`logger = logging.getLogger(__name__)\n${V020_SOUL_TURN_PROLOGUE_HELPER}`,
|
|
1283
|
+
"v0.21 SOUL helper"
|
|
1284
|
+
);
|
|
1285
|
+
return replaceExactlyOnce(
|
|
1286
|
+
patched,
|
|
1287
|
+
" # System prompt is cached per session for prefix caching.\n if agent._cached_system_prompt is None:\n restore_or_build_system_prompt(agent, system_message, conversation_history)",
|
|
1288
|
+
" # System prompt is cached per session for prefix caching.\n _sellable_agent_restore_or_build_proven_soul(\n agent, system_message, conversation_history, restore_or_build_system_prompt,\n )",
|
|
1289
|
+
"v0.21 SOUL turn boundary"
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1170
1293
|
if (patchKind === "gateway-config") {
|
|
1171
1294
|
let patched = replaceExactlyOnce(
|
|
1172
1295
|
source,
|
|
@@ -1670,7 +1793,10 @@ export function patchHermesSourceFile(patchKind, source) {
|
|
|
1670
1793
|
return patched;
|
|
1671
1794
|
}
|
|
1672
1795
|
|
|
1673
|
-
if (
|
|
1796
|
+
if (
|
|
1797
|
+
patchKind === "skills-cache-freshness-v020" ||
|
|
1798
|
+
patchKind === "skills-cache-freshness-v021"
|
|
1799
|
+
) {
|
|
1674
1800
|
let patched = replaceExactlyOnce(
|
|
1675
1801
|
source,
|
|
1676
1802
|
" sig.append((str(d), m))",
|
|
@@ -1706,8 +1832,12 @@ export function patchHermesSourceFile(patchKind, source) {
|
|
|
1706
1832
|
);
|
|
1707
1833
|
patched = replaceExactlyOnce(
|
|
1708
1834
|
patched,
|
|
1709
|
-
|
|
1710
|
-
|
|
1835
|
+
patchKind === "skills-cache-freshness-v021"
|
|
1836
|
+
? " if name in seen_names or name in disabled:\n continue"
|
|
1837
|
+
: " if name in disabled:\n continue",
|
|
1838
|
+
patchKind === "skills-cache-freshness-v021"
|
|
1839
|
+
? " if name in seen_names or name in filtered_disabled:\n continue"
|
|
1840
|
+
: " if name in filtered_disabled:\n continue",
|
|
1711
1841
|
"v0.20 filtered disabled behavior"
|
|
1712
1842
|
);
|
|
1713
1843
|
patched = replaceExactlyOnce(
|
|
@@ -1727,7 +1857,10 @@ export function patchHermesSourceFile(patchKind, source) {
|
|
|
1727
1857
|
return patched;
|
|
1728
1858
|
}
|
|
1729
1859
|
|
|
1730
|
-
if (
|
|
1860
|
+
if (
|
|
1861
|
+
patchKind === "skills-prompt-freshness-v020" ||
|
|
1862
|
+
patchKind === "skills-prompt-freshness-v021"
|
|
1863
|
+
) {
|
|
1731
1864
|
let patched = replaceExactlyOnce(
|
|
1732
1865
|
source,
|
|
1733
1866
|
"_SKILLS_PROMPT_CACHE_LOCK = threading.Lock()",
|
|
@@ -1775,9 +1908,14 @@ export function patchHermesSourceFile(patchKind, source) {
|
|
|
1775
1908
|
);
|
|
1776
1909
|
patched = replaceExactlyOnce(
|
|
1777
1910
|
patched,
|
|
1778
|
-
|
|
1911
|
+
(patchKind === "skills-prompt-freshness-v021"
|
|
1912
|
+
? " _platform_hint, tuple(sorted(disabled)), tuple(sorted(compact_categories or ())),"
|
|
1913
|
+
: " tuple(sorted(compact_categories or ())),") +
|
|
1914
|
+
"\n )\n with _SKILLS_PROMPT_CACHE_LOCK:\n cached = _SKILLS_PROMPT_CACHE.get(cache_key)",
|
|
1779
1915
|
[
|
|
1780
|
-
|
|
1916
|
+
patchKind === "skills-prompt-freshness-v021"
|
|
1917
|
+
? " _platform_hint, tuple(sorted(disabled)), tuple(sorted(compact_categories or ())),"
|
|
1918
|
+
: " tuple(sorted(compact_categories or ())),",
|
|
1781
1919
|
" live_signature,",
|
|
1782
1920
|
" )",
|
|
1783
1921
|
" global _SELLABLE_AGENT_SKILLS_PROMPT_SIGNATURE",
|
|
@@ -2061,6 +2199,72 @@ export const HERMES_AGENT_BRIDGE_DEDICATED_V020_CONTRACT = normalizeContract({
|
|
|
2061
2199
|
],
|
|
2062
2200
|
});
|
|
2063
2201
|
|
|
2202
|
+
export const HERMES_AGENT_BRIDGE_DEDICATED_V021_CONTRACT = normalizeContract({
|
|
2203
|
+
"bridgeId": "sellable-agent-hermes-bridge/v1",
|
|
2204
|
+
"hermesVersion": "0.21.1",
|
|
2205
|
+
"versionFile": {
|
|
2206
|
+
"path": "hermes_cli/__init__.py",
|
|
2207
|
+
"sha256": "efae023ac37622277aac298ac15c646b855e870132c38970feeda9552f1ecc27"
|
|
2208
|
+
},
|
|
2209
|
+
"files": [
|
|
2210
|
+
{
|
|
2211
|
+
"path": "gateway/config_env.py",
|
|
2212
|
+
"patchKind": "gateway-config-dedicated-v021",
|
|
2213
|
+
"sourceSha256": "984ae7b68befecd52fc17b6d38e763f40c7040a2ba69ed4a2c39f1a88ae6734a",
|
|
2214
|
+
"patchedSha256": "0180fbcb1df6e5a7651f172c492a8e317f7455c8ed0e955c7a4f0488fa23b6c2"
|
|
2215
|
+
},
|
|
2216
|
+
{
|
|
2217
|
+
"path": "plugins/platforms/slack/adapter.py",
|
|
2218
|
+
"patchKind": "slack-adapter-dedicated-v021",
|
|
2219
|
+
"sourceSha256": "23048a107304993686250af044256199be2f73171bb5b4cca6a900ea272a6064",
|
|
2220
|
+
"patchedSha256": "bc05fd5f227e42f733ee78c80ffab6c7c53840975fc5bc9755aa2ef8f2c8ff47"
|
|
2221
|
+
},
|
|
2222
|
+
{
|
|
2223
|
+
"path": "tools/mcp_tool_handlers.py",
|
|
2224
|
+
"patchKind": "mcp-tool-endpoint-v021",
|
|
2225
|
+
"sourceSha256": "e6af250f0fbfeb12a936c340a46f40f8d40332ed8bbc4eb63fded66e37aaa7bf",
|
|
2226
|
+
"patchedSha256": "2dae40d821eb6b8986251d10161dbcc9de009628041f282cc6fc2b4d0787a765"
|
|
2227
|
+
},
|
|
2228
|
+
{
|
|
2229
|
+
"path": "gateway/run.py",
|
|
2230
|
+
"patchKind": "gateway-session-v021",
|
|
2231
|
+
"sourceSha256": "3b1f670243d4932ac1f2a2a68bee61a8c7f9bcdac7bd48d43289db90f17bab67",
|
|
2232
|
+
"patchedSha256": "2e25d50add6c49748b2cdd4c2bd115039e72c0b4eeb9c882eb1cd07a2a25c304"
|
|
2233
|
+
},
|
|
2234
|
+
{
|
|
2235
|
+
"path": "gateway/run_turn_runner.py",
|
|
2236
|
+
"patchKind": "gateway-provider-preflight-v021",
|
|
2237
|
+
"sourceSha256": "8ebd74318db2259ac52c35dbf9568b98dbb19c1d2e30289f4efc76a12ac8ceeb",
|
|
2238
|
+
"patchedSha256": "26a13f167835036c37ff92da3c1d036267544cf8a6529f5d40c3ee0ae58fa1cb"
|
|
2239
|
+
},
|
|
2240
|
+
{
|
|
2241
|
+
"path": "agent/turn_context.py",
|
|
2242
|
+
"patchKind": "turn-context-v021",
|
|
2243
|
+
"sourceSha256": "d1a84a24c5b50a7a866268fde453694c6b98790e31d7d4310d8756027149863a",
|
|
2244
|
+
"patchedSha256": "e039fa0e814fc8c3963269c13ed3aab290997428509a8c7c756dac19d3b1ab7a"
|
|
2245
|
+
},
|
|
2246
|
+
{
|
|
2247
|
+
"path": "cron/scheduler.py",
|
|
2248
|
+
"patchKind": "cron-scheduler-v020",
|
|
2249
|
+
"sourceSha256": "f1891e94697ccf5f7b66ae7788345368b452af447fbc43356ae5376a3c753518",
|
|
2250
|
+
"patchedSha256": "977197af3b3dca572b0ed94089c05ae4d2748cce795e11247c5ca2a6ba20dc2c"
|
|
2251
|
+
},
|
|
2252
|
+
{
|
|
2253
|
+
"path": "tools/skills_tool.py",
|
|
2254
|
+
"patchKind": "skills-cache-freshness-v021",
|
|
2255
|
+
"sourceSha256": "0f205b8c2d9266d272d8c6cae3d189b86a5aebce98f03b61605d266b65f9497c",
|
|
2256
|
+
"patchedSha256": "8696b37ef58c0cc570c437abac730ac0e670e44365ddeae452ad40a501f13a1b"
|
|
2257
|
+
},
|
|
2258
|
+
{
|
|
2259
|
+
"path": "agent/prompt_builder.py",
|
|
2260
|
+
"patchKind": "skills-prompt-freshness-v021",
|
|
2261
|
+
"sourceSha256": "83160fab72772012bc308bc730a0085e7acb126f1bea49d62f990505f00ce677",
|
|
2262
|
+
"patchedSha256": "6ab2aa30e06b124333d28ed73fc7b56394fbc71f040f3387f6fbc835d799f266"
|
|
2263
|
+
}
|
|
2264
|
+
],
|
|
2265
|
+
"contractDigest": "a77ab3480e335d52dde12b50c9c7ace29419f02ec3e13fe1678126ec4aed6cce"
|
|
2266
|
+
});
|
|
2267
|
+
|
|
2064
2268
|
function assertRelativeContractPath(path) {
|
|
2065
2269
|
if (
|
|
2066
2270
|
!path ||
|
|
@@ -123,7 +123,7 @@ const LEGACY_HERMES_SNAPSHOT_SCRIPT = "scripts/hermes-memory-reconcile.sh";
|
|
|
123
123
|
|
|
124
124
|
const HERMES = "/opt/hermes/.venv/bin/hermes";
|
|
125
125
|
const HERMES_PYTHON = "/opt/hermes/.venv/bin/python";
|
|
126
|
-
const VERSION = "0.
|
|
126
|
+
const VERSION = "0.21.1";
|
|
127
127
|
const ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
|
|
128
128
|
const BOUNDED_ID = /^[A-Za-z0-9][A-Za-z0-9_./:-]{0,255}$/;
|
|
129
129
|
const SHA256 = /^[a-f0-9]{64}$/;
|
|
@@ -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.722-wip.hermes.20260909.1";
|
|
42
42
|
const MCP_PACKAGE = "@sellable/mcp@0.1.915";
|
|
43
43
|
const HERMES_VERSION = "0.18.0";
|
|
44
44
|
const SHA256 = /^[a-f0-9]{64}$/;
|
|
@@ -1546,8 +1546,7 @@ export function compileClaimToProfileDesired(activeClaim, config) {
|
|
|
1546
1546
|
]) ||
|
|
1547
1547
|
pinned.hermesCli !== "hermes" ||
|
|
1548
1548
|
pinned.hermesVersion !== "0.18.0" ||
|
|
1549
|
-
pinned.installerPackage !==
|
|
1550
|
-
"@sellable/install@0.1.721" ||
|
|
1549
|
+
pinned.installerPackage !== "@sellable/install@0.1.722-wip.hermes.20260909.1" ||
|
|
1551
1550
|
pinned.mcpPackage !== "@sellable/mcp@0.1.915" ||
|
|
1552
1551
|
!Array.isArray(policy.toolInclude) ||
|
|
1553
1552
|
policy.toolInclude.length === 0 ||
|
|
@@ -282,8 +282,7 @@ function validateDesired(desired) {
|
|
|
282
282
|
desired.serviceCredentialGeneration < 1 ||
|
|
283
283
|
desired.hermesCli !== "hermes" ||
|
|
284
284
|
desired.hermesVersion !== "0.18.0" ||
|
|
285
|
-
desired.installerPackage !==
|
|
286
|
-
"@sellable/install@0.1.721" ||
|
|
285
|
+
desired.installerPackage !== "@sellable/install@0.1.722-wip.hermes.20260909.1" ||
|
|
287
286
|
desired.mcpPackage !== "@sellable/mcp@0.1.915" ||
|
|
288
287
|
!Array.isArray(desired.toolInclude) ||
|
|
289
288
|
desired.toolInclude.length === 0 ||
|
|
@@ -2520,7 +2519,7 @@ function validateNativeSlackDesired(desired) {
|
|
|
2520
2519
|
!SHA256.test(desired.accessPolicyHash ?? "") ||
|
|
2521
2520
|
!SHA256.test(desired.runtimeDesiredHash ?? "") ||
|
|
2522
2521
|
!FINGERPRINT.test(desired.apiServerKeyFingerprint ?? "") ||
|
|
2523
|
-
desired.hermesVersion !== "0.
|
|
2522
|
+
desired.hermesVersion !== "0.21.1" ||
|
|
2524
2523
|
!/^@sellable\/install@\d+\.\d+\.\d+(?:[-.A-Za-z0-9]+)?$/.test(
|
|
2525
2524
|
desired.installerPackage ?? ""
|
|
2526
2525
|
) ||
|
|
@@ -25,8 +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 =
|
|
29
|
-
"@sellable/install@0.1.721";
|
|
28
|
+
export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.722-wip.hermes.20260909.1";
|
|
30
29
|
export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.915";
|
|
31
30
|
const PINNED_INSTALL_VERSION = PINNED_INSTALL_PACKAGE.slice(
|
|
32
31
|
PINNED_INSTALL_PACKAGE.lastIndexOf("@") + 1
|