@sellable/install 0.1.608-wip.phase188.20260812.1 → 0.1.608
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 +0 -24
- package/lib/sellable-agent/external-runtime-builder.mjs +1 -1
- package/lib/sellable-agent/fly-customer-image/Dockerfile +3 -3
- package/lib/sellable-agent/fly-customer-image/customer-runtime.mjs +96 -454
- package/lib/sellable-agent/fly-runtime-identity.mjs +1 -10
- 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/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.608
|
|
7
|
-
ARG MCP_PACKAGE=@sellable/mcp@0.1.
|
|
6
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.608
|
|
7
|
+
ARG MCP_PACKAGE=@sellable/mcp@0.1.865
|
|
8
8
|
ARG INSTALLER_INTEGRITY
|
|
9
|
-
ARG MCP_INTEGRITY=sha512-
|
|
9
|
+
ARG MCP_INTEGRITY=sha512-282pkYFADFrrIXyQcdUpQwakXiwe/AFUDpDZkTh29CFcGHnN7dvC0MCvaU4LffXA+ZuKCpuf02uxCLGiIEOjbw==
|
|
10
10
|
|
|
11
|
-
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.608
|
|
12
|
-
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.
|
|
11
|
+
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.608" \
|
|
12
|
+
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.865" \
|
|
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.608-wip.phase188.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.608-wip.
|
|
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.608-wip.hermes-desktop.20260811160530' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.865') 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.608
|
|
28
|
+
&& node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.608', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.865', 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.608
|
|
6
|
+
`@sellable/install@0.1.608` 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.608-wip.
|
|
12
|
+
--tag sellable-agent-host:0.1.608-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`.
|
|
@@ -80,30 +80,6 @@ export function validateAdminRuntimeTuple(tuple) {
|
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
if (tuple.profileContractTemplate !== undefined) {
|
|
84
|
-
const templatePackages = tuple.profileContractTemplate?.packages;
|
|
85
|
-
const profilePackageByTupleName = {
|
|
86
|
-
"@sellable/mcp": templatePackages?.sellable?.mcp,
|
|
87
|
-
"@sellable/install": templatePackages?.sellable?.install,
|
|
88
|
-
"@sellable/admin-mcp": templatePackages?.["sellable-admin"]?.mcp,
|
|
89
|
-
"@sellable/admin-install": templatePackages?.["sellable-admin"]?.install,
|
|
90
|
-
};
|
|
91
|
-
for (const name of ADMIN_RUNTIME_TUPLE_MEMBERS) {
|
|
92
|
-
const tuplePackage = tuple.packages[name];
|
|
93
|
-
const profilePackage = profilePackageByTupleName[name];
|
|
94
|
-
if (
|
|
95
|
-
profilePackage?.name !== tuplePackage.name ||
|
|
96
|
-
profilePackage?.version !== tuplePackage.version ||
|
|
97
|
-
profilePackage?.integrity !== tuplePackage.integrity
|
|
98
|
-
) {
|
|
99
|
-
return {
|
|
100
|
-
ok: false,
|
|
101
|
-
code: "admin_runtime_tuple_profile_package_mismatch",
|
|
102
|
-
packageName: name,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
83
|
if (
|
|
108
84
|
!SHA256.test(tuple.lockedTreeDigest ?? "") ||
|
|
109
85
|
!SHA256.test(tuple.artifactDigest ?? "")
|
|
@@ -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.865";
|
|
32
32
|
const SAFE_COMPONENT = /^[A-Za-z0-9@._+-]+$/;
|
|
33
33
|
const HERMES_EXCLUDES = new Set([".git", ".playwright", "node_modules"]);
|
|
34
34
|
|
|
@@ -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.608
|
|
7
|
+
ARG MCP_PACKAGE=@sellable/mcp@0.1.865
|
|
8
|
+
ARG MCP_INTEGRITY=sha512-282pkYFADFrrIXyQcdUpQwakXiwe/AFUDpDZkTh29CFcGHnN7dvC0MCvaU4LffXA+ZuKCpuf02uxCLGiIEOjbw==
|
|
9
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.608
|
|
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.608
|
|
99
|
+
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.608";
|
|
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.865";
|
|
102
102
|
|
|
103
103
|
const sha256 = (value) => createHash("sha256").update(value).digest("hex");
|
|
104
104
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -259,10 +259,6 @@ function compileDesired(claim, trustRootPem) {
|
|
|
259
259
|
if (
|
|
260
260
|
!operation ||
|
|
261
261
|
operation.targetRevisionId !== claim?.revision?.id ||
|
|
262
|
-
!Number.isSafeInteger(operation.configGeneration) ||
|
|
263
|
-
operation.configGeneration < 1 ||
|
|
264
|
-
!Number.isSafeInteger(operation.lifecycleGeneration) ||
|
|
265
|
-
operation.lifecycleGeneration < 0 ||
|
|
266
262
|
!slack ||
|
|
267
263
|
!service ||
|
|
268
264
|
!model ||
|
|
@@ -2141,83 +2137,26 @@ async function startNativeGateway({ desired, runtimeConfig, secrets }) {
|
|
|
2141
2137
|
|
|
2142
2138
|
export const API_SERVER_READINESS_DEADLINE_MS = 240_000;
|
|
2143
2139
|
const API_SERVER_READINESS_RETRY_MS = 1_000;
|
|
2144
|
-
export const NATIVE_GATEWAY_SERVING_PROOF_INTERVAL_MS = 30_000;
|
|
2145
|
-
export const NATIVE_GATEWAY_RECOVERY_COOLDOWN_MS = 60_000;
|
|
2146
|
-
export const NATIVE_GATEWAY_RECOVERY_MAX_ATTEMPTS = 3;
|
|
2147
|
-
const NATIVE_GATEWAY_RECOVERY_BUDGET_MS = 30_000;
|
|
2148
|
-
const NATIVE_GATEWAY_RECOVERY_BACKOFF_MS = 500;
|
|
2149
|
-
const NATIVE_GATEWAY_RECOVERY_BACKOFF_CAP_MS = 2_000;
|
|
2150
|
-
|
|
2151
|
-
const NATIVE_GATEWAY_SERVING_REASONS = new Set([
|
|
2152
|
-
"GATEWAY_STOPPED",
|
|
2153
|
-
"SLACK_DISCONNECTED",
|
|
2154
|
-
"API_UNAVAILABLE",
|
|
2155
|
-
"AUTH_UNAVAILABLE",
|
|
2156
|
-
"CAPABILITIES_UNAVAILABLE",
|
|
2157
|
-
"IDENTITY_MISMATCH",
|
|
2158
|
-
"RECOVERY_FAILED",
|
|
2159
|
-
"RECOVERY_IN_PROGRESS",
|
|
2160
|
-
]);
|
|
2161
|
-
|
|
2162
|
-
function nativeGatewayServingFailure(reasonCode) {
|
|
2163
|
-
const error = new Error(
|
|
2164
|
-
`customer_native_gateway_serving_rejected:${reasonCode}`
|
|
2165
|
-
);
|
|
2166
|
-
error.reasonCode = reasonCode;
|
|
2167
|
-
return error;
|
|
2168
|
-
}
|
|
2169
|
-
|
|
2170
|
-
function nativeGatewayServingFailureReason(error) {
|
|
2171
|
-
return NATIVE_GATEWAY_SERVING_REASONS.has(error?.reasonCode)
|
|
2172
|
-
? error.reasonCode
|
|
2173
|
-
: "RECOVERY_FAILED";
|
|
2174
|
-
}
|
|
2175
|
-
|
|
2176
|
-
function throwIfAborted(signal) {
|
|
2177
|
-
if (!signal?.aborted) return;
|
|
2178
|
-
throw signal.reason instanceof Error
|
|
2179
|
-
? signal.reason
|
|
2180
|
-
: new DOMException("Aborted", "AbortError");
|
|
2181
|
-
}
|
|
2182
|
-
|
|
2183
|
-
function nativeGatewayRuntimeStatusReason(
|
|
2184
|
-
status,
|
|
2185
|
-
{ expectedPid, expectedHome }
|
|
2186
|
-
) {
|
|
2187
|
-
if (!status) return "API_UNAVAILABLE";
|
|
2188
|
-
if (
|
|
2189
|
-
!Number.isSafeInteger(expectedPid) ||
|
|
2190
|
-
expectedPid <= 1 ||
|
|
2191
|
-
status.pid !== expectedPid ||
|
|
2192
|
-
status.kind !== "hermes-gateway" ||
|
|
2193
|
-
!Array.isArray(status.argv) ||
|
|
2194
|
-
!status.argv.includes("gateway") ||
|
|
2195
|
-
!status.argv.includes("run") ||
|
|
2196
|
-
!Number.isSafeInteger(status.start_time) ||
|
|
2197
|
-
status.start_time <= 0 ||
|
|
2198
|
-
status.hermes_home !== expectedHome
|
|
2199
|
-
) {
|
|
2200
|
-
return "IDENTITY_MISMATCH";
|
|
2201
|
-
}
|
|
2202
|
-
if (status.gateway_state !== "running") return "GATEWAY_STOPPED";
|
|
2203
|
-
if (status.platforms?.slack?.state !== "connected") {
|
|
2204
|
-
return "SLACK_DISCONNECTED";
|
|
2205
|
-
}
|
|
2206
|
-
if (status.platforms?.api_server?.state !== "connected") {
|
|
2207
|
-
return "API_UNAVAILABLE";
|
|
2208
|
-
}
|
|
2209
|
-
return null;
|
|
2210
|
-
}
|
|
2211
2140
|
|
|
2212
2141
|
export function nativeGatewayRuntimeStatusAccepted(
|
|
2213
2142
|
status,
|
|
2214
2143
|
{ expectedPid, expectedHome }
|
|
2215
2144
|
) {
|
|
2216
|
-
return (
|
|
2217
|
-
|
|
2218
|
-
expectedPid
|
|
2219
|
-
|
|
2220
|
-
|
|
2145
|
+
return Boolean(
|
|
2146
|
+
status &&
|
|
2147
|
+
Number.isSafeInteger(expectedPid) &&
|
|
2148
|
+
expectedPid > 1 &&
|
|
2149
|
+
status.pid === expectedPid &&
|
|
2150
|
+
status.kind === "hermes-gateway" &&
|
|
2151
|
+
Array.isArray(status.argv) &&
|
|
2152
|
+
status.argv.includes("gateway") &&
|
|
2153
|
+
status.argv.includes("run") &&
|
|
2154
|
+
Number.isSafeInteger(status.start_time) &&
|
|
2155
|
+
status.start_time > 0 &&
|
|
2156
|
+
status.hermes_home === expectedHome &&
|
|
2157
|
+
status.gateway_state === "running" &&
|
|
2158
|
+
status.platforms?.slack?.state === "connected" &&
|
|
2159
|
+
status.platforms?.api_server?.state === "connected"
|
|
2221
2160
|
);
|
|
2222
2161
|
}
|
|
2223
2162
|
|
|
@@ -2240,140 +2179,86 @@ function readNativeGatewayRuntimeStatus(runtimeHome) {
|
|
|
2240
2179
|
}
|
|
2241
2180
|
}
|
|
2242
2181
|
|
|
2243
|
-
|
|
2182
|
+
async function proveApiServerReadiness({
|
|
2244
2183
|
desired,
|
|
2245
2184
|
apiServerKey,
|
|
2246
2185
|
gateway,
|
|
2247
2186
|
runtimeConfig,
|
|
2248
|
-
signal,
|
|
2249
|
-
fetchImpl = fetch,
|
|
2250
|
-
readStatus = readNativeGatewayRuntimeStatus,
|
|
2251
2187
|
}) {
|
|
2252
2188
|
const origin = "http://127.0.0.1:8642";
|
|
2253
|
-
const requestSignal = () =>
|
|
2254
|
-
signal
|
|
2255
|
-
? AbortSignal.any([signal, AbortSignal.timeout(5_000)])
|
|
2256
|
-
: AbortSignal.timeout(5_000);
|
|
2257
2189
|
const request = (authorization) =>
|
|
2258
|
-
|
|
2190
|
+
fetch(`${origin}/v1/capabilities`, {
|
|
2259
2191
|
headers: authorization ? { authorization } : {},
|
|
2260
|
-
signal:
|
|
2192
|
+
signal: AbortSignal.timeout(5_000),
|
|
2261
2193
|
});
|
|
2262
|
-
throwIfAborted(signal);
|
|
2263
|
-
const gatewayReadback = gateway?.readback();
|
|
2264
|
-
if (
|
|
2265
|
-
!gatewayReadback?.running ||
|
|
2266
|
-
!Number.isSafeInteger(gateway?.child?.pid) ||
|
|
2267
|
-
gateway.child.pid <= 1
|
|
2268
|
-
) {
|
|
2269
|
-
throw nativeGatewayServingFailure("GATEWAY_STOPPED");
|
|
2270
|
-
}
|
|
2271
|
-
const statusReason = nativeGatewayRuntimeStatusReason(
|
|
2272
|
-
readStatus(runtimeConfig.runtimeHome),
|
|
2273
|
-
{
|
|
2274
|
-
expectedPid: gateway.child.pid,
|
|
2275
|
-
expectedHome: runtimeConfig.runtimeHome,
|
|
2276
|
-
}
|
|
2277
|
-
);
|
|
2278
|
-
if (statusReason) throw nativeGatewayServingFailure(statusReason);
|
|
2279
|
-
let health;
|
|
2280
|
-
let missing;
|
|
2281
|
-
let wrong;
|
|
2282
|
-
let correct;
|
|
2283
|
-
try {
|
|
2284
|
-
[health, missing, wrong, correct] = await Promise.all([
|
|
2285
|
-
fetchImpl(`${origin}/health`, { signal: requestSignal() }),
|
|
2286
|
-
request(null),
|
|
2287
|
-
request("Bearer phase166-readiness-intentionally-wrong"),
|
|
2288
|
-
request(`Bearer ${apiServerKey}`),
|
|
2289
|
-
]);
|
|
2290
|
-
} catch (error) {
|
|
2291
|
-
throwIfAborted(signal);
|
|
2292
|
-
throw nativeGatewayServingFailure("API_UNAVAILABLE");
|
|
2293
|
-
}
|
|
2294
|
-
throwIfAborted(signal);
|
|
2295
|
-
if (health.status !== 200) {
|
|
2296
|
-
throw nativeGatewayServingFailure("API_UNAVAILABLE");
|
|
2297
|
-
}
|
|
2298
|
-
if (
|
|
2299
|
-
missing.status !== 401 ||
|
|
2300
|
-
wrong.status !== 401 ||
|
|
2301
|
-
correct.status !== 200
|
|
2302
|
-
) {
|
|
2303
|
-
throw nativeGatewayServingFailure("AUTH_UNAVAILABLE");
|
|
2304
|
-
}
|
|
2305
|
-
let capabilities;
|
|
2306
|
-
try {
|
|
2307
|
-
capabilities = await correct.json();
|
|
2308
|
-
} catch {
|
|
2309
|
-
throw nativeGatewayServingFailure("CAPABILITIES_UNAVAILABLE");
|
|
2310
|
-
}
|
|
2311
|
-
const finalStatusReason = nativeGatewayRuntimeStatusReason(
|
|
2312
|
-
readStatus(runtimeConfig.runtimeHome),
|
|
2313
|
-
{
|
|
2314
|
-
expectedPid: gateway.child.pid,
|
|
2315
|
-
expectedHome: runtimeConfig.runtimeHome,
|
|
2316
|
-
}
|
|
2317
|
-
);
|
|
2318
|
-
if (finalStatusReason) {
|
|
2319
|
-
throw nativeGatewayServingFailure(finalStatusReason);
|
|
2320
|
-
}
|
|
2321
|
-
if (
|
|
2322
|
-
capabilities?.auth?.required !== true ||
|
|
2323
|
-
capabilities?.features?.run_submission !== true ||
|
|
2324
|
-
capabilities?.features?.run_events_sse !== true
|
|
2325
|
-
) {
|
|
2326
|
-
throw nativeGatewayServingFailure("CAPABILITIES_UNAVAILABLE");
|
|
2327
|
-
}
|
|
2328
|
-
return Object.freeze({
|
|
2329
|
-
host: "127.0.0.1",
|
|
2330
|
-
port: 8642,
|
|
2331
|
-
cors: false,
|
|
2332
|
-
keyGeneration: desired.apiServerKeyGeneration,
|
|
2333
|
-
keyFingerprint: desired.apiServerKeyFingerprint,
|
|
2334
|
-
authStatusDigest: sha256(
|
|
2335
|
-
JSON.stringify({
|
|
2336
|
-
health: health.status,
|
|
2337
|
-
missing: missing.status,
|
|
2338
|
-
wrong: wrong.status,
|
|
2339
|
-
correct: correct.status,
|
|
2340
|
-
})
|
|
2341
|
-
),
|
|
2342
|
-
capabilitiesDigest: sha256(JSON.stringify(capabilities)),
|
|
2343
|
-
fixtureDigest: HERMES_RUNS_FIXTURE_IDENTITY.fixtureDigest,
|
|
2344
|
-
relayCapability: "fixture_bound_runs_relay_v1",
|
|
2345
|
-
runtimeGeneration: desired.runtimeGeneration,
|
|
2346
|
-
gatewayPid: gateway.child.pid,
|
|
2347
|
-
});
|
|
2348
|
-
}
|
|
2349
|
-
|
|
2350
|
-
async function proveApiServerReadiness({
|
|
2351
|
-
desired,
|
|
2352
|
-
apiServerKey,
|
|
2353
|
-
gateway,
|
|
2354
|
-
runtimeConfig,
|
|
2355
|
-
signal,
|
|
2356
|
-
}) {
|
|
2357
2194
|
const deadlineAt = Date.now() + API_SERVER_READINESS_DEADLINE_MS;
|
|
2358
2195
|
while (Date.now() < deadlineAt) {
|
|
2196
|
+
const gatewayReadback = gateway?.readback();
|
|
2197
|
+
if (!gatewayReadback?.running) {
|
|
2198
|
+
throw new Error(
|
|
2199
|
+
`customer_native_gateway_start_rejected:${redactRuntimeDiagnostic(
|
|
2200
|
+
gatewayReadback?.output ?? "candidate exited before readiness"
|
|
2201
|
+
)}`
|
|
2202
|
+
);
|
|
2203
|
+
}
|
|
2204
|
+
const candidateStatus = readNativeGatewayRuntimeStatus(
|
|
2205
|
+
runtimeConfig.runtimeHome
|
|
2206
|
+
);
|
|
2207
|
+
if (
|
|
2208
|
+
!nativeGatewayRuntimeStatusAccepted(candidateStatus, {
|
|
2209
|
+
expectedPid: gateway.child.pid,
|
|
2210
|
+
expectedHome: runtimeConfig.runtimeHome,
|
|
2211
|
+
})
|
|
2212
|
+
) {
|
|
2213
|
+
await sleep(API_SERVER_READINESS_RETRY_MS);
|
|
2214
|
+
continue;
|
|
2215
|
+
}
|
|
2359
2216
|
try {
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2217
|
+
const [health, missing, wrong, correct] = await Promise.all([
|
|
2218
|
+
fetch(`${origin}/health`, { signal: AbortSignal.timeout(5_000) }),
|
|
2219
|
+
request(null),
|
|
2220
|
+
request("Bearer phase166-readiness-intentionally-wrong"),
|
|
2221
|
+
request(`Bearer ${apiServerKey}`),
|
|
2222
|
+
]);
|
|
2223
|
+
const capabilities = correct.ok ? await correct.json() : null;
|
|
2224
|
+
if (
|
|
2225
|
+
health.status === 200 &&
|
|
2226
|
+
missing.status === 401 &&
|
|
2227
|
+
wrong.status === 401 &&
|
|
2228
|
+
correct.status === 200 &&
|
|
2229
|
+
nativeGatewayRuntimeStatusAccepted(
|
|
2230
|
+
readNativeGatewayRuntimeStatus(runtimeConfig.runtimeHome),
|
|
2231
|
+
{
|
|
2232
|
+
expectedPid: gateway.child.pid,
|
|
2233
|
+
expectedHome: runtimeConfig.runtimeHome,
|
|
2234
|
+
}
|
|
2235
|
+
) &&
|
|
2236
|
+
capabilities?.auth?.required === true &&
|
|
2237
|
+
capabilities?.features?.run_submission === true &&
|
|
2238
|
+
capabilities?.features?.run_events_sse === true
|
|
2239
|
+
) {
|
|
2240
|
+
return Object.freeze({
|
|
2241
|
+
host: "127.0.0.1",
|
|
2242
|
+
port: 8642,
|
|
2243
|
+
cors: false,
|
|
2244
|
+
keyGeneration: desired.apiServerKeyGeneration,
|
|
2245
|
+
keyFingerprint: desired.apiServerKeyFingerprint,
|
|
2246
|
+
authStatusDigest: sha256(
|
|
2247
|
+
JSON.stringify({
|
|
2248
|
+
health: health.status,
|
|
2249
|
+
missing: missing.status,
|
|
2250
|
+
wrong: wrong.status,
|
|
2251
|
+
correct: correct.status,
|
|
2252
|
+
})
|
|
2253
|
+
),
|
|
2254
|
+
capabilitiesDigest: sha256(JSON.stringify(capabilities)),
|
|
2255
|
+
fixtureDigest: HERMES_RUNS_FIXTURE_IDENTITY.fixtureDigest,
|
|
2256
|
+
relayCapability: "fixture_bound_runs_relay_v1",
|
|
2257
|
+
runtimeGeneration: desired.runtimeGeneration,
|
|
2258
|
+
gatewayPid: gateway.child.pid,
|
|
2259
|
+
});
|
|
2376
2260
|
}
|
|
2261
|
+
} catch {
|
|
2377
2262
|
// Hermes can hold its singleton lock before the local HTTP listener is
|
|
2378
2263
|
// accepting connections. Keep the lifecycle claim fenced while the
|
|
2379
2264
|
// bounded readiness window converges instead of replacing it again.
|
|
@@ -2390,12 +2275,10 @@ export async function startProvenNativeGateway({
|
|
|
2390
2275
|
startGateway = startNativeGateway,
|
|
2391
2276
|
proveReadiness = proveApiServerReadiness,
|
|
2392
2277
|
stopGateway = stopNativeGateway,
|
|
2393
|
-
signal,
|
|
2394
2278
|
}) {
|
|
2395
2279
|
let gateway = null;
|
|
2396
2280
|
try {
|
|
2397
|
-
gateway = await startGateway({ desired, runtimeConfig, secrets
|
|
2398
|
-
throwIfAborted(signal);
|
|
2281
|
+
gateway = await startGateway({ desired, runtimeConfig, secrets });
|
|
2399
2282
|
const gatewayReadback = gateway.readback();
|
|
2400
2283
|
if (!gatewayReadback.running) {
|
|
2401
2284
|
throw new Error("customer_native_gateway_readback_rejected");
|
|
@@ -2405,7 +2288,6 @@ export async function startProvenNativeGateway({
|
|
|
2405
2288
|
apiServerKey: secrets.hermesApiServer,
|
|
2406
2289
|
gateway,
|
|
2407
2290
|
runtimeConfig,
|
|
2408
|
-
signal,
|
|
2409
2291
|
});
|
|
2410
2292
|
return { gateway, gatewayReadback, apiServerReadiness };
|
|
2411
2293
|
} catch (error) {
|
|
@@ -2428,230 +2310,6 @@ export async function startProvenNativeGateway({
|
|
|
2428
2310
|
}
|
|
2429
2311
|
}
|
|
2430
2312
|
|
|
2431
|
-
function nativeGatewayServingObservation({
|
|
2432
|
-
runtime,
|
|
2433
|
-
bootSessionId,
|
|
2434
|
-
flyGeneration,
|
|
2435
|
-
state,
|
|
2436
|
-
reasonCode,
|
|
2437
|
-
checkedAt,
|
|
2438
|
-
}) {
|
|
2439
|
-
const gatewayPid = runtime.gateway?.child?.pid;
|
|
2440
|
-
const desired = runtime.desired;
|
|
2441
|
-
const authority = runtime.servingAuthority;
|
|
2442
|
-
if (
|
|
2443
|
-
!Number.isSafeInteger(gatewayPid) ||
|
|
2444
|
-
gatewayPid <= 1 ||
|
|
2445
|
-
authority?.runtimeId !== desired.runtimeId ||
|
|
2446
|
-
authority?.profileId !== desired.profileId ||
|
|
2447
|
-
!Number.isSafeInteger(authority?.runtimeGeneration) ||
|
|
2448
|
-
authority.runtimeGeneration < 1 ||
|
|
2449
|
-
!Number.isSafeInteger(authority?.configGeneration) ||
|
|
2450
|
-
authority.configGeneration < 1 ||
|
|
2451
|
-
!Number.isSafeInteger(authority?.lifecycleGeneration) ||
|
|
2452
|
-
authority.lifecycleGeneration < 0
|
|
2453
|
-
) {
|
|
2454
|
-
throw new Error("customer_native_gateway_observation_rejected");
|
|
2455
|
-
}
|
|
2456
|
-
return Object.freeze({
|
|
2457
|
-
version: 1,
|
|
2458
|
-
runtimeId: authority.runtimeId,
|
|
2459
|
-
flyGeneration,
|
|
2460
|
-
runtimeGeneration: authority.runtimeGeneration,
|
|
2461
|
-
configGeneration: authority.configGeneration,
|
|
2462
|
-
lifecycleGeneration: authority.lifecycleGeneration,
|
|
2463
|
-
bootSessionId,
|
|
2464
|
-
profileId: desired.profileId,
|
|
2465
|
-
hermesHomeId: flyRuntimeDigest({
|
|
2466
|
-
schemaVersion: "sellable-agent-hermes-home/v1",
|
|
2467
|
-
root: DATA_ROOT,
|
|
2468
|
-
profileId: desired.profileId,
|
|
2469
|
-
}),
|
|
2470
|
-
gatewayPid,
|
|
2471
|
-
checkedAt: new Date(checkedAt).toISOString(),
|
|
2472
|
-
state,
|
|
2473
|
-
reasonCode,
|
|
2474
|
-
});
|
|
2475
|
-
}
|
|
2476
|
-
|
|
2477
|
-
/**
|
|
2478
|
-
* One in-process, single-flight owner beside the existing signed claim loop.
|
|
2479
|
-
* It creates no timer: the lifecycle loop decides when to call poll().
|
|
2480
|
-
*/
|
|
2481
|
-
export function createNativeGatewayServingSupervisor({
|
|
2482
|
-
runtime,
|
|
2483
|
-
getActiveRuntime,
|
|
2484
|
-
bootSessionId,
|
|
2485
|
-
flyGeneration,
|
|
2486
|
-
now = Date.now,
|
|
2487
|
-
random = Math.random,
|
|
2488
|
-
pause = (ms, signal) => abortablePause(ms, signal, sleep),
|
|
2489
|
-
proveServing = proveNativeGatewayServing,
|
|
2490
|
-
startProvenGateway = startProvenNativeGateway,
|
|
2491
|
-
stopGateway = stopNativeGateway,
|
|
2492
|
-
}) {
|
|
2493
|
-
let stopped = false;
|
|
2494
|
-
let episode = null;
|
|
2495
|
-
let episodeController = null;
|
|
2496
|
-
let nextEligibleAt = now() + NATIVE_GATEWAY_SERVING_PROOF_INTERVAL_MS;
|
|
2497
|
-
let current = null;
|
|
2498
|
-
const outgoing = [];
|
|
2499
|
-
|
|
2500
|
-
const authorityCurrent = () =>
|
|
2501
|
-
!stopped &&
|
|
2502
|
-
getActiveRuntime() === runtime &&
|
|
2503
|
-
runtime.servingAuthority?.flyGeneration === flyGeneration;
|
|
2504
|
-
|
|
2505
|
-
const publish = (state, reasonCode) => {
|
|
2506
|
-
if (!authorityCurrent()) return null;
|
|
2507
|
-
const observation = nativeGatewayServingObservation({
|
|
2508
|
-
runtime,
|
|
2509
|
-
bootSessionId,
|
|
2510
|
-
flyGeneration,
|
|
2511
|
-
state,
|
|
2512
|
-
reasonCode,
|
|
2513
|
-
checkedAt: now(),
|
|
2514
|
-
});
|
|
2515
|
-
current = observation;
|
|
2516
|
-
if (state !== "READY") {
|
|
2517
|
-
while (outgoing[0]?.state === "READY") outgoing.shift();
|
|
2518
|
-
}
|
|
2519
|
-
const previous = outgoing.at(-1);
|
|
2520
|
-
if (
|
|
2521
|
-
previous?.state === observation.state &&
|
|
2522
|
-
previous?.reasonCode === observation.reasonCode &&
|
|
2523
|
-
previous?.gatewayPid === observation.gatewayPid
|
|
2524
|
-
) {
|
|
2525
|
-
outgoing[outgoing.length - 1] = observation;
|
|
2526
|
-
} else {
|
|
2527
|
-
outgoing.push(observation);
|
|
2528
|
-
}
|
|
2529
|
-
return observation;
|
|
2530
|
-
};
|
|
2531
|
-
|
|
2532
|
-
publish("READY", null);
|
|
2533
|
-
|
|
2534
|
-
const runEpisode = async (signal) => {
|
|
2535
|
-
let failureReason = "RECOVERY_FAILED";
|
|
2536
|
-
try {
|
|
2537
|
-
await proveServing({
|
|
2538
|
-
desired: runtime.desired,
|
|
2539
|
-
apiServerKey: runtime.secrets.hermesApiServer,
|
|
2540
|
-
gateway: runtime.gateway,
|
|
2541
|
-
runtimeConfig: runtime.runtimeConfig,
|
|
2542
|
-
signal,
|
|
2543
|
-
});
|
|
2544
|
-
if (!authorityCurrent()) return;
|
|
2545
|
-
publish("READY", null);
|
|
2546
|
-
nextEligibleAt = now() + NATIVE_GATEWAY_SERVING_PROOF_INTERVAL_MS;
|
|
2547
|
-
return;
|
|
2548
|
-
} catch (error) {
|
|
2549
|
-
throwIfAborted(signal);
|
|
2550
|
-
failureReason = nativeGatewayServingFailureReason(error);
|
|
2551
|
-
}
|
|
2552
|
-
|
|
2553
|
-
publish("RECOVERING", "RECOVERY_IN_PROGRESS");
|
|
2554
|
-
const deadlineAt = now() + NATIVE_GATEWAY_RECOVERY_BUDGET_MS;
|
|
2555
|
-
for (
|
|
2556
|
-
let attempt = 1;
|
|
2557
|
-
attempt <= NATIVE_GATEWAY_RECOVERY_MAX_ATTEMPTS;
|
|
2558
|
-
attempt += 1
|
|
2559
|
-
) {
|
|
2560
|
-
const backoff = Math.round(
|
|
2561
|
-
Math.min(
|
|
2562
|
-
NATIVE_GATEWAY_RECOVERY_BACKOFF_CAP_MS,
|
|
2563
|
-
NATIVE_GATEWAY_RECOVERY_BACKOFF_MS * 2 ** (attempt - 1)
|
|
2564
|
-
) *
|
|
2565
|
-
(0.75 + random() * 0.5)
|
|
2566
|
-
);
|
|
2567
|
-
await pause(backoff, signal);
|
|
2568
|
-
throwIfAborted(signal);
|
|
2569
|
-
if (!authorityCurrent()) return;
|
|
2570
|
-
if (now() >= deadlineAt) {
|
|
2571
|
-
failureReason = "RECOVERY_FAILED";
|
|
2572
|
-
break;
|
|
2573
|
-
}
|
|
2574
|
-
try {
|
|
2575
|
-
await stopGateway(runtime.gateway);
|
|
2576
|
-
throwIfAborted(signal);
|
|
2577
|
-
if (!authorityCurrent()) return;
|
|
2578
|
-
if (now() >= deadlineAt) {
|
|
2579
|
-
failureReason = "RECOVERY_FAILED";
|
|
2580
|
-
break;
|
|
2581
|
-
}
|
|
2582
|
-
const proven = await startProvenGateway({
|
|
2583
|
-
desired: runtime.desired,
|
|
2584
|
-
runtimeConfig: runtime.runtimeConfig,
|
|
2585
|
-
secrets: runtime.secrets,
|
|
2586
|
-
signal,
|
|
2587
|
-
proveReadiness: (input) => proveServing({ ...input, signal }),
|
|
2588
|
-
});
|
|
2589
|
-
throwIfAborted(signal);
|
|
2590
|
-
if (!authorityCurrent()) {
|
|
2591
|
-
await stopGateway(proven.gateway);
|
|
2592
|
-
return;
|
|
2593
|
-
}
|
|
2594
|
-
if (now() >= deadlineAt) {
|
|
2595
|
-
await stopGateway(proven.gateway);
|
|
2596
|
-
failureReason = "RECOVERY_FAILED";
|
|
2597
|
-
break;
|
|
2598
|
-
}
|
|
2599
|
-
runtime.gateway = proven.gateway;
|
|
2600
|
-
publish("READY", null);
|
|
2601
|
-
nextEligibleAt = now() + NATIVE_GATEWAY_SERVING_PROOF_INTERVAL_MS;
|
|
2602
|
-
return;
|
|
2603
|
-
} catch (error) {
|
|
2604
|
-
throwIfAborted(signal);
|
|
2605
|
-
failureReason = nativeGatewayServingFailureReason(error);
|
|
2606
|
-
}
|
|
2607
|
-
}
|
|
2608
|
-
if (!authorityCurrent()) return;
|
|
2609
|
-
publish("DEGRADED", failureReason);
|
|
2610
|
-
nextEligibleAt = now() + NATIVE_GATEWAY_RECOVERY_COOLDOWN_MS;
|
|
2611
|
-
};
|
|
2612
|
-
|
|
2613
|
-
return Object.freeze({
|
|
2614
|
-
pendingObservation() {
|
|
2615
|
-
return outgoing[0] ?? current;
|
|
2616
|
-
},
|
|
2617
|
-
observationSent(observation) {
|
|
2618
|
-
if (outgoing[0] === observation) outgoing.shift();
|
|
2619
|
-
},
|
|
2620
|
-
poll({ force = false } = {}) {
|
|
2621
|
-
if (
|
|
2622
|
-
stopped ||
|
|
2623
|
-
episode ||
|
|
2624
|
-
!authorityCurrent() ||
|
|
2625
|
-
(!force && now() < nextEligibleAt)
|
|
2626
|
-
) {
|
|
2627
|
-
return episode;
|
|
2628
|
-
}
|
|
2629
|
-
episodeController = new AbortController();
|
|
2630
|
-
const activeEpisode = runEpisode(episodeController.signal)
|
|
2631
|
-
.catch((error) => {
|
|
2632
|
-
if (error?.name !== "AbortError") throw error;
|
|
2633
|
-
})
|
|
2634
|
-
.finally(() => {
|
|
2635
|
-
if (episode === activeEpisode) {
|
|
2636
|
-
episode = null;
|
|
2637
|
-
episodeController = null;
|
|
2638
|
-
}
|
|
2639
|
-
});
|
|
2640
|
-
episode = activeEpisode;
|
|
2641
|
-
return activeEpisode;
|
|
2642
|
-
},
|
|
2643
|
-
async stop() {
|
|
2644
|
-
stopped = true;
|
|
2645
|
-
episodeController?.abort();
|
|
2646
|
-
try {
|
|
2647
|
-
await episode;
|
|
2648
|
-
} catch (error) {
|
|
2649
|
-
if (error?.name !== "AbortError") throw error;
|
|
2650
|
-
}
|
|
2651
|
-
},
|
|
2652
|
-
});
|
|
2653
|
-
}
|
|
2654
|
-
|
|
2655
2313
|
async function proveProvisionedApplication({
|
|
2656
2314
|
desired,
|
|
2657
2315
|
runtimeConfig,
|
|
@@ -3158,14 +2816,6 @@ async function processOperation({ claim, exchange, client, identity }) {
|
|
|
3158
2816
|
runtimeConfig,
|
|
3159
2817
|
trustRootPem,
|
|
3160
2818
|
action: claim.operation.action,
|
|
3161
|
-
servingAuthority: Object.freeze({
|
|
3162
|
-
runtimeId: RUNTIME_ID,
|
|
3163
|
-
flyGeneration: RUNTIME_GENERATION,
|
|
3164
|
-
runtimeGeneration: RUNTIME_GENERATION,
|
|
3165
|
-
configGeneration: claim.operation.configGeneration,
|
|
3166
|
-
lifecycleGeneration: claim.operation.lifecycleGeneration,
|
|
3167
|
-
profileId: desired.profileId,
|
|
3168
|
-
}),
|
|
3169
2819
|
gateway: proof.gateway ?? null,
|
|
3170
2820
|
modelHealthReporter,
|
|
3171
2821
|
};
|
|
@@ -3215,7 +2865,6 @@ async function main() {
|
|
|
3215
2865
|
});
|
|
3216
2866
|
const controller = new AbortController();
|
|
3217
2867
|
let activeRuntime = null;
|
|
3218
|
-
let servingSupervisor = null;
|
|
3219
2868
|
let shutdownMemorySnapshot = null;
|
|
3220
2869
|
const endpointRelay = createCustomerEndpointRelay({
|
|
3221
2870
|
getControlClient: () => client,
|
|
@@ -3229,8 +2878,6 @@ async function main() {
|
|
|
3229
2878
|
const runtime = activeRuntime;
|
|
3230
2879
|
shutdownMemorySnapshot = (async () => {
|
|
3231
2880
|
try {
|
|
3232
|
-
await servingSupervisor?.stop();
|
|
3233
|
-
servingSupervisor = null;
|
|
3234
2881
|
await endpointRelay.fenceActive("RUNTIME_SHUTDOWN");
|
|
3235
2882
|
await stopNativeGateway(runtime.gateway);
|
|
3236
2883
|
runtime.gateway = null;
|
|
@@ -3272,10 +2919,6 @@ async function main() {
|
|
|
3272
2919
|
callerProfileId: activeRuntime.desired.profileId,
|
|
3273
2920
|
}),
|
|
3274
2921
|
leaseSeconds: 60,
|
|
3275
|
-
servingObservation:
|
|
3276
|
-
servingSupervisor?.pendingObservation() ?? undefined,
|
|
3277
|
-
onServingObservationSent: (observation) =>
|
|
3278
|
-
servingSupervisor?.observationSent(observation),
|
|
3279
2922
|
})
|
|
3280
2923
|
: await client.claim(60);
|
|
3281
2924
|
} catch (error) {
|
|
@@ -3320,12 +2963,20 @@ async function main() {
|
|
|
3320
2963
|
),
|
|
3321
2964
|
});
|
|
3322
2965
|
}
|
|
3323
|
-
|
|
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
|
+
}
|
|
3324
2977
|
await sleep(1_000);
|
|
3325
2978
|
continue;
|
|
3326
2979
|
}
|
|
3327
|
-
await servingSupervisor?.stop();
|
|
3328
|
-
servingSupervisor = null;
|
|
3329
2980
|
operationHeartbeat = startOperationHeartbeat({
|
|
3330
2981
|
claim,
|
|
3331
2982
|
exchange,
|
|
@@ -3340,15 +2991,6 @@ async function main() {
|
|
|
3340
2991
|
client,
|
|
3341
2992
|
identity,
|
|
3342
2993
|
});
|
|
3343
|
-
servingSupervisor =
|
|
3344
|
-
activeRuntime.action === "INSTANTIATE"
|
|
3345
|
-
? null
|
|
3346
|
-
: createNativeGatewayServingSupervisor({
|
|
3347
|
-
runtime: activeRuntime,
|
|
3348
|
-
getActiveRuntime: () => activeRuntime,
|
|
3349
|
-
bootSessionId: BOOT_SESSION_ID,
|
|
3350
|
-
flyGeneration: RUNTIME_GENERATION,
|
|
3351
|
-
});
|
|
3352
2994
|
soulClaimTracker = createSoulClaimObservationTracker({
|
|
3353
2995
|
runtimeId: RUNTIME_ID,
|
|
3354
2996
|
profileId: activeRuntime.desired.profileId,
|
|
@@ -784,8 +784,6 @@ export async function claimWithSoulObservation({
|
|
|
784
784
|
tracker,
|
|
785
785
|
readSoul,
|
|
786
786
|
leaseSeconds = 60,
|
|
787
|
-
servingObservation,
|
|
788
|
-
onServingObservationSent,
|
|
789
787
|
}) {
|
|
790
788
|
// Deliver lifecycle work before any potentially slow local loader proof.
|
|
791
789
|
// Reconciliation uses the same cadence only after the server confirms that
|
|
@@ -793,9 +791,7 @@ export async function claimWithSoulObservation({
|
|
|
793
791
|
const tick = await client.claimTick({
|
|
794
792
|
leaseSeconds,
|
|
795
793
|
soulObservation: tracker.pending(),
|
|
796
|
-
...(servingObservation ? { servingObservation } : {}),
|
|
797
794
|
});
|
|
798
|
-
if (servingObservation) onServingObservationSent?.(servingObservation);
|
|
799
795
|
if (tick?.claim) return tick.claim;
|
|
800
796
|
|
|
801
797
|
const acknowledgement = tick?.soulAcknowledgement;
|
|
@@ -1078,15 +1074,10 @@ export function createFlyCustomerControlClient({
|
|
|
1078
1074
|
}
|
|
1079
1075
|
return { ...result, signedUrl: undefined, bytes };
|
|
1080
1076
|
};
|
|
1081
|
-
const claimTick = async ({
|
|
1082
|
-
leaseSeconds = 60,
|
|
1083
|
-
soulObservation,
|
|
1084
|
-
servingObservation,
|
|
1085
|
-
} = {}) =>
|
|
1077
|
+
const claimTick = async ({ leaseSeconds = 60, soulObservation } = {}) =>
|
|
1086
1078
|
post("claim", "/api/v3/sellable-agent/worker/claim", {
|
|
1087
1079
|
leaseSeconds,
|
|
1088
1080
|
...(soulObservation ? { soulObservation } : {}),
|
|
1089
|
-
...(servingObservation ? { servingObservation } : {}),
|
|
1090
1081
|
});
|
|
1091
1082
|
return Object.freeze({
|
|
1092
1083
|
claimTick,
|
|
@@ -38,8 +38,8 @@ 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.608
|
|
42
|
-
const MCP_PACKAGE = "@sellable/mcp@0.1.
|
|
41
|
+
const INSTALLER_PACKAGE = "@sellable/install@0.1.608";
|
|
42
|
+
const MCP_PACKAGE = "@sellable/mcp@0.1.865";
|
|
43
43
|
const HERMES_VERSION = "0.18.0";
|
|
44
44
|
const SHA256 = /^[a-f0-9]{64}$/;
|
|
45
45
|
const SHA256_16 = /^[a-f0-9]{16}$/;
|
|
@@ -1546,8 +1546,8 @@ export function compileClaimToProfileDesired(activeClaim, config) {
|
|
|
1546
1546
|
]) ||
|
|
1547
1547
|
pinned.hermesCli !== "hermes" ||
|
|
1548
1548
|
pinned.hermesVersion !== "0.18.0" ||
|
|
1549
|
-
pinned.installerPackage !== "@sellable/install@0.1.608
|
|
1550
|
-
pinned.mcpPackage !== "@sellable/mcp@0.1.
|
|
1549
|
+
pinned.installerPackage !== "@sellable/install@0.1.608" ||
|
|
1550
|
+
pinned.mcpPackage !== "@sellable/mcp@0.1.865" ||
|
|
1551
1551
|
!Array.isArray(policy.toolInclude) ||
|
|
1552
1552
|
policy.toolInclude.length === 0 ||
|
|
1553
1553
|
!Number.isSafeInteger(slack.generation) ||
|
|
@@ -278,8 +278,8 @@ function validateDesired(desired) {
|
|
|
278
278
|
desired.serviceCredentialGeneration < 1 ||
|
|
279
279
|
desired.hermesCli !== "hermes" ||
|
|
280
280
|
desired.hermesVersion !== "0.18.0" ||
|
|
281
|
-
desired.installerPackage !== "@sellable/install@0.1.608
|
|
282
|
-
desired.mcpPackage !== "@sellable/mcp@0.1.
|
|
281
|
+
desired.installerPackage !== "@sellable/install@0.1.608" ||
|
|
282
|
+
desired.mcpPackage !== "@sellable/mcp@0.1.865" ||
|
|
283
283
|
!Array.isArray(desired.toolInclude) ||
|
|
284
284
|
desired.toolInclude.length === 0 ||
|
|
285
285
|
desired.toolInclude.some((tool) => !/^[a-z][a-z0-9_]{0,127}$/.test(tool)) ||
|
|
@@ -25,8 +25,8 @@ import { fileURLToPath } from "node:url";
|
|
|
25
25
|
import { deriveContainedProfileId } from "./profile-materializer.mjs";
|
|
26
26
|
|
|
27
27
|
export const PROVISIONING_ACTION = "PROVISION_HERMES_PROFILE";
|
|
28
|
-
export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.608
|
|
29
|
-
export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.
|
|
28
|
+
export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.608";
|
|
29
|
+
export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.865";
|
|
30
30
|
const PINNED_INSTALL_VERSION = PINNED_INSTALL_PACKAGE.slice(
|
|
31
31
|
PINNED_INSTALL_PACKAGE.lastIndexOf("@") + 1
|
|
32
32
|
);
|
package/package.json
CHANGED
|
@@ -5,7 +5,9 @@ visibility: public
|
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- mcp__sellable__refill_sends
|
|
7
7
|
- mcp__sellable__refill_v3_advance
|
|
8
|
+
- mcp__sellable__refill_v3_world_state
|
|
8
9
|
- mcp__sellable__refill_v3_continue
|
|
10
|
+
- mcp__sellable__get_campaigns
|
|
9
11
|
- mcp__sellable__get_refill_target_plan
|
|
10
12
|
- mcp__sellable__get_scheduler_fill_capacity
|
|
11
13
|
- mcp__sellable__get_subskill_asset
|
|
@@ -604,6 +606,19 @@ Synchronous source, configuration, rubric, and exhaustion authorities already
|
|
|
604
606
|
return after their own exact reread, so they return `settlement.ready:true` from
|
|
605
607
|
that bounded authority receipt and never wait on unrelated campaign-wide
|
|
606
608
|
processing cells.
|
|
609
|
+
|
|
610
|
+
When a non-yolo request names a campaign, resolve that visible name with one
|
|
611
|
+
authorized bounded `get_campaigns` call carrying an explicit `limit`. Require
|
|
612
|
+
exactly one case-insensitive exact match; if none or more than one match, stop
|
|
613
|
+
without preview or mutation. Retain only the returned stable campaign ID. Call
|
|
614
|
+
`refill_v3_world_state({workspaceId, scope, campaignId})`, render that exact
|
|
615
|
+
campaign's two-lane readiness and its explicit zero product side effects, then
|
|
616
|
+
stop at the product-native question with exactly Accept and Decline. Do not call
|
|
617
|
+
`refill_v3_advance` before Accept. Accept calls
|
|
618
|
+
`refill_v3_advance({workspaceId, scope, campaignId})`; Decline stops. Carry the
|
|
619
|
+
same campaignId through every advance, continue, replay, partial-wait, and
|
|
620
|
+
post-wait repeat. Display-name changes never alter this retained identity, and
|
|
621
|
+
no sibling campaign may replace it.
|
|
607
622
|
Call the next advance only when `settlement.ready` is true. When it is false,
|
|
608
623
|
call `wait_for_campaign_processing` with `settlement.resumeInput` **unchanged**;
|
|
609
624
|
that carries the exact `preparationJobId`, `requirePreparationTerminal:true`,
|