@sellable/install 0.1.612-wip.phase189.20260812.2 → 0.1.612
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 +7 -7
- package/container/README.md +2 -2
- package/lib/sellable-agent/admin-runtime-reconciler.mjs +2 -2
- package/lib/sellable-agent/external-runtime-builder.mjs +1 -1
- package/lib/sellable-agent/fly-admin-image/admin-runtime.mjs +29 -142
- package/lib/sellable-agent/fly-customer-image/Dockerfile +3 -3
- package/lib/sellable-agent/fly-customer-image/customer-runtime.mjs +2 -2
- package/lib/sellable-agent/fly-customer-worker.mjs +0 -71
- package/lib/sellable-agent/fly-runtime-identity.mjs +14 -89
- package/lib/sellable-agent/hermes-memory-snapshot.mjs +276 -182
- package/lib/sellable-agent/host-bootstrap.mjs +2 -2
- package/lib/sellable-agent/host-worker.mjs +2 -2
- package/lib/sellable-agent/profile-materializer.mjs +2 -2
- package/lib/sellable-agent/provisioning-adapter.mjs +2 -2
- package/lib/sellable-agent/runtime-reconciler.mjs +0 -47
- package/package.json +1 -1
package/container/Dockerfile
CHANGED
|
@@ -3,13 +3,13 @@ FROM ${HERMES_IMAGE}
|
|
|
3
3
|
|
|
4
4
|
USER root
|
|
5
5
|
|
|
6
|
-
ARG INSTALLER_PACKAGE=@sellable/install@0.1.612
|
|
7
|
-
ARG MCP_PACKAGE=@sellable/mcp@0.1.
|
|
6
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.612
|
|
7
|
+
ARG MCP_PACKAGE=@sellable/mcp@0.1.867
|
|
8
8
|
ARG INSTALLER_INTEGRITY
|
|
9
|
-
ARG MCP_INTEGRITY=sha512-
|
|
9
|
+
ARG MCP_INTEGRITY=sha512-8acJ5MfqgXiu9ylK65LlQ7LUBN5GN5fEXOwDTArxYAN7nBvVLhdz0nTGMERc6qCFNBbILTKvo4IErJOnAcJTDA==
|
|
10
10
|
|
|
11
|
-
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.612
|
|
12
|
-
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.
|
|
11
|
+
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.612" \
|
|
12
|
+
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.867" \
|
|
13
13
|
&& test -n "${INSTALLER_INTEGRITY}" \
|
|
14
14
|
&& test "$(npm view "${INSTALLER_PACKAGE}" dist.integrity)" = "${INSTALLER_INTEGRITY}" \
|
|
15
15
|
&& test "$(npm view "${MCP_PACKAGE}" dist.integrity)" = "${MCP_INTEGRITY}" \
|
|
@@ -23,9 +23,9 @@ RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.612-wip.phase189.202608
|
|
|
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.609-wip.hermes-desktop.20260811160530' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.867') 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.612
|
|
28
|
+
&& node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.612', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.867', 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.612
|
|
6
|
+
`@sellable/install@0.1.612` 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.609-wip.hermes-desktop.20260811160530 .
|
|
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`.
|
|
@@ -20,6 +20,7 @@ import { createHash } from "node:crypto";
|
|
|
20
20
|
|
|
21
21
|
const SHA256 = /^[a-f0-9]{64}$/;
|
|
22
22
|
const EXACT_VERSION = /^\d+\.\d+\.\d+(?:[-.A-Za-z0-9]+)?$/;
|
|
23
|
+
const PACKAGE_DISPOSITIONS = new Set(["changed", "reused"]);
|
|
23
24
|
const PUBLIC_ACTIONS = new Set(["INSTANTIATE", "REFRESH"]);
|
|
24
25
|
const RESTORATION_ACTIONS = new Set([
|
|
25
26
|
"START",
|
|
@@ -64,14 +65,13 @@ export function validateAdminRuntimeTuple(tuple) {
|
|
|
64
65
|
}
|
|
65
66
|
for (const name of ADMIN_RUNTIME_TUPLE_MEMBERS) {
|
|
66
67
|
const item = tuple.packages[name];
|
|
67
|
-
const expectedDisposition = name === "@sellable/mcp" ? "reused" : "changed";
|
|
68
68
|
if (
|
|
69
69
|
!item ||
|
|
70
70
|
item.name !== name ||
|
|
71
71
|
!EXACT_VERSION.test(item.version ?? "") ||
|
|
72
72
|
typeof item.integrity !== "string" ||
|
|
73
73
|
!/^sha512-[A-Za-z0-9+/]{80,}={0,2}$/.test(item.integrity) ||
|
|
74
|
-
item.disposition
|
|
74
|
+
!PACKAGE_DISPOSITIONS.has(item.disposition)
|
|
75
75
|
) {
|
|
76
76
|
return {
|
|
77
77
|
ok: false,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
|
|
29
29
|
const MANIFEST_VERSION = "sellable-agent-external-runtime-closure/v1";
|
|
30
30
|
const MCP_PACKAGE = "@sellable/mcp";
|
|
31
|
-
const MCP_VERSION = "0.1.
|
|
31
|
+
const MCP_VERSION = "0.1.867";
|
|
32
32
|
const SAFE_COMPONENT = /^[A-Za-z0-9@._+-]+$/;
|
|
33
33
|
const HERMES_EXCLUDES = new Set([".git", ".playwright", "node_modules"]);
|
|
34
34
|
|
|
@@ -40,7 +40,6 @@ import {
|
|
|
40
40
|
import {
|
|
41
41
|
buildAdminAgentServiceEnvironment,
|
|
42
42
|
buildNativeHermesLaunch,
|
|
43
|
-
buildRuntimeServingObservation,
|
|
44
43
|
} from "../admin-artifact/node_modules/@sellable/install/lib/sellable-agent/fly-customer-worker.mjs";
|
|
45
44
|
import sodium from "../admin-artifact/node_modules/libsodium-wrappers/dist/modules-esm/libsodium-wrappers.mjs";
|
|
46
45
|
import { SELLABLE_ALLOWED_TOOLSETS } from "../fly-capabilities-bridge.mjs";
|
|
@@ -68,11 +67,11 @@ import {
|
|
|
68
67
|
HERMES_SNAPSHOT_HOOKS,
|
|
69
68
|
HERMES_SNAPSHOT_HOOK_ALLOWLIST_BYTES,
|
|
70
69
|
applyHermesHostedBackgroundReviewPolicy,
|
|
70
|
+
ensureHermesOpportunitySequencesCron,
|
|
71
71
|
ensureHermesSnapshotCron,
|
|
72
72
|
markHermesSnapshotDirty,
|
|
73
73
|
mergeHermesState,
|
|
74
74
|
migrateLegacyHermesUserState,
|
|
75
|
-
quarantineLegacyLightfieldVenv,
|
|
76
75
|
reconcileHermesSnapshot,
|
|
77
76
|
restoreLatestHermesSnapshot,
|
|
78
77
|
} from "../hermes-memory-snapshot.mjs";
|
|
@@ -695,6 +694,10 @@ const ADMIN_LIVE_SKILLS_CONFIG_KEYS = Object.freeze([
|
|
|
695
694
|
"disabled",
|
|
696
695
|
"platform_disabled",
|
|
697
696
|
]);
|
|
697
|
+
const ADMIN_SEALED_SKILL_DIRECTORIES = new Set([
|
|
698
|
+
"sellable",
|
|
699
|
+
"opportunity-sequences",
|
|
700
|
+
]);
|
|
698
701
|
|
|
699
702
|
function adminPreservedSlackToolsets(config) {
|
|
700
703
|
const platformToolsets = config?.platform_toolsets;
|
|
@@ -724,7 +727,7 @@ function adminPreservedSkillsIdentity(profileRoot) {
|
|
|
724
727
|
const walk = (directory, relativePath = "") => {
|
|
725
728
|
for (const name of readdirSync(directory).sort()) {
|
|
726
729
|
const nextRelative = relativePath ? `${relativePath}/${name}` : name;
|
|
727
|
-
if (nextRelative
|
|
730
|
+
if (ADMIN_SEALED_SKILL_DIRECTORIES.has(nextRelative)) continue;
|
|
728
731
|
const path = join(directory, name);
|
|
729
732
|
const stat = lstatSync(path);
|
|
730
733
|
if (stat.isSymbolicLink()) {
|
|
@@ -1031,12 +1034,22 @@ async function materializeAdminProfile({
|
|
|
1031
1034
|
hermesCli: HERMES,
|
|
1032
1035
|
});
|
|
1033
1036
|
if (!memoryCron.ok) throw new Error(memoryCron.code);
|
|
1037
|
+
const opportunitySequencesCron = await ensureHermesOpportunitySequencesCron({
|
|
1038
|
+
profileId: profile,
|
|
1039
|
+
profileRoot: profileHome,
|
|
1040
|
+
hermesRoot: DATA_ROOT,
|
|
1041
|
+
statePath: layout.statePath,
|
|
1042
|
+
});
|
|
1043
|
+
if (!opportunitySequencesCron.ok) {
|
|
1044
|
+
throw new Error(opportunitySequencesCron.code);
|
|
1045
|
+
}
|
|
1034
1046
|
return {
|
|
1035
1047
|
stage,
|
|
1036
1048
|
promotion: promoted.promotion,
|
|
1037
1049
|
profile,
|
|
1038
1050
|
profileHome,
|
|
1039
1051
|
adminHome,
|
|
1052
|
+
opportunitySequencesCron,
|
|
1040
1053
|
observedSoulRawSha256: promoted.observedSoulRawSha256,
|
|
1041
1054
|
servers: [serverProofs.sellable, serverProofs["sellable-admin"]].map(
|
|
1042
1055
|
({ tools: _tools, ...proof }) => proof
|
|
@@ -1187,70 +1200,6 @@ function operationIdentity(claim, exchange) {
|
|
|
1187
1200
|
};
|
|
1188
1201
|
}
|
|
1189
1202
|
|
|
1190
|
-
function operationSnapshotIdentity(claim, exchange) {
|
|
1191
|
-
const authority = claim?.runtimeAuthority;
|
|
1192
|
-
if (!authority) return undefined;
|
|
1193
|
-
return {
|
|
1194
|
-
operationId: claim.operation.id,
|
|
1195
|
-
authorityGeneration: authority.authorityGeneration,
|
|
1196
|
-
runtimeArtifactId: authority.assignedRuntimeArtifactId,
|
|
1197
|
-
sourceMachineId: exchange.workerId,
|
|
1198
|
-
sourceMachineVersion: authority.machineVersion,
|
|
1199
|
-
sourceBootSessionId: BOOT_SESSION_ID,
|
|
1200
|
-
profileSchemaVersion: "admin-native-profile/v2",
|
|
1201
|
-
};
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
function adminServingObservation(activeLaunch, activeGateway, exchange) {
|
|
1205
|
-
try {
|
|
1206
|
-
const authority = activeLaunch?.claim?.runtimeAuthority;
|
|
1207
|
-
const running =
|
|
1208
|
-
activeGateway?.child?.exitCode === null && !activeGateway.child.killed;
|
|
1209
|
-
const productMcp = activeLaunch?.profile?.servers?.find(
|
|
1210
|
-
(server) => server.alias === "sellable"
|
|
1211
|
-
);
|
|
1212
|
-
const adminMcp = activeLaunch?.profile?.servers?.find(
|
|
1213
|
-
(server) => server.alias === "sellable-admin"
|
|
1214
|
-
);
|
|
1215
|
-
if (!authority || !running || !activeLaunch.slackProof) return null;
|
|
1216
|
-
const observedAt = new Date();
|
|
1217
|
-
return buildRuntimeServingObservation({
|
|
1218
|
-
scope: authority.scope,
|
|
1219
|
-
kind: "ADMIN",
|
|
1220
|
-
operationId: activeLaunch.claim.operation.id,
|
|
1221
|
-
workspaceId: activeLaunch.claim.operation.workspaceId,
|
|
1222
|
-
agentId: activeLaunch.claim.operation.agentId,
|
|
1223
|
-
runtimeId: authority.runtimeId,
|
|
1224
|
-
authorityGeneration: authority.authorityGeneration,
|
|
1225
|
-
runtimeArtifactId: authority.runtimeArtifactId,
|
|
1226
|
-
machineId: exchange.workerId,
|
|
1227
|
-
machineVersion: authority.machineVersion,
|
|
1228
|
-
bootSessionId: BOOT_SESSION_ID,
|
|
1229
|
-
profileIdentity: activeLaunch.profile.profile,
|
|
1230
|
-
processIdentity: `gateway-${activeGateway.child.pid}-${activeLaunch.claim.operation.fence}`,
|
|
1231
|
-
observedAt: observedAt.toISOString(),
|
|
1232
|
-
expiresAt: new Date(observedAt.getTime() + 120_000).toISOString(),
|
|
1233
|
-
slack: {
|
|
1234
|
-
authenticated: true,
|
|
1235
|
-
socketModeConnected: true,
|
|
1236
|
-
apiReachable: true,
|
|
1237
|
-
},
|
|
1238
|
-
productMcp: {
|
|
1239
|
-
serverInfoName: productMcp?.serverInfoName,
|
|
1240
|
-
toolCount: productMcp?.toolCount,
|
|
1241
|
-
},
|
|
1242
|
-
adminMcp: {
|
|
1243
|
-
serverInfoName: adminMcp?.serverInfoName,
|
|
1244
|
-
toolCount: adminMcp?.toolCount,
|
|
1245
|
-
},
|
|
1246
|
-
nativeGateway: true,
|
|
1247
|
-
customerRouting: false,
|
|
1248
|
-
});
|
|
1249
|
-
} catch {
|
|
1250
|
-
return null;
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
1203
|
function startOperationHeartbeat({ claim, exchange, client }) {
|
|
1255
1204
|
const identity = operationIdentity(claim, exchange);
|
|
1256
1205
|
let stopped = false;
|
|
@@ -1759,42 +1708,6 @@ function nativeSlackProfileInput(claim, installed) {
|
|
|
1759
1708
|
};
|
|
1760
1709
|
}
|
|
1761
1710
|
|
|
1762
|
-
async function proveAdminSlackServing(claim, secrets) {
|
|
1763
|
-
const slack = claim.runtimeContract?.slack;
|
|
1764
|
-
if (!slack) throw new Error("admin_runtime_slack_contract_rejected");
|
|
1765
|
-
const post = (method, token) =>
|
|
1766
|
-
fetch(`https://slack.com/api/${method}`, {
|
|
1767
|
-
method: "POST",
|
|
1768
|
-
headers: {
|
|
1769
|
-
authorization: `Bearer ${token}`,
|
|
1770
|
-
"content-type": "application/x-www-form-urlencoded",
|
|
1771
|
-
},
|
|
1772
|
-
body: "",
|
|
1773
|
-
signal: AbortSignal.timeout(15_000),
|
|
1774
|
-
});
|
|
1775
|
-
const [authResponse, socketResponse] = await Promise.all([
|
|
1776
|
-
post("auth.test", secrets.slackBot),
|
|
1777
|
-
post("apps.connections.open", secrets.slackApp),
|
|
1778
|
-
]);
|
|
1779
|
-
const [auth, socket] = await Promise.all([
|
|
1780
|
-
authResponse.json(),
|
|
1781
|
-
socketResponse.json(),
|
|
1782
|
-
]);
|
|
1783
|
-
if (
|
|
1784
|
-
!authResponse.ok ||
|
|
1785
|
-
auth?.ok !== true ||
|
|
1786
|
-
auth.team_id !== slack.teamId ||
|
|
1787
|
-
auth.user_id !== slack.botUserId ||
|
|
1788
|
-
!socketResponse.ok ||
|
|
1789
|
-
socket?.ok !== true ||
|
|
1790
|
-
typeof socket.url !== "string" ||
|
|
1791
|
-
!/^wss:\/\//.test(socket.url)
|
|
1792
|
-
) {
|
|
1793
|
-
throw new Error("admin_runtime_slack_serving_rejected");
|
|
1794
|
-
}
|
|
1795
|
-
return true;
|
|
1796
|
-
}
|
|
1797
|
-
|
|
1798
1711
|
async function fetchDeliveryCiphertext({
|
|
1799
1712
|
operation,
|
|
1800
1713
|
identity,
|
|
@@ -2351,12 +2264,7 @@ function completionReceipt({
|
|
|
2351
2264
|
};
|
|
2352
2265
|
}
|
|
2353
2266
|
|
|
2354
|
-
async function flushHermesSnapshot({
|
|
2355
|
-
client,
|
|
2356
|
-
profileId,
|
|
2357
|
-
profileRoot,
|
|
2358
|
-
operationIdentity,
|
|
2359
|
-
}) {
|
|
2267
|
+
async function flushHermesSnapshot({ client, profileId, profileRoot }) {
|
|
2360
2268
|
try {
|
|
2361
2269
|
markHermesSnapshotDirty(profileRoot);
|
|
2362
2270
|
return await reconcileHermesSnapshot({
|
|
@@ -2365,24 +2273,13 @@ async function flushHermesSnapshot({
|
|
|
2365
2273
|
hermesRoot: DATA_ROOT,
|
|
2366
2274
|
hermesVersion: "0.20.0",
|
|
2367
2275
|
debounceMs: 0,
|
|
2368
|
-
|
|
2369
|
-
upload: ({
|
|
2370
|
-
archiveDigest,
|
|
2371
|
-
archiveBytes,
|
|
2372
|
-
sourceProfileId,
|
|
2373
|
-
bytes,
|
|
2374
|
-
ok: _ok,
|
|
2375
|
-
status: _status,
|
|
2376
|
-
hermesVersion: _hermesVersion,
|
|
2377
|
-
...v2
|
|
2378
|
-
}) =>
|
|
2276
|
+
upload: ({ archiveDigest, archiveBytes, sourceProfileId, bytes }) =>
|
|
2379
2277
|
client.putMemorySnapshot(
|
|
2380
2278
|
{
|
|
2381
2279
|
archiveDigest,
|
|
2382
2280
|
archiveBytes,
|
|
2383
2281
|
hermesVersion: "0.20.0",
|
|
2384
2282
|
sourceProfileId,
|
|
2385
|
-
...v2,
|
|
2386
2283
|
},
|
|
2387
2284
|
bytes
|
|
2388
2285
|
),
|
|
@@ -3095,11 +2992,6 @@ async function runAdminProduction() {
|
|
|
3095
2992
|
callerProfileId: activeLaunch.profile.profile,
|
|
3096
2993
|
}),
|
|
3097
2994
|
leaseSeconds: 60,
|
|
3098
|
-
servingObservation: adminServingObservation(
|
|
3099
|
-
activeLaunch,
|
|
3100
|
-
activeGateway,
|
|
3101
|
-
exchange
|
|
3102
|
-
),
|
|
3103
2995
|
})
|
|
3104
2996
|
: await client.claim(60);
|
|
3105
2997
|
} catch (error) {
|
|
@@ -3169,6 +3061,16 @@ async function runAdminProduction() {
|
|
|
3169
3061
|
),
|
|
3170
3062
|
});
|
|
3171
3063
|
}
|
|
3064
|
+
if (
|
|
3065
|
+
activeLaunch &&
|
|
3066
|
+
(!activeGateway ||
|
|
3067
|
+
activeGateway.child.exitCode !== null ||
|
|
3068
|
+
activeGateway.child.killed)
|
|
3069
|
+
) {
|
|
3070
|
+
await sleep(Math.min(30_000, 500 * 2 ** Math.min(failures, 6)));
|
|
3071
|
+
activeGateway = await startNativeGateway(activeLaunch);
|
|
3072
|
+
failures = 0;
|
|
3073
|
+
}
|
|
3172
3074
|
await sleep(1_000);
|
|
3173
3075
|
continue;
|
|
3174
3076
|
}
|
|
@@ -3280,14 +3182,6 @@ async function runAdminProduction() {
|
|
|
3280
3182
|
fetchLatest: () => client.getMemorySnapshot(),
|
|
3281
3183
|
});
|
|
3282
3184
|
if (!memoryRestore.ok) throw new Error(memoryRestore.code);
|
|
3283
|
-
const legacyLightfieldVenv = quarantineLegacyLightfieldVenv({
|
|
3284
|
-
profileId: basename(profileContract.profileHome),
|
|
3285
|
-
profileRoot: profileContract.profileHome,
|
|
3286
|
-
hermesRoot: DATA_ROOT,
|
|
3287
|
-
});
|
|
3288
|
-
if (!legacyLightfieldVenv.ok) {
|
|
3289
|
-
throw new Error(legacyLightfieldVenv.code);
|
|
3290
|
-
}
|
|
3291
3185
|
const legacyUserState = migrateLegacyHermesUserState({
|
|
3292
3186
|
profileId: basename(profileContract.profileHome),
|
|
3293
3187
|
profileRoot: profileContract.profileHome,
|
|
@@ -3320,7 +3214,6 @@ async function runAdminProduction() {
|
|
|
3320
3214
|
client,
|
|
3321
3215
|
profileId: profile.profile,
|
|
3322
3216
|
profileRoot: profile.profileHome,
|
|
3323
|
-
operationIdentity: operationSnapshotIdentity(claim, exchange),
|
|
3324
3217
|
});
|
|
3325
3218
|
const settings = settingsProof(
|
|
3326
3219
|
imported.receipt.generationRoot,
|
|
@@ -3355,14 +3248,8 @@ async function runAdminProduction() {
|
|
|
3355
3248
|
modelAuth,
|
|
3356
3249
|
});
|
|
3357
3250
|
operationHeartbeat.assertHealthy();
|
|
3358
|
-
const slackProof =
|
|
3359
|
-
desiredLifecycle === "ON"
|
|
3360
|
-
? await proveAdminSlackServing(claim, secrets)
|
|
3361
|
-
: false;
|
|
3362
3251
|
const candidateLaunch =
|
|
3363
|
-
desiredLifecycle === "ON"
|
|
3364
|
-
? { profile, claim, installed, slackProof }
|
|
3365
|
-
: null;
|
|
3252
|
+
desiredLifecycle === "ON" ? { profile, claim, installed } : null;
|
|
3366
3253
|
let candidateGateway = null;
|
|
3367
3254
|
try {
|
|
3368
3255
|
if (candidateLaunch) {
|
|
@@ -4,9 +4,9 @@ FROM ${HERMES_IMAGE}
|
|
|
4
4
|
|
|
5
5
|
USER root
|
|
6
6
|
|
|
7
|
-
ARG MCP_PACKAGE=@sellable/mcp@0.1.
|
|
8
|
-
ARG MCP_INTEGRITY=sha512-
|
|
9
|
-
ARG INSTALLER_PACKAGE=@sellable/install@0.1.612
|
|
7
|
+
ARG MCP_PACKAGE=@sellable/mcp@0.1.867
|
|
8
|
+
ARG MCP_INTEGRITY=sha512-8acJ5MfqgXiu9ylK65LlQ7LUBN5GN5fEXOwDTArxYAN7nBvVLhdz0nTGMERc6qCFNBbILTKvo4IErJOnAcJTDA==
|
|
9
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.612
|
|
10
10
|
ARG ADMIN_MCP_PACKAGE=@sellable/admin-mcp@0.1.186-wip.phase158.20260802034727
|
|
11
11
|
ARG ADMIN_MCP_INTEGRITY=sha512-qYNew5wd2IfgVcp1UYOG9QvpF/8Mx19YJ5HQtMKzvSfUf3evePjtaKsoXLf2C/GUJ5QlLlHJo84VWOI3V01+9A==
|
|
12
12
|
ARG TUS_JS_CLIENT_VERSION=4.3.1
|
|
@@ -96,9 +96,9 @@ const RUNTIME_GENERATION = Number(
|
|
|
96
96
|
const BOOT_SESSION_ID = randomUUID();
|
|
97
97
|
const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.20.0";
|
|
98
98
|
const INSTALLER_PACKAGE =
|
|
99
|
-
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.612
|
|
99
|
+
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.612";
|
|
100
100
|
const MCP_PACKAGE =
|
|
101
|
-
process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.
|
|
101
|
+
process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.867";
|
|
102
102
|
|
|
103
103
|
const sha256 = (value) => createHash("sha256").update(value).digest("hex");
|
|
104
104
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -32,77 +32,6 @@ function stableJson(value) {
|
|
|
32
32
|
return JSON.stringify(value);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
// CUSTOMER and ADMIN runtimes share this one serving-observation builder.
|
|
36
|
-
export function buildRuntimeServingObservation(input = {}) {
|
|
37
|
-
const keys = [
|
|
38
|
-
"scope",
|
|
39
|
-
"kind",
|
|
40
|
-
"operationId",
|
|
41
|
-
"workspaceId",
|
|
42
|
-
"agentId",
|
|
43
|
-
"runtimeId",
|
|
44
|
-
"authorityGeneration",
|
|
45
|
-
"runtimeArtifactId",
|
|
46
|
-
"machineId",
|
|
47
|
-
"machineVersion",
|
|
48
|
-
"bootSessionId",
|
|
49
|
-
"profileIdentity",
|
|
50
|
-
"processIdentity",
|
|
51
|
-
"observedAt",
|
|
52
|
-
"expiresAt",
|
|
53
|
-
"slack",
|
|
54
|
-
"productMcp",
|
|
55
|
-
"adminMcp",
|
|
56
|
-
"nativeGateway",
|
|
57
|
-
"customerRouting",
|
|
58
|
-
];
|
|
59
|
-
const identity = (value) =>
|
|
60
|
-
typeof value === "string" && SAFE_ID.test(value);
|
|
61
|
-
const mcp = (value, name, tools) =>
|
|
62
|
-
exactKeys(value, ["serverInfoName", "toolCount"]) &&
|
|
63
|
-
value.serverInfoName === name &&
|
|
64
|
-
value.toolCount === tools;
|
|
65
|
-
const observedAt = Date.parse(input.observedAt ?? "");
|
|
66
|
-
const expiresAt = Date.parse(input.expiresAt ?? "");
|
|
67
|
-
const kindExact =
|
|
68
|
-
input.kind === "CUSTOMER"
|
|
69
|
-
? input.adminMcp === null && input.customerRouting === true
|
|
70
|
-
: input.kind === "ADMIN" &&
|
|
71
|
-
mcp(input.adminMcp, "sellable-admin-mcp", 46) &&
|
|
72
|
-
input.customerRouting === false;
|
|
73
|
-
if (
|
|
74
|
-
!exactKeys(input, keys) ||
|
|
75
|
-
!["ASSIGNED", "CANDIDATE"].includes(input.scope) ||
|
|
76
|
-
!keys
|
|
77
|
-
.slice(2, 13)
|
|
78
|
-
.filter((key) => key !== "authorityGeneration")
|
|
79
|
-
.every((key) => identity(input[key])) ||
|
|
80
|
-
!Number.isSafeInteger(input.authorityGeneration) ||
|
|
81
|
-
input.authorityGeneration < 1 ||
|
|
82
|
-
!Number.isFinite(observedAt) ||
|
|
83
|
-
!Number.isFinite(expiresAt) ||
|
|
84
|
-
expiresAt <= observedAt ||
|
|
85
|
-
expiresAt - observedAt > 5 * 60_000 ||
|
|
86
|
-
!exactKeys(input.slack, [
|
|
87
|
-
"authenticated",
|
|
88
|
-
"socketModeConnected",
|
|
89
|
-
"apiReachable",
|
|
90
|
-
]) ||
|
|
91
|
-
input.slack.authenticated !== true ||
|
|
92
|
-
input.slack.socketModeConnected !== true ||
|
|
93
|
-
input.slack.apiReachable !== true ||
|
|
94
|
-
!mcp(input.productMcp, "sellable-mcp", 193) ||
|
|
95
|
-
input.nativeGateway !== true ||
|
|
96
|
-
!kindExact
|
|
97
|
-
) {
|
|
98
|
-
throw new Error("runtime_serving_observation_rejected");
|
|
99
|
-
}
|
|
100
|
-
return Object.freeze({
|
|
101
|
-
schemaVersion: "sellable-agent-serving-observation/v2",
|
|
102
|
-
...input,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
35
|
export function buildAgentServiceEnvironment({
|
|
107
36
|
workspaceId,
|
|
108
37
|
agentId,
|
|
@@ -541,7 +541,7 @@ function tusUploadAccepted(value) {
|
|
|
541
541
|
value.signedUploadToken.length <= 16_384 &&
|
|
542
542
|
!/\s/.test(value.signedUploadToken) &&
|
|
543
543
|
value.bucketName === "agent-hermes-backups-v1" &&
|
|
544
|
-
/^workspaces\/[A-Za-z0-9_-]{1,128}\/(?:
|
|
544
|
+
/^workspaces\/[A-Za-z0-9_-]{1,128}\/(?:customer|admin)\/archives\/[a-f0-9]{64}\.zip$/.test(
|
|
545
545
|
value?.objectName ?? ""
|
|
546
546
|
)
|
|
547
547
|
);
|
|
@@ -784,7 +784,6 @@ export async function claimWithSoulObservation({
|
|
|
784
784
|
tracker,
|
|
785
785
|
readSoul,
|
|
786
786
|
leaseSeconds = 60,
|
|
787
|
-
servingObservation = /** @type {any} */ (null),
|
|
788
787
|
}) {
|
|
789
788
|
// Deliver lifecycle work before any potentially slow local loader proof.
|
|
790
789
|
// Reconciliation uses the same cadence only after the server confirms that
|
|
@@ -792,7 +791,6 @@ export async function claimWithSoulObservation({
|
|
|
792
791
|
const tick = await client.claimTick({
|
|
793
792
|
leaseSeconds,
|
|
794
793
|
soulObservation: tracker.pending(),
|
|
795
|
-
...(servingObservation ? { servingObservation } : {}),
|
|
796
794
|
});
|
|
797
795
|
if (tick?.claim) return tick.claim;
|
|
798
796
|
|
|
@@ -960,9 +958,9 @@ export function createFlyCustomerControlClient({
|
|
|
960
958
|
}
|
|
961
959
|
}
|
|
962
960
|
};
|
|
963
|
-
const memorySnapshotControl = async (method, body, verb
|
|
961
|
+
const memorySnapshotControl = async (method, body, verb) => {
|
|
964
962
|
const response = await fetchImpl(
|
|
965
|
-
`${endpoint}/api/v3/sellable-agent/worker/memory-snapshot
|
|
963
|
+
`${endpoint}/api/v3/sellable-agent/worker/memory-snapshot`,
|
|
966
964
|
{
|
|
967
965
|
method,
|
|
968
966
|
headers: {
|
|
@@ -990,37 +988,6 @@ export function createFlyCustomerControlClient({
|
|
|
990
988
|
value.archiveBytes <= 128 * 1024 * 1024 &&
|
|
991
989
|
value.hermesVersion === "0.20.0" &&
|
|
992
990
|
/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(value?.sourceProfileId ?? "");
|
|
993
|
-
const boundedSnapshotIdentity = (value) =>
|
|
994
|
-
/^[A-Za-z0-9][A-Za-z0-9_./:-]{0,255}$/.test(value ?? "");
|
|
995
|
-
const metadataV2Accepted = (value) =>
|
|
996
|
-
metadataAccepted(value) &&
|
|
997
|
-
Object.keys(value).sort().join(",") ===
|
|
998
|
-
"archiveBytes,archiveDigest,authorityGeneration,hermesVersion,operationId,profileSchemaVersion,runtimeArtifactId,sourceBootSessionId,sourceMachineId,sourceMachineVersion,sourceProfileId" &&
|
|
999
|
-
boundedSnapshotIdentity(value.operationId) &&
|
|
1000
|
-
Number.isSafeInteger(value.authorityGeneration) &&
|
|
1001
|
-
value.authorityGeneration > 0 &&
|
|
1002
|
-
boundedSnapshotIdentity(value.runtimeArtifactId) &&
|
|
1003
|
-
boundedSnapshotIdentity(value.profileSchemaVersion) &&
|
|
1004
|
-
[
|
|
1005
|
-
value.sourceMachineId,
|
|
1006
|
-
value.sourceMachineVersion,
|
|
1007
|
-
value.sourceBootSessionId,
|
|
1008
|
-
].every(
|
|
1009
|
-
(identityValue) =>
|
|
1010
|
-
identityValue === null || boundedSnapshotIdentity(identityValue)
|
|
1011
|
-
);
|
|
1012
|
-
const snapshotReadbackExpected = (value) =>
|
|
1013
|
-
value &&
|
|
1014
|
-
typeof value === "object" &&
|
|
1015
|
-
!Array.isArray(value) &&
|
|
1016
|
-
Object.keys(value).sort().join(",") ===
|
|
1017
|
-
"authorityGeneration,operationId,profileSchemaVersion,runtimeArtifactId,sourceProfileId" &&
|
|
1018
|
-
boundedSnapshotIdentity(value.operationId) &&
|
|
1019
|
-
Number.isSafeInteger(value.authorityGeneration) &&
|
|
1020
|
-
value.authorityGeneration > 0 &&
|
|
1021
|
-
boundedSnapshotIdentity(value.runtimeArtifactId) &&
|
|
1022
|
-
/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(value.sourceProfileId ?? "") &&
|
|
1023
|
-
boundedSnapshotIdentity(value.profileSchemaVersion);
|
|
1024
991
|
const signedUrlAccepted = (value) => {
|
|
1025
992
|
try {
|
|
1026
993
|
return new URL(value).protocol === "https:";
|
|
@@ -1029,9 +996,8 @@ export function createFlyCustomerControlClient({
|
|
|
1029
996
|
}
|
|
1030
997
|
};
|
|
1031
998
|
const putMemorySnapshot = async (metadata, archive) => {
|
|
1032
|
-
const isV2 = Object.hasOwn(metadata ?? {}, "operationId");
|
|
1033
999
|
if (
|
|
1034
|
-
!
|
|
1000
|
+
!metadataAccepted(metadata) ||
|
|
1035
1001
|
!Buffer.isBuffer(archive) ||
|
|
1036
1002
|
metadata.archiveBytes !== archive.byteLength ||
|
|
1037
1003
|
createHash("sha256").update(archive).digest("hex") !==
|
|
@@ -1043,20 +1009,14 @@ export function createFlyCustomerControlClient({
|
|
|
1043
1009
|
const value = await memorySnapshotControl(
|
|
1044
1010
|
"PUT",
|
|
1045
1011
|
{
|
|
1046
|
-
schemaVersion:
|
|
1047
|
-
? "sellable-hermes-snapshot-upload/v2"
|
|
1048
|
-
: "sellable-hermes-snapshot-upload/v1",
|
|
1012
|
+
schemaVersion: "sellable-hermes-snapshot-upload/v1",
|
|
1049
1013
|
...metadata,
|
|
1050
1014
|
},
|
|
1051
1015
|
"memory-snapshot-put"
|
|
1052
1016
|
);
|
|
1053
1017
|
if (
|
|
1054
1018
|
!tusUploadAccepted(value) ||
|
|
1055
|
-
!value.objectName.endsWith(
|
|
1056
|
-
isV2
|
|
1057
|
-
? `/operations/${metadata.operationId}/${metadata.archiveDigest}.zip`
|
|
1058
|
-
: `/${metadata.archiveDigest}.zip`
|
|
1059
|
-
)
|
|
1019
|
+
!value.objectName.endsWith(`/${metadata.archiveDigest}.zip`)
|
|
1060
1020
|
) {
|
|
1061
1021
|
throw new WorkerControlResponseError(
|
|
1062
1022
|
"memory_snapshot_upload_url_rejected"
|
|
@@ -1074,9 +1034,7 @@ export function createFlyCustomerControlClient({
|
|
|
1074
1034
|
const committed = await memorySnapshotControl(
|
|
1075
1035
|
"POST",
|
|
1076
1036
|
{
|
|
1077
|
-
schemaVersion:
|
|
1078
|
-
? "sellable-hermes-snapshot-commit/v2"
|
|
1079
|
-
: "sellable-hermes-snapshot-commit/v1",
|
|
1037
|
+
schemaVersion: "sellable-hermes-snapshot-commit/v1",
|
|
1080
1038
|
...metadata,
|
|
1081
1039
|
},
|
|
1082
1040
|
"memory-snapshot-put"
|
|
@@ -1089,42 +1047,14 @@ export function createFlyCustomerControlClient({
|
|
|
1089
1047
|
}
|
|
1090
1048
|
return committed;
|
|
1091
1049
|
};
|
|
1092
|
-
const getMemorySnapshot = async (
|
|
1093
|
-
const isV2 = expected !== undefined;
|
|
1094
|
-
if (isV2 && !snapshotReadbackExpected(expected)) {
|
|
1095
|
-
throw new WorkerControlResponseError("memory_snapshot_download_rejected");
|
|
1096
|
-
}
|
|
1097
|
-
const body = isV2
|
|
1098
|
-
? {
|
|
1099
|
-
schemaVersion: "sellable-hermes-snapshot-get/v2",
|
|
1100
|
-
operationId: expected.operationId,
|
|
1101
|
-
}
|
|
1102
|
-
: { schemaVersion: "sellable-hermes-snapshot-get/v1" };
|
|
1103
|
-
const search = isV2
|
|
1104
|
-
? `?${new URLSearchParams(body).toString()}`
|
|
1105
|
-
: "";
|
|
1050
|
+
const getMemorySnapshot = async () => {
|
|
1106
1051
|
const result = await memorySnapshotControl(
|
|
1107
1052
|
"GET",
|
|
1108
|
-
|
|
1109
|
-
"memory-snapshot-get"
|
|
1110
|
-
search
|
|
1053
|
+
{ schemaVersion: "sellable-hermes-snapshot-get/v1" },
|
|
1054
|
+
"memory-snapshot-get"
|
|
1111
1055
|
);
|
|
1112
1056
|
if (!result) return null;
|
|
1113
|
-
|
|
1114
|
-
!isV2 ||
|
|
1115
|
-
(result.operationId === expected.operationId &&
|
|
1116
|
-
result.authorityGeneration === expected.authorityGeneration &&
|
|
1117
|
-
result.runtimeArtifactId === expected.runtimeArtifactId &&
|
|
1118
|
-
result.sourceProfileId === expected.sourceProfileId &&
|
|
1119
|
-
result.profileSchemaVersion === expected.profileSchemaVersion &&
|
|
1120
|
-
/^[a-f0-9]{64}$/.test(result.archiveDigest ?? "") &&
|
|
1121
|
-
Number.isSafeInteger(result.archiveBytes) &&
|
|
1122
|
-
result.archiveBytes > 0 &&
|
|
1123
|
-
result.archiveBytes <= 128 * 1024 * 1024);
|
|
1124
|
-
if (
|
|
1125
|
-
!(isV2 ? identityReadbackAccepted : metadataAccepted(result)) ||
|
|
1126
|
-
!signedUrlAccepted(result.signedUrl)
|
|
1127
|
-
) {
|
|
1057
|
+
if (!metadataAccepted(result) || !signedUrlAccepted(result.signedUrl)) {
|
|
1128
1058
|
throw new WorkerControlResponseError(
|
|
1129
1059
|
"memory_snapshot_download_url_rejected"
|
|
1130
1060
|
);
|
|
@@ -1144,15 +1074,10 @@ export function createFlyCustomerControlClient({
|
|
|
1144
1074
|
}
|
|
1145
1075
|
return { ...result, signedUrl: undefined, bytes };
|
|
1146
1076
|
};
|
|
1147
|
-
const claimTick = async ({
|
|
1148
|
-
leaseSeconds = 60,
|
|
1149
|
-
soulObservation,
|
|
1150
|
-
servingObservation,
|
|
1151
|
-
} = {}) =>
|
|
1077
|
+
const claimTick = async ({ leaseSeconds = 60, soulObservation } = {}) =>
|
|
1152
1078
|
post("claim", "/api/v3/sellable-agent/worker/claim", {
|
|
1153
1079
|
leaseSeconds,
|
|
1154
1080
|
...(soulObservation ? { soulObservation } : {}),
|
|
1155
|
-
...(servingObservation ? { servingObservation } : {}),
|
|
1156
1081
|
});
|
|
1157
1082
|
return Object.freeze({
|
|
1158
1083
|
claimTick,
|
|
@@ -1228,8 +1153,8 @@ export function createFlyCustomerControlClient({
|
|
|
1228
1153
|
async putMemorySnapshot(metadata, archive) {
|
|
1229
1154
|
return putMemorySnapshot(metadata, archive);
|
|
1230
1155
|
},
|
|
1231
|
-
async getMemorySnapshot(
|
|
1232
|
-
return getMemorySnapshot(
|
|
1156
|
+
async getMemorySnapshot() {
|
|
1157
|
+
return getMemorySnapshot();
|
|
1233
1158
|
},
|
|
1234
1159
|
});
|
|
1235
1160
|
}
|