@sellable/install 0.1.651 → 0.1.653-wip.phase189.20260814.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/admin-native-gateway-supervisor.mjs +24 -0
- package/lib/sellable-agent/fly-admin-image/Dockerfile +3 -0
- package/lib/sellable-agent/fly-admin-image/admin-runtime-phase-telemetry.mjs +80 -0
- package/lib/sellable-agent/fly-admin-image/admin-runtime.mjs +421 -240
- package/lib/sellable-agent/fly-customer-image/Dockerfile +1 -1
- package/lib/sellable-agent/fly-customer-image/customer-runtime.mjs +1 -1
- package/lib/sellable-agent/host-bootstrap.mjs +1 -1
- package/lib/sellable-agent/host-worker.mjs +1 -1
- package/lib/sellable-agent/profile-materializer.mjs +1 -1
- package/lib/sellable-agent/provisioning-adapter.mjs +1 -1
- package/package.json +1 -1
package/container/Dockerfile
CHANGED
|
@@ -3,12 +3,12 @@ FROM ${HERMES_IMAGE}
|
|
|
3
3
|
|
|
4
4
|
USER root
|
|
5
5
|
|
|
6
|
-
ARG INSTALLER_PACKAGE=@sellable/install@0.1.
|
|
6
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.653-wip.phase189.20260814.1
|
|
7
7
|
ARG MCP_PACKAGE=@sellable/mcp@0.1.880
|
|
8
8
|
ARG INSTALLER_INTEGRITY
|
|
9
9
|
ARG MCP_INTEGRITY=sha512-+Fo8aQlYduLaQMQff+ZA25LeUhWZ7I7UH5DECpND84LQ+rHSjKeKFj4Zs93bVHXwQCIy2OLbr75y4f4+CyT57w==
|
|
10
10
|
|
|
11
|
-
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.
|
|
11
|
+
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.653-wip.phase189.20260814.1" \
|
|
12
12
|
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.880" \
|
|
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.651" \
|
|
|
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.653-wip.phase189.20260814.1' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.880') 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.653-wip.phase189.20260814.1', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.880', 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.653-wip.phase189.20260814.1` is
|
|
7
7
|
published:
|
|
8
8
|
|
|
9
9
|
```sh
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const ADMIN_NATIVE_GATEWAY_MAX_LOCAL_RESTARTS = 3;
|
|
2
|
+
|
|
3
|
+
export function nativeGatewayRunning(gateway) {
|
|
4
|
+
const child = gateway?.child;
|
|
5
|
+
return Boolean(
|
|
6
|
+
child &&
|
|
7
|
+
child.exitCode === null &&
|
|
8
|
+
child.signalCode === null &&
|
|
9
|
+
child.killed !== true
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function nextAdminNativeGatewayRecovery({ attempts }) {
|
|
14
|
+
const completedAttempts = Number.isInteger(attempts)
|
|
15
|
+
? Math.max(0, attempts)
|
|
16
|
+
: 0;
|
|
17
|
+
if (completedAttempts >= ADMIN_NATIVE_GATEWAY_MAX_LOCAL_RESTARTS) {
|
|
18
|
+
return {
|
|
19
|
+
action: "EXHAUSTED",
|
|
20
|
+
attempt: ADMIN_NATIVE_GATEWAY_MAX_LOCAL_RESTARTS,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return { action: "RESTART", attempt: completedAttempts + 1 };
|
|
24
|
+
}
|
|
@@ -148,8 +148,10 @@ RUN --mount=type=secret,id=mcp_trust_root,required=true \
|
|
|
148
148
|
test "$(sha256sum /usr/local/share/sellable-agent/mcp-turn-trust-root.pem | cut -d ' ' -f 1)" = "${MCP_TRUST_ROOT_FINGERPRINT}"
|
|
149
149
|
|
|
150
150
|
COPY fly-admin-image/admin-runtime.mjs /usr/local/lib/sellable-agent/admin-runtime/admin-runtime.mjs
|
|
151
|
+
COPY fly-admin-image/admin-runtime-phase-telemetry.mjs /usr/local/lib/sellable-agent/admin-runtime/admin-runtime-phase-telemetry.mjs
|
|
151
152
|
COPY fly-admin-image/admin-state-generation.mjs /usr/local/lib/sellable-agent/admin-runtime/admin-state-generation.mjs
|
|
152
153
|
COPY fly-admin-image/start-admin-runtime /usr/local/lib/sellable-agent/admin-runtime/start-admin-runtime
|
|
154
|
+
COPY admin-native-gateway-supervisor.mjs /usr/local/lib/sellable-agent/admin-native-gateway-supervisor.mjs
|
|
153
155
|
COPY abandoned-runtime-scratch.mjs /usr/local/lib/sellable-agent/abandoned-runtime-scratch.mjs
|
|
154
156
|
COPY mcp-context-proxy.mjs /usr/local/lib/sellable-agent/mcp-context-proxy.mjs
|
|
155
157
|
COPY mcp-runtime-binding.mjs /usr/local/lib/sellable-agent/mcp-runtime-binding.mjs
|
|
@@ -237,6 +239,7 @@ RUN set -eux; \
|
|
|
237
239
|
/usr/local/lib/sellable-agent/admin-runtime/admin-runtime.mjs \
|
|
238
240
|
/usr/local/lib/sellable-agent/admin-runtime/admin-state-generation.mjs \
|
|
239
241
|
/usr/local/lib/sellable-agent/admin-runtime/start-admin-runtime \
|
|
242
|
+
/usr/local/lib/sellable-agent/admin-native-gateway-supervisor.mjs \
|
|
240
243
|
/usr/local/lib/sellable-agent/abandoned-runtime-scratch.mjs \
|
|
241
244
|
/usr/local/lib/sellable-agent/mcp-context-proxy.mjs \
|
|
242
245
|
/usr/local/lib/sellable-agent/mcp-runtime-binding.mjs \
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const ADMIN_RUNTIME_PHASES = new Set([
|
|
2
|
+
"runtime_secrets",
|
|
3
|
+
"snapshot_restore",
|
|
4
|
+
"profile_settings_delivery",
|
|
5
|
+
"profile_stage",
|
|
6
|
+
"profile_live_merge_promote",
|
|
7
|
+
"profile_crons",
|
|
8
|
+
"default_profile_bundle",
|
|
9
|
+
"memory_snapshot",
|
|
10
|
+
"slack_serving_proof",
|
|
11
|
+
"native_gateway",
|
|
12
|
+
"serving_observation",
|
|
13
|
+
"operation_completion",
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
function phaseFailureCode(error) {
|
|
17
|
+
const code = error instanceof Error ? error.message : "";
|
|
18
|
+
return /^[A-Za-z0-9_:-]{1,160}$/.test(code)
|
|
19
|
+
? code
|
|
20
|
+
: "admin_runtime_phase_failed";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function createAdminRuntimePhaseTelemetry({
|
|
24
|
+
bootSessionId,
|
|
25
|
+
operationId,
|
|
26
|
+
now = () => Date.now(),
|
|
27
|
+
write = (entry) => process.stderr.write(`${JSON.stringify(entry)}\n`),
|
|
28
|
+
}) {
|
|
29
|
+
if (
|
|
30
|
+
typeof bootSessionId !== "string" ||
|
|
31
|
+
bootSessionId.length < 1 ||
|
|
32
|
+
typeof operationId !== "string" ||
|
|
33
|
+
operationId.length < 1
|
|
34
|
+
) {
|
|
35
|
+
throw new Error("admin_runtime_phase_identity_rejected");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const run = async (phase, work) => {
|
|
39
|
+
if (!ADMIN_RUNTIME_PHASES.has(phase) || typeof work !== "function") {
|
|
40
|
+
throw new Error("admin_runtime_phase_rejected");
|
|
41
|
+
}
|
|
42
|
+
const startedAt = now();
|
|
43
|
+
write({
|
|
44
|
+
ok: true,
|
|
45
|
+
code: "admin_runtime_phase",
|
|
46
|
+
phase,
|
|
47
|
+
status: "started",
|
|
48
|
+
elapsedMs: 0,
|
|
49
|
+
bootSessionId,
|
|
50
|
+
operationId,
|
|
51
|
+
});
|
|
52
|
+
try {
|
|
53
|
+
const result = await work();
|
|
54
|
+
write({
|
|
55
|
+
ok: true,
|
|
56
|
+
code: "admin_runtime_phase",
|
|
57
|
+
phase,
|
|
58
|
+
status: "completed",
|
|
59
|
+
elapsedMs: Math.max(0, now() - startedAt),
|
|
60
|
+
bootSessionId,
|
|
61
|
+
operationId,
|
|
62
|
+
});
|
|
63
|
+
return result;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
write({
|
|
66
|
+
ok: false,
|
|
67
|
+
code: "admin_runtime_phase",
|
|
68
|
+
phase,
|
|
69
|
+
status: "failed",
|
|
70
|
+
elapsedMs: Math.max(0, now() - startedAt),
|
|
71
|
+
bootSessionId,
|
|
72
|
+
operationId,
|
|
73
|
+
errorCode: phaseFailureCode(error),
|
|
74
|
+
});
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return { run };
|
|
80
|
+
}
|
|
@@ -28,6 +28,7 @@ import { createRequire } from "node:module";
|
|
|
28
28
|
import { basename, dirname, join } from "node:path";
|
|
29
29
|
import { createInterface } from "node:readline";
|
|
30
30
|
|
|
31
|
+
import { pruneAbandonedRuntimeScratch } from "../abandoned-runtime-scratch.mjs";
|
|
31
32
|
import {
|
|
32
33
|
buildAdminSettingsManifest,
|
|
33
34
|
encodeAdminSettingsBundle,
|
|
@@ -45,6 +46,15 @@ import {
|
|
|
45
46
|
isOperationBoundBackupClaim,
|
|
46
47
|
} from "../admin-artifact/node_modules/@sellable/install/lib/sellable-agent/fly-customer-worker.mjs";
|
|
47
48
|
import sodium from "../admin-artifact/node_modules/libsodium-wrappers/dist/modules-esm/libsodium-wrappers.mjs";
|
|
49
|
+
import {
|
|
50
|
+
nativeGatewayRunning,
|
|
51
|
+
nextAdminNativeGatewayRecovery,
|
|
52
|
+
} from "../admin-native-gateway-supervisor.mjs";
|
|
53
|
+
import {
|
|
54
|
+
bindDefaultProfileProbeCron,
|
|
55
|
+
buildDefaultProfileProjection,
|
|
56
|
+
reconcileDefaultProfileBundle,
|
|
57
|
+
} from "../default-profile-reconciler.mjs";
|
|
48
58
|
import {
|
|
49
59
|
SELLABLE_BROWSER_TOOLSET_DEFAULT_MARKER,
|
|
50
60
|
sellableSlackToolsetsForMaterialization,
|
|
@@ -63,18 +73,13 @@ import {
|
|
|
63
73
|
renewFlyAdminControlSession,
|
|
64
74
|
signedWorkerAuthorization,
|
|
65
75
|
} from "../fly-runtime-identity.mjs";
|
|
76
|
+
import { proveDefaultProfileNativeSkills } from "../fly-skills-bridge.mjs";
|
|
66
77
|
import {
|
|
67
78
|
executeExternalSoulBridgeOperation,
|
|
68
79
|
observeProfileSoul,
|
|
69
80
|
prepareSoulLockRoot,
|
|
70
81
|
withProfileSoulLock,
|
|
71
82
|
} from "../fly-soul-bridge.mjs";
|
|
72
|
-
import { proveDefaultProfileNativeSkills } from "../fly-skills-bridge.mjs";
|
|
73
|
-
import {
|
|
74
|
-
bindDefaultProfileProbeCron,
|
|
75
|
-
buildDefaultProfileProjection,
|
|
76
|
-
reconcileDefaultProfileBundle,
|
|
77
|
-
} from "../default-profile-reconciler.mjs";
|
|
78
83
|
import {
|
|
79
84
|
HERMES_SNAPSHOT_HOOKS,
|
|
80
85
|
HERMES_SNAPSHOT_HOOK_ALLOWLIST_BYTES,
|
|
@@ -98,12 +103,12 @@ import {
|
|
|
98
103
|
reconcileHermesModelAuth,
|
|
99
104
|
} from "../model-auth-reconciler.mjs";
|
|
100
105
|
import { createModelHealthReporter } from "../model-health-reporter.mjs";
|
|
106
|
+
import { createAdminRuntimePhaseTelemetry } from "./admin-runtime-phase-telemetry.mjs";
|
|
101
107
|
import {
|
|
102
108
|
completeAdminStateReceiptRecovery,
|
|
103
109
|
prepareAdminStateGeneration,
|
|
104
110
|
recoverAdminStateReceiptMismatch,
|
|
105
111
|
} from "./admin-state-generation.mjs";
|
|
106
|
-
import { pruneAbandonedRuntimeScratch } from "../abandoned-runtime-scratch.mjs";
|
|
107
112
|
|
|
108
113
|
export const ADMIN_RUNTIME_SETTINGS_DIRECTORY =
|
|
109
114
|
"/run/sellable-agent/admin-settings";
|
|
@@ -268,7 +273,10 @@ function adminMaterializerReceipt(profileId, profileRoot) {
|
|
|
268
273
|
fileHashes: Object.fromEntries(
|
|
269
274
|
paths
|
|
270
275
|
.filter((relative) => existsSync(join(profileRoot, relative)))
|
|
271
|
-
.map((relative) => [
|
|
276
|
+
.map((relative) => [
|
|
277
|
+
relative,
|
|
278
|
+
sha256(readFileSync(join(profileRoot, relative))),
|
|
279
|
+
])
|
|
272
280
|
),
|
|
273
281
|
};
|
|
274
282
|
}
|
|
@@ -997,6 +1005,7 @@ async function materializeAdminProfile({
|
|
|
997
1005
|
soul,
|
|
998
1006
|
slack = null,
|
|
999
1007
|
agentServiceEnv = {},
|
|
1008
|
+
runPhase = async (_phase, work) => work(),
|
|
1000
1009
|
}) {
|
|
1001
1010
|
const profileHome = profileContract.profileHome;
|
|
1002
1011
|
const profile = basename(profileHome);
|
|
@@ -1023,91 +1032,99 @@ async function materializeAdminProfile({
|
|
|
1023
1032
|
const layout = adminHomeLayout(profileHome);
|
|
1024
1033
|
// stageAdminProfile canonicalizes these two paths before staging, so the
|
|
1025
1034
|
// Admin home working copies must exist before the factory runs.
|
|
1026
|
-
|
|
1035
|
+
await runPhase("profile_settings_delivery", async () => {
|
|
1036
|
+
deliverAdminSettings(settingsRoot, layout);
|
|
1037
|
+
});
|
|
1027
1038
|
const serverProofs = {};
|
|
1028
|
-
const stageWithAuthority = async (stagedAuthority) =>
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1039
|
+
const stageWithAuthority = async (stagedAuthority) =>
|
|
1040
|
+
runPhase("profile_stage", async () => {
|
|
1041
|
+
const stage = await stageAdminProfile({
|
|
1042
|
+
mode: slack ? "direct_soul_native_slack" : "direct_soul",
|
|
1043
|
+
lifecycleAuthorized: slack ? true : undefined,
|
|
1044
|
+
ephemeralProductConfig: Boolean(slack),
|
|
1045
|
+
ephemeralSlackSecrets: Boolean(slack),
|
|
1046
|
+
profile,
|
|
1047
|
+
profilesRoot: PROFILES_ROOT,
|
|
1048
|
+
stageRoot,
|
|
1049
|
+
finalProfileRoot: profileHome,
|
|
1050
|
+
rootAuthPath,
|
|
1051
|
+
canonicalAdminConfigPath: layout.configPath,
|
|
1052
|
+
canonicalAdminStatePath: layout.statePath,
|
|
1053
|
+
productConfigPath,
|
|
1054
|
+
model: "gpt-5.6-sol",
|
|
1055
|
+
reasoningEffort: "high",
|
|
1056
|
+
soul: stagedAuthority,
|
|
1057
|
+
...(slack
|
|
1058
|
+
? {
|
|
1059
|
+
slackSecretFile: slack.secretFile,
|
|
1060
|
+
teamId: slack.teamId,
|
|
1061
|
+
primaryChannel: slack.primaryChannel,
|
|
1062
|
+
secondaryReadChannelIds: slack.secondaryReadChannelIds,
|
|
1063
|
+
allowedSlackUserIds: slack.allowedSlackUserIds,
|
|
1064
|
+
slackApp: slack.slackApp,
|
|
1065
|
+
}
|
|
1066
|
+
: {}),
|
|
1067
|
+
skillsRoot: join(ADMIN_INSTALL_ASSET_ROOT, "mcp", "skills"),
|
|
1068
|
+
installRoot: layout.adminRoot,
|
|
1069
|
+
adminHome: layout.adminHome,
|
|
1070
|
+
installAdmin: async () =>
|
|
1071
|
+
profilePackageReceipt(
|
|
1072
|
+
profileContract,
|
|
1073
|
+
"sellable-admin",
|
|
1074
|
+
"mcp",
|
|
1075
|
+
layout
|
|
1076
|
+
),
|
|
1077
|
+
installProduct: async () =>
|
|
1078
|
+
profilePackageReceipt(
|
|
1079
|
+
profileContract,
|
|
1080
|
+
"sellable",
|
|
1081
|
+
"mcp",
|
|
1082
|
+
layout,
|
|
1083
|
+
agentServiceEnv
|
|
1084
|
+
),
|
|
1085
|
+
verifyMcp: async ({ kind, profileRoot, config }) => {
|
|
1086
|
+
const alias = kind === "admin" ? "sellable-admin" : "sellable";
|
|
1087
|
+
const server = config.mcp_servers[alias];
|
|
1088
|
+
const inventoryProof =
|
|
1089
|
+
alias === "sellable" ? productMcpInventoryProofEnvironment() : null;
|
|
1090
|
+
let proof;
|
|
1091
|
+
try {
|
|
1092
|
+
proof = await inspectMcp({
|
|
1093
|
+
alias,
|
|
1094
|
+
command: server.command,
|
|
1095
|
+
args: server.args,
|
|
1096
|
+
env: {
|
|
1097
|
+
HOME: profileRoot,
|
|
1098
|
+
HERMES_HOME: profileRoot,
|
|
1099
|
+
...adminToolEnv(layout),
|
|
1100
|
+
...agentServiceEnv,
|
|
1101
|
+
SELLABLE_CONFIG_PATH: productConfigPath,
|
|
1102
|
+
...(server.env ?? {}),
|
|
1103
|
+
...(inventoryProof?.env ?? {}),
|
|
1104
|
+
},
|
|
1105
|
+
packageProof: profileContract.packages[alias].mcp,
|
|
1106
|
+
});
|
|
1107
|
+
} finally {
|
|
1108
|
+
if (inventoryProof) {
|
|
1109
|
+
rmSync(inventoryProof.root, { recursive: true, force: true });
|
|
1110
|
+
}
|
|
1053
1111
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
profilePackageReceipt(profileContract, "sellable-admin", "mcp", layout),
|
|
1060
|
-
installProduct: async () =>
|
|
1061
|
-
profilePackageReceipt(
|
|
1062
|
-
profileContract,
|
|
1063
|
-
"sellable",
|
|
1064
|
-
"mcp",
|
|
1065
|
-
layout,
|
|
1066
|
-
agentServiceEnv
|
|
1067
|
-
),
|
|
1068
|
-
verifyMcp: async ({ kind, profileRoot, config }) => {
|
|
1069
|
-
const alias = kind === "admin" ? "sellable-admin" : "sellable";
|
|
1070
|
-
const server = config.mcp_servers[alias];
|
|
1071
|
-
const inventoryProof =
|
|
1072
|
-
alias === "sellable" ? productMcpInventoryProofEnvironment() : null;
|
|
1073
|
-
let proof;
|
|
1074
|
-
try {
|
|
1075
|
-
proof = await inspectMcp({
|
|
1076
|
-
alias,
|
|
1077
|
-
command: server.command,
|
|
1078
|
-
args: server.args,
|
|
1079
|
-
env: {
|
|
1080
|
-
HOME: profileRoot,
|
|
1081
|
-
HERMES_HOME: profileRoot,
|
|
1082
|
-
...adminToolEnv(layout),
|
|
1083
|
-
...agentServiceEnv,
|
|
1084
|
-
SELLABLE_CONFIG_PATH: productConfigPath,
|
|
1085
|
-
...(server.env ?? {}),
|
|
1086
|
-
...(inventoryProof?.env ?? {}),
|
|
1087
|
-
},
|
|
1088
|
-
packageProof: profileContract.packages[alias].mcp,
|
|
1089
|
-
});
|
|
1090
|
-
} finally {
|
|
1091
|
-
if (inventoryProof) {
|
|
1092
|
-
rmSync(inventoryProof.root, { recursive: true, force: true });
|
|
1112
|
+
if (
|
|
1113
|
+
JSON.stringify(proof.tools) !==
|
|
1114
|
+
JSON.stringify(profileContract.tools[alias])
|
|
1115
|
+
) {
|
|
1116
|
+
throw new Error("admin_runtime_tool_inventory_mismatch");
|
|
1093
1117
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
},
|
|
1118
|
+
serverProofs[alias] = proof;
|
|
1119
|
+
return { ok: true, ...proof };
|
|
1120
|
+
},
|
|
1121
|
+
});
|
|
1122
|
+
if (slack) installWritableAdminSlackChannelPolicy(stageRoot, slack);
|
|
1123
|
+
installAdminModelConfig(stageRoot, modelConfig);
|
|
1124
|
+
installAdminHermesMemoryHook(stageRoot);
|
|
1125
|
+
stageAdminRuntimeTree(stageRoot);
|
|
1126
|
+
return stage;
|
|
1104
1127
|
});
|
|
1105
|
-
if (slack) installWritableAdminSlackChannelPolicy(stageRoot, slack);
|
|
1106
|
-
installAdminModelConfig(stageRoot, modelConfig);
|
|
1107
|
-
installAdminHermesMemoryHook(stageRoot);
|
|
1108
|
-
stageAdminRuntimeTree(stageRoot);
|
|
1109
|
-
return stage;
|
|
1110
|
-
};
|
|
1111
1128
|
|
|
1112
1129
|
let stagedAuthority = withProfileSoulLock(profile, readAuthoritativeSoul, {
|
|
1113
1130
|
dataRoot: DATA_ROOT,
|
|
@@ -1116,79 +1133,83 @@ async function materializeAdminProfile({
|
|
|
1116
1133
|
let promoted = null;
|
|
1117
1134
|
for (let attempt = 0; attempt < 2 && !promoted; attempt += 1) {
|
|
1118
1135
|
stage = await stageWithAuthority(stagedAuthority);
|
|
1119
|
-
const committed = await
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
stagedSoul
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
finalSoul
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1136
|
+
const committed = await runPhase("profile_live_merge_promote", async () =>
|
|
1137
|
+
withProfileSoulLock(
|
|
1138
|
+
profile,
|
|
1139
|
+
async () => {
|
|
1140
|
+
const finalAuthority = readAuthoritativeSoul();
|
|
1141
|
+
if (
|
|
1142
|
+
finalAuthority.sha256 !== stagedAuthority.sha256 ||
|
|
1143
|
+
!finalAuthority.bytes.equals(stagedAuthority.bytes)
|
|
1144
|
+
) {
|
|
1145
|
+
rmSync(stageRoot, { recursive: true, force: true });
|
|
1146
|
+
return { status: "RESTAGE", stagedAuthority: finalAuthority };
|
|
1147
|
+
}
|
|
1148
|
+
const liveSkillsIdentity = adminPreservedSkillsIdentity(profileHome);
|
|
1149
|
+
if (liveSkillsIdentity !== null) {
|
|
1150
|
+
replaceHermesState(
|
|
1151
|
+
join(profileHome, "skills"),
|
|
1152
|
+
join(stageRoot, "skills"),
|
|
1153
|
+
{
|
|
1154
|
+
preserveTargetTopLevelNames: [
|
|
1155
|
+
...ADMIN_SEALED_SKILL_DIRECTORIES,
|
|
1156
|
+
],
|
|
1157
|
+
}
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
mergeHermesState(profileHome, stageRoot, {
|
|
1161
|
+
skipSourceTopLevelNames:
|
|
1162
|
+
liveSkillsIdentity === null ? [] : ["skills"],
|
|
1163
|
+
});
|
|
1164
|
+
mergeAdminLiveSkillsConfig(profileHome, stageRoot);
|
|
1165
|
+
installAdminBrowserToolsetDefault(profileHome, stageRoot);
|
|
1166
|
+
if (
|
|
1167
|
+
liveSkillsIdentity !== null &&
|
|
1168
|
+
adminPreservedSkillsIdentity(stageRoot) !== liveSkillsIdentity
|
|
1169
|
+
) {
|
|
1170
|
+
rmSync(stageRoot, { recursive: true, force: true });
|
|
1171
|
+
return { status: "RESTAGE", stagedAuthority: finalAuthority };
|
|
1172
|
+
}
|
|
1173
|
+
const stagedSoulPath = join(stageRoot, "SOUL.md");
|
|
1174
|
+
const stagedSoul = lstatSync(stagedSoulPath);
|
|
1175
|
+
const stagedSoulBytes = readFileSync(stagedSoulPath);
|
|
1176
|
+
if (
|
|
1177
|
+
stagedSoul.isSymbolicLink() ||
|
|
1178
|
+
!stagedSoul.isFile() ||
|
|
1179
|
+
!stagedSoulBytes.equals(finalAuthority.bytes)
|
|
1180
|
+
) {
|
|
1181
|
+
throw new Error("admin_runtime_staged_soul_rejected");
|
|
1182
|
+
}
|
|
1183
|
+
const promotion = await promoteAdminProfileAtomically({
|
|
1184
|
+
profile,
|
|
1185
|
+
currentRoot: profileHome,
|
|
1186
|
+
stagedRoot: stageRoot,
|
|
1187
|
+
backupRoot,
|
|
1188
|
+
});
|
|
1189
|
+
const finalSoulPath = join(profileHome, "SOUL.md");
|
|
1190
|
+
const finalSoul = lstatSync(finalSoulPath);
|
|
1191
|
+
const finalSoulBytes = readFileSync(finalSoulPath);
|
|
1192
|
+
if (
|
|
1193
|
+
finalSoul.isSymbolicLink() ||
|
|
1194
|
+
!finalSoul.isFile() ||
|
|
1195
|
+
finalSoul.uid !== (process.getuid?.() ?? finalSoul.uid) ||
|
|
1196
|
+
finalSoul.gid !== (process.getgid?.() ?? finalSoul.gid) ||
|
|
1197
|
+
!finalSoulBytes.equals(finalAuthority.bytes)
|
|
1198
|
+
) {
|
|
1199
|
+
throw new Error("admin_runtime_live_soul_readback_rejected");
|
|
1200
|
+
}
|
|
1201
|
+
chmodSync(finalSoulPath, 0o600);
|
|
1202
|
+
if ((lstatSync(finalSoulPath).mode & 0o777) !== 0o600) {
|
|
1203
|
+
throw new Error("admin_runtime_live_soul_mode_rejected");
|
|
1204
|
+
}
|
|
1205
|
+
return {
|
|
1206
|
+
status: "PROMOTED",
|
|
1207
|
+
promotion,
|
|
1208
|
+
observedSoulRawSha256: sha256(finalSoulBytes),
|
|
1209
|
+
};
|
|
1210
|
+
},
|
|
1211
|
+
{ dataRoot: DATA_ROOT }
|
|
1212
|
+
)
|
|
1192
1213
|
);
|
|
1193
1214
|
if (committed.status === "PROMOTED") {
|
|
1194
1215
|
promoted = committed;
|
|
@@ -1201,20 +1222,27 @@ async function materializeAdminProfile({
|
|
|
1201
1222
|
}
|
|
1202
1223
|
// The installer bakes absolute paths into every generated wrapper, so bin/
|
|
1203
1224
|
// is generated once the promoted Admin home is at its final path.
|
|
1204
|
-
const adminHome
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1225
|
+
const { adminHome, memoryCron, opportunitySequencesCron } = await runPhase(
|
|
1226
|
+
"profile_crons",
|
|
1227
|
+
async () => {
|
|
1228
|
+
const adminHome = installAdminHome({ settingsRoot, layout });
|
|
1229
|
+
const memoryCron = await ensureHermesSnapshotCron({
|
|
1230
|
+
profileId: profile,
|
|
1231
|
+
profileRoot: profileHome,
|
|
1232
|
+
hermesRoot: DATA_ROOT,
|
|
1233
|
+
hermesCli: HERMES,
|
|
1234
|
+
});
|
|
1235
|
+
if (!memoryCron.ok) throw new Error(memoryCron.code);
|
|
1236
|
+
const opportunitySequencesCron =
|
|
1237
|
+
await ensureHermesOpportunitySequencesCron({
|
|
1238
|
+
profileId: profile,
|
|
1239
|
+
profileRoot: profileHome,
|
|
1240
|
+
hermesRoot: DATA_ROOT,
|
|
1241
|
+
statePath: layout.statePath,
|
|
1242
|
+
});
|
|
1243
|
+
return { adminHome, memoryCron, opportunitySequencesCron };
|
|
1244
|
+
}
|
|
1245
|
+
);
|
|
1218
1246
|
return {
|
|
1219
1247
|
stage,
|
|
1220
1248
|
promotion: promoted.promotion,
|
|
@@ -1453,8 +1481,7 @@ function operationSnapshotIdentity(claim, exchange) {
|
|
|
1453
1481
|
function adminServingObservation(activeLaunch, activeGateway, exchange) {
|
|
1454
1482
|
try {
|
|
1455
1483
|
const authority = activeLaunch?.claim?.runtimeAuthority;
|
|
1456
|
-
const running =
|
|
1457
|
-
activeGateway?.child?.exitCode === null && !activeGateway.child.killed;
|
|
1484
|
+
const running = nativeGatewayRunning(activeGateway);
|
|
1458
1485
|
const productMcp = activeLaunch?.profile?.servers?.find(
|
|
1459
1486
|
(server) => server.alias === "sellable"
|
|
1460
1487
|
);
|
|
@@ -2813,7 +2840,7 @@ async function startNativeGateway({ profile, claim }) {
|
|
|
2813
2840
|
}
|
|
2814
2841
|
|
|
2815
2842
|
async function stopNativeGateway(gateway) {
|
|
2816
|
-
if (!gateway
|
|
2843
|
+
if (!nativeGatewayRunning(gateway)) return;
|
|
2817
2844
|
gateway.child.kill("SIGTERM");
|
|
2818
2845
|
const stopped = await Promise.race([
|
|
2819
2846
|
gateway.closed.then(() => true),
|
|
@@ -2825,6 +2852,100 @@ async function stopNativeGateway(gateway) {
|
|
|
2825
2852
|
}
|
|
2826
2853
|
}
|
|
2827
2854
|
|
|
2855
|
+
async function recoverUnexpectedNativeGateway({
|
|
2856
|
+
activeLaunch,
|
|
2857
|
+
activeGateway,
|
|
2858
|
+
activeModelHealthReporter,
|
|
2859
|
+
identity,
|
|
2860
|
+
exchange,
|
|
2861
|
+
attempts,
|
|
2862
|
+
}) {
|
|
2863
|
+
const recovery = nextAdminNativeGatewayRecovery({ attempts });
|
|
2864
|
+
await activeModelHealthReporter?.stop();
|
|
2865
|
+
await stopNativeGateway(activeGateway);
|
|
2866
|
+
if (recovery.action === "EXHAUSTED") {
|
|
2867
|
+
process.stderr.write(
|
|
2868
|
+
`${JSON.stringify({
|
|
2869
|
+
ok: false,
|
|
2870
|
+
code: "admin_native_gateway_restart_exhausted",
|
|
2871
|
+
attempts: recovery.attempt,
|
|
2872
|
+
manualRetryAvailable: true,
|
|
2873
|
+
})}\n`
|
|
2874
|
+
);
|
|
2875
|
+
return {
|
|
2876
|
+
activeGateway: null,
|
|
2877
|
+
activeModelHealthReporter: null,
|
|
2878
|
+
attempts: recovery.attempt,
|
|
2879
|
+
exhausted: true,
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2882
|
+
let candidateGateway = null;
|
|
2883
|
+
try {
|
|
2884
|
+
process.stderr.write(
|
|
2885
|
+
`${JSON.stringify({
|
|
2886
|
+
ok: false,
|
|
2887
|
+
code: "admin_native_gateway_exit_detected",
|
|
2888
|
+
attempt: recovery.attempt,
|
|
2889
|
+
})}\n`
|
|
2890
|
+
);
|
|
2891
|
+
candidateGateway = await startNativeGateway(activeLaunch);
|
|
2892
|
+
if (!nativeGatewayRunning(candidateGateway)) {
|
|
2893
|
+
throw new Error("admin_native_gateway_local_restart_failed");
|
|
2894
|
+
}
|
|
2895
|
+
const nextModelHealthReporter = adminModelHealthReporter({
|
|
2896
|
+
identity,
|
|
2897
|
+
exchange,
|
|
2898
|
+
installed: activeLaunch.installed,
|
|
2899
|
+
claim: activeLaunch.claim,
|
|
2900
|
+
});
|
|
2901
|
+
try {
|
|
2902
|
+
await nextModelHealthReporter.start("gateway-recovered");
|
|
2903
|
+
} catch (error) {
|
|
2904
|
+
process.stderr.write(
|
|
2905
|
+
`${JSON.stringify({
|
|
2906
|
+
ok: false,
|
|
2907
|
+
code:
|
|
2908
|
+
error instanceof Error
|
|
2909
|
+
? error.message
|
|
2910
|
+
: "model_health_report_failed",
|
|
2911
|
+
})}\n`
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
process.stderr.write(
|
|
2915
|
+
`${JSON.stringify({
|
|
2916
|
+
ok: true,
|
|
2917
|
+
code: "admin_native_gateway_local_restart",
|
|
2918
|
+
attempt: recovery.attempt,
|
|
2919
|
+
processIdentity: `gateway-${candidateGateway.child.pid}-${activeLaunch.claim.operation.fence}`,
|
|
2920
|
+
})}\n`
|
|
2921
|
+
);
|
|
2922
|
+
return {
|
|
2923
|
+
activeGateway: candidateGateway,
|
|
2924
|
+
activeModelHealthReporter: nextModelHealthReporter,
|
|
2925
|
+
attempts: recovery.attempt,
|
|
2926
|
+
exhausted: false,
|
|
2927
|
+
};
|
|
2928
|
+
} catch (error) {
|
|
2929
|
+
await stopNativeGateway(candidateGateway);
|
|
2930
|
+
process.stderr.write(
|
|
2931
|
+
`${JSON.stringify({
|
|
2932
|
+
ok: false,
|
|
2933
|
+
code:
|
|
2934
|
+
error instanceof Error
|
|
2935
|
+
? error.message
|
|
2936
|
+
: "admin_native_gateway_local_restart_failed",
|
|
2937
|
+
attempt: recovery.attempt,
|
|
2938
|
+
})}\n`
|
|
2939
|
+
);
|
|
2940
|
+
return {
|
|
2941
|
+
activeGateway: null,
|
|
2942
|
+
activeModelHealthReporter: null,
|
|
2943
|
+
attempts: recovery.attempt,
|
|
2944
|
+
exhausted: false,
|
|
2945
|
+
};
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2828
2949
|
export async function runAdminCanary() {
|
|
2829
2950
|
const step = process.env.SELLABLE_AGENT_ADMIN_CANARY_STEP;
|
|
2830
2951
|
const recoverySecretPath =
|
|
@@ -3379,6 +3500,8 @@ async function runAdminProduction() {
|
|
|
3379
3500
|
process.once("SIGTERM", stop);
|
|
3380
3501
|
let operationHeartbeat = null;
|
|
3381
3502
|
let soulClaimTracker = null;
|
|
3503
|
+
let gatewayRestartAttempts = 0;
|
|
3504
|
+
let gatewayRestartExhaustionReported = false;
|
|
3382
3505
|
let failures = 0;
|
|
3383
3506
|
while (!controller.signal.aborted) {
|
|
3384
3507
|
let claimedOperation = null;
|
|
@@ -3454,6 +3577,23 @@ async function runAdminProduction() {
|
|
|
3454
3577
|
if (controller.signal.aborted) break;
|
|
3455
3578
|
if (!claim) {
|
|
3456
3579
|
if (activeLaunch) {
|
|
3580
|
+
if (
|
|
3581
|
+
!nativeGatewayRunning(activeGateway) &&
|
|
3582
|
+
!gatewayRestartExhaustionReported
|
|
3583
|
+
) {
|
|
3584
|
+
const recovery = await recoverUnexpectedNativeGateway({
|
|
3585
|
+
activeLaunch,
|
|
3586
|
+
activeGateway,
|
|
3587
|
+
activeModelHealthReporter,
|
|
3588
|
+
identity,
|
|
3589
|
+
exchange,
|
|
3590
|
+
attempts: gatewayRestartAttempts,
|
|
3591
|
+
});
|
|
3592
|
+
activeGateway = recovery.activeGateway;
|
|
3593
|
+
activeModelHealthReporter = recovery.activeModelHealthReporter;
|
|
3594
|
+
gatewayRestartAttempts = recovery.attempts;
|
|
3595
|
+
gatewayRestartExhaustionReported = recovery.exhausted;
|
|
3596
|
+
}
|
|
3457
3597
|
await reconcileHermesSnapshot({
|
|
3458
3598
|
profileId: activeLaunch.profile.profile,
|
|
3459
3599
|
profileRoot: activeLaunch.profile.profileHome,
|
|
@@ -3521,6 +3661,10 @@ async function runAdminProduction() {
|
|
|
3521
3661
|
exchange,
|
|
3522
3662
|
client,
|
|
3523
3663
|
});
|
|
3664
|
+
const phaseTelemetry = createAdminRuntimePhaseTelemetry({
|
|
3665
|
+
bootSessionId: BOOT_SESSION_ID,
|
|
3666
|
+
operationId: claim.operation.id,
|
|
3667
|
+
});
|
|
3524
3668
|
await activeModelHealthReporter?.stop();
|
|
3525
3669
|
activeModelHealthReporter = null;
|
|
3526
3670
|
await stopNativeGateway(activeGateway);
|
|
@@ -3594,31 +3738,42 @@ async function runAdminProduction() {
|
|
|
3594
3738
|
// Authority is fully decidable from the claim and durable settings
|
|
3595
3739
|
// metadata. Refuse it before consuming any lease secret so the failure
|
|
3596
3740
|
// can be reported as exact and retryable instead of uncertain.
|
|
3597
|
-
const secrets = await consumeRuntimeSecrets({
|
|
3598
|
-
claim,
|
|
3599
|
-
exchange,
|
|
3600
|
-
client,
|
|
3601
|
-
});
|
|
3602
|
-
claimSecretsConsumed = true;
|
|
3603
3741
|
const desiredLifecycle = desiredLifecycleForClaim(claim);
|
|
3604
|
-
const installed =
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3742
|
+
const { secrets, installed } = await phaseTelemetry.run(
|
|
3743
|
+
"runtime_secrets",
|
|
3744
|
+
async () => {
|
|
3745
|
+
const secrets = await consumeRuntimeSecrets({
|
|
3746
|
+
claim,
|
|
3747
|
+
exchange,
|
|
3748
|
+
client,
|
|
3749
|
+
});
|
|
3750
|
+
claimSecretsConsumed = true;
|
|
3751
|
+
const installed = installRuntimeSecrets({
|
|
3752
|
+
claim,
|
|
3753
|
+
secrets,
|
|
3754
|
+
desiredLifecycle,
|
|
3755
|
+
});
|
|
3756
|
+
return { secrets, installed };
|
|
3757
|
+
}
|
|
3758
|
+
);
|
|
3609
3759
|
const proofOperation = operation ?? {
|
|
3610
3760
|
runtimeGenerationId: claim.adminRuntimeGeneration.id,
|
|
3611
3761
|
generationFence: claim.adminRuntimeGeneration.fence,
|
|
3612
3762
|
profileContract,
|
|
3613
3763
|
};
|
|
3614
|
-
const memoryRestore = await
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3764
|
+
const memoryRestore = await phaseTelemetry.run(
|
|
3765
|
+
"snapshot_restore",
|
|
3766
|
+
async () =>
|
|
3767
|
+
restoreLatestHermesSnapshot({
|
|
3768
|
+
profileId: basename(profileContract.profileHome),
|
|
3769
|
+
profileRoot: profileContract.profileHome,
|
|
3770
|
+
hermesRoot: DATA_ROOT,
|
|
3771
|
+
hermesVersion: "0.20.0",
|
|
3772
|
+
fetchLatest: () => client.getMemorySnapshot(),
|
|
3773
|
+
})
|
|
3774
|
+
);
|
|
3621
3775
|
if (!memoryRestore.ok) throw new Error(memoryRestore.code);
|
|
3776
|
+
await operationHeartbeat.reattest();
|
|
3622
3777
|
const legacyLightfieldVenv = quarantineLegacyLightfieldVenv({
|
|
3623
3778
|
profileId: basename(profileContract.profileHome),
|
|
3624
3779
|
profileRoot: profileContract.profileHome,
|
|
@@ -3646,7 +3801,9 @@ async function runAdminProduction() {
|
|
|
3646
3801
|
soul: exactSoul(claim),
|
|
3647
3802
|
slack: nativeSlackProfileInput(claim, installed),
|
|
3648
3803
|
agentServiceEnv: adminAgentServiceEnvironment(claim),
|
|
3804
|
+
runPhase: phaseTelemetry.run,
|
|
3649
3805
|
});
|
|
3806
|
+
await operationHeartbeat.reattest();
|
|
3650
3807
|
const materializerReceipt = adminMaterializerReceipt(
|
|
3651
3808
|
profile.profile,
|
|
3652
3809
|
profile.profileHome
|
|
@@ -3655,21 +3812,25 @@ async function runAdminProduction() {
|
|
|
3655
3812
|
kind: "ADMIN",
|
|
3656
3813
|
claim,
|
|
3657
3814
|
});
|
|
3658
|
-
const reconciledDefaultProfile = await
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3815
|
+
const reconciledDefaultProfile = await phaseTelemetry.run(
|
|
3816
|
+
"default_profile_bundle",
|
|
3817
|
+
async () =>
|
|
3818
|
+
reconcileDefaultProfileBundle({
|
|
3819
|
+
activeProjection,
|
|
3820
|
+
materializerReceipt,
|
|
3821
|
+
profileRoot: profile.profileHome,
|
|
3822
|
+
bundleRoot: DEFAULT_PROFILE_BUNDLE_ROOT,
|
|
3823
|
+
dataRoot: DATA_ROOT,
|
|
3824
|
+
reattestAuthority: async () => {
|
|
3825
|
+
await operationHeartbeat.reattest();
|
|
3826
|
+
return activeProjection;
|
|
3827
|
+
},
|
|
3828
|
+
cronAdapter: adminDefaultProfileCronAdapter(
|
|
3829
|
+
profile.profile,
|
|
3830
|
+
profile.profileHome
|
|
3831
|
+
),
|
|
3832
|
+
})
|
|
3833
|
+
);
|
|
3673
3834
|
if (!reconciledDefaultProfile.ok)
|
|
3674
3835
|
throw new Error(reconciledDefaultProfile.code);
|
|
3675
3836
|
const defaultProfile = reconciledDefaultProfile;
|
|
@@ -3692,11 +3853,16 @@ async function runAdminProduction() {
|
|
|
3692
3853
|
profile.opportunitySequencesCron.status !== "REUSED";
|
|
3693
3854
|
if (materializerChanged || defaultProfile.changed)
|
|
3694
3855
|
markHermesSnapshotDirty(profile.profileHome);
|
|
3695
|
-
const memorySnapshot = await
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3856
|
+
const memorySnapshot = await phaseTelemetry.run(
|
|
3857
|
+
"memory_snapshot",
|
|
3858
|
+
async () =>
|
|
3859
|
+
flushHermesSnapshot({
|
|
3860
|
+
client,
|
|
3861
|
+
profileId: profile.profile,
|
|
3862
|
+
profileRoot: profile.profileHome,
|
|
3863
|
+
})
|
|
3864
|
+
);
|
|
3865
|
+
await operationHeartbeat.reattest();
|
|
3700
3866
|
const settings = settingsProof(
|
|
3701
3867
|
imported.receipt.generationRoot,
|
|
3702
3868
|
imported.receipt.settingsDigest
|
|
@@ -3719,10 +3885,14 @@ async function runAdminProduction() {
|
|
|
3719
3885
|
});
|
|
3720
3886
|
}
|
|
3721
3887
|
operationHeartbeat.assertHealthy();
|
|
3722
|
-
const slackProof =
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3888
|
+
const slackProof = await phaseTelemetry.run(
|
|
3889
|
+
"slack_serving_proof",
|
|
3890
|
+
async () =>
|
|
3891
|
+
desiredLifecycle === "ON"
|
|
3892
|
+
? proveAdminSlackServing(claim, secrets)
|
|
3893
|
+
: false
|
|
3894
|
+
);
|
|
3895
|
+
await operationHeartbeat.reattest();
|
|
3726
3896
|
const candidateLaunch =
|
|
3727
3897
|
desiredLifecycle === "ON"
|
|
3728
3898
|
? { profile, claim, installed, slackProof }
|
|
@@ -3730,7 +3900,11 @@ async function runAdminProduction() {
|
|
|
3730
3900
|
let candidateGateway = null;
|
|
3731
3901
|
try {
|
|
3732
3902
|
if (candidateLaunch) {
|
|
3733
|
-
candidateGateway = await
|
|
3903
|
+
candidateGateway = await phaseTelemetry.run(
|
|
3904
|
+
"native_gateway",
|
|
3905
|
+
async () => startNativeGateway(candidateLaunch)
|
|
3906
|
+
);
|
|
3907
|
+
await operationHeartbeat.reattest();
|
|
3734
3908
|
}
|
|
3735
3909
|
const nativeDefaultProfileProof = candidateGateway
|
|
3736
3910
|
? proveDefaultProfileNativeSkills({
|
|
@@ -3752,26 +3926,31 @@ async function runAdminProduction() {
|
|
|
3752
3926
|
activeProjection,
|
|
3753
3927
|
nativeDefaultProfileProof,
|
|
3754
3928
|
});
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
if (!servingObservation) {
|
|
3762
|
-
throw new Error("admin_serving_observation_unavailable");
|
|
3763
|
-
}
|
|
3764
|
-
const serving = await client.claimTick({
|
|
3765
|
-
leaseSeconds: 60,
|
|
3766
|
-
servingObservation,
|
|
3767
|
-
});
|
|
3768
|
-
if (serving?.servingObservationStatus !== "accepted") {
|
|
3769
|
-
throw new Error(
|
|
3770
|
-
`admin_serving_observation_rejected:${serving?.servingObservationCode ?? serving?.servingObservationStatus ?? "unknown"}`
|
|
3929
|
+
await phaseTelemetry.run("serving_observation", async () => {
|
|
3930
|
+
if (claim.runtimeAuthority && candidateLaunch && candidateGateway) {
|
|
3931
|
+
const servingObservation = adminServingObservation(
|
|
3932
|
+
candidateLaunch,
|
|
3933
|
+
candidateGateway,
|
|
3934
|
+
exchange
|
|
3771
3935
|
);
|
|
3936
|
+
if (!servingObservation) {
|
|
3937
|
+
throw new Error("admin_serving_observation_unavailable");
|
|
3938
|
+
}
|
|
3939
|
+
const serving = await client.claimTick({
|
|
3940
|
+
leaseSeconds: 60,
|
|
3941
|
+
servingObservation,
|
|
3942
|
+
});
|
|
3943
|
+
if (serving?.servingObservationStatus !== "accepted") {
|
|
3944
|
+
throw new Error(
|
|
3945
|
+
`admin_serving_observation_rejected:${serving?.servingObservationCode ?? serving?.servingObservationStatus ?? "unknown"}`
|
|
3946
|
+
);
|
|
3947
|
+
}
|
|
3772
3948
|
}
|
|
3773
|
-
}
|
|
3774
|
-
const completed = await
|
|
3949
|
+
});
|
|
3950
|
+
const completed = await phaseTelemetry.run(
|
|
3951
|
+
"operation_completion",
|
|
3952
|
+
async () => client.complete(receipt)
|
|
3953
|
+
);
|
|
3775
3954
|
if (completed?.accepted !== true) {
|
|
3776
3955
|
throw new Error("admin_runtime_completion_rejected");
|
|
3777
3956
|
}
|
|
@@ -3818,6 +3997,8 @@ async function runAdminProduction() {
|
|
|
3818
3997
|
}
|
|
3819
3998
|
await operationHeartbeat.stop();
|
|
3820
3999
|
operationHeartbeat = null;
|
|
4000
|
+
gatewayRestartAttempts = 0;
|
|
4001
|
+
gatewayRestartExhaustionReported = false;
|
|
3821
4002
|
failures = 0;
|
|
3822
4003
|
} catch (error) {
|
|
3823
4004
|
try {
|
|
@@ -8,7 +8,7 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright
|
|
|
8
8
|
|
|
9
9
|
ARG MCP_PACKAGE=@sellable/mcp@0.1.880
|
|
10
10
|
ARG MCP_INTEGRITY=sha512-+Fo8aQlYduLaQMQff+ZA25LeUhWZ7I7UH5DECpND84LQ+rHSjKeKFj4Zs93bVHXwQCIy2OLbr75y4f4+CyT57w==
|
|
11
|
-
ARG INSTALLER_PACKAGE=@sellable/install@0.1.
|
|
11
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.653-wip.phase189.20260814.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
|
|
@@ -105,7 +105,7 @@ const BOOT_SESSION_ID = randomUUID();
|
|
|
105
105
|
const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.20.0";
|
|
106
106
|
const INSTALLER_PACKAGE =
|
|
107
107
|
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ??
|
|
108
|
-
"@sellable/install@0.1.
|
|
108
|
+
"@sellable/install@0.1.653-wip.phase189.20260814.1";
|
|
109
109
|
const MCP_PACKAGE =
|
|
110
110
|
process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.880";
|
|
111
111
|
const DEFAULT_PROFILE_BUNDLE_ROOT =
|
|
@@ -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.653-wip.phase189.20260814.1";
|
|
42
42
|
const MCP_PACKAGE = "@sellable/mcp@0.1.880";
|
|
43
43
|
const HERMES_VERSION = "0.18.0";
|
|
44
44
|
const SHA256 = /^[a-f0-9]{64}$/;
|
|
@@ -1547,7 +1547,7 @@ export function compileClaimToProfileDesired(activeClaim, config) {
|
|
|
1547
1547
|
pinned.hermesCli !== "hermes" ||
|
|
1548
1548
|
pinned.hermesVersion !== "0.18.0" ||
|
|
1549
1549
|
pinned.installerPackage !==
|
|
1550
|
-
"@sellable/install@0.1.
|
|
1550
|
+
"@sellable/install@0.1.653-wip.phase189.20260814.1" ||
|
|
1551
1551
|
pinned.mcpPackage !== "@sellable/mcp@0.1.880" ||
|
|
1552
1552
|
!Array.isArray(policy.toolInclude) ||
|
|
1553
1553
|
policy.toolInclude.length === 0 ||
|
|
@@ -283,7 +283,7 @@ function validateDesired(desired) {
|
|
|
283
283
|
desired.hermesCli !== "hermes" ||
|
|
284
284
|
desired.hermesVersion !== "0.18.0" ||
|
|
285
285
|
desired.installerPackage !==
|
|
286
|
-
"@sellable/install@0.1.
|
|
286
|
+
"@sellable/install@0.1.653-wip.phase189.20260814.1" ||
|
|
287
287
|
desired.mcpPackage !== "@sellable/mcp@0.1.880" ||
|
|
288
288
|
!Array.isArray(desired.toolInclude) ||
|
|
289
289
|
desired.toolInclude.length === 0 ||
|
|
@@ -26,7 +26,7 @@ import { deriveContainedProfileId } from "./profile-materializer.mjs";
|
|
|
26
26
|
|
|
27
27
|
export const PROVISIONING_ACTION = "PROVISION_HERMES_PROFILE";
|
|
28
28
|
export const PINNED_INSTALL_PACKAGE =
|
|
29
|
-
"@sellable/install@0.1.
|
|
29
|
+
"@sellable/install@0.1.653-wip.phase189.20260814.1";
|
|
30
30
|
export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.880";
|
|
31
31
|
const PINNED_INSTALL_VERSION = PINNED_INSTALL_PACKAGE.slice(
|
|
32
32
|
PINNED_INSTALL_PACKAGE.lastIndexOf("@") + 1
|