@sema-agent/settings-schema 6.0.0 → 6.2.0
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/CHANGELOG.md +278 -0
- package/README.md +62 -5
- package/conformance/VERSION +1 -1
- package/conformance/cases/c01-entry-salvage-then-typo/expected.json +3 -0
- package/conformance/cases/c02-hosts-clamp-stacked/expected.json +3 -0
- package/conformance/cases/c23-mixed-validity-models/expected.json +3 -0
- package/conformance/cases/c40-s712-identity-only-what-reads-out/expected.json +166 -0
- package/conformance/cases/c40-s712-identity-only-what-reads-out/input.json +64 -0
- package/conformance/cases/c40-s712-identity-only-what-reads-out/meta.json +13 -0
- package/conformance/cases/cli01-model-name-nfc-vs-nfd/expected.json +8 -0
- package/conformance/cases/cli02-control-and-zero-width-chars/expected.json +8 -0
- package/conformance/cases/t04-secret-literal-never-in-warning-value/expected.json +4 -0
- package/dist/api/auth-bridge.d.ts +2 -2
- package/dist/center-credential.d.ts +116 -0
- package/dist/center-credential.js +149 -0
- package/dist/config-fns.d.ts +52 -3
- package/dist/config-fns.js +78 -8
- package/dist/conformance/types.d.ts +6 -1
- package/dist/conformance/warnings.js +9 -2
- package/dist/fleet.d.ts +260 -8
- package/dist/fleet.js +167 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +72 -66
- package/dist/types.js +7 -1
- package/package.json +2 -2
package/dist/config-fns.js
CHANGED
|
@@ -16,16 +16,28 @@
|
|
|
16
16
|
// zod is a VALUE import here (not just a type): {@link salvageEntries} introspects a domain schema to find
|
|
17
17
|
// its entry collections. zod is this package's one runtime dependency, so this costs nothing new.
|
|
18
18
|
import { z } from "zod";
|
|
19
|
-
import {
|
|
19
|
+
import { MODEL_ENTRY_REFUSAL_CODES, ModelRefusalsWire } from "./fleet.js";
|
|
20
|
+
import { DOMAIN_SCHEMAS, GOVERNANCE_MIRROR_KEYS, HOSTS_ARRAY_MAX, HOST_NAME_MAX, InfraCostRates, LimitsConfig, PRIVATE_KEY_PEM_HEADER, SkillsManifestSchema, containsSecretToken } from "./types.js";
|
|
20
21
|
/** Redact secret-shaped tokens from a validation MESSAGE — a zod error echoes the offending value (e.g.
|
|
21
22
|
* `received 'sk-live-…'`), so a secret pasted into the WRONG field would leak via the error string. Redacts
|
|
22
23
|
* space-spanning auth credentials (`Bearer <token>`) FIRST (the token pass can't see across the space), then
|
|
23
24
|
* any secret-shaped run (`%` kept in the run so a percent-encoded secret stays whole for the percent-aware
|
|
24
|
-
* containsSecretToken). invariant ①: a secret VALUE never rides an error. R13/R14.
|
|
25
|
+
* containsSecretToken). invariant ①: a secret VALUE never rides an error. R13/R14.
|
|
26
|
+
*
|
|
27
|
+
* **Output invariant** (S-712 verification pass N1 + codex review [high] ×2): a string holding a private-key PEM
|
|
28
|
+
* header ANYWHERE ({@link PRIVATE_KEY_PEM_HEADER}) is withheld WHOLE, and no result trips `containsSecretToken`.
|
|
29
|
+
* The PEM header is judged on the ORIGINAL text, before any replacement: the token pass can rewrite the header
|
|
30
|
+
* itself away (`sk-note:-----BEGIN` is one token-charset run that starts with `sk-`, so it becomes
|
|
31
|
+
* `[redacted-secret]` and the key body after it would ride out untouched — codex verification round). A PEM header
|
|
32
|
+
* carries spaces, so the targeted passes can never isolate it, and the detector only sees it at the very start of
|
|
33
|
+
* a string. Every warning face that goes through here (coordinates, error text, entry identity) inherits the same
|
|
34
|
+
* guarantee. The validation-side detector `containsSecretToken` is deliberately NOT widened here: making it reject
|
|
35
|
+
* a mid-string PEM would tighten every field it validates (a major). */
|
|
25
36
|
export function redactSecrets(text) {
|
|
26
|
-
|
|
37
|
+
const targeted = text
|
|
27
38
|
.replace(/\b(bearer|basic)(\s+)\S+/gi, "$1$2[redacted-secret]")
|
|
28
39
|
.replace(/[A-Za-z0-9_./:+=%-]{6,}/g, (tok) => (containsSecretToken(tok) ? "[redacted-secret]" : tok));
|
|
40
|
+
return PRIVATE_KEY_PEM_HEADER.test(text) || containsSecretToken(targeted) ? "[redacted-secret]" : targeted;
|
|
29
41
|
}
|
|
30
42
|
/** Sanitize a ZodError IN PLACE so no surfaced error (message / `received` / nested `unionErrors`) echoes a
|
|
31
43
|
* pasted secret. RECURSES into `unionErrors` (a `z.union` failure nests a ZodError per member — e.g. RoleTarget
|
|
@@ -173,6 +185,64 @@ const PLAIN_PATH_SEGMENT = /^(?!(?:0|[1-9]\d*)$)[^."\u0000-\u001f]+$/;
|
|
|
173
185
|
export function formatConfigPath(p) {
|
|
174
186
|
return p.map((seg) => (typeof seg === "number" ? String(seg) : PLAIN_PATH_SEGMENT.test(seg) ? seg : JSON.stringify(seg))).join(".");
|
|
175
187
|
}
|
|
188
|
+
/** 身份串 —— 「一个用户写的身份串(条目的 `name` / `id`)能不能**原样**出门」的**唯一**判据:
|
|
189
|
+
* {@link redactSecrets} 原样放行。入参是 `string`、返回 `boolean`,**刻意不是**类型谓词(`v is string`):它对一些
|
|
190
|
+
* 字符串答 false,谓词形会让调用方的否定分支把一个 `string` 收窄成 `never`;「是不是字符串」由调用点自己判
|
|
191
|
+
* (运行期收到非字符串照答 false、不抛)。秘密形的串按**无名**处置(不带,而不是打码:打过码的串不再是身份)。
|
|
192
|
+
* 与坐标段脱敏、`error` 文案脱敏({@link sanitizeZodError})**同一只**判据:告警面上任何一处用户写的串,要么过这一只
|
|
193
|
+
* 脱敏器出门,要么(可缺的身份)不出门。两只判据会在同一条告警里给出两种答案(对抗轮 #2 真复现:`Bearer` 后接 NBSP
|
|
194
|
+
* 或短令牌时,旧判据放行、坐标却打码)。
|
|
195
|
+
* 执法面(同一条律):被丢条目的身份({@link DroppedEntryIdentity},本文件铸);`ModelRefusalsWire` 的 `entries` 键
|
|
196
|
+
* (经 {@link isReportableRefusalName},S-768);引擎自己的日志 / 拒因句里要不要写出一个名字,同样问这一只。 */
|
|
197
|
+
export function isDisclosableIdentity(v) {
|
|
198
|
+
return typeof v === "string" && redactSecrets(v) === v; // 非字符串(JS / any 调用方)⇒ false,不抛(秘密轴 fail-closed)
|
|
199
|
+
}
|
|
200
|
+
/** S-768 —— 「一个目录条目名能不能当 {@link ModelRefusalsWire} `entries` 的键」的**唯一**判据;引擎铸键前只调这一只,
|
|
201
|
+
* 不过 ⇒ 按无名处置(不进键、不计数)。两条律的合取,不手抄任何一条:
|
|
202
|
+
* ① 这只 schema **自己载得住**这个键 —— 就地拿 schema 判(空串不过键规则;`__proto__` 这类名被 zod 的 record 静默丢掉,
|
|
203
|
+
* 载不住),不写名单;
|
|
204
|
+
* ② {@link isDisclosableIdentity}(秘密形 ⇒ 不出门)。
|
|
205
|
+
* 非字符串 ⇒ false。住在本文件而不是 `./fleet`:`fleet.ts` 被 `types.ts` 引,它再引本文件会成环。 */
|
|
206
|
+
export function isReportableRefusalName(name) {
|
|
207
|
+
if (!isDisclosableIdentity(name))
|
|
208
|
+
return false;
|
|
209
|
+
const probe = ModelRefusalsWire.safeParse({ entries: { [name]: MODEL_ENTRY_REFUSAL_CODES[0] } });
|
|
210
|
+
return probe.success && Object.hasOwn(probe.data.entries, name);
|
|
211
|
+
}
|
|
212
|
+
/** 一条被丢条目原文的身份({@link DroppedEntryIdentity} 的**唯一**读法)。只看纯 JSON 对象的**自有数据
|
|
213
|
+
* 属性**;任何反射/陷阱抛错 ⇒ 当作读不出(身份是诊断面的附件,绝不许把抢救拖下水)。 */
|
|
214
|
+
function readEntryIdentity(entry) {
|
|
215
|
+
try {
|
|
216
|
+
if (!isPlainJsonObject(entry))
|
|
217
|
+
return undefined;
|
|
218
|
+
const own = (k) => {
|
|
219
|
+
const d = Object.getOwnPropertyDescriptor(entry, k);
|
|
220
|
+
return d !== undefined && "value" in d ? d.value : undefined;
|
|
221
|
+
};
|
|
222
|
+
const name = own("name");
|
|
223
|
+
const id = own("id");
|
|
224
|
+
const enabled = own("enabled");
|
|
225
|
+
const identity = {
|
|
226
|
+
...(typeof name === "string" && isDisclosableIdentity(name) ? { name } : {}),
|
|
227
|
+
...(typeof id === "string" && isDisclosableIdentity(id) ? { id } : {}),
|
|
228
|
+
...(typeof enabled === "boolean" ? { enabled } : {}),
|
|
229
|
+
};
|
|
230
|
+
return Object.keys(identity).length > 0 ? identity : undefined;
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/** 一条记账行 → 它的 `entry-dropped` 告警。域面({@link buildEffective})与 wire 面(skills manifest)共用
|
|
237
|
+
* 这**一只** —— 告警形只在这里拼,新字段不会在一条腿上漏铸。 */
|
|
238
|
+
function entryDroppedWarning(domain, d) {
|
|
239
|
+
return d.identity === undefined ? { domain, kind: "entry-dropped", path: d.path, error: d.error } : { domain, kind: "entry-dropped", path: d.path, error: d.error, identity: d.identity };
|
|
240
|
+
}
|
|
241
|
+
/** 一条被丢条目的记账行 —— `salvageEntries` 两种集合形(数组 / Record)共用的**唯一**铸点。 */
|
|
242
|
+
function droppedEntry(path, error, entry) {
|
|
243
|
+
const identity = readEntryIdentity(entry);
|
|
244
|
+
return identity === undefined ? { path, error } : { path, error, identity };
|
|
245
|
+
}
|
|
176
246
|
/** 一次「什么都没丢」的抢救的位移表(冻结的共享空表 —— {@link rebaseWarningPaths} 对它恒等)。 */
|
|
177
247
|
const NO_ENTRIES_DROPPED = Object.freeze({});
|
|
178
248
|
/** 一条条目的 parse —— 坏就返回它的(脱敏)错误。恶意 getter/Proxy 在遍历中抛错时**绝不**外泄其文案
|
|
@@ -250,7 +320,7 @@ function salvageEntriesUnsafe(schema, raw) {
|
|
|
250
320
|
keptFrom.push(i); // 压缩后第 keptFrom.length-1 条 = 原文档第 i 条
|
|
251
321
|
return true;
|
|
252
322
|
}
|
|
253
|
-
bad.push(
|
|
323
|
+
bad.push(droppedEntry([field, i], error, entry));
|
|
254
324
|
return false;
|
|
255
325
|
});
|
|
256
326
|
// 零幸存者 ⇒ 没有要抢救的东西(见头注:绝不凭空造出一个空集合)。
|
|
@@ -270,7 +340,7 @@ function salvageEntriesUnsafe(schema, raw) {
|
|
|
270
340
|
if (error === undefined)
|
|
271
341
|
kept[key] = entry;
|
|
272
342
|
else
|
|
273
|
-
bad.push(
|
|
343
|
+
bad.push(droppedEntry([field, redactSecrets(key)], error, entry)); // 键是用户写的,可能就是一条粘错位置的凭据
|
|
274
344
|
}
|
|
275
345
|
if (bad.length > 0 && Object.keys(kept).length > 0) {
|
|
276
346
|
dropped.push(...bad);
|
|
@@ -362,7 +432,7 @@ export function projectWarning(w) {
|
|
|
362
432
|
case "legacy-governance-lifted":
|
|
363
433
|
return { kind: w.kind, domain: w.domain, keys: w.keys };
|
|
364
434
|
case "entry-dropped":
|
|
365
|
-
return { kind: w.kind, domain: w.domain, path: w.path };
|
|
435
|
+
return { kind: w.kind, domain: w.domain, path: w.path, ...(w.identity === undefined ? {} : { identity: w.identity }) };
|
|
366
436
|
case "unread-config-file":
|
|
367
437
|
return { kind: w.kind, file: w.file, ...(w.domain === undefined ? {} : { domain: w.domain }), why: w.why };
|
|
368
438
|
default: {
|
|
@@ -764,7 +834,7 @@ export function buildEffective(version, getRaw, updatedAt, onWarning) {
|
|
|
764
834
|
// 不过本层自己的表,直接交给出口(出口会补上更外层的换算);`buffered` 里的是对**本层压缩后**
|
|
765
835
|
// 文档的诊断,是从下方上来的 ⇒ 先过本层的表,再交给出口。scan 函数一字不动。
|
|
766
836
|
for (const d of salvage.dropped)
|
|
767
|
-
emit?.(
|
|
837
|
+
emit?.(entryDroppedWarning(domain, d));
|
|
768
838
|
for (const w of buffered)
|
|
769
839
|
emit?.(rebaseWarningPaths(w, salvage.keptIndex));
|
|
770
840
|
return parsed;
|
|
@@ -875,7 +945,7 @@ export function readEffectiveWire(raw, onWarning) {
|
|
|
875
945
|
const retry = salvage.dropped.length > 0 ? SkillsManifestSchema.safeParse(salvage.raw) : undefined;
|
|
876
946
|
if (retry?.success) {
|
|
877
947
|
for (const d of salvage.dropped)
|
|
878
|
-
onWarning?.(
|
|
948
|
+
onWarning?.(entryDroppedWarning("skills", d));
|
|
879
949
|
skills = retry.data;
|
|
880
950
|
}
|
|
881
951
|
else {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 规范化(canonical JSON、告警投影、坐标深相等)全部在 kit 内**单铸** —— 消费方零复述,因为一份被各自
|
|
10
10
|
* 复述的规范化就是第二份真源,而两份真源的分歧正是本门要抓的那类病。
|
|
11
11
|
*/
|
|
12
|
-
import type { EffectiveReadWarning } from "../config-fns.js";
|
|
12
|
+
import type { EffectiveReadWarning, WarningProjection } from "../config-fns.js";
|
|
13
13
|
/** 三条读腿的名字。**闭集** —— 新开一条腿要同时在语料派生表(`derive.ts` 的 `DERIVATION_TRANSFORMS`)里表态。
|
|
14
14
|
*
|
|
15
15
|
* · `domains`:各域**原始文档**(center 侧存储形)→ effective。契约仓的 `buildEffective`。
|
|
@@ -192,6 +192,11 @@ export interface ConformanceNormalizer {
|
|
|
192
192
|
why: string;
|
|
193
193
|
/** 把一条坐标归一到共同空间。有损归一器在这里把段拼成串。 */
|
|
194
194
|
path(p: readonly (string | number)[]): unknown;
|
|
195
|
+
/** 把一条**告警投影**归一到共同空间(可选;先于 {@link ConformanceNormalizer.path} 作用,期望与实际两侧同过)。
|
|
196
|
+
* 用途:剥掉旧版**铸不出**的 additive 字段(S-712 的 `entry-dropped.identity`)—— 旧版没有这个字段不是读法
|
|
197
|
+
* 分歧,不许被记成「旧版行为红」。剥掉的字段同样是有损的一部分,`why` 里写明。release 模式禁归一器,故
|
|
198
|
+
* 这只钩子只作用于 historical 读数。 */
|
|
199
|
+
warning?(p: WarningProjection): WarningProjection;
|
|
195
200
|
}
|
|
196
201
|
/**
|
|
197
202
|
* 一格结局。`pass` / `expected-rejected` / `diff-ledgered` 不阻塞,`diff` / `harness-error` 红,
|
|
@@ -51,13 +51,20 @@ export function normalizeProjection(p, f) {
|
|
|
51
51
|
}
|
|
52
52
|
/** 恒等坐标映射(无归一器时的那一只)。 */
|
|
53
53
|
const identityPath = (path) => path;
|
|
54
|
+
/** 归一器对一条投影的**整体**作用:先过投影级钩子({@link ConformanceNormalizer.warning},可缺),再做坐标映射 +
|
|
55
|
+
* 集合字段按袋。比对键与报告值共用这一只,两处不会各归一各的。 */
|
|
56
|
+
function normalizedProjection(p, normalizer) {
|
|
57
|
+
if (normalizer === undefined)
|
|
58
|
+
return normalizeProjection(p, identityPath);
|
|
59
|
+
return normalizeProjection(normalizer.warning === undefined ? p : normalizer.warning(p), normalizer.path);
|
|
60
|
+
}
|
|
54
61
|
/** 一条投影的比对键。 */
|
|
55
62
|
export function projectionKey(p, normalizer) {
|
|
56
|
-
return stableKey(
|
|
63
|
+
return stableKey(normalizedProjection(p, normalizer));
|
|
57
64
|
}
|
|
58
65
|
/** 一条投影的规范形(进报告的 `missing`/`extra` 用,人读)。 */
|
|
59
66
|
export function projectionValue(p, normalizer) {
|
|
60
|
-
return canonicalize(
|
|
67
|
+
return canonicalize(normalizedProjection(p, normalizer));
|
|
61
68
|
}
|
|
62
69
|
/**
|
|
63
70
|
* 两个投影**多重集**的差。重数保留:期望两条、实际一条 ⇒ `missing` 里留一条。
|
package/dist/fleet.d.ts
CHANGED
|
@@ -45,6 +45,142 @@ export type DrainVerdict = "wait" | "exit_clean" | "exit_forced";
|
|
|
45
45
|
/** The drain state machine's single decision point: keep waiting, exit clean (drained), or force the exit
|
|
46
46
|
* (grace exhausted with legs still in flight — durable legs resume cross-instance via resumeFromRunId). */
|
|
47
47
|
export declare function drainVerdict(s: DrainSnapshot): DrainVerdict;
|
|
48
|
+
/**
|
|
49
|
+
* "This instance must restart to apply config" — the engine's OWN observation (S-741), defined ONCE for both exits that
|
|
50
|
+
* carry it: the fleet heartbeat ({@link InstanceAnnouncement}`.restart`, typed) and GET /health (`restart`, read through
|
|
51
|
+
* {@link readRestartSignal} — {@link InstanceHealth} keeps carrying it untyped, see there). One mint point, two exits —
|
|
52
|
+
* never a second judgement.
|
|
53
|
+
*
|
|
54
|
+
* Shape = what the server already ships on /health (`restart: { reasons, version, since }`). PRESENCE is the signal:
|
|
55
|
+
* present ⇔ a restart is required, absent ⇔ not; there is deliberately no `required` boolean inside (a boolean next
|
|
56
|
+
* to presence is redundant, and two spellings of one fact drift).
|
|
57
|
+
* - `reasons` — which restart-to-apply slices differ from the value baked at boot (the vocabulary is the engine's;
|
|
58
|
+
* the contract only guarantees non-empty words, at least one).
|
|
59
|
+
* - `version` — the config-center version that currently differs from boot (the LATEST seen — it moves forward with
|
|
60
|
+
* later publishes, so a "one restart per version" dedupe bounds restarts per config version, not per reason set).
|
|
61
|
+
* Judged exactly as `readEffectiveWire` judges a config version (a finite number) — one quantity, one rule.
|
|
62
|
+
* - `since` — epoch ms when this restart first became required (stable across refreshes while `reasons` holds).
|
|
63
|
+
*/
|
|
64
|
+
export declare const RestartSignal: z.ZodObject<{
|
|
65
|
+
reasons: z.ZodArray<z.ZodString, "many">;
|
|
66
|
+
version: z.ZodNumber;
|
|
67
|
+
since: z.ZodNumber;
|
|
68
|
+
}, "strip", z.ZodTypeAny, {
|
|
69
|
+
reasons: string[];
|
|
70
|
+
version: number;
|
|
71
|
+
since: number;
|
|
72
|
+
}, {
|
|
73
|
+
reasons: string[];
|
|
74
|
+
version: number;
|
|
75
|
+
since: number;
|
|
76
|
+
}>;
|
|
77
|
+
export type RestartSignal = z.infer<typeof RestartSignal>;
|
|
78
|
+
/**
|
|
79
|
+
* The /health exit of {@link RestartSignal}: `health.restart` judged against the one shape. Shape mismatch, a missing
|
|
80
|
+
* key, a non-object body, even a throwing accessor ⇒ `undefined` — never throws (a probe must not die on one odd body).
|
|
81
|
+
* Presence law: only `restart` counts; the top-level `restartRequired` boolean the server also ships is not read.
|
|
82
|
+
*/
|
|
83
|
+
export declare function readRestartSignal(health: unknown): RestartSignal | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* The model-refusal cause codes (S-768) — CLOSED sets, owned HERE, never a hand-copied second list. Adding a code is
|
|
86
|
+
* one edit in this file; who goes red at compile time: a reader that switches on the code exhaustively (a center), and
|
|
87
|
+
* a producer only where it holds a table KEYED BY the code (an exhaustive `switch` over the code, or a
|
|
88
|
+
* `Record<ModelEntryRefusalCode, …>`) — a producer table that merely `satisfies` these codes as values proves a subset,
|
|
89
|
+
* not equality, and stays green.
|
|
90
|
+
*
|
|
91
|
+
* - {@link MODEL_ENTRY_REFUSAL_CODES} — why ONE catalog entry is not served on this host (the other entries keep serving):
|
|
92
|
+
* `config.model_no_brain` — the entry's provider rides a brain this host has not mounted;
|
|
93
|
+
* `config.model_entry_invalid` — the entry does not match the `ModelEntry` shape (dropped by the read, or re-judged at
|
|
94
|
+
* the engine's own second boundary).
|
|
95
|
+
* - {@link MODEL_PLANE_REFUSAL_CODES} — why the model plane of the catalog the engine was handed was refused AS A GROUP
|
|
96
|
+
* (the engine keeps serving the model plane it had). This set mirrors the engine's group-refusal code TABLE as a
|
|
97
|
+
* whole family — never picked by which invariant is reachable on the hot path today (reachability moves, the table
|
|
98
|
+
* does not):
|
|
99
|
+
* `config.default_model_missing` — the candidate has no default model;
|
|
100
|
+
* `config.model_refused_name_minted` — a refused entry's name would be re-minted onto another entry by the
|
|
101
|
+
* tier / alias expansion (or the dropped-entry carrier cannot prove it would not be);
|
|
102
|
+
* `config.model_route_endpoint_missing` — an openai-compatible route of the candidate resolves to no endpoint;
|
|
103
|
+
* `config.gateway_fallback_without_primary` — fallback gateways are declared without a primary.
|
|
104
|
+
* Source (engine): the first two in `src/model-entry-refusal.ts`, the last two in `src/model-route-endpoint.ts:40,42`
|
|
105
|
+
* (invariant `model-route-endpoint`, judged by the model-plane stage in `src/config-center/apply-effective.ts:664-665`).
|
|
106
|
+
*/
|
|
107
|
+
export declare const MODEL_ENTRY_REFUSAL_CODES: readonly ["config.model_no_brain", "config.model_entry_invalid"];
|
|
108
|
+
export declare const ModelEntryRefusalCode: z.ZodEnum<["config.model_no_brain", "config.model_entry_invalid"]>;
|
|
109
|
+
export type ModelEntryRefusalCode = z.infer<typeof ModelEntryRefusalCode>;
|
|
110
|
+
export declare const MODEL_PLANE_REFUSAL_CODES: readonly ["config.default_model_missing", "config.model_refused_name_minted", "config.model_route_endpoint_missing", "config.gateway_fallback_without_primary"];
|
|
111
|
+
export declare const ModelPlaneRefusalCode: z.ZodEnum<["config.default_model_missing", "config.model_refused_name_minted", "config.model_route_endpoint_missing", "config.gateway_fallback_without_primary"]>;
|
|
112
|
+
export type ModelPlaneRefusalCode = z.infer<typeof ModelPlaneRefusalCode>;
|
|
113
|
+
/**
|
|
114
|
+
* "This instance does not serve part (or all) of the model catalog it was handed" — the engine's OWN per-host verdict
|
|
115
|
+
* (S-768), defined ONCE for both exits that carry it: the fleet heartbeat ({@link InstanceAnnouncement}`.modelRefusals`)
|
|
116
|
+
* and GET /health (`modelRefusals`; {@link InstanceHealth} keeps carrying it untyped, as it does `restart`). One mint point,
|
|
117
|
+
* two exits, ONE reader for both: {@link readModelRefusals}.
|
|
118
|
+
*
|
|
119
|
+
* Two faces, split on purpose — the failure direction must match the meaning (this is a DIAGNOSTIC key; an unknown word
|
|
120
|
+
* in it must never make a healthy instance unroutable):
|
|
121
|
+
* - {@link ModelRefusalsWire} — what rides the wire and what a stored heartbeat row holds: the SHAPE law only, codes as
|
|
122
|
+
* open non-empty strings. A code this package version does not know passes (it is "not said" to this reader, and the
|
|
123
|
+
* stored row still carries it for a reader that is upgraded later).
|
|
124
|
+
* - {@link ModelRefusalsSignal} — what {@link readModelRefusals} returns: the wire narrowed to the closed sets above (an
|
|
125
|
+
* unknown entry code or plane code is dropped; narrowed to nothing ⇒ `undefined`, rendered like "not said"). The
|
|
126
|
+
* closed-set types stay the compile-time red source for readers when a code is added.
|
|
127
|
+
*
|
|
128
|
+
* The wire shape:
|
|
129
|
+
* - `entries` — catalog entry NAME → its code. The value is ONLY the code, on purpose: the engine's refusal fact also
|
|
130
|
+
* holds the upstream `id`, the `provider` word, the bad field keys and an operator sentence, and none of them rides
|
|
131
|
+
* here — an `entry_invalid` row is by definition one nothing validated, so its `id` / `provider` may be whatever the
|
|
132
|
+
* broken row held, and the sentence is prose, not contract. "Which name, which reason" is the whole wire. A name that
|
|
133
|
+
* cannot ride out is treated as NAMELESS — the engine leaves it out of this map entirely (not keyed, not counted). The
|
|
134
|
+
* ONE predicate is `isReportableRefusalName(name)` (main barrel): this schema can carry the key (non-empty; `__proto__`
|
|
135
|
+
* is silently dropped by the record, so it cannot) AND `isDisclosableIdentity(name)` (not secret-shaped — the rule the
|
|
136
|
+
* dropped-entry identity on read warnings already uses). A producer calls it before keying; this schema deliberately
|
|
137
|
+
* does not re-judge secrecy (see the 6.2.0 notes).
|
|
138
|
+
* - `plane` — present ⇔ the model plane of the latest catalog the engine was handed was refused as a group (the engine
|
|
139
|
+
* keeps serving the model plane it had); ONE code, never a list — one candidate has exactly one group verdict (the
|
|
140
|
+
* engine judges the group invariants in a fixed order and stops at the first that fails; a refusal from the Runner
|
|
141
|
+
* swap step carries no code and is outside this signal).
|
|
142
|
+
* - `entries` and `plane` describe the same latest candidate catalog — the one the engine was handed last; with `plane`
|
|
143
|
+
* present, that is the refused candidate (its per-entry verdicts are usually why it has no default). The engine does
|
|
144
|
+
* not swap its own refusal table while the group stays refused (its selection channels still refuse by the previous
|
|
145
|
+
* generation), so with `plane` present read `entries` as "why this catalog cannot land", not "what a pick is refused
|
|
146
|
+
* right now".
|
|
147
|
+
*
|
|
148
|
+
* PRESENCE is the signal (same law as {@link RestartSignal}): present ⇔ at least one reportable refusal (an entry refusal
|
|
149
|
+
* under a name that rides out, or a group refusal). An empty signal (no entries and no `plane`) is refused by the shape
|
|
150
|
+
* law — "none" has exactly one spelling, the absent key. Inside, "no entry refusals" is spelled `entries: {}` (the key is
|
|
151
|
+
* required) — one spelling at each level.
|
|
152
|
+
*/
|
|
153
|
+
export declare const ModelRefusalsWire: z.ZodEffects<z.ZodObject<{
|
|
154
|
+
entries: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
155
|
+
plane: z.ZodOptional<z.ZodString>;
|
|
156
|
+
}, "strip", z.ZodTypeAny, {
|
|
157
|
+
entries: Record<string, string>;
|
|
158
|
+
plane?: string | undefined;
|
|
159
|
+
}, {
|
|
160
|
+
entries: Record<string, string>;
|
|
161
|
+
plane?: string | undefined;
|
|
162
|
+
}>, {
|
|
163
|
+
entries: Record<string, string>;
|
|
164
|
+
plane?: string | undefined;
|
|
165
|
+
}, {
|
|
166
|
+
entries: Record<string, string>;
|
|
167
|
+
plane?: string | undefined;
|
|
168
|
+
}>;
|
|
169
|
+
export type ModelRefusalsWire = z.infer<typeof ModelRefusalsWire>;
|
|
170
|
+
/** {@link ModelRefusalsWire} narrowed to the closed code sets — the return type of {@link readModelRefusals}. */
|
|
171
|
+
export type ModelRefusalsSignal = {
|
|
172
|
+
entries: Record<string, ModelEntryRefusalCode>;
|
|
173
|
+
plane?: ModelPlaneRefusalCode;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* The ONE reader of the model-refusals signal, for BOTH exits: pass a /health body or a parsed
|
|
177
|
+
* {@link InstanceAnnouncement} — it reads the `modelRefusals` key of whatever it is given. Shape mismatch, a missing key, a
|
|
178
|
+
* non-object body, even a throwing accessor ⇒ `undefined` — never throws. Then it narrows to the closed sets: an unknown
|
|
179
|
+
* entry code drops that entry, an unknown plane code drops `plane`; narrowed to nothing ⇒ `undefined`. So `undefined`
|
|
180
|
+
* means "nothing this reader can say"; whether that is "none" or "unknown" depends on whether the engine reports this key
|
|
181
|
+
* at all — it answers the key only and never judges the engine's version (see {@link InstanceAnnouncement}`.modelRefusals`).
|
|
182
|
+
*/
|
|
183
|
+
export declare function readModelRefusals(body: unknown): ModelRefusalsSignal | undefined;
|
|
48
184
|
/** The `/health` self-description every orchestrator reads (shipped shape, server 1.96.0). Liveness and
|
|
49
185
|
* readiness are deliberately SPLIT: `status` stays ok while draining (the process is alive); routability is
|
|
50
186
|
* `isReadyForTraffic`. Unknown fields pass through — health is a self-description, not a closed record. */
|
|
@@ -93,25 +229,83 @@ export declare const InstanceAnnouncement: z.ZodObject<{
|
|
|
93
229
|
/** reachable base URL for the deployment form: k8s pod IP:port / docker net alias / 127.0.0.1:<engine.port> */
|
|
94
230
|
address: z.ZodString;
|
|
95
231
|
version: z.ZodString;
|
|
96
|
-
/** self-declared:
|
|
232
|
+
/** self-declared readiness: model roster landed AND not draining (server ≥7.102.0; older engines: !draining only) */
|
|
97
233
|
ready: z.ZodBoolean;
|
|
98
234
|
draining: z.ZodDefault<z.ZodBoolean>;
|
|
99
235
|
heartbeatMs: z.ZodDefault<z.ZodNumber>;
|
|
236
|
+
/** S-741: present ⇔ this instance must restart to apply config — the SAME {@link RestartSignal} /health carries,
|
|
237
|
+
* minted by the same getter (self-reported: a worker-scoped credential can only report for its own worker).
|
|
238
|
+
* Every heartbeat re-announces the full row, so a later heartbeat WITHOUT it means "no longer required" — the
|
|
239
|
+
* stored row ({@link InstanceRegistration}) is replaced, never merged. */
|
|
240
|
+
restart: z.ZodOptional<z.ZodObject<{
|
|
241
|
+
reasons: z.ZodArray<z.ZodString, "many">;
|
|
242
|
+
version: z.ZodNumber;
|
|
243
|
+
since: z.ZodNumber;
|
|
244
|
+
}, "strip", z.ZodTypeAny, {
|
|
245
|
+
reasons: string[];
|
|
246
|
+
version: number;
|
|
247
|
+
since: number;
|
|
248
|
+
}, {
|
|
249
|
+
reasons: string[];
|
|
250
|
+
version: number;
|
|
251
|
+
since: number;
|
|
252
|
+
}>>;
|
|
253
|
+
/** S-768: present ⇔ this instance has at least one reportable model refusal — the SAME {@link ModelRefusalsWire} /health
|
|
254
|
+
* carries, minted by the same getter; read it with {@link readModelRefusals}`(announcement)` (never off this field
|
|
255
|
+
* directly: the field is the open wire form, the reader narrows to the closed sets). Every heartbeat re-announces the
|
|
256
|
+
* full row, so a later heartbeat WITHOUT it means "no reportable refusals any more" (the stored row is replaced, never
|
|
257
|
+
* merged). Reading absence: from an engine that reports this key, absent = none reportable; an engine that predates
|
|
258
|
+
* the key never sends it, so there absence is UNKNOWN, not "none" — tell the two apart by the announced `version`.
|
|
259
|
+
* Failure direction = meaning direction (this is a diagnostic key): a code this package version does not know passes
|
|
260
|
+
* (it is "not said" to the reader), and a value that breaks the {@link ModelRefusalsWire} shape law is dropped to
|
|
261
|
+
* ABSENT (`.catch`) — it never fails the announcement, so it can never make a healthy instance unroutable (unlike
|
|
262
|
+
* `restart`, whose shape still gates the row). The key may then be present with the value `undefined`. */
|
|
263
|
+
modelRefusals: z.ZodCatch<z.ZodOptional<z.ZodEffects<z.ZodObject<{
|
|
264
|
+
entries: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
265
|
+
plane: z.ZodOptional<z.ZodString>;
|
|
266
|
+
}, "strip", z.ZodTypeAny, {
|
|
267
|
+
entries: Record<string, string>;
|
|
268
|
+
plane?: string | undefined;
|
|
269
|
+
}, {
|
|
270
|
+
entries: Record<string, string>;
|
|
271
|
+
plane?: string | undefined;
|
|
272
|
+
}>, {
|
|
273
|
+
entries: Record<string, string>;
|
|
274
|
+
plane?: string | undefined;
|
|
275
|
+
}, {
|
|
276
|
+
entries: Record<string, string>;
|
|
277
|
+
plane?: string | undefined;
|
|
278
|
+
}>>>;
|
|
100
279
|
}, "strip", z.ZodTypeAny, {
|
|
101
280
|
draining: boolean;
|
|
102
|
-
instanceId: string;
|
|
103
281
|
version: string;
|
|
282
|
+
instanceId: string;
|
|
104
283
|
worker: string;
|
|
105
284
|
address: string;
|
|
106
285
|
ready: boolean;
|
|
107
286
|
heartbeatMs: number;
|
|
287
|
+
restart?: {
|
|
288
|
+
reasons: string[];
|
|
289
|
+
version: number;
|
|
290
|
+
since: number;
|
|
291
|
+
} | undefined;
|
|
292
|
+
modelRefusals?: {
|
|
293
|
+
entries: Record<string, string>;
|
|
294
|
+
plane?: string | undefined;
|
|
295
|
+
} | undefined;
|
|
108
296
|
}, {
|
|
109
|
-
instanceId: string;
|
|
110
297
|
version: string;
|
|
298
|
+
instanceId: string;
|
|
111
299
|
worker: string;
|
|
112
300
|
address: string;
|
|
113
301
|
ready: boolean;
|
|
114
302
|
draining?: boolean | undefined;
|
|
303
|
+
restart?: {
|
|
304
|
+
reasons: string[];
|
|
305
|
+
version: number;
|
|
306
|
+
since: number;
|
|
307
|
+
} | undefined;
|
|
308
|
+
modelRefusals?: unknown;
|
|
115
309
|
heartbeatMs?: number | undefined;
|
|
116
310
|
}>;
|
|
117
311
|
export type InstanceAnnouncement = z.infer<typeof InstanceAnnouncement>;
|
|
@@ -139,34 +333,92 @@ export declare const InstanceRegistration: z.ZodObject<{
|
|
|
139
333
|
/** reachable base URL for the deployment form: k8s pod IP:port / docker net alias / 127.0.0.1:<engine.port> */
|
|
140
334
|
address: z.ZodString;
|
|
141
335
|
version: z.ZodString;
|
|
142
|
-
/** self-declared:
|
|
336
|
+
/** self-declared readiness: model roster landed AND not draining (server ≥7.102.0; older engines: !draining only) */
|
|
143
337
|
ready: z.ZodBoolean;
|
|
144
338
|
draining: z.ZodDefault<z.ZodBoolean>;
|
|
145
339
|
heartbeatMs: z.ZodDefault<z.ZodNumber>;
|
|
340
|
+
/** S-741: present ⇔ this instance must restart to apply config — the SAME {@link RestartSignal} /health carries,
|
|
341
|
+
* minted by the same getter (self-reported: a worker-scoped credential can only report for its own worker).
|
|
342
|
+
* Every heartbeat re-announces the full row, so a later heartbeat WITHOUT it means "no longer required" — the
|
|
343
|
+
* stored row ({@link InstanceRegistration}) is replaced, never merged. */
|
|
344
|
+
restart: z.ZodOptional<z.ZodObject<{
|
|
345
|
+
reasons: z.ZodArray<z.ZodString, "many">;
|
|
346
|
+
version: z.ZodNumber;
|
|
347
|
+
since: z.ZodNumber;
|
|
348
|
+
}, "strip", z.ZodTypeAny, {
|
|
349
|
+
reasons: string[];
|
|
350
|
+
version: number;
|
|
351
|
+
since: number;
|
|
352
|
+
}, {
|
|
353
|
+
reasons: string[];
|
|
354
|
+
version: number;
|
|
355
|
+
since: number;
|
|
356
|
+
}>>;
|
|
357
|
+
/** S-768: present ⇔ this instance has at least one reportable model refusal — the SAME {@link ModelRefusalsWire} /health
|
|
358
|
+
* carries, minted by the same getter; read it with {@link readModelRefusals}`(announcement)` (never off this field
|
|
359
|
+
* directly: the field is the open wire form, the reader narrows to the closed sets). Every heartbeat re-announces the
|
|
360
|
+
* full row, so a later heartbeat WITHOUT it means "no reportable refusals any more" (the stored row is replaced, never
|
|
361
|
+
* merged). Reading absence: from an engine that reports this key, absent = none reportable; an engine that predates
|
|
362
|
+
* the key never sends it, so there absence is UNKNOWN, not "none" — tell the two apart by the announced `version`.
|
|
363
|
+
* Failure direction = meaning direction (this is a diagnostic key): a code this package version does not know passes
|
|
364
|
+
* (it is "not said" to the reader), and a value that breaks the {@link ModelRefusalsWire} shape law is dropped to
|
|
365
|
+
* ABSENT (`.catch`) — it never fails the announcement, so it can never make a healthy instance unroutable (unlike
|
|
366
|
+
* `restart`, whose shape still gates the row). The key may then be present with the value `undefined`. */
|
|
367
|
+
modelRefusals: z.ZodCatch<z.ZodOptional<z.ZodEffects<z.ZodObject<{
|
|
368
|
+
entries: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
369
|
+
plane: z.ZodOptional<z.ZodString>;
|
|
370
|
+
}, "strip", z.ZodTypeAny, {
|
|
371
|
+
entries: Record<string, string>;
|
|
372
|
+
plane?: string | undefined;
|
|
373
|
+
}, {
|
|
374
|
+
entries: Record<string, string>;
|
|
375
|
+
plane?: string | undefined;
|
|
376
|
+
}>, {
|
|
377
|
+
entries: Record<string, string>;
|
|
378
|
+
plane?: string | undefined;
|
|
379
|
+
}, {
|
|
380
|
+
entries: Record<string, string>;
|
|
381
|
+
plane?: string | undefined;
|
|
382
|
+
}>>>;
|
|
146
383
|
} & {
|
|
147
384
|
registeredMs: z.ZodNumber;
|
|
148
385
|
lastSeenMs: z.ZodNumber;
|
|
149
386
|
observedReady: z.ZodOptional<z.ZodBoolean>;
|
|
150
387
|
}, "strip", z.ZodTypeAny, {
|
|
151
388
|
draining: boolean;
|
|
152
|
-
instanceId: string;
|
|
153
389
|
version: string;
|
|
390
|
+
instanceId: string;
|
|
154
391
|
worker: string;
|
|
155
392
|
address: string;
|
|
156
393
|
ready: boolean;
|
|
157
394
|
heartbeatMs: number;
|
|
158
395
|
registeredMs: number;
|
|
159
396
|
lastSeenMs: number;
|
|
397
|
+
restart?: {
|
|
398
|
+
reasons: string[];
|
|
399
|
+
version: number;
|
|
400
|
+
since: number;
|
|
401
|
+
} | undefined;
|
|
402
|
+
modelRefusals?: {
|
|
403
|
+
entries: Record<string, string>;
|
|
404
|
+
plane?: string | undefined;
|
|
405
|
+
} | undefined;
|
|
160
406
|
observedReady?: boolean | undefined;
|
|
161
407
|
}, {
|
|
162
|
-
instanceId: string;
|
|
163
408
|
version: string;
|
|
409
|
+
instanceId: string;
|
|
164
410
|
worker: string;
|
|
165
411
|
address: string;
|
|
166
412
|
ready: boolean;
|
|
167
413
|
registeredMs: number;
|
|
168
414
|
lastSeenMs: number;
|
|
169
415
|
draining?: boolean | undefined;
|
|
416
|
+
restart?: {
|
|
417
|
+
reasons: string[];
|
|
418
|
+
version: number;
|
|
419
|
+
since: number;
|
|
420
|
+
} | undefined;
|
|
421
|
+
modelRefusals?: unknown;
|
|
170
422
|
heartbeatMs?: number | undefined;
|
|
171
423
|
observedReady?: boolean | undefined;
|
|
172
424
|
}>;
|
|
@@ -410,7 +662,6 @@ export declare const UsageReport: z.ZodObject<{
|
|
|
410
662
|
quotaWeightAtUse?: number | undefined;
|
|
411
663
|
}>, "many">;
|
|
412
664
|
}, "strip", z.ZodTypeAny, {
|
|
413
|
-
instanceId: string;
|
|
414
665
|
entries: {
|
|
415
666
|
principal: string;
|
|
416
667
|
model: string;
|
|
@@ -421,11 +672,11 @@ export declare const UsageReport: z.ZodObject<{
|
|
|
421
672
|
runs?: number | undefined;
|
|
422
673
|
quotaWeightAtUse?: number | undefined;
|
|
423
674
|
}[];
|
|
675
|
+
instanceId: string;
|
|
424
676
|
worker: string;
|
|
425
677
|
windowStartMs: number;
|
|
426
678
|
windowEndMs: number;
|
|
427
679
|
}, {
|
|
428
|
-
instanceId: string;
|
|
429
680
|
entries: {
|
|
430
681
|
principal: string;
|
|
431
682
|
model: string;
|
|
@@ -436,6 +687,7 @@ export declare const UsageReport: z.ZodObject<{
|
|
|
436
687
|
runs?: number | undefined;
|
|
437
688
|
quotaWeightAtUse?: number | undefined;
|
|
438
689
|
}[];
|
|
690
|
+
instanceId: string;
|
|
439
691
|
worker: string;
|
|
440
692
|
windowStartMs: number;
|
|
441
693
|
windowEndMs: number;
|