@sellable/install 0.1.483 → 0.1.486-wip.phase169.20260804.2
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 +2 -2
- package/lib/sellable-agent/admin-runtime-reconciler.mjs +1 -5
- package/lib/sellable-agent/fly-admin-image/Dockerfile +4 -0
- package/lib/sellable-agent/fly-admin-image/admin-runtime.mjs +327 -86
- package/lib/sellable-agent/fly-customer-image/Dockerfile +3 -2
- package/lib/sellable-agent/fly-customer-image/customer-runtime.mjs +233 -100
- package/lib/sellable-agent/fly-customer-image/model-auth-store.mjs +6 -0
- package/lib/sellable-agent/fly-customer-model.mjs +16 -148
- package/lib/sellable-agent/host-bootstrap.mjs +1 -1
- package/lib/sellable-agent/host-worker.mjs +1 -1
- package/lib/sellable-agent/model-auth-reconciler.mjs +574 -0
- package/lib/sellable-agent/model-health-reporter.mjs +377 -0
- package/lib/sellable-agent/profile-materializer.mjs +17 -2
- package/lib/sellable-agent/provisioning-adapter.mjs +4 -4
- package/package.json +1 -1
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
chmodSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
lstatSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
renameSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
|
|
13
|
+
const SHA256 = /^[a-f0-9]{64}$/;
|
|
14
|
+
const SAFE_ID = /^[A-Za-z0-9_.:-]{1,256}$/;
|
|
15
|
+
const FINGERPRINT = /^[a-f0-9]{16,64}$/;
|
|
16
|
+
const SAFE_REASON = /^[a-z0-9_.:-]{1,128}$/;
|
|
17
|
+
|
|
18
|
+
function isRecord(value) {
|
|
19
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function rejectIdentity() {
|
|
23
|
+
throw new Error("model_health_report_identity_rejected");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function safeOrigin(value, allowHttpLoopback) {
|
|
27
|
+
try {
|
|
28
|
+
const parsed = new URL(value);
|
|
29
|
+
const testHttp =
|
|
30
|
+
allowHttpLoopback === true &&
|
|
31
|
+
parsed.protocol === "http:" &&
|
|
32
|
+
["127.0.0.1", "localhost", "host.docker.internal"].includes(
|
|
33
|
+
parsed.hostname
|
|
34
|
+
);
|
|
35
|
+
if (
|
|
36
|
+
(parsed.protocol !== "https:" && !testHttp) ||
|
|
37
|
+
parsed.username ||
|
|
38
|
+
parsed.password ||
|
|
39
|
+
parsed.pathname !== "/" ||
|
|
40
|
+
parsed.search ||
|
|
41
|
+
parsed.hash
|
|
42
|
+
) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return parsed.origin;
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function safeSnapshot(snapshot) {
|
|
52
|
+
if (
|
|
53
|
+
!isRecord(snapshot) ||
|
|
54
|
+
snapshot.schemaVersion !== "sellable-agent-model-auth-readback/v2" ||
|
|
55
|
+
!SAFE_ID.test(snapshot.activationId ?? "") ||
|
|
56
|
+
!Number.isSafeInteger(snapshot.bundleGeneration) ||
|
|
57
|
+
snapshot.bundleGeneration < 1 ||
|
|
58
|
+
!SHA256.test(snapshot.policyHash ?? "") ||
|
|
59
|
+
snapshot.provider !== "openai-codex" ||
|
|
60
|
+
!SAFE_ID.test(snapshot.model ?? "") ||
|
|
61
|
+
!Number.isSafeInteger(snapshot.fallbackDepth) ||
|
|
62
|
+
snapshot.fallbackDepth < 0 ||
|
|
63
|
+
snapshot.fallbackDepth > 8 ||
|
|
64
|
+
!["converged", "unknown"].includes(snapshot.desiredStatus) ||
|
|
65
|
+
!["current", "pending_start"].includes(snapshot.materializedStatus) ||
|
|
66
|
+
!Array.isArray(snapshot.slots) ||
|
|
67
|
+
snapshot.slots.length > 2 ||
|
|
68
|
+
!isRecord(snapshot.openrouter) ||
|
|
69
|
+
!FINGERPRINT.test(snapshot.openrouter.keyFingerprint ?? "") ||
|
|
70
|
+
!["ok", "exhausted", "dead"].includes(snapshot.openrouter.state) ||
|
|
71
|
+
(snapshot.activeProvider !== undefined &&
|
|
72
|
+
snapshot.activeProvider !== null &&
|
|
73
|
+
!["openai-codex", "openrouter"].includes(snapshot.activeProvider)) ||
|
|
74
|
+
(snapshot.activeFallbackModelId !== undefined &&
|
|
75
|
+
snapshot.activeFallbackModelId !== null &&
|
|
76
|
+
(typeof snapshot.activeFallbackModelId !== "string" ||
|
|
77
|
+
snapshot.activeFallbackModelId.length < 1 ||
|
|
78
|
+
snapshot.activeFallbackModelId.length > 256))
|
|
79
|
+
) {
|
|
80
|
+
throw new Error("model_health_snapshot_rejected");
|
|
81
|
+
}
|
|
82
|
+
const slots = snapshot.slots.map((slot) => {
|
|
83
|
+
if (
|
|
84
|
+
!isRecord(slot) ||
|
|
85
|
+
!["dittto-primary", "kaigo-secondary"].includes(slot.slotId) ||
|
|
86
|
+
!FINGERPRINT.test(slot.grantFingerprint ?? "") ||
|
|
87
|
+
!Number.isFinite(Date.parse(slot.expiresAt ?? "")) ||
|
|
88
|
+
!["ok", "exhausted", "dead"].includes(slot.state) ||
|
|
89
|
+
(slot.lastErrorReason !== null &&
|
|
90
|
+
!SAFE_REASON.test(slot.lastErrorReason ?? "")) ||
|
|
91
|
+
(slot.lastErrorResetAt !== null &&
|
|
92
|
+
!Number.isFinite(Date.parse(slot.lastErrorResetAt ?? "")))
|
|
93
|
+
) {
|
|
94
|
+
throw new Error("model_health_snapshot_rejected");
|
|
95
|
+
}
|
|
96
|
+
return Object.freeze({
|
|
97
|
+
slotId: slot.slotId,
|
|
98
|
+
grantFingerprint: slot.grantFingerprint,
|
|
99
|
+
expiresAt: new Date(slot.expiresAt).toISOString(),
|
|
100
|
+
state: slot.state,
|
|
101
|
+
lastErrorReason: slot.lastErrorReason,
|
|
102
|
+
lastErrorResetAt:
|
|
103
|
+
slot.lastErrorResetAt === null
|
|
104
|
+
? null
|
|
105
|
+
: new Date(slot.lastErrorResetAt).toISOString(),
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
return Object.freeze({
|
|
109
|
+
activationId: snapshot.activationId,
|
|
110
|
+
bundleGeneration: snapshot.bundleGeneration,
|
|
111
|
+
policyHash: snapshot.policyHash,
|
|
112
|
+
provider: snapshot.provider,
|
|
113
|
+
model: snapshot.model,
|
|
114
|
+
fallbackDepth: snapshot.fallbackDepth,
|
|
115
|
+
desiredStatus: snapshot.desiredStatus,
|
|
116
|
+
materializedStatus: snapshot.materializedStatus,
|
|
117
|
+
slots: Object.freeze(slots),
|
|
118
|
+
openrouter: Object.freeze({
|
|
119
|
+
keyFingerprint: snapshot.openrouter.keyFingerprint,
|
|
120
|
+
state: snapshot.openrouter.state,
|
|
121
|
+
}),
|
|
122
|
+
activeProvider: snapshot.activeProvider ?? null,
|
|
123
|
+
activeFallbackModelId: snapshot.activeFallbackModelId ?? null,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function codexHealthState(slot) {
|
|
128
|
+
if (slot.state === "ok") return "HEALTHY";
|
|
129
|
+
if (slot.state === "exhausted") return "REFRESHING";
|
|
130
|
+
if (/revok/.test(slot.lastErrorReason ?? "")) return "REVOKED";
|
|
131
|
+
if (/(?:auth|unauthor|invalid|expired)/.test(slot.lastErrorReason ?? "")) {
|
|
132
|
+
return "REAUTH_REQUIRED";
|
|
133
|
+
}
|
|
134
|
+
return "DEAD";
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function fallbackHealthState(snapshot) {
|
|
138
|
+
const allFailed =
|
|
139
|
+
snapshot.slots.length === 2 &&
|
|
140
|
+
snapshot.slots.every((slot) => slot.state === "dead") &&
|
|
141
|
+
snapshot.openrouter.state === "dead";
|
|
142
|
+
if (allFailed) return Object.freeze({ state: "ALL_FAILED", modelId: null });
|
|
143
|
+
if (snapshot.activeProvider === "openrouter") {
|
|
144
|
+
return Object.freeze({
|
|
145
|
+
state: "OPENROUTER_ACTIVE",
|
|
146
|
+
modelId:
|
|
147
|
+
typeof snapshot.activeFallbackModelId === "string"
|
|
148
|
+
? snapshot.activeFallbackModelId
|
|
149
|
+
: null,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return Object.freeze({ state: "CODEX_ACTIVE", modelId: null });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function loadSequence(path, identity) {
|
|
156
|
+
if (!path || !existsSync(path)) return 0;
|
|
157
|
+
const stat = lstatSync(path);
|
|
158
|
+
if (stat.isSymbolicLink() || !stat.isFile() || (stat.mode & 0o077) !== 0) {
|
|
159
|
+
throw new Error("model_health_sequence_store_rejected");
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const value = JSON.parse(readFileSync(path, "utf8"));
|
|
163
|
+
if (
|
|
164
|
+
value?.schemaVersion !== "sellable-agent-model-health-sequence/v1" ||
|
|
165
|
+
value.runtimeGeneration !== identity.runtimeGeneration ||
|
|
166
|
+
value.workloadKeyHash !== identity.workloadKeyHash
|
|
167
|
+
) {
|
|
168
|
+
return 0;
|
|
169
|
+
}
|
|
170
|
+
if (!Number.isSafeInteger(value.sequence) || value.sequence < 0) {
|
|
171
|
+
throw new Error("model_health_sequence_store_rejected");
|
|
172
|
+
}
|
|
173
|
+
return value.sequence;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (
|
|
176
|
+
error instanceof Error &&
|
|
177
|
+
error.message === "model_health_sequence_store_rejected"
|
|
178
|
+
) {
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
throw new Error("model_health_sequence_store_rejected");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function persistSequence(path, identity, sequence) {
|
|
186
|
+
if (!path) return;
|
|
187
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
188
|
+
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
189
|
+
writeFileSync(
|
|
190
|
+
temporary,
|
|
191
|
+
`${JSON.stringify({
|
|
192
|
+
schemaVersion: "sellable-agent-model-health-sequence/v1",
|
|
193
|
+
runtimeGeneration: identity.runtimeGeneration,
|
|
194
|
+
workloadKeyHash: identity.workloadKeyHash,
|
|
195
|
+
sequence,
|
|
196
|
+
})}\n`,
|
|
197
|
+
{ flag: "wx", mode: 0o600 }
|
|
198
|
+
);
|
|
199
|
+
chmodSync(temporary, 0o600);
|
|
200
|
+
renameSync(temporary, path);
|
|
201
|
+
chmodSync(path, 0o600);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function modelHealthCadenceMs(workloadKeyHash) {
|
|
205
|
+
if (!SHA256.test(workloadKeyHash ?? "")) rejectIdentity();
|
|
206
|
+
const jitter = Number.parseInt(workloadKeyHash.slice(0, 8), 16) % 60_001;
|
|
207
|
+
return 240_000 + jitter;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function buildModelHealthReport(input) {
|
|
211
|
+
if (
|
|
212
|
+
!["CUSTOMER", "ADMIN"].includes(input?.kind) ||
|
|
213
|
+
!Number.isSafeInteger(input.runtimeGeneration) ||
|
|
214
|
+
input.runtimeGeneration < 1 ||
|
|
215
|
+
!SHA256.test(input.workloadKeyHash ?? "") ||
|
|
216
|
+
!SAFE_ID.test(input.machineId ?? "") ||
|
|
217
|
+
!Number.isSafeInteger(input.sequence) ||
|
|
218
|
+
input.sequence < 1 ||
|
|
219
|
+
!Number.isFinite(Date.parse(input.observedAt ?? "")) ||
|
|
220
|
+
(input.kind === "ADMIN"
|
|
221
|
+
? !SAFE_ID.test(input.adminRuntimeGenerationId ?? "") ||
|
|
222
|
+
!/^[1-9][0-9]{0,19}$/.test(input.adminRuntimeGenerationFence ?? "")
|
|
223
|
+
: input.adminRuntimeGenerationId !== null ||
|
|
224
|
+
input.adminRuntimeGenerationFence !== null)
|
|
225
|
+
) {
|
|
226
|
+
rejectIdentity();
|
|
227
|
+
}
|
|
228
|
+
const snapshot = safeSnapshot(input.snapshot);
|
|
229
|
+
const slots = new Map(snapshot.slots.map((slot) => [slot.slotId, slot]));
|
|
230
|
+
const primary = slots.get("dittto-primary");
|
|
231
|
+
const secondary = slots.get("kaigo-secondary");
|
|
232
|
+
if (!primary || !secondary) throw new Error("model_health_snapshot_rejected");
|
|
233
|
+
return Object.freeze({
|
|
234
|
+
schemaVersion: "sellable-agent-model-health/v1",
|
|
235
|
+
sequence: input.sequence,
|
|
236
|
+
observedAt: new Date(input.observedAt).toISOString(),
|
|
237
|
+
machineId: input.machineId,
|
|
238
|
+
runtimeGeneration: input.runtimeGeneration,
|
|
239
|
+
workloadKeyHash: input.workloadKeyHash,
|
|
240
|
+
kind: input.kind,
|
|
241
|
+
adminRuntimeGenerationId: input.adminRuntimeGenerationId,
|
|
242
|
+
adminRuntimeGenerationFence: input.adminRuntimeGenerationFence,
|
|
243
|
+
activationId: snapshot.activationId,
|
|
244
|
+
bundleGeneration: snapshot.bundleGeneration,
|
|
245
|
+
policyHash: snapshot.policyHash,
|
|
246
|
+
codex: Object.freeze({
|
|
247
|
+
primary: codexHealthState(primary),
|
|
248
|
+
secondary: codexHealthState(secondary),
|
|
249
|
+
}),
|
|
250
|
+
fallback: fallbackHealthState(snapshot),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* @param {{
|
|
256
|
+
* origin: string,
|
|
257
|
+
* identity: Record<string, unknown>,
|
|
258
|
+
* observe: () => unknown,
|
|
259
|
+
* authorize: (body: unknown) => string,
|
|
260
|
+
* fetchImpl?: typeof fetch,
|
|
261
|
+
* now?: () => Date,
|
|
262
|
+
* onError?: (error: unknown) => void,
|
|
263
|
+
* setIntervalImpl?: typeof setInterval,
|
|
264
|
+
* clearIntervalImpl?: typeof clearInterval,
|
|
265
|
+
* allowHttpLoopback?: boolean,
|
|
266
|
+
* sequencePath?: string | null,
|
|
267
|
+
* }} input
|
|
268
|
+
*/
|
|
269
|
+
export function createModelHealthReporter({
|
|
270
|
+
origin,
|
|
271
|
+
identity,
|
|
272
|
+
observe,
|
|
273
|
+
authorize,
|
|
274
|
+
fetchImpl = fetch,
|
|
275
|
+
now = () => new Date(),
|
|
276
|
+
onError = () => {},
|
|
277
|
+
setIntervalImpl = setInterval,
|
|
278
|
+
clearIntervalImpl = clearInterval,
|
|
279
|
+
allowHttpLoopback = false,
|
|
280
|
+
sequencePath = null,
|
|
281
|
+
}) {
|
|
282
|
+
const endpoint = safeOrigin(origin, allowHttpLoopback);
|
|
283
|
+
if (
|
|
284
|
+
!endpoint ||
|
|
285
|
+
!isRecord(identity) ||
|
|
286
|
+
typeof observe !== "function" ||
|
|
287
|
+
typeof authorize !== "function" ||
|
|
288
|
+
typeof fetchImpl !== "function" ||
|
|
289
|
+
typeof now !== "function" ||
|
|
290
|
+
(sequencePath !== null &&
|
|
291
|
+
(typeof sequencePath !== "string" || !sequencePath))
|
|
292
|
+
) {
|
|
293
|
+
rejectIdentity();
|
|
294
|
+
}
|
|
295
|
+
// Validate the workload binding before any timer or network side effect.
|
|
296
|
+
const cadenceMs = modelHealthCadenceMs(identity.workloadKeyHash);
|
|
297
|
+
buildModelHealthReport({
|
|
298
|
+
...identity,
|
|
299
|
+
sequence: 1,
|
|
300
|
+
observedAt: now().toISOString(),
|
|
301
|
+
snapshot: observe(),
|
|
302
|
+
});
|
|
303
|
+
let sequence = loadSequence(sequencePath, identity);
|
|
304
|
+
let inFlight = null;
|
|
305
|
+
let timer = null;
|
|
306
|
+
let stopped = false;
|
|
307
|
+
|
|
308
|
+
const emit = (_transition = "periodic") => {
|
|
309
|
+
if (stopped)
|
|
310
|
+
return Promise.reject(new Error("model_health_reporter_stopped"));
|
|
311
|
+
if (inFlight) return inFlight;
|
|
312
|
+
const nextSequence = sequence + 1;
|
|
313
|
+
const snapshot = observe();
|
|
314
|
+
const body = buildModelHealthReport({
|
|
315
|
+
...identity,
|
|
316
|
+
sequence: nextSequence,
|
|
317
|
+
observedAt: now().toISOString(),
|
|
318
|
+
snapshot,
|
|
319
|
+
});
|
|
320
|
+
persistSequence(sequencePath, identity, nextSequence);
|
|
321
|
+
sequence = nextSequence;
|
|
322
|
+
const authorization = authorize(body);
|
|
323
|
+
if (
|
|
324
|
+
typeof authorization !== "string" ||
|
|
325
|
+
!authorization.startsWith("Worker ")
|
|
326
|
+
) {
|
|
327
|
+
return Promise.reject(new Error("model_health_report_unsigned"));
|
|
328
|
+
}
|
|
329
|
+
inFlight = (async () => {
|
|
330
|
+
const response = await fetchImpl(
|
|
331
|
+
`${endpoint}/api/v3/sellable-agent/worker/model-health`,
|
|
332
|
+
{
|
|
333
|
+
method: "POST",
|
|
334
|
+
headers: { authorization, "content-type": "application/json" },
|
|
335
|
+
body: JSON.stringify(body),
|
|
336
|
+
signal: AbortSignal.timeout(20_000),
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
let result;
|
|
340
|
+
try {
|
|
341
|
+
result = await response.json();
|
|
342
|
+
} catch {
|
|
343
|
+
throw new Error("model_health_report_response_rejected");
|
|
344
|
+
}
|
|
345
|
+
if (!response.ok || result?.accepted !== true) {
|
|
346
|
+
throw new Error("model_health_report_rejected");
|
|
347
|
+
}
|
|
348
|
+
return Object.freeze({ accepted: true, sequence });
|
|
349
|
+
})().finally(() => {
|
|
350
|
+
inFlight = null;
|
|
351
|
+
});
|
|
352
|
+
return inFlight;
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
return Object.freeze({
|
|
356
|
+
cadenceMs,
|
|
357
|
+
emit,
|
|
358
|
+
async start(transition = "materialized") {
|
|
359
|
+
try {
|
|
360
|
+
return await emit(transition);
|
|
361
|
+
} finally {
|
|
362
|
+
if (!timer && !stopped) {
|
|
363
|
+
timer = setIntervalImpl(() => {
|
|
364
|
+
emit("periodic").catch(onError);
|
|
365
|
+
}, cadenceMs);
|
|
366
|
+
timer?.unref?.();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
async stop() {
|
|
371
|
+
stopped = true;
|
|
372
|
+
if (timer) clearIntervalImpl(timer);
|
|
373
|
+
timer = null;
|
|
374
|
+
await inFlight;
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
}
|
|
@@ -197,7 +197,7 @@ function validateDesired(desired) {
|
|
|
197
197
|
desired.serviceCredentialGeneration < 1 ||
|
|
198
198
|
desired.hermesCli !== "hermes" ||
|
|
199
199
|
desired.hermesVersion !== "0.18.0" ||
|
|
200
|
-
desired.installerPackage !== "@sellable/install@0.1.
|
|
200
|
+
desired.installerPackage !== "@sellable/install@0.1.486-wip.phase169.20260804.2" ||
|
|
201
201
|
desired.mcpPackage !==
|
|
202
202
|
"@sellable/mcp@0.1.760-wip.phase143.20260727100513" ||
|
|
203
203
|
!Array.isArray(desired.toolInclude) ||
|
|
@@ -2023,6 +2023,9 @@ const NATIVE_SLACK_KEYS = [
|
|
|
2023
2023
|
"membershipGeneration",
|
|
2024
2024
|
"serviceCredentialGeneration",
|
|
2025
2025
|
"modelCredentialGeneration",
|
|
2026
|
+
"modelActivationId",
|
|
2027
|
+
"modelBundleGeneration",
|
|
2028
|
+
"modelPolicyHash",
|
|
2026
2029
|
"runtimeGeneration",
|
|
2027
2030
|
"selectedChannelIds",
|
|
2028
2031
|
"defaultChannelId",
|
|
@@ -2062,8 +2065,12 @@ function validateNativeSlackDesired(desired) {
|
|
|
2062
2065
|
"membershipGeneration",
|
|
2063
2066
|
"serviceCredentialGeneration",
|
|
2064
2067
|
"modelCredentialGeneration",
|
|
2068
|
+
"modelBundleGeneration",
|
|
2065
2069
|
"runtimeGeneration",
|
|
2066
2070
|
].some((key) => !Number.isSafeInteger(desired[key]) || desired[key] < 1) ||
|
|
2071
|
+
desired.modelCredentialGeneration !== desired.modelBundleGeneration ||
|
|
2072
|
+
!SAFE_ID.test(desired.modelActivationId ?? "") ||
|
|
2073
|
+
!SHA256.test(desired.modelPolicyHash ?? "") ||
|
|
2067
2074
|
!Array.isArray(desired.selectedChannelIds) ||
|
|
2068
2075
|
desired.selectedChannelIds.length < 1 ||
|
|
2069
2076
|
desired.selectedChannelIds.some(
|
|
@@ -2117,6 +2124,9 @@ function nativeSlackCustomerFiles(desired) {
|
|
|
2117
2124
|
membershipGeneration: desired.membershipGeneration,
|
|
2118
2125
|
serviceCredentialGeneration: desired.serviceCredentialGeneration,
|
|
2119
2126
|
modelCredentialGeneration: desired.modelCredentialGeneration,
|
|
2127
|
+
modelActivationId: desired.modelActivationId,
|
|
2128
|
+
modelBundleGeneration: desired.modelBundleGeneration,
|
|
2129
|
+
modelPolicyHash: desired.modelPolicyHash,
|
|
2120
2130
|
runtimeGeneration: desired.runtimeGeneration,
|
|
2121
2131
|
selectedChannelIds: [...desired.selectedChannelIds],
|
|
2122
2132
|
memberAllowlist: [...desired.memberAllowlist],
|
|
@@ -2140,7 +2150,9 @@ function nativeSlackCustomerFiles(desired) {
|
|
|
2140
2150
|
provider: FLY_CUSTOMER_MODEL_PROVIDER,
|
|
2141
2151
|
default: FLY_CUSTOMER_MODEL_ID,
|
|
2142
2152
|
reasoning_effort: FLY_CUSTOMER_MODEL_REASONING_EFFORT,
|
|
2143
|
-
generation: desired.
|
|
2153
|
+
generation: desired.modelBundleGeneration,
|
|
2154
|
+
activation_id: desired.modelActivationId,
|
|
2155
|
+
policy_hash: desired.modelPolicyHash,
|
|
2144
2156
|
},
|
|
2145
2157
|
slack: {
|
|
2146
2158
|
require_mention: true,
|
|
@@ -2237,6 +2249,9 @@ function nativeSlackManifest(desired, files) {
|
|
|
2237
2249
|
membershipGeneration: desired.membershipGeneration,
|
|
2238
2250
|
serviceCredentialGeneration: desired.serviceCredentialGeneration,
|
|
2239
2251
|
modelCredentialGeneration: desired.modelCredentialGeneration,
|
|
2252
|
+
modelActivationId: desired.modelActivationId,
|
|
2253
|
+
modelBundleGeneration: desired.modelBundleGeneration,
|
|
2254
|
+
modelPolicyHash: desired.modelPolicyHash,
|
|
2240
2255
|
runtimeGeneration: desired.runtimeGeneration,
|
|
2241
2256
|
installerPackage: desired.installerPackage,
|
|
2242
2257
|
mcpPackage: desired.mcpPackage,
|
|
@@ -25,7 +25,7 @@ import { fileURLToPath } from "node:url";
|
|
|
25
25
|
import { deriveContainedProfileId } from "./profile-materializer.mjs";
|
|
26
26
|
|
|
27
27
|
export const PROVISIONING_ACTION = "PROVISION_HERMES_PROFILE";
|
|
28
|
-
export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.
|
|
28
|
+
export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.486-wip.phase169.20260804.2";
|
|
29
29
|
export const PINNED_MCP_PACKAGE =
|
|
30
30
|
"@sellable/mcp@0.1.760-wip.phase143.20260727100513";
|
|
31
31
|
|
|
@@ -612,7 +612,7 @@ function inspectProvisionedProfile({
|
|
|
612
612
|
!matchesReadback(manifest, request) ||
|
|
613
613
|
manifest.serviceCredentialReference !== serviceCredentialReference ||
|
|
614
614
|
manifest.installerPackage !== PINNED_INSTALL_PACKAGE ||
|
|
615
|
-
manifest.installerVersion !== "0.1.
|
|
615
|
+
manifest.installerVersion !== "0.1.486-wip.phase169.20260804.2" ||
|
|
616
616
|
manifest.mcpPackage !== PINNED_MCP_PACKAGE ||
|
|
617
617
|
manifest.credentialKeyVersion !== credentialKeyVersion ||
|
|
618
618
|
!/^[a-f0-9]{16}$/.test(manifest.credentialFingerprint) ||
|
|
@@ -826,7 +826,7 @@ function successReceipt({
|
|
|
826
826
|
subject: `agent-profile:${observed.profileId}`,
|
|
827
827
|
cli: {
|
|
828
828
|
installPackage: PINNED_INSTALL_PACKAGE,
|
|
829
|
-
installVersion: "0.1.
|
|
829
|
+
installVersion: "0.1.486-wip.phase169.20260804.2",
|
|
830
830
|
mcpPackage: PINNED_MCP_PACKAGE,
|
|
831
831
|
command: "hermes profile bootstrap",
|
|
832
832
|
},
|
|
@@ -874,7 +874,7 @@ function failure(code, stages, extra = {}, request = null) {
|
|
|
874
874
|
: "agent-profile:unbound",
|
|
875
875
|
cli: {
|
|
876
876
|
installPackage: PINNED_INSTALL_PACKAGE,
|
|
877
|
-
installVersion: "0.1.
|
|
877
|
+
installVersion: "0.1.486-wip.phase169.20260804.2",
|
|
878
878
|
mcpPackage: PINNED_MCP_PACKAGE,
|
|
879
879
|
command: "hermes profile bootstrap",
|
|
880
880
|
},
|