@sellable/install 0.1.611-wip.phase189.20260812.1 → 0.1.611
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/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 +15 -88
- 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 +292 -187
- 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/skill-templates/refill-sends.md +15 -0
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.611
|
|
7
|
-
ARG MCP_PACKAGE=@sellable/mcp@0.1.
|
|
6
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.611
|
|
7
|
+
ARG MCP_PACKAGE=@sellable/mcp@0.1.866
|
|
8
8
|
ARG INSTALLER_INTEGRITY
|
|
9
|
-
ARG MCP_INTEGRITY=sha512-
|
|
9
|
+
ARG MCP_INTEGRITY=sha512-ry4l91gBit9s1z7aw+v2nq7ULkdugt5BGubJsZ9OPJHwNsCVe6x1iuEe/Alr5Fxr3tdSwUE1fti5PnusD2w86g==
|
|
10
10
|
|
|
11
|
-
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.611
|
|
12
|
-
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.
|
|
11
|
+
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.611" \
|
|
12
|
+
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.866" \
|
|
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.611-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.866') 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.611
|
|
28
|
+
&& node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.611', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.866', 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.611
|
|
6
|
+
`@sellable/install@0.1.611` 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`.
|
|
@@ -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.866";
|
|
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.611
|
|
7
|
+
ARG MCP_PACKAGE=@sellable/mcp@0.1.866
|
|
8
|
+
ARG MCP_INTEGRITY=sha512-ry4l91gBit9s1z7aw+v2nq7ULkdugt5BGubJsZ9OPJHwNsCVe6x1iuEe/Alr5Fxr3tdSwUE1fti5PnusD2w86g==
|
|
9
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.611
|
|
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
|
|
@@ -19,7 +19,6 @@ import { dirname, join } from "node:path";
|
|
|
19
19
|
import { pathToFileURL } from "node:url";
|
|
20
20
|
import {
|
|
21
21
|
buildNativeHermesLaunch,
|
|
22
|
-
buildRuntimeServingObservation,
|
|
23
22
|
clearCustomerRuntimeSecrets,
|
|
24
23
|
installCustomerRuntimeSecrets,
|
|
25
24
|
} from "../fly-customer-worker.mjs";
|
|
@@ -46,7 +45,6 @@ import {
|
|
|
46
45
|
ensureHermesSnapshotCron,
|
|
47
46
|
markHermesSnapshotDirty,
|
|
48
47
|
migrateLegacyHermesUserState,
|
|
49
|
-
quarantineLegacyLightfieldVenv,
|
|
50
48
|
reconcileHermesSnapshot,
|
|
51
49
|
restoreLatestHermesSnapshot,
|
|
52
50
|
} from "../hermes-memory-snapshot.mjs";
|
|
@@ -98,9 +96,9 @@ const RUNTIME_GENERATION = Number(
|
|
|
98
96
|
const BOOT_SESSION_ID = randomUUID();
|
|
99
97
|
const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.20.0";
|
|
100
98
|
const INSTALLER_PACKAGE =
|
|
101
|
-
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.611
|
|
99
|
+
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.611";
|
|
102
100
|
const MCP_PACKAGE =
|
|
103
|
-
process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.
|
|
101
|
+
process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.866";
|
|
104
102
|
|
|
105
103
|
const sha256 = (value) => createHash("sha256").update(value).digest("hex");
|
|
106
104
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -402,59 +400,6 @@ function operationIdentity(claim, exchange) {
|
|
|
402
400
|
};
|
|
403
401
|
}
|
|
404
402
|
|
|
405
|
-
function operationSnapshotIdentity(claim, exchange) {
|
|
406
|
-
const authority = claim?.runtimeAuthority;
|
|
407
|
-
if (!authority) return undefined;
|
|
408
|
-
return {
|
|
409
|
-
operationId: claim.operation.id,
|
|
410
|
-
authorityGeneration: authority.authorityGeneration,
|
|
411
|
-
runtimeArtifactId: authority.assignedRuntimeArtifactId,
|
|
412
|
-
sourceMachineId: exchange.workerId,
|
|
413
|
-
sourceMachineVersion: authority.machineVersion,
|
|
414
|
-
sourceBootSessionId: BOOT_SESSION_ID,
|
|
415
|
-
profileSchemaVersion: "native-slack-profile/v2",
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
function customerServingObservation(activeRuntime, exchange) {
|
|
420
|
-
try {
|
|
421
|
-
const authority = activeRuntime?.claim?.runtimeAuthority;
|
|
422
|
-
const gateway = activeRuntime?.gateway?.readback?.();
|
|
423
|
-
if (!authority || !gateway?.running || !activeRuntime.apiServerReadiness) {
|
|
424
|
-
return null;
|
|
425
|
-
}
|
|
426
|
-
const observedAt = new Date();
|
|
427
|
-
return buildRuntimeServingObservation({
|
|
428
|
-
scope: authority.scope,
|
|
429
|
-
kind: "CUSTOMER",
|
|
430
|
-
operationId: activeRuntime.claim.operation.id,
|
|
431
|
-
workspaceId: activeRuntime.claim.operation.workspaceId,
|
|
432
|
-
agentId: activeRuntime.claim.operation.agentId,
|
|
433
|
-
runtimeId: authority.runtimeId,
|
|
434
|
-
authorityGeneration: authority.authorityGeneration,
|
|
435
|
-
runtimeArtifactId: authority.runtimeArtifactId,
|
|
436
|
-
machineId: exchange.workerId,
|
|
437
|
-
machineVersion: authority.machineVersion,
|
|
438
|
-
bootSessionId: BOOT_SESSION_ID,
|
|
439
|
-
profileIdentity: activeRuntime.desired.profileId,
|
|
440
|
-
processIdentity: `gateway-${gateway.pid}-${activeRuntime.claim.operation.fence}`,
|
|
441
|
-
observedAt: observedAt.toISOString(),
|
|
442
|
-
expiresAt: new Date(observedAt.getTime() + 120_000).toISOString(),
|
|
443
|
-
slack: {
|
|
444
|
-
authenticated: true,
|
|
445
|
-
socketModeConnected: true,
|
|
446
|
-
apiReachable: true,
|
|
447
|
-
},
|
|
448
|
-
productMcp: { serverInfoName: "sellable-mcp", toolCount: 193 },
|
|
449
|
-
adminMcp: null,
|
|
450
|
-
nativeGateway: true,
|
|
451
|
-
customerRouting: true,
|
|
452
|
-
});
|
|
453
|
-
} catch {
|
|
454
|
-
return null;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
403
|
function startOperationHeartbeat({ claim, exchange, client }) {
|
|
459
404
|
const identity = operationIdentity(claim, exchange);
|
|
460
405
|
let stopped = false;
|
|
@@ -2641,12 +2586,7 @@ function completionReceipt({
|
|
|
2641
2586
|
};
|
|
2642
2587
|
}
|
|
2643
2588
|
|
|
2644
|
-
async function flushHermesSnapshot({
|
|
2645
|
-
client,
|
|
2646
|
-
profileId,
|
|
2647
|
-
profileRoot,
|
|
2648
|
-
operationIdentity,
|
|
2649
|
-
}) {
|
|
2589
|
+
async function flushHermesSnapshot({ client, profileId, profileRoot }) {
|
|
2650
2590
|
try {
|
|
2651
2591
|
markHermesSnapshotDirty(profileRoot);
|
|
2652
2592
|
return await reconcileHermesSnapshot({
|
|
@@ -2655,24 +2595,13 @@ async function flushHermesSnapshot({
|
|
|
2655
2595
|
hermesRoot: DATA_ROOT,
|
|
2656
2596
|
hermesVersion: HERMES_VERSION,
|
|
2657
2597
|
debounceMs: 0,
|
|
2658
|
-
|
|
2659
|
-
upload: ({
|
|
2660
|
-
archiveDigest,
|
|
2661
|
-
archiveBytes,
|
|
2662
|
-
sourceProfileId,
|
|
2663
|
-
bytes,
|
|
2664
|
-
ok: _ok,
|
|
2665
|
-
status: _status,
|
|
2666
|
-
hermesVersion: _hermesVersion,
|
|
2667
|
-
...v2
|
|
2668
|
-
}) =>
|
|
2598
|
+
upload: ({ archiveDigest, archiveBytes, sourceProfileId, bytes }) =>
|
|
2669
2599
|
client.putMemorySnapshot(
|
|
2670
2600
|
{
|
|
2671
2601
|
archiveDigest,
|
|
2672
2602
|
archiveBytes,
|
|
2673
2603
|
hermesVersion: HERMES_VERSION,
|
|
2674
2604
|
sourceProfileId,
|
|
2675
|
-
...v2,
|
|
2676
2605
|
},
|
|
2677
2606
|
bytes
|
|
2678
2607
|
),
|
|
@@ -2746,12 +2675,6 @@ async function processOperation({ claim, exchange, client, identity }) {
|
|
|
2746
2675
|
fetchLatest: () => client.getMemorySnapshot(),
|
|
2747
2676
|
});
|
|
2748
2677
|
if (!memoryRestore.ok) throw new Error(memoryRestore.code);
|
|
2749
|
-
const legacyLightfieldVenv = quarantineLegacyLightfieldVenv({
|
|
2750
|
-
profileId: desired.profileId,
|
|
2751
|
-
profileRoot,
|
|
2752
|
-
hermesRoot: DATA_ROOT,
|
|
2753
|
-
});
|
|
2754
|
-
if (!legacyLightfieldVenv.ok) throw new Error(legacyLightfieldVenv.code);
|
|
2755
2678
|
const legacyUserState = migrateLegacyHermesUserState({
|
|
2756
2679
|
profileId: desired.profileId,
|
|
2757
2680
|
profileRoot,
|
|
@@ -2803,7 +2726,6 @@ async function processOperation({ claim, exchange, client, identity }) {
|
|
|
2803
2726
|
client,
|
|
2804
2727
|
profileId: desired.profileId,
|
|
2805
2728
|
profileRoot,
|
|
2806
|
-
operationIdentity: operationSnapshotIdentity(claim, exchange),
|
|
2807
2729
|
});
|
|
2808
2730
|
const prove =
|
|
2809
2731
|
claim.operation.action === "INSTANTIATE"
|
|
@@ -2886,7 +2808,6 @@ async function processOperation({ claim, exchange, client, identity }) {
|
|
|
2886
2808
|
sharedAppCredentialPresent: false,
|
|
2887
2809
|
});
|
|
2888
2810
|
return {
|
|
2889
|
-
claim,
|
|
2890
2811
|
desired,
|
|
2891
2812
|
expected: secrets.expected,
|
|
2892
2813
|
secrets,
|
|
@@ -2896,7 +2817,6 @@ async function processOperation({ claim, exchange, client, identity }) {
|
|
|
2896
2817
|
trustRootPem,
|
|
2897
2818
|
action: claim.operation.action,
|
|
2898
2819
|
gateway: proof.gateway ?? null,
|
|
2899
|
-
apiServerReadiness: proof.apiServerReadiness ?? null,
|
|
2900
2820
|
modelHealthReporter,
|
|
2901
2821
|
};
|
|
2902
2822
|
}
|
|
@@ -2999,10 +2919,6 @@ async function main() {
|
|
|
2999
2919
|
callerProfileId: activeRuntime.desired.profileId,
|
|
3000
2920
|
}),
|
|
3001
2921
|
leaseSeconds: 60,
|
|
3002
|
-
servingObservation: customerServingObservation(
|
|
3003
|
-
activeRuntime,
|
|
3004
|
-
exchange
|
|
3005
|
-
),
|
|
3006
2922
|
})
|
|
3007
2923
|
: await client.claim(60);
|
|
3008
2924
|
} catch (error) {
|
|
@@ -3047,6 +2963,17 @@ async function main() {
|
|
|
3047
2963
|
),
|
|
3048
2964
|
});
|
|
3049
2965
|
}
|
|
2966
|
+
if (
|
|
2967
|
+
activeRuntime?.action !== "INSTANTIATE" &&
|
|
2968
|
+
activeRuntime?.gateway &&
|
|
2969
|
+
!activeRuntime.gateway.readback().running
|
|
2970
|
+
) {
|
|
2971
|
+
activeRuntime.gateway = await startNativeGateway({
|
|
2972
|
+
desired: activeRuntime.desired,
|
|
2973
|
+
runtimeConfig: activeRuntime.runtimeConfig,
|
|
2974
|
+
secrets: activeRuntime.secrets,
|
|
2975
|
+
});
|
|
2976
|
+
}
|
|
3050
2977
|
await sleep(1_000);
|
|
3051
2978
|
continue;
|
|
3052
2979
|
}
|
|
@@ -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,
|