@sema-agent/settings-schema 10.1.1 → 11.1.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 +469 -1
- package/README.md +144 -66
- package/bin/settings-schema.mjs +10 -12
- package/conformance/VERSION +1 -1
- package/conformance/baseline.json +3 -1
- package/conformance/cases/c01-entry-salvage-then-typo/expected.json +3 -1
- package/conformance/cases/c02-hosts-clamp-stacked/expected.json +12 -5
- package/conformance/cases/c02-hosts-clamp-stacked/meta.json +1 -1
- package/conformance/cases/c03-literal-dotted-top-key/expected.json +22 -2
- package/conformance/cases/c04-all-bad-collection-not-salvaged/expected.json +3 -1
- package/conformance/cases/c06-legacy-governance-lift/expected.json +11 -101
- package/conformance/cases/c06-legacy-governance-lift/meta.json +1 -1
- package/conformance/cases/c07-hooks-is-not-a-config-domain/expected.json +3 -1
- package/conformance/cases/c08-limits-unknown-carried-64/expected.json +203 -2
- package/conformance/cases/c09-limits-unknown-carried-65/expected.json +204 -2
- package/conformance/cases/c10-record-key-secret-redacted-twice/expected.json +3 -1
- package/conformance/cases/c11-unicode-unknown-keys/expected.json +20 -2
- package/conformance/cases/c12-prototype-name-top-keys/expected.json +3 -1
- package/conformance/cases/c13-models-field-null/expected.json +3 -1
- package/conformance/cases/c14-models-field-empty-array/expected.json +6 -1
- package/conformance/cases/c15-models-field-number/expected.json +3 -1
- package/conformance/cases/c16-deep-nesting-205/expected.json +14 -2
- package/conformance/cases/c17-empty-domain-file/expected.json +3 -1
- package/conformance/cases/c18-wire-envelope-not-an-object/expected.json +3 -1
- package/conformance/cases/c19-wire-envelope-no-version/expected.json +3 -1
- package/conformance/cases/c20-governance-absent-lifts/expected.json +11 -86
- package/conformance/cases/c20-governance-absent-lifts/meta.json +1 -1
- package/conformance/cases/c21-governance-empty-object-wins/expected.json +6 -5
- package/conformance/cases/c21-governance-empty-object-wins/input.json +1 -4
- package/conformance/cases/c21-governance-empty-object-wins/meta.json +1 -1
- package/conformance/cases/c23-mixed-validity-models/expected.json +3 -1
- package/conformance/cases/c24-valid-entries-with-unknown-keys/expected.json +22 -2
- package/conformance/cases/c25-published-roundtrip-zero-warnings/expected.json +6 -1
- package/conformance/cases/c26-hosts-array-1024/expected.json +3 -1
- package/conformance/cases/c27-hosts-array-1025/expected.json +6 -7175
- package/conformance/cases/c27-hosts-array-1025/meta.json +1 -1
- package/conformance/cases/c28-large-document-over-1mb/expected.json +14 -2
- package/conformance/cases/c29-salvage-then-domain-refine-fails/expected.json +3 -1
- package/conformance/cases/c30-version-triple-a-first/expected.json +6 -1
- package/conformance/cases/c31-version-triple-b-changed/expected.json +6 -1
- package/conformance/cases/c32-version-triple-a-restored/expected.json +6 -1
- package/conformance/cases/c33-limits-nested-carried-key/expected.json +18 -2
- package/conformance/cases/c34-skills-content-vs-manifest/expected.json +19 -3
- package/conformance/cases/c35-portable-domain-file-is-null/expected.json +3 -1
- package/conformance/cases/c36-skills-entry-without-content/expected.json +6 -2
- package/conformance/cases/c37-skills-field-null/expected.json +6 -2
- package/conformance/cases/c38-remote-exec-is-a-non-domain-file/expected.json +3 -1
- package/conformance/cases/c39-hidden-file-in-config-d/expected.json +3 -1
- package/conformance/cases/c40-s712-identity-only-what-reads-out/expected.json +11 -4
- package/conformance/cases/c41-s792-encoded-pem-identity/expected.json +9 -3
- package/conformance/cases/c42-s792-unknown-key-secret-redacted/expected.json +39 -2
- package/conformance/cases/c43-s792-form-and-deep-encoded-pem/expected.json +4 -1
- package/conformance/cases/c44-s792-utf8-escape-glued-token/expected.json +3 -1
- package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/expected.json +19 -10
- package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/meta.json +1 -1
- package/conformance/cases/{c46-s811-host-name-url-userinfo-characterization → c46-s811-host-name-url-userinfo-withheld}/expected.json +12 -5
- package/conformance/cases/c46-s811-host-name-url-userinfo-withheld/meta.json +9 -0
- package/conformance/cases/{c47-s811-roles-key-url-userinfo-characterization → c47-s811-roles-key-url-userinfo-withheld}/expected.json +23 -3
- package/conformance/cases/{c47-s811-roles-key-url-userinfo-characterization → c47-s811-roles-key-url-userinfo-withheld}/input.json +6 -0
- package/conformance/cases/c47-s811-roles-key-url-userinfo-withheld/meta.json +10 -0
- package/conformance/cases/c48-s935-model-headers-transported/expected.json +6 -1
- package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/expected.json +3 -1
- package/conformance/cases/c50-s784-lane-host-legal/expected.json +3 -1
- package/conformance/cases/c51-s784-lane-e2b-legal/expected.json +3 -1
- package/conformance/cases/c52-s784-lane-k8s-legal/expected.json +3 -1
- package/conformance/cases/c53-s784-lane-ssh-legal/expected.json +3 -1
- package/conformance/cases/c54-s784-lane-adb-legal/expected.json +3 -1
- package/conformance/cases/c55-s784-lane-local-docker-legal/expected.json +3 -1
- package/conformance/cases/c56-s784-lane-device-legal/expected.json +3 -1
- package/conformance/cases/c57-s784-g1-enabled-no-lane/expected.json +3 -1
- package/conformance/cases/c58-s784-g2-multitenant-host/expected.json +3 -1
- package/conformance/cases/c59-s784-g3-k8s-missing-image/expected.json +3 -1
- package/conformance/cases/c60-s784-g3-e2b-missing-key/expected.json +3 -1
- package/conformance/cases/c61-s784-g3-other-lane-key/expected.json +3 -1
- package/conformance/cases/c62-s784-g7a-session-tidb/expected.json +3 -1
- package/conformance/cases/c63-s784-host-two-replicas/expected.json +3 -1
- package/conformance/cases/c64-s784-multitenant-local-store/expected.json +3 -1
- package/conformance/cases/c65-s784-pg-without-db/expected.json +3 -1
- package/conformance/cases/c66-s784-memory-backend-mismatch/expected.json +3 -1
- package/conformance/cases/c67-s784-retired-field-loud/expected.json +3 -1
- package/conformance/cases/c67-s784-retired-field-loud/meta.json +1 -1
- package/conformance/cases/c68-s784-device-local-store/expected.json +3 -1
- package/conformance/cases/c69-deep-encoded-token-past-every-reading/expected.json +3 -1
- package/conformance/cases/c70-rewritten-token-in-front-of-encoded-pem-header/expected.json +3 -1
- package/conformance/cases/c71-one-scanner-write-reads-display-forms/expected.json +119 -0
- package/conformance/cases/c71-one-scanner-write-reads-display-forms/input.json +24 -0
- package/conformance/cases/c71-one-scanner-write-reads-display-forms/meta.json +10 -0
- package/conformance/cases/c72-dangling-reference-write-seat-refuses/expected.json +111 -0
- package/conformance/cases/c72-dangling-reference-write-seat-refuses/input.json +26 -0
- package/conformance/cases/c72-dangling-reference-write-seat-refuses/meta.json +6 -0
- package/conformance/cases/cc01-mcp-toolfaces-non-object/expected.json +3 -1
- package/conformance/cases/cc02-mcp-source-key-is-transported/expected.json +3 -1
- package/conformance/cases/cc03-model-compat-unknown-subkey/expected.json +6 -1
- package/conformance/cases/cli01-model-name-nfc-vs-nfd/expected.json +11 -1
- package/conformance/cases/cli02-control-and-zero-width-chars/expected.json +11 -1
- package/conformance/cases/cli03-credential-pasted-into-unknown-key/expected.json +16 -2
- package/conformance/cases/cli04-env-value-with-newline/expected.json +3 -1
- package/conformance/cases/cli05-long-and-prefix-colliding-names/expected.json +6 -1
- package/conformance/cases/t01-absent-domain-key-not-null/expected.json +3 -1
- package/conformance/cases/t03-prototype-name-key-not-swallowed/expected.json +29 -2
- package/conformance/cases/t04-secret-literal-never-in-warning-value/expected.json +3 -1
- package/conformance/cases/z01-every-domain-empty-object/expected.json +8 -4
- package/conformance/cases/z01-every-domain-empty-object/input.json +0 -1
- package/conformance/cases/z01-every-domain-empty-object/meta.json +1 -1
- package/conformance/cases/z02-every-domain-unknown-key/expected.json +163 -23
- package/conformance/cases/z02-every-domain-unknown-key/input.json +0 -3
- package/conformance/cases/z02-every-domain-unknown-key/meta.json +1 -1
- package/dist/api/auth-bridge.d.ts +51 -181
- package/dist/api/auth-bridge.js +58 -33
- package/dist/api/auth.d.ts +37 -146
- package/dist/api/auth.js +16 -26
- package/dist/api/feedback.d.ts +87 -0
- package/dist/api/feedback.js +66 -0
- package/dist/api/pull-token.d.ts +32 -0
- package/dist/api/pull-token.js +88 -0
- package/dist/api/scopes.d.ts +82 -174
- package/dist/api/scopes.js +23 -18
- package/dist/atomic-write.d.ts +14 -0
- package/dist/atomic-write.js +60 -0
- package/dist/bundle.d.ts +15 -7
- package/dist/bundle.js +28 -27
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +149 -80
- package/dist/config-d-file.d.ts +14 -0
- package/dist/config-d-file.js +67 -0
- package/dist/config-fns.d.ts +87 -186
- package/dist/config-fns.js +207 -262
- package/dist/conformance/contract-adapter.d.ts +9 -2
- package/dist/conformance/contract-adapter.js +15 -7
- package/dist/conformance/corpus.d.ts +3 -0
- package/dist/conformance/corpus.js +5 -0
- package/dist/conformance/derive.d.ts +3 -0
- package/dist/conformance/index.d.ts +2 -2
- package/dist/conformance/index.js +2 -2
- package/dist/conformance/run.js +67 -22
- package/dist/conformance/types.d.ts +31 -15
- package/dist/conformance/types.js +5 -0
- package/dist/conformance/warnings.d.ts +1 -1
- package/dist/conformance/warnings.js +1 -4
- package/dist/cross-domain.d.ts +66 -22
- package/dist/cross-domain.js +164 -93
- package/dist/cross-session.d.ts +29 -11
- package/dist/default-model.d.ts +43 -0
- package/dist/default-model.js +34 -0
- package/dist/execution-lane-index.d.ts +4 -8
- package/dist/execution-lane-index.js +4 -8
- package/dist/execution-lanes.d.ts +31 -8
- package/dist/execution-lanes.js +23 -5
- package/dist/file-edit.d.ts +3 -3
- package/dist/file-edit.js +11 -18
- package/dist/file-store.d.ts +7 -6
- package/dist/file-store.js +37 -50
- package/dist/fleet.d.ts +364 -762
- package/dist/fleet.js +277 -224
- package/dist/hooks.d.ts +1151 -4737
- package/dist/hooks.js +59 -15
- package/dist/index.d.ts +10 -4
- package/dist/index.js +13 -7
- package/dist/integer.d.ts +11 -0
- package/dist/integer.js +11 -0
- package/dist/issue-path.d.ts +10 -0
- package/dist/issue-path.js +10 -0
- package/dist/local-load.d.ts +1 -1
- package/dist/local-load.js +4 -14
- package/dist/loopback.d.ts +26 -0
- package/dist/loopback.js +54 -0
- package/dist/migration-receipt.d.ts +42 -0
- package/dist/migration-receipt.js +12 -0
- package/dist/one-time-migrations.d.ts +62 -0
- package/dist/one-time-migrations.js +828 -0
- package/dist/prompts-digest.d.ts +1 -1
- package/dist/prompts-digest.js +3 -2
- package/dist/reader.d.ts +2 -2
- package/dist/remote-exec.d.ts +45 -187
- package/dist/remote-exec.js +16 -10
- package/dist/resolve-roster.d.ts +18 -3
- package/dist/resolve-roster.js +28 -25
- package/dist/retired-envs.d.ts +24 -0
- package/dist/retired-envs.js +114 -0
- package/dist/retired-form.d.ts +31 -0
- package/dist/retired-form.js +28 -0
- package/dist/safety-merge-spec.d.ts +131 -191
- package/dist/safety-merge-spec.js +18 -14
- package/dist/scheduler-lifetime.d.ts +5 -0
- package/dist/scheduler-lifetime.js +7 -0
- package/dist/scheduler-store-migration.d.ts +14 -0
- package/dist/scheduler-store-migration.js +14 -0
- package/dist/scheduler-store-node.d.ts +28 -12
- package/dist/scheduler-store-node.js +32 -23
- package/dist/scheduler-store.d.ts +47 -14
- package/dist/scheduler-store.js +32 -61
- package/dist/secret-refs.d.ts +3 -3
- package/dist/secret-refs.js +1 -1
- package/dist/secret-scan.d.ts +14 -24
- package/dist/secret-scan.js +45 -23
- package/dist/sha256-core.d.ts +16 -0
- package/dist/sha256-core.js +138 -0
- package/dist/sha256.d.ts +6 -1
- package/dist/sha256.js +8 -98
- package/dist/skills-manifest.js +1 -1
- package/dist/types.d.ts +1509 -13536
- package/dist/types.js +526 -438
- package/dist/url-shape.d.ts +34 -0
- package/dist/url-shape.js +61 -0
- package/dist/web-admin-credential.d.ts +96 -0
- package/dist/{center-credential.js → web-admin-credential.js} +49 -46
- package/dist/web-admin-env.d.ts +33 -0
- package/dist/web-admin-env.js +33 -0
- package/dist/worker-row-verdict.d.ts +16 -0
- package/dist/worker-row-verdict.js +31 -0
- package/dist/workers-rows.d.ts +34 -0
- package/dist/workers-rows.js +42 -0
- package/dist/zod-unwrap.d.ts +11 -0
- package/dist/zod-unwrap.js +25 -0
- package/package.json +17 -9
- package/conformance/cases/c46-s811-host-name-url-userinfo-characterization/meta.json +0 -6
- package/conformance/cases/c47-s811-roles-key-url-userinfo-characterization/meta.json +0 -6
- package/conformance/migrate-workers/m01-g5-k8s-env-lifted/expected.json +0 -49
- package/conformance/migrate-workers/m01-g5-k8s-env-lifted/input.json +0 -26
- package/conformance/migrate-workers/m01-g5-k8s-env-lifted/meta.json +0 -4
- package/conformance/migrate-workers/m02-g5-idempotent/expected.json +0 -38
- package/conformance/migrate-workers/m02-g5-idempotent/input.json +0 -33
- package/conformance/migrate-workers/m02-g5-idempotent/meta.json +0 -5
- package/conformance/migrate-workers/m03-g5-refused-no-lane/expected.json +0 -71
- package/conformance/migrate-workers/m03-g5-refused-no-lane/input.json +0 -42
- package/conformance/migrate-workers/m03-g5-refused-no-lane/meta.json +0 -4
- package/conformance/migrate-workers/m04-g7b-session-tidb-lifted/expected.json +0 -52
- package/conformance/migrate-workers/m04-g7b-session-tidb-lifted/input.json +0 -30
- package/conformance/migrate-workers/m04-g7b-session-tidb-lifted/meta.json +0 -4
- package/conformance/migrate-workers/m05-row-verdict-reserved-env/expected.json +0 -82
- package/conformance/migrate-workers/m05-row-verdict-reserved-env/input.json +0 -68
- package/conformance/migrate-workers/m05-row-verdict-reserved-env/meta.json +0 -4
- package/dist/center-credential.d.ts +0 -116
- package/dist/issue-text.d.ts +0 -10
- package/dist/issue-text.js +0 -8
- package/dist/migrate-workers.d.ts +0 -71
- package/dist/migrate-workers.js +0 -351
- package/dist/migrate.d.ts +0 -255
- package/dist/migrate.js +0 -542
- /package/conformance/cases/{c46-s811-host-name-url-userinfo-characterization → c46-s811-host-name-url-userinfo-withheld}/input.json +0 -0
package/dist/fleet.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @sema-agent/settings-schema/fleet — the PURE scheduling/fleet contract for the
|
|
2
|
+
* @sema-agent/settings-schema/fleet — the PURE scheduling/fleet contract for the web-admin control plane
|
|
3
3
|
* (design: the cc-parity control-plane scheduling design note, 产品拍板 2026-07-04 — "one contract, three orchestrators").
|
|
4
4
|
*
|
|
5
5
|
* Seven domains (rolling update / discovery / traffic / failover / LB / quota / token governance) are ONE
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* but all three read THESE shapes:
|
|
8
8
|
* - TOC shell (engineLifecycleManager): health readiness + upgrade classification + local blue-green demo
|
|
9
9
|
* - TOB (user k8s / fixed baseurl): preStop→drain semantics + readiness probe = !draining
|
|
10
|
-
* -
|
|
10
|
+
* - web-admin (orchestrator reconciler): instance discovery table + /api/dispatch gateway + rolling primitive
|
|
11
11
|
*
|
|
12
12
|
* 🔴 Contract discipline (same rules as scheduler-store):
|
|
13
13
|
* - ABSTRACT: types + zod wire shapes + PURE decision functions. ZERO IO, ZERO node:* — browser-safe barrel.
|
|
@@ -19,16 +19,16 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { z } from "zod";
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
* calls one (the engine, the executor) and whoever serves or routes it (
|
|
22
|
+
* Web-admin's `/api/fleet/*` endpoints that are addressed from outside web-admin, relative to its origin. ONE spelling: whoever
|
|
23
|
+
* calls one (the engine, the executor) and whoever serves or routes it (web-admin, the deployment's Service / Ingress) imports it
|
|
24
24
|
* here — nobody writes the string. Membership rule: an endpoint is here iff this package types its body, or it belongs to the
|
|
25
|
-
* liveness group ({@link FLEET_LIVE_PATHS}); a
|
|
26
|
-
* - `instances` — `POST` = the heartbeat ({@link InstanceAnnouncement}; its 2xx reply carries
|
|
27
|
-
* {@link
|
|
25
|
+
* liveness group ({@link FLEET_LIVE_PATHS}); a web-admin-internal route is not.
|
|
26
|
+
* - `instances` — `POST` = the heartbeat ({@link InstanceAnnouncement}; its 2xx reply carries web-admin's
|
|
27
|
+
* {@link WebAdminCapabilities} under {@link HEARTBEAT_REPLY_CAPABILITIES_KEY}), `DELETE` = the goodbye ({@link InstanceDeregistration}).
|
|
28
28
|
* - `usage` — `POST` a {@link UsageReport}.
|
|
29
29
|
* - `quotaLeases` — the quota lease ({@link QuotaLease}).
|
|
30
30
|
* - `servingLease` — `POST` a {@link ServingLeaseRequest}, answered with a {@link ServingLeaseResponse}.
|
|
31
|
-
* - `hostPlanner` — `GET` one host's planner record, for the executor (its body is
|
|
31
|
+
* - `hostPlanner` — `GET` one host's planner record, for the executor (its body is web-admin's and the executor's contract, not
|
|
32
32
|
* this package's; it is here because the deployment routes it as a member of the liveness group).
|
|
33
33
|
* Notation: a value is a path TEMPLATE — a parameter is a whole segment written `{name}` (`hostPlanner` ends in `/{host}`, filled
|
|
34
34
|
* with the URL-encoded host id); the other values have none and are the path itself. A prefix router (an Ingress rule) takes the
|
|
@@ -42,8 +42,8 @@ export declare const FLEET_PATHS: {
|
|
|
42
42
|
readonly hostPlanner: "/api/fleet/live/host-planner/{host}";
|
|
43
43
|
};
|
|
44
44
|
/**
|
|
45
|
-
* The LIVENESS group — the fleet endpoints a
|
|
46
|
-
* (they read no config domain and take service credentials only), so that a
|
|
45
|
+
* The LIVENESS group — the fleet endpoints a web-admin answers without its readiness gate and without its config-governance gates
|
|
46
|
+
* (they read no config domain and take service credentials only), so that a web-admin whose management face is refused never turns
|
|
47
47
|
* into a fleet that gets no answers. A deployment routes exactly these past the readiness gate (on Kubernetes: a second Service
|
|
48
48
|
* with `publishNotReadyAddresses`, and the Ingress rules for them point at it). Membership is THIS list, not a path prefix — the
|
|
49
49
|
* heartbeat predates the `/api/fleet/live/` prefix; every {@link FLEET_PATHS} entry under that prefix is a member. The
|
|
@@ -60,7 +60,7 @@ export declare const DRAIN_PHASES: readonly ["running", "draining", "drained"];
|
|
|
60
60
|
export type DrainPhase = (typeof DRAIN_PHASES)[number];
|
|
61
61
|
/** Shipped default (server 1.96.0): generous by design — the only unrecoverable loss on restart is the
|
|
62
62
|
* interactive turn currently streaming, so we wait long rather than cut short. Deployments override via
|
|
63
|
-
* DRAIN_GRACE_MS;
|
|
63
|
+
* DRAIN_GRACE_MS; web-admin overrides per-worker through the worker spec. */
|
|
64
64
|
export declare const DEFAULT_DRAIN_GRACE_MS = 600000;
|
|
65
65
|
/** Hard floor — a grace below this is treated as this (protects against a fat-fingered 0). */
|
|
66
66
|
export declare const DRAIN_GRACE_FLOOR_MS = 10000;
|
|
@@ -81,6 +81,10 @@ export type DrainVerdict = "wait" | "exit_clean" | "exit_forced";
|
|
|
81
81
|
/** The drain state machine's single decision point: keep waiting, exit clean (drained), or force the exit
|
|
82
82
|
* (grace exhausted with legs still in flight — durable legs resume cross-instance via resumeFromRunId). */
|
|
83
83
|
export declare function drainVerdict(s: DrainSnapshot): DrainVerdict;
|
|
84
|
+
/** Drain action id grammar — one identifier shape for the drain request's `actionId` and the `EngineDraining.actionId` it echoes
|
|
85
|
+
* (the engine republishes it on the unauthenticated `/health` face, so it must never carry free text): 1–128 chars, first
|
|
86
|
+
* alphanumeric, then alphanumerics / `.` / `_` / `:` / `-`. Readers and writers share this one constant. */
|
|
87
|
+
export declare const DRAIN_ACTION_ID: RegExp;
|
|
84
88
|
export declare function isCanonicalGeneration(value: unknown): value is string;
|
|
85
89
|
/** Numeric order of two canonical generations: `-1` when `a` is older than `b`, `0` when equal, `1` when newer — `"9"` is
|
|
86
90
|
* older than `"10"`; any number of digits. Throws a `TypeError` on an input that is not canonical (a comparison that quietly
|
|
@@ -90,27 +94,32 @@ export declare function compareGeneration(a: string, b: string): -1 | 0 | 1;
|
|
|
90
94
|
/**
|
|
91
95
|
* The serving lease as the ENGINE holds it — a CLOSED set of four words, owned here (the engine's vocabulary, word for word;
|
|
92
96
|
* a reader that switches on it exhaustively is told by the compiler when a word is added):
|
|
93
|
-
* - `none` — the lease is not in effect:
|
|
94
|
-
* says `undeclared` — an older
|
|
97
|
+
* - `none` — the lease is not in effect: web-admin has declared no {@link WebAdminCapabilities}`.servingLease` ({@link readCapabilityReading}
|
|
98
|
+
* says `undeclared` — an older web-admin — or `declared` without the bit). The engine behaves exactly as an engine without a lease —
|
|
95
99
|
* no renewals, no fence. A reply that cannot be read (`unreadable`) is not a reading: it does not move the engine into or out of
|
|
96
100
|
* `none` — it keeps the state it had.
|
|
97
101
|
* - `granted` — the engine holds a lease; `leaseUntil` says until when (epoch ms, the engine's own computation from the
|
|
98
102
|
* granted `ttlMs`, see {@link ServingLeaseResponse}).
|
|
99
|
-
* - `expired` — the lease is in effect but no grant is valid right now (
|
|
103
|
+
* - `expired` — the lease is in effect but no grant is valid right now (web-admin is unreachable, answered `unknown`, or has
|
|
100
104
|
* not granted one yet): the engine keeps serving until its own outage allowance runs out. Not a denial.
|
|
101
|
-
* - `denied` —
|
|
105
|
+
* - `denied` — web-admin denied this instance on positive evidence (the `denied` verdict): sticky; the engine is draining and
|
|
102
106
|
* stops at its deadline.
|
|
103
107
|
* Only a fleet member says it: an engine without a generation has no lease and carries no `leaseState` at all (there is no
|
|
104
108
|
* fifth word for "not a member" — absence is that).
|
|
105
109
|
*/
|
|
106
110
|
export declare const LEASE_STATES: readonly ["none", "granted", "expired", "denied"];
|
|
107
|
-
export declare const LeaseState: z.ZodEnum<
|
|
111
|
+
export declare const LeaseState: z.ZodEnum<{
|
|
112
|
+
none: "none";
|
|
113
|
+
granted: "granted";
|
|
114
|
+
expired: "expired";
|
|
115
|
+
denied: "denied";
|
|
116
|
+
}>;
|
|
108
117
|
export type LeaseState = z.infer<typeof LeaseState>;
|
|
109
118
|
/**
|
|
110
|
-
* "This instance must restart to apply config" — the engine's OWN observation (S-741), defined ONCE
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
119
|
+
* "This instance must restart to apply config" — the engine's OWN observation (S-741), defined ONCE. It is part of the engine's
|
|
120
|
+
* state, so it rides inside {@link EngineSignal} (`restart`) on both exits — the fleet heartbeat and GET /health — and is read
|
|
121
|
+
* there and only there: `readEngineSignal(body)?.restart`, or `engineSignal?.restart` on a parsed heartbeat / health body. The
|
|
122
|
+
* flat `restart` key the two bodies still carry is the pre-11 dialect (see {@link EngineSignal}); nothing reads it.
|
|
114
123
|
*
|
|
115
124
|
* Shape = what the server already ships on /health (`restart: { reasons, version, since }`, plus the optional `withheld`
|
|
116
125
|
* qualifier below). PRESENCE is the signal:
|
|
@@ -118,7 +127,7 @@ export type LeaseState = z.infer<typeof LeaseState>;
|
|
|
118
127
|
* to presence is redundant, and two spellings of one fact drift).
|
|
119
128
|
* - `reasons` — which restart-to-apply slices differ from the value baked at boot (the vocabulary is the engine's;
|
|
120
129
|
* the contract only guarantees non-empty words, at least one).
|
|
121
|
-
* - `version` — the
|
|
130
|
+
* - `version` — the web-admin config version that currently differs from boot (the LATEST seen — it moves forward with
|
|
122
131
|
* later publishes, so a "one restart per version" dedupe bounds restarts per config version, not per reason set).
|
|
123
132
|
* Judged exactly as `readEffectiveWire` judges a config version (a finite number, `ConfigVersion`) — one quantity, one rule;
|
|
124
133
|
* {@link EngineSignal}'s two version-ledger keys use the same one.
|
|
@@ -129,31 +138,15 @@ export type LeaseState = z.infer<typeof LeaseState>;
|
|
|
129
138
|
* it. It is a qualifier INSIDE the restart signal, never a signal of its own: it exists only where `restart` does.
|
|
130
139
|
*/
|
|
131
140
|
export declare const RestartSignal: z.ZodObject<{
|
|
132
|
-
reasons: z.ZodArray<z.ZodString
|
|
141
|
+
reasons: z.ZodArray<z.ZodString>;
|
|
133
142
|
version: z.ZodNumber;
|
|
134
143
|
since: z.ZodNumber;
|
|
135
144
|
withheld: z.ZodOptional<z.ZodBoolean>;
|
|
136
|
-
},
|
|
137
|
-
reasons: string[];
|
|
138
|
-
version: number;
|
|
139
|
-
since: number;
|
|
140
|
-
withheld?: boolean | undefined;
|
|
141
|
-
}, {
|
|
142
|
-
reasons: string[];
|
|
143
|
-
version: number;
|
|
144
|
-
since: number;
|
|
145
|
-
withheld?: boolean | undefined;
|
|
146
|
-
}>;
|
|
145
|
+
}, z.core.$strip>;
|
|
147
146
|
export type RestartSignal = z.infer<typeof RestartSignal>;
|
|
148
|
-
/**
|
|
149
|
-
* The /health exit of {@link RestartSignal}: `health.restart` judged against the one shape. Shape mismatch, a missing
|
|
150
|
-
* key, a non-object body, even a throwing accessor ⇒ `undefined` — never throws (a probe must not die on one odd body).
|
|
151
|
-
* Presence law: only `restart` counts; the top-level `restartRequired` boolean the server also ships is not read.
|
|
152
|
-
*/
|
|
153
|
-
export declare function readRestartSignal(health: unknown): RestartSignal | undefined;
|
|
154
147
|
/**
|
|
155
148
|
* The model-refusal cause codes (S-768) — CLOSED sets, owned HERE, never a hand-copied second list. Adding a code is
|
|
156
|
-
* one edit in this file; who goes red at compile time: a reader that switches on the code exhaustively (a
|
|
149
|
+
* one edit in this file; who goes red at compile time: a reader that switches on the code exhaustively (a web-admin), and
|
|
157
150
|
* a producer only where it holds a table KEYED BY the code (an exhaustive `switch` over the code, or a
|
|
158
151
|
* `Record<ModelEntryRefusalCode, …>`) — a producer table that merely `satisfies` these codes as values proves a subset,
|
|
159
152
|
* not equality, and stays green.
|
|
@@ -175,10 +168,18 @@ export declare function readRestartSignal(health: unknown): RestartSignal | unde
|
|
|
175
168
|
* (invariant `model-route-endpoint`, judged by the model-plane stage in `src/config-center/apply-effective.ts:664-665`).
|
|
176
169
|
*/
|
|
177
170
|
export declare const MODEL_ENTRY_REFUSAL_CODES: readonly ["config.model_no_brain", "config.model_entry_invalid"];
|
|
178
|
-
export declare const ModelEntryRefusalCode: z.ZodEnum<
|
|
171
|
+
export declare const ModelEntryRefusalCode: z.ZodEnum<{
|
|
172
|
+
"config.model_no_brain": "config.model_no_brain";
|
|
173
|
+
"config.model_entry_invalid": "config.model_entry_invalid";
|
|
174
|
+
}>;
|
|
179
175
|
export type ModelEntryRefusalCode = z.infer<typeof ModelEntryRefusalCode>;
|
|
180
176
|
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"];
|
|
181
|
-
export declare const ModelPlaneRefusalCode: z.ZodEnum<
|
|
177
|
+
export declare const ModelPlaneRefusalCode: z.ZodEnum<{
|
|
178
|
+
"config.default_model_missing": "config.default_model_missing";
|
|
179
|
+
"config.model_refused_name_minted": "config.model_refused_name_minted";
|
|
180
|
+
"config.model_route_endpoint_missing": "config.model_route_endpoint_missing";
|
|
181
|
+
"config.gateway_fallback_without_primary": "config.gateway_fallback_without_primary";
|
|
182
|
+
}>;
|
|
182
183
|
export type ModelPlaneRefusalCode = z.infer<typeof ModelPlaneRefusalCode>;
|
|
183
184
|
/**
|
|
184
185
|
* "This instance does not serve part (or all) of the model catalog it was handed" — the engine's OWN per-host verdict
|
|
@@ -221,22 +222,10 @@ export type ModelPlaneRefusalCode = z.infer<typeof ModelPlaneRefusalCode>;
|
|
|
221
222
|
* law — "none" has exactly one spelling, the absent key. Inside, "no entry refusals" is spelled `entries: {}` (the key is
|
|
222
223
|
* required) — one spelling at each level.
|
|
223
224
|
*/
|
|
224
|
-
export declare const ModelRefusalsWire: z.
|
|
225
|
+
export declare const ModelRefusalsWire: z.ZodObject<{
|
|
225
226
|
entries: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
226
227
|
plane: z.ZodOptional<z.ZodString>;
|
|
227
|
-
},
|
|
228
|
-
entries: Record<string, string>;
|
|
229
|
-
plane?: string | undefined;
|
|
230
|
-
}, {
|
|
231
|
-
entries: Record<string, string>;
|
|
232
|
-
plane?: string | undefined;
|
|
233
|
-
}>, {
|
|
234
|
-
entries: Record<string, string>;
|
|
235
|
-
plane?: string | undefined;
|
|
236
|
-
}, {
|
|
237
|
-
entries: Record<string, string>;
|
|
238
|
-
plane?: string | undefined;
|
|
239
|
-
}>;
|
|
228
|
+
}, z.core.$strip>;
|
|
240
229
|
export type ModelRefusalsWire = z.infer<typeof ModelRefusalsWire>;
|
|
241
230
|
/** {@link ModelRefusalsWire} narrowed to the closed code sets — the return type of {@link readModelRefusals}. */
|
|
242
231
|
export type ModelRefusalsSignal = {
|
|
@@ -252,466 +241,297 @@ export type ModelRefusalsSignal = {
|
|
|
252
241
|
* at all — it answers the key only and never judges the engine's version (see {@link InstanceAnnouncement}`.modelRefusals`).
|
|
253
242
|
*/
|
|
254
243
|
export declare function readModelRefusals(body: unknown): ModelRefusalsSignal | undefined;
|
|
255
|
-
/**
|
|
256
|
-
*
|
|
257
|
-
*
|
|
244
|
+
/**
|
|
245
|
+
* The engine's draining fact — ONE object wherever the engine says "I am draining": inside {@link EngineSignal}`.draining`
|
|
246
|
+
* and as the body of a {@link DrainReceipt}.
|
|
247
|
+
* - `actionId` — the action that put the engine into draining (idempotency key of the drain request).
|
|
248
|
+
* - `since` — epoch ms when draining began; it does not move when the same action repeats.
|
|
249
|
+
* - `deadlineAt` — epoch ms by which the engine stops on its own.
|
|
250
|
+
*/
|
|
251
|
+
export declare const EngineDraining: z.ZodObject<{
|
|
252
|
+
actionId: z.ZodString;
|
|
253
|
+
since: z.ZodNumber;
|
|
254
|
+
deadlineAt: z.ZodNumber;
|
|
255
|
+
}, z.core.$strip>;
|
|
256
|
+
export type EngineDraining = z.infer<typeof EngineDraining>;
|
|
257
|
+
/**
|
|
258
|
+
* Everything the ENGINE itself mints about its own state, as one object. One mint point on the engine; it rides BOTH exits —
|
|
259
|
+
* the `/health` self-description ({@link InstanceHealth}) and the fleet heartbeat ({@link InstanceAnnouncement}) — WHOLE, under
|
|
260
|
+
* the one key {@link ENGINE_SIGNAL_KEY}. Readers read it there and only there: {@link readEngineSignal} for a body,
|
|
261
|
+
* `engineSignal` on a parsed heartbeat / health body; the readiness / routing judges ({@link judgeReadiness},
|
|
262
|
+
* {@link judgeRoutability}) read nothing else.
|
|
263
|
+
*
|
|
264
|
+
* One fact, one place on a body: the engine's STATE is this object; the row's IDENTITY (`instanceId`, `worker`, `address`,
|
|
265
|
+
* `version`, `heartbeatMs`) is the body's top level and is not repeated here. The flat keys of this object's names that the two
|
|
266
|
+
* bodies still carry (`ready`, `draining` as a boolean + `drainingSince`, `restart`, `generation`, `leaseState`, `leaseUntil`)
|
|
267
|
+
* are the pre-11 flat dialect: sent during its contraction window for readers that predate this key, unchanged in shape, read by
|
|
268
|
+
* nothing in this package; their removal is registered (`ONE_TIME_MIGRATIONS` row `SS-W1`).
|
|
269
|
+
*
|
|
270
|
+
* Nested under one key, not flat on the body: the boolean dialect (`draining: boolean` + `drainingSince`, {@link InstanceHealth} /
|
|
271
|
+
* {@link InstanceAnnouncement}) is still sent during its contraction window while `draining` here is an OBJECT — one key cannot hold
|
|
272
|
+
* both, and a flat object `draining` would make every reader still on the boolean refuse a draining engine's heartbeat. Nested, an
|
|
273
|
+
* older reader never sees it (the heartbeat shape strips unknown keys; `/health` is an open record) and a newer one reads only it.
|
|
274
|
+
*
|
|
275
|
+
* - `ready` — always said (never "absent while ready"): the engine can take new work right now.
|
|
276
|
+
* - `draining` — present ⇔ draining ({@link EngineDraining}).
|
|
277
|
+
* - `restart` — the one {@link RestartSignal}, same schema object, no copy.
|
|
278
|
+
* - `generation` — echo of the env the config renderer set on this instance ({@link FLEET_GENERATION_ENV}), a canonical
|
|
279
|
+
* decimal string ({@link isCanonicalGeneration}). Absent ⇔ not a fleet member (a single-machine engine): it takes no lease
|
|
280
|
+
* and is not fenced.
|
|
281
|
+
* - `leaseState` — the serving lease as the engine holds it ({@link LeaseState}: `none` / `granted` / `expired` / `denied`);
|
|
282
|
+
* present ⇔ fleet member (the engine says it on every beat once it has a generation).
|
|
283
|
+
* - `leaseUntil` — epoch ms until which the engine holds its serving lease, as the engine computed it from the last
|
|
284
|
+
* `ttlMs` it was granted ({@link ServingLeaseResponse}); present ⇔ `leaseState` is `granted`.
|
|
285
|
+
* - `configTargetVersion` / `configAppliedVersion` — the web-admin config version the engine was told to converge on, and the
|
|
286
|
+
* one it has applied: numbers, as the engine mints them (finite, the same rule as {@link RestartSignal}`.version`; equal ⇔ converged).
|
|
287
|
+
*
|
|
288
|
+
* Reading: the default strip — only these keys come out. A shape violation in ANY key makes the whole signal `undefined` (it is
|
|
289
|
+
* never "the rest of it"). `undefined` means "no signal this reader can say" — an engine that does not carry the key, or a
|
|
290
|
+
* malformed one — and is NEVER "ready": the judges answer `no-engine-signal`.
|
|
291
|
+
*/
|
|
292
|
+
export declare const EngineSignal: z.ZodObject<{
|
|
293
|
+
ready: z.ZodBoolean;
|
|
294
|
+
draining: z.ZodOptional<z.ZodObject<{
|
|
295
|
+
actionId: z.ZodString;
|
|
296
|
+
since: z.ZodNumber;
|
|
297
|
+
deadlineAt: z.ZodNumber;
|
|
298
|
+
}, z.core.$strip>>;
|
|
299
|
+
restart: z.ZodOptional<z.ZodObject<{
|
|
300
|
+
reasons: z.ZodArray<z.ZodString>;
|
|
301
|
+
version: z.ZodNumber;
|
|
302
|
+
since: z.ZodNumber;
|
|
303
|
+
withheld: z.ZodOptional<z.ZodBoolean>;
|
|
304
|
+
}, z.core.$strip>>;
|
|
305
|
+
generation: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
|
|
306
|
+
leaseState: z.ZodOptional<z.ZodEnum<{
|
|
307
|
+
none: "none";
|
|
308
|
+
granted: "granted";
|
|
309
|
+
expired: "expired";
|
|
310
|
+
denied: "denied";
|
|
311
|
+
}>>;
|
|
312
|
+
leaseUntil: z.ZodOptional<z.ZodNumber>;
|
|
313
|
+
configTargetVersion: z.ZodOptional<z.ZodNumber>;
|
|
314
|
+
configAppliedVersion: z.ZodOptional<z.ZodNumber>;
|
|
315
|
+
}, z.core.$strip>;
|
|
316
|
+
export type EngineSignal = z.infer<typeof EngineSignal>;
|
|
317
|
+
/** The one key {@link EngineSignal} rides under, on the `/health` body and on the heartbeat body alike. Both sides import THIS
|
|
318
|
+
* constant; neither spells the key. */
|
|
319
|
+
export declare const ENGINE_SIGNAL_KEY = "engineSignal";
|
|
320
|
+
/**
|
|
321
|
+
* The reader of {@link EngineSignal} on a body — and so of every part of it (`readEngineSignal(body)?.restart` is THE restart
|
|
322
|
+
* reading): the value under {@link ENGINE_SIGNAL_KEY}, judged by the same guarded body reader as {@link readModelRefusals}, so the
|
|
323
|
+
* same odd input gets the same answer from both. A missing key, shape mismatch (a missing `ready` included), a non-object body,
|
|
324
|
+
* even a throwing accessor ⇒ `undefined` — never throws. Keys of the signal's names lying flat on the body are NOT read (the flat
|
|
325
|
+
* dialect; the carrier is the one key).
|
|
326
|
+
*/
|
|
327
|
+
export declare function readEngineSignal(body: unknown): EngineSignal | undefined;
|
|
328
|
+
/** The `/health` self-description every orchestrator reads. Liveness and readiness are deliberately SPLIT: `status` stays ok while
|
|
329
|
+
* draining (the process is alive); readiness is {@link judgeReadiness} over the engine signal the body carries under
|
|
330
|
+
* {@link ENGINE_SIGNAL_KEY}. Unknown fields pass through — health is a self-description, not a closed record: `modelRefusals`
|
|
331
|
+
* rides it untyped and is read with {@link readModelRefusals}; the flat dialect keys other than the boolean pair (`ready`,
|
|
332
|
+
* `restart`, `generation`, `leaseState`, `leaseUntil`) ride it untyped too and are read by nothing. */
|
|
258
333
|
export declare const InstanceHealth: z.ZodObject<{
|
|
259
334
|
instanceId: z.ZodString;
|
|
260
|
-
/** build self-description — "did the restart actually land new code" (upgrade classification) */
|
|
261
|
-
version: z.ZodString;
|
|
262
|
-
/** absent = not draining */
|
|
263
|
-
draining: z.ZodOptional<z.ZodBoolean>;
|
|
264
|
-
/** epoch ms, present iff draining */
|
|
265
|
-
drainingSince: z.ZodOptional<z.ZodNumber>;
|
|
266
|
-
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
267
|
-
instanceId: z.ZodString;
|
|
268
|
-
/** build self-description — "did the restart actually land new code" (upgrade classification) */
|
|
269
|
-
version: z.ZodString;
|
|
270
|
-
/** absent = not draining */
|
|
271
|
-
draining: z.ZodOptional<z.ZodBoolean>;
|
|
272
|
-
/** epoch ms, present iff draining */
|
|
273
|
-
drainingSince: z.ZodOptional<z.ZodNumber>;
|
|
274
|
-
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
275
|
-
instanceId: z.ZodString;
|
|
276
|
-
/** build self-description — "did the restart actually land new code" (upgrade classification) */
|
|
277
335
|
version: z.ZodString;
|
|
278
|
-
/** absent = not draining */
|
|
279
336
|
draining: z.ZodOptional<z.ZodBoolean>;
|
|
280
|
-
/** epoch ms, present iff draining */
|
|
281
337
|
drainingSince: z.ZodOptional<z.ZodNumber>;
|
|
282
|
-
|
|
338
|
+
engineSignal: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
339
|
+
ready: z.ZodBoolean;
|
|
340
|
+
draining: z.ZodOptional<z.ZodObject<{
|
|
341
|
+
actionId: z.ZodString;
|
|
342
|
+
since: z.ZodNumber;
|
|
343
|
+
deadlineAt: z.ZodNumber;
|
|
344
|
+
}, z.core.$strip>>;
|
|
345
|
+
restart: z.ZodOptional<z.ZodObject<{
|
|
346
|
+
reasons: z.ZodArray<z.ZodString>;
|
|
347
|
+
version: z.ZodNumber;
|
|
348
|
+
since: z.ZodNumber;
|
|
349
|
+
withheld: z.ZodOptional<z.ZodBoolean>;
|
|
350
|
+
}, z.core.$strip>>;
|
|
351
|
+
generation: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
|
|
352
|
+
leaseState: z.ZodOptional<z.ZodEnum<{
|
|
353
|
+
none: "none";
|
|
354
|
+
granted: "granted";
|
|
355
|
+
expired: "expired";
|
|
356
|
+
denied: "denied";
|
|
357
|
+
}>>;
|
|
358
|
+
leaseUntil: z.ZodOptional<z.ZodNumber>;
|
|
359
|
+
configTargetVersion: z.ZodOptional<z.ZodNumber>;
|
|
360
|
+
configAppliedVersion: z.ZodOptional<z.ZodNumber>;
|
|
361
|
+
}, z.core.$strip>>>;
|
|
362
|
+
}, z.core.$loose>;
|
|
283
363
|
export type InstanceHealth = z.infer<typeof InstanceHealth>;
|
|
284
|
-
/**
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
|
|
290
|
-
|
|
364
|
+
/** Why an instance is not routable — a CLOSED set, in the order the judges check:
|
|
365
|
+
* - `no-engine-signal` — no {@link EngineSignal} to read (an engine that does not carry it, or a malformed one). Never "ready".
|
|
366
|
+
* - `stale` — silent past its declared heartbeat cadence × {@link STALE_HEARTBEAT_MULTIPLIER} (stored rows only).
|
|
367
|
+
* - `observer-not-ready` — the reconciler's own observation vetoes it (stored rows only; the SECOND source, AND-merged).
|
|
368
|
+
* - `draining` — the engine says it is draining.
|
|
369
|
+
* - `not-ready` — the engine says it is not ready. */
|
|
370
|
+
export declare const NOT_ROUTABLE_CODES: readonly ["no-engine-signal", "stale", "observer-not-ready", "draining", "not-ready"];
|
|
371
|
+
export type NotRoutableCode = (typeof NOT_ROUTABLE_CODES)[number];
|
|
372
|
+
/** A judge's verdict: routable, or not with a closed-set code and one sentence for logs / operator surfaces. */
|
|
373
|
+
export type RoutingVerdict = {
|
|
374
|
+
routable: true;
|
|
375
|
+
} | {
|
|
376
|
+
routable: false;
|
|
377
|
+
code: NotRoutableCode;
|
|
378
|
+
why: string;
|
|
379
|
+
};
|
|
380
|
+
/** Readiness — the ONE judge of "may new work go to this engine", from the engine's own signal only (k8s readiness probe,
|
|
381
|
+
* gateway health filter, shell seam switch): ready ⇔ a signal is present, says `ready`, and is not draining. Pass
|
|
382
|
+
* {@link readEngineSignal}`(healthBody)`; `undefined` (no signal) is `no-engine-signal`, never ready. */
|
|
383
|
+
export declare function judgeReadiness(signal: EngineSignal | undefined): RoutingVerdict;
|
|
384
|
+
/** {@link judgeReadiness} as a boolean (the same judge, no second reading). */
|
|
385
|
+
export declare function isReadyForTraffic(signal: EngineSignal | undefined): boolean;
|
|
386
|
+
/** Heartbeat cadence the announcement declares (web-admin judges staleness against the DECLARED cadence, so a
|
|
291
387
|
* slow-heartbeat worker is not falsely reaped). */
|
|
292
388
|
export declare const DEFAULT_HEARTBEAT_MS = 30000;
|
|
293
389
|
/** An instance is stale once silent for this many missed heartbeats (aligned with the reconciler's own
|
|
294
390
|
* stale window: 2× cadence, not a fixed wall-clock). */
|
|
295
391
|
export declare const STALE_HEARTBEAT_MULTIPLIER = 2;
|
|
296
|
-
/** Register/heartbeat upsert (worker →
|
|
392
|
+
/** Register/heartbeat upsert (worker → web-admin, D2: worker-active). ONE shape for both — registration is the
|
|
297
393
|
* first heartbeat; every heartbeat re-announces the full row (idempotent upsert, no drift between a
|
|
298
|
-
* "register" and a "renew" schema).
|
|
299
|
-
* moment the stop signal lands — faster than any
|
|
394
|
+
* "register" and a "renew" schema). The engine's {@link EngineSignal} rides it whole under {@link ENGINE_SIGNAL_KEY}: a signal
|
|
395
|
+
* saying "draining" is the instance SELF-removing from the routable set the moment the stop signal lands — faster than any
|
|
396
|
+
* observer.
|
|
300
397
|
* Scope: the body carries NO `scope` — this shape has no such key, and one sent anyway is stripped. The row lands in the scope
|
|
301
398
|
* the CREDENTIAL names: under a per-worker token, the scope the token binds (a `?scope=` query parameter, when given, must equal
|
|
302
399
|
* it); under the shared token, the `?scope=` query parameter, absent ⇒ `"global"`. */
|
|
303
400
|
export declare const InstanceAnnouncement: z.ZodObject<{
|
|
304
401
|
instanceId: z.ZodString;
|
|
305
|
-
/** worker (deployment) this instance belongs to — the dispatch key */
|
|
306
402
|
worker: z.ZodString;
|
|
307
|
-
/** reachable base URL for the deployment form: k8s pod IP:port / docker net alias / 127.0.0.1:<engine.port> */
|
|
308
403
|
address: z.ZodString;
|
|
309
404
|
version: z.ZodString;
|
|
310
|
-
/** self-declared readiness: model roster landed AND not draining (server ≥7.102.0; older engines: !draining only) */
|
|
311
405
|
ready: z.ZodBoolean;
|
|
312
406
|
draining: z.ZodDefault<z.ZodBoolean>;
|
|
313
407
|
heartbeatMs: z.ZodDefault<z.ZodNumber>;
|
|
314
|
-
/** S-741: present ⇔ this instance must restart to apply config — the SAME {@link RestartSignal} /health carries,
|
|
315
|
-
* minted by the same getter (self-reported: a worker-scoped credential can only report for its own worker).
|
|
316
|
-
* Every heartbeat re-announces the full row, so a later heartbeat WITHOUT it means "no longer required" — the
|
|
317
|
-
* stored row ({@link InstanceRegistration}) is replaced, never merged. */
|
|
318
408
|
restart: z.ZodOptional<z.ZodObject<{
|
|
319
|
-
reasons: z.ZodArray<z.ZodString
|
|
409
|
+
reasons: z.ZodArray<z.ZodString>;
|
|
320
410
|
version: z.ZodNumber;
|
|
321
411
|
since: z.ZodNumber;
|
|
322
412
|
withheld: z.ZodOptional<z.ZodBoolean>;
|
|
323
|
-
},
|
|
324
|
-
|
|
325
|
-
version: number;
|
|
326
|
-
since: number;
|
|
327
|
-
withheld?: boolean | undefined;
|
|
328
|
-
}, {
|
|
329
|
-
reasons: string[];
|
|
330
|
-
version: number;
|
|
331
|
-
since: number;
|
|
332
|
-
withheld?: boolean | undefined;
|
|
333
|
-
}>>;
|
|
334
|
-
/** S-768: present ⇔ this instance has at least one reportable model refusal — the SAME {@link ModelRefusalsWire} /health
|
|
335
|
-
* carries, minted by the same getter; read it with {@link readModelRefusals}`(announcement)` (never off this field
|
|
336
|
-
* directly: the field is the open wire form, the reader narrows to the closed sets). Every heartbeat re-announces the
|
|
337
|
-
* full row, so a later heartbeat WITHOUT it means "no reportable refusals any more" (the stored row is replaced, never
|
|
338
|
-
* merged). Reading absence: from an engine that reports this key, absent = none reportable; an engine that predates
|
|
339
|
-
* the key never sends it, so there absence is UNKNOWN, not "none" — tell the two apart by the announced `version`.
|
|
340
|
-
* Failure direction = meaning direction (this is a diagnostic key): a code this package version does not know passes
|
|
341
|
-
* (it is "not said" to the reader), and a value that breaks the {@link ModelRefusalsWire} shape law is dropped to
|
|
342
|
-
* ABSENT (`.catch`) — it never fails the announcement, so it can never make a healthy instance unroutable (unlike
|
|
343
|
-
* `restart`, whose shape still gates the row). The key may then be present with the value `undefined`. */
|
|
344
|
-
modelRefusals: z.ZodCatch<z.ZodOptional<z.ZodEffects<z.ZodObject<{
|
|
413
|
+
}, z.core.$strip>>;
|
|
414
|
+
modelRefusals: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
345
415
|
entries: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
346
416
|
plane: z.ZodOptional<z.ZodString>;
|
|
347
|
-
},
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
entries: Record<string, string>;
|
|
355
|
-
plane?: string | undefined;
|
|
356
|
-
}, {
|
|
357
|
-
entries: Record<string, string>;
|
|
358
|
-
plane?: string | undefined;
|
|
417
|
+
}, z.core.$strip>>>;
|
|
418
|
+
generation: z.ZodCatch<z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>>;
|
|
419
|
+
leaseState: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
|
|
420
|
+
none: "none";
|
|
421
|
+
granted: "granted";
|
|
422
|
+
expired: "expired";
|
|
423
|
+
denied: "denied";
|
|
359
424
|
}>>>;
|
|
360
|
-
/** Fleet membership and the lease reading, flat on the heartbeat body under the names and values {@link EngineSignal} gives
|
|
361
|
-
* them (one mint point on the engine; this shape reuses that shape's schemas, not copies):
|
|
362
|
-
* - `generation` — the canonical decimal generation ({@link isCanonicalGeneration}); present ⇔ the instance is a fleet member.
|
|
363
|
-
* - `leaseState` — {@link LeaseState}; present ⇔ fleet member.
|
|
364
|
-
* - `leaseUntil` — epoch ms until which the held lease runs; present ⇔ `leaseState` is `granted`.
|
|
365
|
-
* The presence law between the three is the engine's; this shape does not judge it across keys. Every heartbeat re-announces
|
|
366
|
-
* the full row, so a later heartbeat without a key means the engine no longer says it (the stored row is replaced, never merged).
|
|
367
|
-
* Failure direction = meaning direction: these are evidence a center keeps, never routing input — a value that breaks its schema
|
|
368
|
-
* drops that ONE key to ABSENT (`.catch`) and the heartbeat is still accepted, so a lease word a newer engine adds can never make
|
|
369
|
-
* an older center refuse every heartbeat of that engine. A dropped key reads exactly like an unsaid one; the key may then be
|
|
370
|
-
* present with the value `undefined`. */
|
|
371
|
-
generation: z.ZodCatch<z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>>;
|
|
372
|
-
leaseState: z.ZodCatch<z.ZodOptional<z.ZodEnum<["none", "granted", "expired", "denied"]>>>;
|
|
373
425
|
leaseUntil: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
address: string;
|
|
400
|
-
ready: boolean;
|
|
401
|
-
draining?: boolean | undefined;
|
|
402
|
-
restart?: {
|
|
403
|
-
reasons: string[];
|
|
404
|
-
version: number;
|
|
405
|
-
since: number;
|
|
406
|
-
withheld?: boolean | undefined;
|
|
407
|
-
} | undefined;
|
|
408
|
-
modelRefusals?: unknown;
|
|
409
|
-
heartbeatMs?: number | undefined;
|
|
410
|
-
generation?: unknown;
|
|
411
|
-
leaseState?: unknown;
|
|
412
|
-
leaseUntil?: unknown;
|
|
413
|
-
}>;
|
|
426
|
+
engineSignal: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
427
|
+
ready: z.ZodBoolean;
|
|
428
|
+
draining: z.ZodOptional<z.ZodObject<{
|
|
429
|
+
actionId: z.ZodString;
|
|
430
|
+
since: z.ZodNumber;
|
|
431
|
+
deadlineAt: z.ZodNumber;
|
|
432
|
+
}, z.core.$strip>>;
|
|
433
|
+
restart: z.ZodOptional<z.ZodObject<{
|
|
434
|
+
reasons: z.ZodArray<z.ZodString>;
|
|
435
|
+
version: z.ZodNumber;
|
|
436
|
+
since: z.ZodNumber;
|
|
437
|
+
withheld: z.ZodOptional<z.ZodBoolean>;
|
|
438
|
+
}, z.core.$strip>>;
|
|
439
|
+
generation: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
|
|
440
|
+
leaseState: z.ZodOptional<z.ZodEnum<{
|
|
441
|
+
none: "none";
|
|
442
|
+
granted: "granted";
|
|
443
|
+
expired: "expired";
|
|
444
|
+
denied: "denied";
|
|
445
|
+
}>>;
|
|
446
|
+
leaseUntil: z.ZodOptional<z.ZodNumber>;
|
|
447
|
+
configTargetVersion: z.ZodOptional<z.ZodNumber>;
|
|
448
|
+
configAppliedVersion: z.ZodOptional<z.ZodNumber>;
|
|
449
|
+
}, z.core.$strip>>>;
|
|
450
|
+
}, z.core.$strip>;
|
|
414
451
|
export type InstanceAnnouncement = z.infer<typeof InstanceAnnouncement>;
|
|
415
452
|
/** Explicit goodbye (stop signal handler) — removes the row immediately instead of waiting out staleness. */
|
|
416
453
|
export declare const InstanceDeregistration: z.ZodObject<{
|
|
417
454
|
instanceId: z.ZodString;
|
|
418
455
|
worker: z.ZodString;
|
|
419
|
-
reason: z.ZodOptional<z.ZodEnum<
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
},
|
|
425
|
-
instanceId: string;
|
|
426
|
-
worker: string;
|
|
427
|
-
reason?: "draining" | "shutdown" | "superseded" | undefined;
|
|
428
|
-
}>;
|
|
456
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
457
|
+
draining: "draining";
|
|
458
|
+
shutdown: "shutdown";
|
|
459
|
+
superseded: "superseded";
|
|
460
|
+
}>>;
|
|
461
|
+
}, z.core.$strip>;
|
|
429
462
|
export type InstanceDeregistration = z.infer<typeof InstanceDeregistration>;
|
|
430
|
-
/** The stored discovery-table row = last announcement +
|
|
463
|
+
/** The stored discovery-table row = last announcement + web-admin-side bookkeeping. `observedReady` is the
|
|
431
464
|
* reconciler's observation (phase Running) — the SECOND source; absent where no reconciler exists (TOC/TOB). */
|
|
432
465
|
export declare const InstanceRegistration: z.ZodObject<{
|
|
433
466
|
instanceId: z.ZodString;
|
|
434
|
-
/** worker (deployment) this instance belongs to — the dispatch key */
|
|
435
467
|
worker: z.ZodString;
|
|
436
|
-
/** reachable base URL for the deployment form: k8s pod IP:port / docker net alias / 127.0.0.1:<engine.port> */
|
|
437
468
|
address: z.ZodString;
|
|
438
469
|
version: z.ZodString;
|
|
439
|
-
/** self-declared readiness: model roster landed AND not draining (server ≥7.102.0; older engines: !draining only) */
|
|
440
470
|
ready: z.ZodBoolean;
|
|
441
471
|
draining: z.ZodDefault<z.ZodBoolean>;
|
|
442
472
|
heartbeatMs: z.ZodDefault<z.ZodNumber>;
|
|
443
|
-
/** S-741: present ⇔ this instance must restart to apply config — the SAME {@link RestartSignal} /health carries,
|
|
444
|
-
* minted by the same getter (self-reported: a worker-scoped credential can only report for its own worker).
|
|
445
|
-
* Every heartbeat re-announces the full row, so a later heartbeat WITHOUT it means "no longer required" — the
|
|
446
|
-
* stored row ({@link InstanceRegistration}) is replaced, never merged. */
|
|
447
473
|
restart: z.ZodOptional<z.ZodObject<{
|
|
448
|
-
reasons: z.ZodArray<z.ZodString
|
|
474
|
+
reasons: z.ZodArray<z.ZodString>;
|
|
449
475
|
version: z.ZodNumber;
|
|
450
476
|
since: z.ZodNumber;
|
|
451
477
|
withheld: z.ZodOptional<z.ZodBoolean>;
|
|
452
|
-
},
|
|
453
|
-
|
|
454
|
-
version: number;
|
|
455
|
-
since: number;
|
|
456
|
-
withheld?: boolean | undefined;
|
|
457
|
-
}, {
|
|
458
|
-
reasons: string[];
|
|
459
|
-
version: number;
|
|
460
|
-
since: number;
|
|
461
|
-
withheld?: boolean | undefined;
|
|
462
|
-
}>>;
|
|
463
|
-
/** S-768: present ⇔ this instance has at least one reportable model refusal — the SAME {@link ModelRefusalsWire} /health
|
|
464
|
-
* carries, minted by the same getter; read it with {@link readModelRefusals}`(announcement)` (never off this field
|
|
465
|
-
* directly: the field is the open wire form, the reader narrows to the closed sets). Every heartbeat re-announces the
|
|
466
|
-
* full row, so a later heartbeat WITHOUT it means "no reportable refusals any more" (the stored row is replaced, never
|
|
467
|
-
* merged). Reading absence: from an engine that reports this key, absent = none reportable; an engine that predates
|
|
468
|
-
* the key never sends it, so there absence is UNKNOWN, not "none" — tell the two apart by the announced `version`.
|
|
469
|
-
* Failure direction = meaning direction (this is a diagnostic key): a code this package version does not know passes
|
|
470
|
-
* (it is "not said" to the reader), and a value that breaks the {@link ModelRefusalsWire} shape law is dropped to
|
|
471
|
-
* ABSENT (`.catch`) — it never fails the announcement, so it can never make a healthy instance unroutable (unlike
|
|
472
|
-
* `restart`, whose shape still gates the row). The key may then be present with the value `undefined`. */
|
|
473
|
-
modelRefusals: z.ZodCatch<z.ZodOptional<z.ZodEffects<z.ZodObject<{
|
|
478
|
+
}, z.core.$strip>>;
|
|
479
|
+
modelRefusals: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
474
480
|
entries: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
475
481
|
plane: z.ZodOptional<z.ZodString>;
|
|
476
|
-
},
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
entries: Record<string, string>;
|
|
484
|
-
plane?: string | undefined;
|
|
485
|
-
}, {
|
|
486
|
-
entries: Record<string, string>;
|
|
487
|
-
plane?: string | undefined;
|
|
482
|
+
}, z.core.$strip>>>;
|
|
483
|
+
generation: z.ZodCatch<z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>>;
|
|
484
|
+
leaseState: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
|
|
485
|
+
none: "none";
|
|
486
|
+
granted: "granted";
|
|
487
|
+
expired: "expired";
|
|
488
|
+
denied: "denied";
|
|
488
489
|
}>>>;
|
|
489
|
-
/** Fleet membership and the lease reading, flat on the heartbeat body under the names and values {@link EngineSignal} gives
|
|
490
|
-
* them (one mint point on the engine; this shape reuses that shape's schemas, not copies):
|
|
491
|
-
* - `generation` — the canonical decimal generation ({@link isCanonicalGeneration}); present ⇔ the instance is a fleet member.
|
|
492
|
-
* - `leaseState` — {@link LeaseState}; present ⇔ fleet member.
|
|
493
|
-
* - `leaseUntil` — epoch ms until which the held lease runs; present ⇔ `leaseState` is `granted`.
|
|
494
|
-
* The presence law between the three is the engine's; this shape does not judge it across keys. Every heartbeat re-announces
|
|
495
|
-
* the full row, so a later heartbeat without a key means the engine no longer says it (the stored row is replaced, never merged).
|
|
496
|
-
* Failure direction = meaning direction: these are evidence a center keeps, never routing input — a value that breaks its schema
|
|
497
|
-
* drops that ONE key to ABSENT (`.catch`) and the heartbeat is still accepted, so a lease word a newer engine adds can never make
|
|
498
|
-
* an older center refuse every heartbeat of that engine. A dropped key reads exactly like an unsaid one; the key may then be
|
|
499
|
-
* present with the value `undefined`. */
|
|
500
|
-
generation: z.ZodCatch<z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>>;
|
|
501
|
-
leaseState: z.ZodCatch<z.ZodOptional<z.ZodEnum<["none", "granted", "expired", "denied"]>>>;
|
|
502
490
|
leaseUntil: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
503
|
-
|
|
491
|
+
engineSignal: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
492
|
+
ready: z.ZodBoolean;
|
|
493
|
+
draining: z.ZodOptional<z.ZodObject<{
|
|
494
|
+
actionId: z.ZodString;
|
|
495
|
+
since: z.ZodNumber;
|
|
496
|
+
deadlineAt: z.ZodNumber;
|
|
497
|
+
}, z.core.$strip>>;
|
|
498
|
+
restart: z.ZodOptional<z.ZodObject<{
|
|
499
|
+
reasons: z.ZodArray<z.ZodString>;
|
|
500
|
+
version: z.ZodNumber;
|
|
501
|
+
since: z.ZodNumber;
|
|
502
|
+
withheld: z.ZodOptional<z.ZodBoolean>;
|
|
503
|
+
}, z.core.$strip>>;
|
|
504
|
+
generation: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
|
|
505
|
+
leaseState: z.ZodOptional<z.ZodEnum<{
|
|
506
|
+
none: "none";
|
|
507
|
+
granted: "granted";
|
|
508
|
+
expired: "expired";
|
|
509
|
+
denied: "denied";
|
|
510
|
+
}>>;
|
|
511
|
+
leaseUntil: z.ZodOptional<z.ZodNumber>;
|
|
512
|
+
configTargetVersion: z.ZodOptional<z.ZodNumber>;
|
|
513
|
+
configAppliedVersion: z.ZodOptional<z.ZodNumber>;
|
|
514
|
+
}, z.core.$strip>>>;
|
|
504
515
|
registeredMs: z.ZodNumber;
|
|
505
516
|
lastSeenMs: z.ZodNumber;
|
|
506
517
|
observedReady: z.ZodOptional<z.ZodBoolean>;
|
|
507
|
-
},
|
|
508
|
-
draining: boolean;
|
|
509
|
-
version: string;
|
|
510
|
-
instanceId: string;
|
|
511
|
-
worker: string;
|
|
512
|
-
address: string;
|
|
513
|
-
ready: boolean;
|
|
514
|
-
heartbeatMs: number;
|
|
515
|
-
registeredMs: number;
|
|
516
|
-
lastSeenMs: number;
|
|
517
|
-
restart?: {
|
|
518
|
-
reasons: string[];
|
|
519
|
-
version: number;
|
|
520
|
-
since: number;
|
|
521
|
-
withheld?: boolean | undefined;
|
|
522
|
-
} | undefined;
|
|
523
|
-
modelRefusals?: {
|
|
524
|
-
entries: Record<string, string>;
|
|
525
|
-
plane?: string | undefined;
|
|
526
|
-
} | undefined;
|
|
527
|
-
generation?: string | undefined;
|
|
528
|
-
leaseState?: "none" | "granted" | "expired" | "denied" | undefined;
|
|
529
|
-
leaseUntil?: number | undefined;
|
|
530
|
-
observedReady?: boolean | undefined;
|
|
531
|
-
}, {
|
|
532
|
-
version: string;
|
|
533
|
-
instanceId: string;
|
|
534
|
-
worker: string;
|
|
535
|
-
address: string;
|
|
536
|
-
ready: boolean;
|
|
537
|
-
registeredMs: number;
|
|
538
|
-
lastSeenMs: number;
|
|
539
|
-
draining?: boolean | undefined;
|
|
540
|
-
restart?: {
|
|
541
|
-
reasons: string[];
|
|
542
|
-
version: number;
|
|
543
|
-
since: number;
|
|
544
|
-
withheld?: boolean | undefined;
|
|
545
|
-
} | undefined;
|
|
546
|
-
modelRefusals?: unknown;
|
|
547
|
-
heartbeatMs?: number | undefined;
|
|
548
|
-
generation?: unknown;
|
|
549
|
-
leaseState?: unknown;
|
|
550
|
-
leaseUntil?: unknown;
|
|
551
|
-
observedReady?: boolean | undefined;
|
|
552
|
-
}>;
|
|
518
|
+
}, z.core.$strip>;
|
|
553
519
|
export type InstanceRegistration = z.infer<typeof InstanceRegistration>;
|
|
554
520
|
/** Silent past the declared cadence × multiplier ⇒ stale (auto-removed from the routable set). */
|
|
555
521
|
export declare function isInstanceStale(r: Pick<InstanceRegistration, "lastSeenMs" | "heartbeatMs">, nowMs: number): boolean;
|
|
556
|
-
/** Health fusion
|
|
557
|
-
*
|
|
558
|
-
*
|
|
559
|
-
|
|
522
|
+
/** Health fusion — the ONE judge of "may dispatch route to this stored instance": a signal is present ∧ the row is not stale ∧
|
|
523
|
+
* the observer does not veto ∧ the engine's own readiness ({@link judgeReadiness} over the row's {@link EngineSignal}). Two sources
|
|
524
|
+
* merge as an AND — EITHER source's "not ready" removes the instance (fail-closed). The row's boolean `draining` is not read. */
|
|
525
|
+
export declare function judgeRoutability(r: InstanceRegistration, nowMs: number): RoutingVerdict;
|
|
526
|
+
/** {@link judgeRoutability} as a boolean (the same judge, no second reading). */
|
|
560
527
|
export declare function isRoutable(r: InstanceRegistration, nowMs: number): boolean;
|
|
561
528
|
export declare function routableInstances(rs: InstanceRegistration[], nowMs: number): InstanceRegistration[];
|
|
562
|
-
/**
|
|
563
|
-
* The engine's draining fact — ONE object wherever the engine says "I am draining": inside {@link EngineSignal}`.draining`
|
|
564
|
-
* and as the body of a {@link DrainReceipt}.
|
|
565
|
-
* - `actionId` — the action that put the engine into draining (idempotency key of the drain request).
|
|
566
|
-
* - `since` — epoch ms when draining began; it does not move when the same action repeats.
|
|
567
|
-
* - `deadlineAt` — epoch ms by which the engine stops on its own.
|
|
568
|
-
*/
|
|
569
|
-
export declare const EngineDraining: z.ZodObject<{
|
|
570
|
-
actionId: z.ZodString;
|
|
571
|
-
since: z.ZodNumber;
|
|
572
|
-
deadlineAt: z.ZodNumber;
|
|
573
|
-
}, "strip", z.ZodTypeAny, {
|
|
574
|
-
since: number;
|
|
575
|
-
actionId: string;
|
|
576
|
-
deadlineAt: number;
|
|
577
|
-
}, {
|
|
578
|
-
since: number;
|
|
579
|
-
actionId: string;
|
|
580
|
-
deadlineAt: number;
|
|
581
|
-
}>;
|
|
582
|
-
export type EngineDraining = z.infer<typeof EngineDraining>;
|
|
583
|
-
/**
|
|
584
|
-
* Everything the ENGINE itself mints about its own state, as one object — the closed form of the engine-owned subset of the
|
|
585
|
-
* /health self-description ({@link InstanceHealth} is that self-description: an open record, `.passthrough()`, which also
|
|
586
|
-
* carries what the host adds — build, pid, port, store posture). One mint point on the engine; {@link readEngineSignal} reads
|
|
587
|
-
* this shape off a body whose keys sit flat on it (no wrapper key).
|
|
588
|
-
*
|
|
589
|
-
* Where it rides today: on NEITHER exit whole. The heartbeat ({@link InstanceAnnouncement}) and `/health` ({@link InstanceHealth})
|
|
590
|
-
* still speak the 8.0 dialect for the keys they share with this shape, in two ways:
|
|
591
|
-
* 1. `draining` is a BOOLEAN there, not the {@link EngineDraining} object. The heartbeat always carries it (`false` included);
|
|
592
|
-
* `/health` carries `draining: true` only while draining.
|
|
593
|
-
* 2. `/health` carries `ready` only while NOT ready (`ready: false`); a ready engine's `/health` body has no `ready` key.
|
|
594
|
-
* So `readEngineSignal` over a heartbeat body is always `undefined` (point 1), and over a `/health` body it is `undefined` whenever
|
|
595
|
-
* either applies. What does ride both exits flat, under these names and with these values: `restart`, `instanceId`
|
|
596
|
-
* and the fleet-membership keys `generation` / `leaseState` / `leaseUntil` — on the heartbeat, read them off the parsed
|
|
597
|
-
* {@link InstanceAnnouncement} (which types all five). The next major retires the boolean pair (`draining` / `drainingSince`),
|
|
598
|
-
* has both exits carry this shape whole and moves {@link isReadyForTraffic} / {@link isRoutable} onto it; until then this is
|
|
599
|
-
* the target form, and `readEngineSignal` is the reader for a body that already speaks it.
|
|
600
|
-
*
|
|
601
|
-
* - `ready` — always said (never "absent while ready"): the engine can take new work right now.
|
|
602
|
-
* - `draining` — present ⇔ draining ({@link EngineDraining}). NOTE the dialect: {@link InstanceHealth}`.draining` and
|
|
603
|
-
* {@link InstanceAnnouncement}`.draining` are BOOLEANS (the 8.0 dialect, kept untouched); here it is an object. A body
|
|
604
|
-
* whose `draining` is a boolean is therefore not an `EngineSignal` and reads as `undefined`.
|
|
605
|
-
* - `restart` — the one {@link RestartSignal}, same schema object, no copy.
|
|
606
|
-
* - `generation` — echo of the env the config renderer set on this instance ({@link FLEET_GENERATION_ENV}), a canonical
|
|
607
|
-
* decimal string ({@link isCanonicalGeneration}). Absent ⇔ not a fleet member (a single-machine engine): it takes no lease
|
|
608
|
-
* and is not fenced.
|
|
609
|
-
* - `instanceId` — the engine's OWN process identity, minted by the engine every time the process starts (never read from
|
|
610
|
-
* env: replicas of one Deployment share one env, and a restarted container reuses its env, so env cannot tell two
|
|
611
|
-
* processes apart). It is present on a standalone engine too — only `generation` marks fleet membership.
|
|
612
|
-
* - `leaseState` — the serving lease as the engine holds it ({@link LeaseState}: `none` / `granted` / `expired` / `denied`);
|
|
613
|
-
* present ⇔ fleet member (the engine says it on every beat once it has a generation).
|
|
614
|
-
* - `leaseUntil` — epoch ms until which the engine holds its serving lease, as the engine computed it from the last
|
|
615
|
-
* `ttlMs` it was granted ({@link ServingLeaseResponse}); present ⇔ `leaseState` is `granted`.
|
|
616
|
-
* - `configTargetVersion` / `configAppliedVersion` — the config-center version the engine was told to converge on, and the
|
|
617
|
-
* one it has applied: numbers, as the engine mints them (finite, the same rule as {@link RestartSignal}`.version`; equal ⇔ converged).
|
|
618
|
-
*
|
|
619
|
-
* Reading: `EngineSignal` is parsed with the default strip — a whole /health body goes in, only these keys come out. A
|
|
620
|
-
* shape violation in ANY key makes the whole signal `undefined` (it is never "the rest of it"). `undefined` means "no
|
|
621
|
-
* signal this reader can say" — an engine that predates the keys, or a malformed body — and is NEVER "ready": a caller on
|
|
622
|
-
* the routing axis treats it as not routable unless it deliberately falls back to the boolean dialect of
|
|
623
|
-
* {@link InstanceHealth}.
|
|
624
|
-
*/
|
|
625
|
-
export declare const EngineSignal: z.ZodObject<{
|
|
626
|
-
ready: z.ZodBoolean;
|
|
627
|
-
draining: z.ZodOptional<z.ZodObject<{
|
|
628
|
-
actionId: z.ZodString;
|
|
629
|
-
since: z.ZodNumber;
|
|
630
|
-
deadlineAt: z.ZodNumber;
|
|
631
|
-
}, "strip", z.ZodTypeAny, {
|
|
632
|
-
since: number;
|
|
633
|
-
actionId: string;
|
|
634
|
-
deadlineAt: number;
|
|
635
|
-
}, {
|
|
636
|
-
since: number;
|
|
637
|
-
actionId: string;
|
|
638
|
-
deadlineAt: number;
|
|
639
|
-
}>>;
|
|
640
|
-
restart: z.ZodOptional<z.ZodObject<{
|
|
641
|
-
reasons: z.ZodArray<z.ZodString, "many">;
|
|
642
|
-
version: z.ZodNumber;
|
|
643
|
-
since: z.ZodNumber;
|
|
644
|
-
withheld: z.ZodOptional<z.ZodBoolean>;
|
|
645
|
-
}, "strip", z.ZodTypeAny, {
|
|
646
|
-
reasons: string[];
|
|
647
|
-
version: number;
|
|
648
|
-
since: number;
|
|
649
|
-
withheld?: boolean | undefined;
|
|
650
|
-
}, {
|
|
651
|
-
reasons: string[];
|
|
652
|
-
version: number;
|
|
653
|
-
since: number;
|
|
654
|
-
withheld?: boolean | undefined;
|
|
655
|
-
}>>;
|
|
656
|
-
generation: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
657
|
-
instanceId: z.ZodOptional<z.ZodString>;
|
|
658
|
-
leaseState: z.ZodOptional<z.ZodEnum<["none", "granted", "expired", "denied"]>>;
|
|
659
|
-
leaseUntil: z.ZodOptional<z.ZodNumber>;
|
|
660
|
-
configTargetVersion: z.ZodOptional<z.ZodNumber>;
|
|
661
|
-
configAppliedVersion: z.ZodOptional<z.ZodNumber>;
|
|
662
|
-
}, "strip", z.ZodTypeAny, {
|
|
663
|
-
ready: boolean;
|
|
664
|
-
draining?: {
|
|
665
|
-
since: number;
|
|
666
|
-
actionId: string;
|
|
667
|
-
deadlineAt: number;
|
|
668
|
-
} | undefined;
|
|
669
|
-
restart?: {
|
|
670
|
-
reasons: string[];
|
|
671
|
-
version: number;
|
|
672
|
-
since: number;
|
|
673
|
-
withheld?: boolean | undefined;
|
|
674
|
-
} | undefined;
|
|
675
|
-
instanceId?: string | undefined;
|
|
676
|
-
generation?: string | undefined;
|
|
677
|
-
leaseState?: "none" | "granted" | "expired" | "denied" | undefined;
|
|
678
|
-
leaseUntil?: number | undefined;
|
|
679
|
-
configTargetVersion?: number | undefined;
|
|
680
|
-
configAppliedVersion?: number | undefined;
|
|
681
|
-
}, {
|
|
682
|
-
ready: boolean;
|
|
683
|
-
draining?: {
|
|
684
|
-
since: number;
|
|
685
|
-
actionId: string;
|
|
686
|
-
deadlineAt: number;
|
|
687
|
-
} | undefined;
|
|
688
|
-
restart?: {
|
|
689
|
-
reasons: string[];
|
|
690
|
-
version: number;
|
|
691
|
-
since: number;
|
|
692
|
-
withheld?: boolean | undefined;
|
|
693
|
-
} | undefined;
|
|
694
|
-
instanceId?: string | undefined;
|
|
695
|
-
generation?: string | undefined;
|
|
696
|
-
leaseState?: "none" | "granted" | "expired" | "denied" | undefined;
|
|
697
|
-
leaseUntil?: number | undefined;
|
|
698
|
-
configTargetVersion?: number | undefined;
|
|
699
|
-
configAppliedVersion?: number | undefined;
|
|
700
|
-
}>;
|
|
701
|
-
export type EngineSignal = z.infer<typeof EngineSignal>;
|
|
702
|
-
/**
|
|
703
|
-
* The reader of {@link EngineSignal}: the shape's keys, flat on the given body, judged by the same guarded body reader as
|
|
704
|
-
* {@link readRestartSignal} and {@link readModelRefusals}, so the same odd input gets the same answer from all three. Shape
|
|
705
|
-
* mismatch, a missing `ready`, a non-object body, even a throwing accessor ⇒ `undefined` — never throws. See {@link EngineSignal}
|
|
706
|
-
* for what `undefined` means (not "ready") and for why today's heartbeat and `/health` bodies read as `undefined` here.
|
|
707
|
-
*/
|
|
708
|
-
export declare function readEngineSignal(health: unknown): EngineSignal | undefined;
|
|
709
529
|
/**
|
|
710
530
|
* The env name a fleet member's generation travels under — the config renderer sets it, the engine reads it back and echoes
|
|
711
531
|
* it as {@link EngineSignal}`.generation`. Both sides import THIS constant; neither spells the name. The value is the
|
|
712
532
|
* generation this instance belongs to (opaque string); absent from the engine's env ⇔ not a fleet member (the standalone
|
|
713
533
|
* form). There is deliberately NO env name for the instance id: an instance's identity is minted by the engine per process
|
|
714
|
-
* ({@link
|
|
534
|
+
* ({@link InstanceHealth}`.instanceId`), and env cannot carry it (see there).
|
|
715
535
|
*/
|
|
716
536
|
export declare const FLEET_GENERATION_ENV = "FLEET_GENERATION";
|
|
717
537
|
/**
|
|
@@ -723,19 +543,19 @@ export declare const FLEET_GENERATION_ENV = "FLEET_GENERATION";
|
|
|
723
543
|
* {@link ServingLeaseRequest} and {@link InstanceAnnouncement}; nothing in the engine's env does); the engine's own identity
|
|
724
544
|
* scope is the one the renderer gave in this env; that the two are the same value is the renderer's obligation.
|
|
725
545
|
* The engine never derives it from the token's format (a token is opaque to it, and a format guess would be a second source
|
|
726
|
-
* that can disagree with the scope
|
|
727
|
-
* the same default
|
|
546
|
+
* that can disagree with the scope web-admin binds). Absent from the engine's env ⇒ the default scope, {@link GLOBAL_SCOPE} (`api/scopes`) —
|
|
547
|
+
* the same default web-admin reads where nothing names a scope; this note does not restate the value. Like
|
|
728
548
|
* {@link FLEET_GENERATION_ENV} it is a reserved worker env name (`WorkerSpec.env` / `secretEnv` cannot set it).
|
|
729
549
|
*/
|
|
730
550
|
export declare const FLEET_SCOPE_ENV = "FLEET_SCOPE";
|
|
731
551
|
/**
|
|
732
552
|
* The serving lease — the ONE fence between a superseded generation and the traffic it must stop taking. A fleet-member
|
|
733
|
-
* engine renews it with
|
|
553
|
+
* engine renews it with web-admin; web-admin answers per the lifecycle object it holds for that instance. Three constants
|
|
734
554
|
* fix the clock, all in ms:
|
|
735
555
|
* - {@link SERVING_LEASE_TTL_DEFAULT_MS} — the lease length when the operator sets none (10 min).
|
|
736
556
|
* - {@link SERVING_LEASE_TTL_FLOOR_MS} — the shortest lease there is: a configured TTL below it is treated as it (1 min),
|
|
737
557
|
* the same way {@link DRAIN_GRACE_FLOOR_MS} protects a fat-fingered grace.
|
|
738
|
-
* - {@link SERVING_LEASE_DRIFT_MARGIN_MS} —
|
|
558
|
+
* - {@link SERVING_LEASE_DRIFT_MARGIN_MS} — web-admin's clock-skew allowance: it adds this to a lease's recorded expiry
|
|
739
559
|
* before it counts that lease as over. It is DERIVED from {@link DEFAULT_HEARTBEAT_MS} (one cadence, one number), never a
|
|
740
560
|
* second constant that could drift from it.
|
|
741
561
|
*/
|
|
@@ -747,177 +567,137 @@ export declare const SERVING_LEASE_DRIFT_MARGIN_MS = 30000;
|
|
|
747
567
|
* {@link SERVING_LEASE_TTL_DEFAULT_MS}; a number below {@link SERVING_LEASE_TTL_FLOOR_MS} (zero and negatives included) ⇒ the
|
|
748
568
|
* floor; otherwise the number, truncated to whole ms, with no ceiling. It is for the ISSUING side's configuration. A reply's
|
|
749
569
|
* `ttlMs` is used exactly as granted and is never clamped upward — an engine that stretched a short grant to the floor would
|
|
750
|
-
* hold the lease past what
|
|
570
|
+
* hold the lease past what web-admin recorded, which is the one thing the fence exists to prevent.
|
|
751
571
|
*/
|
|
752
572
|
export declare function clampServingLeaseTtlMs(configuredMs: number | undefined): number;
|
|
753
573
|
/**
|
|
754
|
-
* Engine →
|
|
574
|
+
* Engine → web-admin: "may this instance keep serving?" The identity triple names the instance exactly — a lease is never
|
|
755
575
|
* asked for by worker name alone. `generation` is the env echo ({@link FLEET_GENERATION_ENV}); `instanceId` is the engine's
|
|
756
|
-
* own per-process identity ({@link
|
|
576
|
+
* own per-process identity ({@link InstanceHealth}`.instanceId`), so a restarted process is a NEW instance to web-admin.
|
|
757
577
|
* `scope` — the tenant scope the lease is asked in. A renewal is taken on a PER-WORKER token only — the credential binds
|
|
758
578
|
* (scope, worker) — so the credential decides the scope and this field can only agree: absent = the scope the token binds; present
|
|
759
579
|
* ⇒ it must equal that scope (a mismatch is refused, never rewritten either way). The shared token may not renew a lease at all
|
|
760
|
-
* (
|
|
580
|
+
* (web-admin answers 403). Only fleet members ask: an engine without a generation (the standalone form) has no lease and sends
|
|
761
581
|
* nothing.
|
|
762
582
|
*/
|
|
763
583
|
export declare const ServingLeaseRequest: z.ZodObject<{
|
|
764
584
|
worker: z.ZodString;
|
|
765
585
|
scope: z.ZodOptional<z.ZodString>;
|
|
766
|
-
generation: z.
|
|
586
|
+
generation: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
|
|
767
587
|
instanceId: z.ZodString;
|
|
768
|
-
},
|
|
769
|
-
instanceId: string;
|
|
770
|
-
worker: string;
|
|
771
|
-
generation: string;
|
|
772
|
-
scope?: string | undefined;
|
|
773
|
-
}, {
|
|
774
|
-
instanceId: string;
|
|
775
|
-
worker: string;
|
|
776
|
-
generation: string;
|
|
777
|
-
scope?: string | undefined;
|
|
778
|
-
}>;
|
|
588
|
+
}, z.core.$strip>;
|
|
779
589
|
export type ServingLeaseRequest = z.infer<typeof ServingLeaseRequest>;
|
|
780
590
|
/**
|
|
781
591
|
* Why a lease is refused — a CLOSED set of exactly two words, owned here:
|
|
782
592
|
* - `superseded` — a newer generation of this worker has taken over; this instance is the old one.
|
|
783
|
-
* - `retired` —
|
|
593
|
+
* - `retired` — web-admin has retired this instance (or the worker it belongs to).
|
|
784
594
|
* Only {@link ServingLeaseResponse}'s `denied` arm carries a reason; `unknown` has none, by shape.
|
|
785
595
|
*/
|
|
786
596
|
export declare const SERVING_LEASE_DENIED_REASONS: readonly ["superseded", "retired"];
|
|
787
|
-
export declare const ServingLeaseDeniedReason: z.ZodEnum<
|
|
597
|
+
export declare const ServingLeaseDeniedReason: z.ZodEnum<{
|
|
598
|
+
superseded: "superseded";
|
|
599
|
+
retired: "retired";
|
|
600
|
+
}>;
|
|
788
601
|
export type ServingLeaseDeniedReason = z.infer<typeof ServingLeaseDeniedReason>;
|
|
789
602
|
/**
|
|
790
|
-
*
|
|
603
|
+
* Web-admin → engine: the verdict, a CLOSED set of three words ({@link SERVING_LEASE_VERDICTS}) — the arms of one
|
|
791
604
|
* discriminated union, so a reader that switches on `verdict` is told by the compiler when a word is added.
|
|
792
605
|
* - `granted` — keep serving; `ttlMs` is how long the lease lasts, a DURATION, never a wall-clock instant: the engine counts
|
|
793
|
-
* it from the instant it SENT the request, on its own monotonic clock (`expiresAt = sendAt + ttlMs`), and
|
|
606
|
+
* it from the instant it SENT the request, on its own monotonic clock (`expiresAt = sendAt + ttlMs`), and web-admin keeps
|
|
794
607
|
* its own expiry from its store time — the two sides never compare clocks, so no clock agreement is needed.
|
|
795
608
|
* - `denied` — this instance must stop: `reason` is {@link ServingLeaseDeniedReason}. The ONLY arm that obliges an engine to
|
|
796
|
-
* stop serving, and it is issued on POSITIVE evidence only:
|
|
609
|
+
* stop serving, and it is issued on POSITIVE evidence only: web-admin keeps, per (scope, worker), a persisted generation
|
|
797
610
|
* floor that is never garbage-collected, and a request whose generation lies below it is `superseded`; `retired` likewise
|
|
798
|
-
* rests on a record
|
|
799
|
-
* - `unknown` —
|
|
611
|
+
* rests on a record web-admin holds. A web-admin that merely holds no such record does not deny — it answers `unknown`.
|
|
612
|
+
* - `unknown` — web-admin holds no lifecycle object for this instance right now (restoring its store, migrating scopes, not
|
|
800
613
|
* yet claimed). It says NOTHING about whether the instance should serve, so it is not a kind of `denied` and absence of the
|
|
801
614
|
* object NEVER turns into a denial: it carries no reason, and an engine never drains on it. To the engine it is the same
|
|
802
|
-
* case as an unreachable
|
|
803
|
-
* This is the law for what a
|
|
615
|
+
* case as an unreachable web-admin.
|
|
616
|
+
* This is the law for what a web-admin SENDS. A reader does not apply it to `reason`: {@link readServingLeaseResponse} judges by
|
|
804
617
|
* the `verdict` key and keeps a `denied` a `denied` even when its reason is outside the set ({@link ServingLeaseReading}). A
|
|
805
618
|
* reply whose `verdict` itself is outside the three words (or a `granted` with no valid `ttlMs`) is not a verdict at all and
|
|
806
|
-
* reads as `undefined` — an unreachable
|
|
619
|
+
* reads as `undefined` — an unreachable web-admin, never `denied`, never `granted`.
|
|
807
620
|
*/
|
|
808
|
-
export declare const ServingLeaseResponse: z.ZodDiscriminatedUnion<
|
|
621
|
+
export declare const ServingLeaseResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
809
622
|
verdict: z.ZodLiteral<"granted">;
|
|
810
623
|
ttlMs: z.ZodNumber;
|
|
811
|
-
},
|
|
812
|
-
verdict: "granted";
|
|
813
|
-
ttlMs: number;
|
|
814
|
-
}, {
|
|
815
|
-
verdict: "granted";
|
|
816
|
-
ttlMs: number;
|
|
817
|
-
}>, z.ZodObject<{
|
|
624
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
818
625
|
verdict: z.ZodLiteral<"denied">;
|
|
819
|
-
reason: z.ZodEnum<
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
}, {
|
|
824
|
-
reason: "superseded" | "retired";
|
|
825
|
-
verdict: "denied";
|
|
826
|
-
}>, z.ZodObject<{
|
|
626
|
+
reason: z.ZodEnum<{
|
|
627
|
+
superseded: "superseded";
|
|
628
|
+
retired: "retired";
|
|
629
|
+
}>;
|
|
630
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
827
631
|
verdict: z.ZodLiteral<"unknown">;
|
|
828
|
-
},
|
|
829
|
-
verdict: "unknown";
|
|
830
|
-
}, {
|
|
831
|
-
verdict: "unknown";
|
|
832
|
-
}>]>;
|
|
632
|
+
}, z.core.$strip>], "verdict">;
|
|
833
633
|
export type ServingLeaseResponse = z.infer<typeof ServingLeaseResponse>;
|
|
834
634
|
/** The reason word a READER puts in place of a `denied` reason it cannot place (outside {@link SERVING_LEASE_DENIED_REASONS},
|
|
835
635
|
* missing, not a string). Read-face only: a producer never sends it, and the strict {@link ServingLeaseResponse} refuses it. */
|
|
836
636
|
export declare const SERVING_LEASE_UNKNOWN_REASON = "unknown_reason";
|
|
837
637
|
/** The read face of {@link ServingLeaseResponse} — same three arms, with the `denied` arm tolerant about `reason`. */
|
|
838
|
-
declare const ServingLeaseReadFace: z.ZodDiscriminatedUnion<
|
|
638
|
+
declare const ServingLeaseReadFace: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
839
639
|
verdict: z.ZodLiteral<"granted">;
|
|
840
640
|
ttlMs: z.ZodNumber;
|
|
841
|
-
},
|
|
842
|
-
verdict: "granted";
|
|
843
|
-
ttlMs: number;
|
|
844
|
-
}, {
|
|
845
|
-
verdict: "granted";
|
|
846
|
-
ttlMs: number;
|
|
847
|
-
}>, z.ZodObject<{
|
|
641
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
848
642
|
verdict: z.ZodLiteral<"denied">;
|
|
849
|
-
reason: z.
|
|
850
|
-
},
|
|
851
|
-
reason: "superseded" | "retired" | "unknown_reason";
|
|
852
|
-
verdict: "denied";
|
|
853
|
-
}, {
|
|
854
|
-
verdict: "denied";
|
|
855
|
-
reason?: unknown;
|
|
856
|
-
}>, z.ZodObject<{
|
|
643
|
+
reason: z.ZodPipe<z.ZodOptional<z.ZodUnknown>, z.ZodTransform<"superseded" | "retired" | "unknown_reason", unknown>>;
|
|
644
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
857
645
|
verdict: z.ZodLiteral<"unknown">;
|
|
858
|
-
},
|
|
859
|
-
verdict: "unknown";
|
|
860
|
-
}, {
|
|
861
|
-
verdict: "unknown";
|
|
862
|
-
}>]>;
|
|
646
|
+
}, z.core.$strip>], "verdict">;
|
|
863
647
|
/** The verdict words as a runtime list. `satisfies` bites a word the union does not have; the converse (a union arm missing
|
|
864
648
|
* from this list) is pinned by a test that reads the union's discriminators. */
|
|
865
649
|
export declare const SERVING_LEASE_VERDICTS: readonly ["granted", "denied", "unknown"];
|
|
866
|
-
/**
|
|
650
|
+
/** Web-admin side: the request body of a serving-lease renewal. Shape mismatch / non-object / throwing accessor ⇒ `undefined`,
|
|
867
651
|
* never throws (same guarded reader as every other read in this file). */
|
|
868
652
|
export declare function readServingLeaseRequest(body: unknown): ServingLeaseRequest | undefined;
|
|
869
653
|
/**
|
|
870
654
|
* What {@link readServingLeaseResponse} returns: {@link ServingLeaseResponse} plus the one thing a reader adds — a `denied`
|
|
871
655
|
* whose reason it could not place carries {@link SERVING_LEASE_UNKNOWN_REASON}. Two faces on purpose (the failure direction
|
|
872
|
-
* must match the meaning): the strict wire law says what a
|
|
873
|
-
* by the `verdict` key alone, so a
|
|
656
|
+
* must match the meaning): the strict wire law says what a web-admin may SEND (a closed two-word reason set); the reader judges
|
|
657
|
+
* by the `verdict` key alone, so a web-admin that learns a third reason word cannot make an older engine read `denied` as
|
|
874
658
|
* `undefined` and keep serving. Branch on `verdict`; `reason` is for the log line.
|
|
875
659
|
*/
|
|
876
660
|
export type ServingLeaseReading = z.output<typeof ServingLeaseReadFace>;
|
|
877
661
|
/** Engine side: the reply to a renewal, judged by its `verdict` key: `granted` needs a valid `ttlMs`, `denied` is `denied`
|
|
878
662
|
* whatever its `reason` (see {@link ServingLeaseReading}), `unknown` is `unknown`. A non-object, a throwing accessor, a
|
|
879
663
|
* `verdict` outside the three words or a `granted` without a valid `ttlMs` ⇒ `undefined`, never throws — and `undefined`
|
|
880
|
-
* is read like an unreachable
|
|
664
|
+
* is read like an unreachable web-admin, never like `denied` and never like `granted`. */
|
|
881
665
|
export declare function readServingLeaseResponse(body: unknown): ServingLeaseReading | undefined;
|
|
882
666
|
/**
|
|
883
|
-
* What
|
|
884
|
-
* - `servingLease` —
|
|
667
|
+
* What web-admin says it can do: capability bits an engine reads before it starts using a web-admin-side endpoint.
|
|
668
|
+
* - `servingLease` — web-admin answers serving-lease renewals ({@link ServingLeaseRequest} / {@link ServingLeaseResponse}).
|
|
885
669
|
*
|
|
886
670
|
* A bit is `true` or absent — one spelling for "not supported" (there is no `false`). An absent bit — in a declared object, or
|
|
887
|
-
* because
|
|
888
|
-
* neither renews a lease nor is fenced by one: this is the compatibility gate that lets an executor ship ahead of
|
|
889
|
-
* without pushing running engines into lease expiry. It is a gate, not a verdict — nothing in this shape says "
|
|
671
|
+
* because web-admin declares nothing at all (an older web-admin) — means web-admin does not support the lease, and the engine then
|
|
672
|
+
* neither renews a lease nor is fenced by one: this is the compatibility gate that lets an executor ship ahead of web-admin
|
|
673
|
+
* without pushing running engines into lease expiry. It is a gate, not a verdict — nothing in this shape says "web-admin refuses
|
|
890
674
|
* leases". Read a heartbeat reply with {@link readCapabilityReading} and start the lease client only on a `declared` reading whose
|
|
891
675
|
* `capabilities.servingLease === true`.
|
|
892
|
-
* Unknown bits are stripped (a newer
|
|
676
|
+
* Unknown bits are stripped (a newer web-admin's extra bits never break an older reader); a known bit whose value is not `true` breaks
|
|
893
677
|
* the declaration — and a broken declaration is NOT an absent one: the reply reads as `unreadable`, which is not a reading (the engine
|
|
894
678
|
* keeps the state it had; see {@link readCapabilityReading}).
|
|
895
679
|
* It rides in ONE place: the heartbeat's 2xx reply ({@link FLEET_PATHS}`.instances`), under {@link HEARTBEAT_REPLY_CAPABILITIES_KEY}.
|
|
896
|
-
*
|
|
680
|
+
* Web-admin answers the bits per CALLER CREDENTIAL: under a per-worker token, by what web-admin supports; under the shared token
|
|
897
681
|
* `servingLease` is always absent (the serving-lease endpoint takes per-worker tokens only, so announcing it there would only push
|
|
898
682
|
* that caller into the unreachable arm).
|
|
899
683
|
*/
|
|
900
|
-
export declare const
|
|
684
|
+
export declare const WebAdminCapabilities: z.ZodObject<{
|
|
901
685
|
servingLease: z.ZodOptional<z.ZodLiteral<true>>;
|
|
902
|
-
},
|
|
903
|
-
|
|
904
|
-
}, {
|
|
905
|
-
servingLease?: true | undefined;
|
|
906
|
-
}>;
|
|
907
|
-
export type CenterCapabilities = z.infer<typeof CenterCapabilities>;
|
|
686
|
+
}, z.core.$strip>;
|
|
687
|
+
export type WebAdminCapabilities = z.infer<typeof WebAdminCapabilities>;
|
|
908
688
|
/**
|
|
909
|
-
* The key of the heartbeat's 2xx reply body under which
|
|
910
|
-
*
|
|
689
|
+
* The key of the heartbeat's 2xx reply body under which web-admin puts its {@link WebAdminCapabilities} — the object's only carrier.
|
|
690
|
+
* Web-admin writes `{ …, [HEARTBEAT_REPLY_CAPABILITIES_KEY]: caps }`; the engine reads the whole reply with
|
|
911
691
|
* `readCapabilityReading(reply)` — the key lookup lives in the reader, one carrier one reader — and only on a 2xx (a failed heartbeat
|
|
912
692
|
* says nothing about the bits). Nested, not flat: the reply's own keys and the capability bits never share a namespace.
|
|
913
|
-
* A reply without the key is an older
|
|
693
|
+
* A reply without the key is an older web-admin — no capability said (`undeclared`). A reply with the key present with a value that
|
|
914
694
|
* breaks the shape is NOT "without the key" — it is `unreadable`, and an unreadable reply says nothing: neither declared nor undeclared.
|
|
915
695
|
*/
|
|
916
696
|
export declare const HEARTBEAT_REPLY_CAPABILITIES_KEY = "capabilities";
|
|
917
|
-
/** What one heartbeat reply says about
|
|
697
|
+
/** What one heartbeat reply says about web-admin's capabilities — three readings of the reply, see {@link readCapabilityReading}. */
|
|
918
698
|
export type CapabilityReading = {
|
|
919
699
|
kind: "declared";
|
|
920
|
-
capabilities:
|
|
700
|
+
capabilities: WebAdminCapabilities;
|
|
921
701
|
} | {
|
|
922
702
|
kind: "undeclared";
|
|
923
703
|
} | {
|
|
@@ -925,16 +705,16 @@ export type CapabilityReading = {
|
|
|
925
705
|
why: "not-an-object" | "invalid-capabilities";
|
|
926
706
|
};
|
|
927
707
|
/**
|
|
928
|
-
* Engine side: what one heartbeat reply says about
|
|
708
|
+
* Engine side: what one heartbeat reply says about web-admin's capabilities. The input is the heartbeat's whole 2xx reply body
|
|
929
709
|
* (the key lookup lives here — one carrier, one reader), the output one of three readings:
|
|
930
|
-
* - `declared` — the key ({@link HEARTBEAT_REPLY_CAPABILITIES_KEY}) is present and its value passes {@link
|
|
710
|
+
* - `declared` — the key ({@link HEARTBEAT_REPLY_CAPABILITIES_KEY}) is present and its value passes {@link WebAdminCapabilities};
|
|
931
711
|
* `capabilities` is the parsed object (unknown bits stripped, an absent bit stays absent).
|
|
932
|
-
* - `undeclared` — the reply is an object and the key is absent: an older
|
|
712
|
+
* - `undeclared` — the reply is an object and the key is absent: an older web-admin, which has said nothing about capabilities.
|
|
933
713
|
* (`undefined` as the key's value is what JSON cannot carry; it reads as the key being absent.)
|
|
934
714
|
* - `unreadable` — the reply is not an object (`why: "not-an-object"`), or the key is present but its value is not a
|
|
935
|
-
* {@link
|
|
715
|
+
* {@link WebAdminCapabilities} (`why: "invalid-capabilities"`; an accessor that throws reads the same way).
|
|
936
716
|
* Only `declared` and `undeclared` are readings. `unreadable` is not a reading: on the fence axis a malformed declaration must not
|
|
937
|
-
* read as "
|
|
717
|
+
* read as "web-admin declared nothing", so the engine acts on it as on no reply at all — it keeps the state it last had (before
|
|
938
718
|
* any reading: no lease) and leaves its clocks, the lease expiry and the outage allowance, as they were. Start the lease client only
|
|
939
719
|
* on `declared` with `capabilities.servingLease === true`. Never throws; judged by the same one guarded body judgement as every
|
|
940
720
|
* other reader here.
|
|
@@ -942,8 +722,8 @@ export type CapabilityReading = {
|
|
|
942
722
|
export declare function readCapabilityReading(reply: unknown): CapabilityReading;
|
|
943
723
|
/**
|
|
944
724
|
* The drain endpoint's request: "put this instance into draining, by this deadline". One persisted action, one validation,
|
|
945
|
-
* whether
|
|
946
|
-
* call from
|
|
725
|
+
* whether web-admin calls directly or an executor retries it. Two lanes, one shape: on the Docker lane (and for a direct
|
|
726
|
+
* call from web-admin) this HTTP request is how an instance is drained; on the Kubernetes lane no per-pod HTTP call is made
|
|
947
727
|
* (per-pod addressing and RBAC make it the wrong tool) — an instance is drained by its serving lease being denied
|
|
948
728
|
* ({@link ServingLeaseResponse}), after which the engine puts itself into draining.
|
|
949
729
|
* - `actionId` — idempotency key: the same id again echoes the first answer ({@link DrainReceipt}`.state` =
|
|
@@ -951,42 +731,29 @@ export declare function readCapabilityReading(reply: unknown): CapabilityReading
|
|
|
951
731
|
* - the identity — `scope?` / `worker` / `expectedGeneration?` / `instanceId?` — names WHICH instance the action is for. The
|
|
952
732
|
* engine compares it field by field against its own, absent against absent: an engine that is not a fleet member has no
|
|
953
733
|
* generation, so a request with no `expectedGeneration` is the standalone form, not a wildcard; `instanceId`, when given,
|
|
954
|
-
* is the engine's own per-process identity ({@link
|
|
734
|
+
* is the engine's own per-process identity ({@link InstanceHealth}`.instanceId`). A mismatch is the endpoint's refusal, not
|
|
955
735
|
* this shape's: this schema only says what a well-formed request looks like.
|
|
956
736
|
* - `deadlineAt` — epoch ms by which the engine stops on its own.
|
|
957
|
-
* - `term` — the claim term the sending executor holds for this action (
|
|
737
|
+
* - `term` — the claim term the sending executor holds for this action (web-admin-issued, opaque); absent where no executor
|
|
958
738
|
* sits in between (a direct call, a hand-issued action).
|
|
959
739
|
*/
|
|
960
740
|
export declare const DrainRequest: z.ZodObject<{
|
|
961
741
|
actionId: z.ZodString;
|
|
962
742
|
scope: z.ZodOptional<z.ZodString>;
|
|
963
743
|
worker: z.ZodString;
|
|
964
|
-
expectedGeneration: z.ZodOptional<z.
|
|
744
|
+
expectedGeneration: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
|
|
965
745
|
instanceId: z.ZodOptional<z.ZodString>;
|
|
966
746
|
deadlineAt: z.ZodNumber;
|
|
967
747
|
term: z.ZodOptional<z.ZodString>;
|
|
968
|
-
},
|
|
969
|
-
worker: string;
|
|
970
|
-
actionId: string;
|
|
971
|
-
deadlineAt: number;
|
|
972
|
-
instanceId?: string | undefined;
|
|
973
|
-
scope?: string | undefined;
|
|
974
|
-
expectedGeneration?: string | undefined;
|
|
975
|
-
term?: string | undefined;
|
|
976
|
-
}, {
|
|
977
|
-
worker: string;
|
|
978
|
-
actionId: string;
|
|
979
|
-
deadlineAt: number;
|
|
980
|
-
instanceId?: string | undefined;
|
|
981
|
-
scope?: string | undefined;
|
|
982
|
-
expectedGeneration?: string | undefined;
|
|
983
|
-
term?: string | undefined;
|
|
984
|
-
}>;
|
|
748
|
+
}, z.core.$strip>;
|
|
985
749
|
export type DrainRequest = z.infer<typeof DrainRequest>;
|
|
986
750
|
/** What a drain request leaves the engine in: `draining` — this call put it into draining; `already_draining` — it already was
|
|
987
751
|
* (the same action repeated), and the receipt reports the first call's facts. */
|
|
988
752
|
export declare const DRAIN_RECEIPT_STATES: readonly ["draining", "already_draining"];
|
|
989
|
-
export declare const DrainReceiptState: z.ZodEnum<
|
|
753
|
+
export declare const DrainReceiptState: z.ZodEnum<{
|
|
754
|
+
draining: "draining";
|
|
755
|
+
already_draining: "already_draining";
|
|
756
|
+
}>;
|
|
990
757
|
export type DrainReceiptState = z.infer<typeof DrainReceiptState>;
|
|
991
758
|
/** The drain endpoint's answer: the engine's draining fact ({@link EngineDraining} — the same object {@link EngineSignal}
|
|
992
759
|
* carries as `draining`, extended, not copied) plus how this call found the engine. */
|
|
@@ -994,19 +761,11 @@ export declare const DrainReceipt: z.ZodObject<{
|
|
|
994
761
|
actionId: z.ZodString;
|
|
995
762
|
since: z.ZodNumber;
|
|
996
763
|
deadlineAt: z.ZodNumber;
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
deadlineAt: number;
|
|
1003
|
-
state: "draining" | "already_draining";
|
|
1004
|
-
}, {
|
|
1005
|
-
since: number;
|
|
1006
|
-
actionId: string;
|
|
1007
|
-
deadlineAt: number;
|
|
1008
|
-
state: "draining" | "already_draining";
|
|
1009
|
-
}>;
|
|
764
|
+
state: z.ZodEnum<{
|
|
765
|
+
draining: "draining";
|
|
766
|
+
already_draining: "already_draining";
|
|
767
|
+
}>;
|
|
768
|
+
}, z.core.$strip>;
|
|
1010
769
|
export type DrainReceipt = z.infer<typeof DrainReceipt>;
|
|
1011
770
|
/** Engine side: the request body of the drain endpoint. Shape mismatch / non-object / throwing accessor ⇒ `undefined`, never throws. */
|
|
1012
771
|
export declare function readDrainRequest(body: unknown): DrainRequest | undefined;
|
|
@@ -1014,34 +773,20 @@ export declare function readDrainRequest(body: unknown): DrainRequest | undefine
|
|
|
1014
773
|
export declare function readDrainReceipt(body: unknown): DrainReceipt | undefined;
|
|
1015
774
|
export declare const DISPATCH_STRATEGIES: readonly ["round-robin", "least-inflight", "consistent-hash"];
|
|
1016
775
|
export type DispatchStrategy = (typeof DISPATCH_STRATEGIES)[number];
|
|
1017
|
-
/** Per-worker dispatch policy (
|
|
776
|
+
/** Per-worker dispatch policy (web-admin-configured; the gateway is OPTIONAL — D3: TOC/self-maintained TOB go
|
|
1018
777
|
* direct, sema-managed fleets route through /api/dispatch). Strategy is an enum on purpose: a new LB
|
|
1019
778
|
* algorithm is a new value, not a new skeleton. */
|
|
1020
779
|
export declare const DispatchPolicy: z.ZodObject<{
|
|
1021
|
-
strategy: z.ZodDefault<z.ZodEnum<
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
780
|
+
strategy: z.ZodDefault<z.ZodEnum<{
|
|
781
|
+
"round-robin": "round-robin";
|
|
782
|
+
"least-inflight": "least-inflight";
|
|
783
|
+
"consistent-hash": "consistent-hash";
|
|
784
|
+
}>>;
|
|
1025
785
|
sessionAffinity: z.ZodDefault<z.ZodBoolean>;
|
|
1026
|
-
/** gateway-level concurrency gate (per worker) */
|
|
1027
786
|
maxInflight: z.ZodOptional<z.ZodNumber>;
|
|
1028
|
-
/** per-actor fairness gate */
|
|
1029
787
|
perActorMaxInflight: z.ZodOptional<z.ZodNumber>;
|
|
1030
|
-
/** cost clamp applied to submissions passing the gateway */
|
|
1031
788
|
maxCostUsd: z.ZodOptional<z.ZodNumber>;
|
|
1032
|
-
},
|
|
1033
|
-
strategy: "round-robin" | "least-inflight" | "consistent-hash";
|
|
1034
|
-
sessionAffinity: boolean;
|
|
1035
|
-
maxInflight?: number | undefined;
|
|
1036
|
-
perActorMaxInflight?: number | undefined;
|
|
1037
|
-
maxCostUsd?: number | undefined;
|
|
1038
|
-
}, {
|
|
1039
|
-
strategy?: "round-robin" | "least-inflight" | "consistent-hash" | undefined;
|
|
1040
|
-
sessionAffinity?: boolean | undefined;
|
|
1041
|
-
maxInflight?: number | undefined;
|
|
1042
|
-
perActorMaxInflight?: number | undefined;
|
|
1043
|
-
maxCostUsd?: number | undefined;
|
|
1044
|
-
}>;
|
|
789
|
+
}, z.core.$strip>;
|
|
1045
790
|
export type DispatchPolicy = z.infer<typeof DispatchPolicy>;
|
|
1046
791
|
/** 🔴 Submit idempotency — the precondition that makes failover re-routing SAFE: a new-run submission MUST
|
|
1047
792
|
* carry the client-minted durable taskId, and the server dedupes on it. Without this, "landed but the
|
|
@@ -1066,8 +811,8 @@ export declare function pickInstance(policy: DispatchPolicy, candidates: Dispatc
|
|
|
1066
811
|
cursor?: number;
|
|
1067
812
|
affinityKey?: string;
|
|
1068
813
|
}): DispatchCandidate | null;
|
|
1069
|
-
/** A pre-authorized slice of a principal's global budget, granted by
|
|
1070
|
-
* locally (no per-call round-trip), renews when running low, and
|
|
814
|
+
/** A pre-authorized slice of a principal's global budget, granted by web-admin to ONE worker: the worker deducts
|
|
815
|
+
* locally (no per-call round-trip), renews when running low, and web-admin reconciles reported consumption
|
|
1071
816
|
* against the grant after the fact — the roaming/CDN pattern.
|
|
1072
817
|
*
|
|
1073
818
|
* 0.6.1 双轴 slice(EXPERT-REDESIGN §4-2/§4-6, service 回执"发布即接单"):slice = **{weightedTokens,
|
|
@@ -1081,59 +826,17 @@ export declare const QuotaLease: z.ZodObject<{
|
|
|
1081
826
|
leaseId: z.ZodString;
|
|
1082
827
|
principal: z.ZodString;
|
|
1083
828
|
worker: z.ZodString;
|
|
1084
|
-
/** granted dimensions — either or both; a dimension absent = not governed by this lease.
|
|
1085
|
-
* `weightedTokens` = 加权 tokens(配额轴)。 */
|
|
1086
829
|
grant: z.ZodObject<{
|
|
1087
830
|
weightedTokens: z.ZodOptional<z.ZodNumber>;
|
|
1088
831
|
costMicroUsd: z.ZodOptional<z.ZodNumber>;
|
|
1089
|
-
},
|
|
1090
|
-
|
|
1091
|
-
costMicroUsd?: number | undefined;
|
|
1092
|
-
}, {
|
|
1093
|
-
weightedTokens?: number | undefined;
|
|
1094
|
-
costMicroUsd?: number | undefined;
|
|
1095
|
-
}>;
|
|
1096
|
-
consumed: z.ZodDefault<z.ZodObject<{
|
|
832
|
+
}, z.core.$strip>;
|
|
833
|
+
consumed: z.ZodPrefault<z.ZodObject<{
|
|
1097
834
|
weightedTokens: z.ZodDefault<z.ZodNumber>;
|
|
1098
835
|
costMicroUsd: z.ZodDefault<z.ZodNumber>;
|
|
1099
|
-
},
|
|
1100
|
-
weightedTokens: number;
|
|
1101
|
-
costMicroUsd: number;
|
|
1102
|
-
}, {
|
|
1103
|
-
weightedTokens?: number | undefined;
|
|
1104
|
-
costMicroUsd?: number | undefined;
|
|
1105
|
-
}>>;
|
|
836
|
+
}, z.core.$strip>>;
|
|
1106
837
|
issuedMs: z.ZodNumber;
|
|
1107
838
|
expiresMs: z.ZodNumber;
|
|
1108
|
-
},
|
|
1109
|
-
worker: string;
|
|
1110
|
-
leaseId: string;
|
|
1111
|
-
principal: string;
|
|
1112
|
-
grant: {
|
|
1113
|
-
weightedTokens?: number | undefined;
|
|
1114
|
-
costMicroUsd?: number | undefined;
|
|
1115
|
-
};
|
|
1116
|
-
consumed: {
|
|
1117
|
-
weightedTokens: number;
|
|
1118
|
-
costMicroUsd: number;
|
|
1119
|
-
};
|
|
1120
|
-
issuedMs: number;
|
|
1121
|
-
expiresMs: number;
|
|
1122
|
-
}, {
|
|
1123
|
-
worker: string;
|
|
1124
|
-
leaseId: string;
|
|
1125
|
-
principal: string;
|
|
1126
|
-
grant: {
|
|
1127
|
-
weightedTokens?: number | undefined;
|
|
1128
|
-
costMicroUsd?: number | undefined;
|
|
1129
|
-
};
|
|
1130
|
-
issuedMs: number;
|
|
1131
|
-
expiresMs: number;
|
|
1132
|
-
consumed?: {
|
|
1133
|
-
weightedTokens?: number | undefined;
|
|
1134
|
-
costMicroUsd?: number | undefined;
|
|
1135
|
-
} | undefined;
|
|
1136
|
-
}>;
|
|
839
|
+
}, z.core.$strip>;
|
|
1137
840
|
export type QuotaLease = z.infer<typeof QuotaLease>;
|
|
1138
841
|
/** Remaining per granted dimension; null = the dimension is not governed by this lease. Never negative. */
|
|
1139
842
|
export declare function leaseRemaining(l: QuotaLease): {
|
|
@@ -1149,7 +852,7 @@ export declare function leaseExhausted(l: QuotaLease, nowMs: number): boolean;
|
|
|
1149
852
|
export declare function shouldRenewLease(l: QuotaLease, nowMs: number, opts?: {
|
|
1150
853
|
renewAtFraction?: number;
|
|
1151
854
|
}): boolean;
|
|
1152
|
-
/** Reconciliation (
|
|
855
|
+
/** Reconciliation (web-admin side): consumption is MONOTONE — the reconciled row takes the max of what web-admin
|
|
1153
856
|
* believed and what the worker reported (a lost report can only under-count, never roll back).
|
|
1154
857
|
* 0.7.1:legacy `tokens` report alias removed with the QuotaLease shim(同一收账点,service 回执). */
|
|
1155
858
|
export declare function reconcileLease(l: QuotaLease, reported: {
|
|
@@ -1163,43 +866,15 @@ export declare function reconcileLease(l: QuotaLease, reported: {
|
|
|
1163
866
|
export declare const UsageEntry: z.ZodObject<{
|
|
1164
867
|
principal: z.ZodString;
|
|
1165
868
|
model: z.ZodString;
|
|
1166
|
-
/** 归一化总输入 token(含 cache 读/写),非未命中分量——见类型头注。 */
|
|
1167
869
|
inputTokens: z.ZodNumber;
|
|
1168
870
|
outputTokens: z.ZodNumber;
|
|
1169
|
-
/** 0.11.0 起 optional(RB-368 生态半场,跨仓记录在案):unpriced 部署(模型无价目)对 cost
|
|
1170
|
-
* 只有「未知」这个业务态,required 逼 worker 编 0 = center 账面一个**精确的谎**。缺席=未知;
|
|
1171
|
-
* 聚合面以 {@link AggregatedPrincipalUsage.costUnknown} 补偿(已知和=显式下界)。写了键 = producer
|
|
1172
|
-
* 声称自己知道(0 就是真 0)。 */
|
|
1173
871
|
costMicroUsd: z.ZodOptional<z.ZodNumber>;
|
|
1174
872
|
runs: z.ZodOptional<z.ZodNumber>;
|
|
1175
|
-
/** 加权 tokens(配额轴计账值,EXPERT-REDESIGN §4-1):=(input+output)×记账时的 quotaWeight,
|
|
1176
|
-
* **weight-at-burn 持久化**——倍率 publish 只影响此后记账,不回算窗口内历史(余量/恢复时间不跳变)。
|
|
1177
|
-
* 0.6.0 additive optional:旧 worker 不报 → 聚合按 quotaWeight=1 兜底折算。 */
|
|
1178
873
|
weightedTokens: z.ZodOptional<z.ZodNumber>;
|
|
1179
|
-
/** 记账时固化的该模型 quotaWeight(审计口径,与 weightedTokens 同时点)——lease 扣减与 registry 聚合两
|
|
1180
|
-
* 路径按同一时点折算(同口径,§11-1)。 */
|
|
1181
874
|
quotaWeightAtUse: z.ZodOptional<z.ZodNumber>;
|
|
1182
|
-
},
|
|
1183
|
-
principal: string;
|
|
1184
|
-
model: string;
|
|
1185
|
-
inputTokens: number;
|
|
1186
|
-
outputTokens: number;
|
|
1187
|
-
weightedTokens?: number | undefined;
|
|
1188
|
-
costMicroUsd?: number | undefined;
|
|
1189
|
-
runs?: number | undefined;
|
|
1190
|
-
quotaWeightAtUse?: number | undefined;
|
|
1191
|
-
}, {
|
|
1192
|
-
principal: string;
|
|
1193
|
-
model: string;
|
|
1194
|
-
inputTokens: number;
|
|
1195
|
-
outputTokens: number;
|
|
1196
|
-
weightedTokens?: number | undefined;
|
|
1197
|
-
costMicroUsd?: number | undefined;
|
|
1198
|
-
runs?: number | undefined;
|
|
1199
|
-
quotaWeightAtUse?: number | undefined;
|
|
1200
|
-
}>;
|
|
875
|
+
}, z.core.$strip>;
|
|
1201
876
|
export type UsageEntry = z.infer<typeof UsageEntry>;
|
|
1202
|
-
/** Periodic batch (worker →
|
|
877
|
+
/** Periodic batch (worker → web-admin POST). Windows are half-open [start, end) so adjacent batches never
|
|
1203
878
|
* double-count a boundary instant. */
|
|
1204
879
|
export declare const UsageReport: z.ZodObject<{
|
|
1205
880
|
worker: z.ZodString;
|
|
@@ -1209,72 +884,14 @@ export declare const UsageReport: z.ZodObject<{
|
|
|
1209
884
|
entries: z.ZodArray<z.ZodObject<{
|
|
1210
885
|
principal: z.ZodString;
|
|
1211
886
|
model: z.ZodString;
|
|
1212
|
-
/** 归一化总输入 token(含 cache 读/写),非未命中分量——见类型头注。 */
|
|
1213
887
|
inputTokens: z.ZodNumber;
|
|
1214
888
|
outputTokens: z.ZodNumber;
|
|
1215
|
-
/** 0.11.0 起 optional(RB-368 生态半场,跨仓记录在案):unpriced 部署(模型无价目)对 cost
|
|
1216
|
-
* 只有「未知」这个业务态,required 逼 worker 编 0 = center 账面一个**精确的谎**。缺席=未知;
|
|
1217
|
-
* 聚合面以 {@link AggregatedPrincipalUsage.costUnknown} 补偿(已知和=显式下界)。写了键 = producer
|
|
1218
|
-
* 声称自己知道(0 就是真 0)。 */
|
|
1219
889
|
costMicroUsd: z.ZodOptional<z.ZodNumber>;
|
|
1220
890
|
runs: z.ZodOptional<z.ZodNumber>;
|
|
1221
|
-
/** 加权 tokens(配额轴计账值,EXPERT-REDESIGN §4-1):=(input+output)×记账时的 quotaWeight,
|
|
1222
|
-
* **weight-at-burn 持久化**——倍率 publish 只影响此后记账,不回算窗口内历史(余量/恢复时间不跳变)。
|
|
1223
|
-
* 0.6.0 additive optional:旧 worker 不报 → 聚合按 quotaWeight=1 兜底折算。 */
|
|
1224
891
|
weightedTokens: z.ZodOptional<z.ZodNumber>;
|
|
1225
|
-
/** 记账时固化的该模型 quotaWeight(审计口径,与 weightedTokens 同时点)——lease 扣减与 registry 聚合两
|
|
1226
|
-
* 路径按同一时点折算(同口径,§11-1)。 */
|
|
1227
892
|
quotaWeightAtUse: z.ZodOptional<z.ZodNumber>;
|
|
1228
|
-
},
|
|
1229
|
-
|
|
1230
|
-
model: string;
|
|
1231
|
-
inputTokens: number;
|
|
1232
|
-
outputTokens: number;
|
|
1233
|
-
weightedTokens?: number | undefined;
|
|
1234
|
-
costMicroUsd?: number | undefined;
|
|
1235
|
-
runs?: number | undefined;
|
|
1236
|
-
quotaWeightAtUse?: number | undefined;
|
|
1237
|
-
}, {
|
|
1238
|
-
principal: string;
|
|
1239
|
-
model: string;
|
|
1240
|
-
inputTokens: number;
|
|
1241
|
-
outputTokens: number;
|
|
1242
|
-
weightedTokens?: number | undefined;
|
|
1243
|
-
costMicroUsd?: number | undefined;
|
|
1244
|
-
runs?: number | undefined;
|
|
1245
|
-
quotaWeightAtUse?: number | undefined;
|
|
1246
|
-
}>, "many">;
|
|
1247
|
-
}, "strip", z.ZodTypeAny, {
|
|
1248
|
-
entries: {
|
|
1249
|
-
principal: string;
|
|
1250
|
-
model: string;
|
|
1251
|
-
inputTokens: number;
|
|
1252
|
-
outputTokens: number;
|
|
1253
|
-
weightedTokens?: number | undefined;
|
|
1254
|
-
costMicroUsd?: number | undefined;
|
|
1255
|
-
runs?: number | undefined;
|
|
1256
|
-
quotaWeightAtUse?: number | undefined;
|
|
1257
|
-
}[];
|
|
1258
|
-
instanceId: string;
|
|
1259
|
-
worker: string;
|
|
1260
|
-
windowStartMs: number;
|
|
1261
|
-
windowEndMs: number;
|
|
1262
|
-
}, {
|
|
1263
|
-
entries: {
|
|
1264
|
-
principal: string;
|
|
1265
|
-
model: string;
|
|
1266
|
-
inputTokens: number;
|
|
1267
|
-
outputTokens: number;
|
|
1268
|
-
weightedTokens?: number | undefined;
|
|
1269
|
-
costMicroUsd?: number | undefined;
|
|
1270
|
-
runs?: number | undefined;
|
|
1271
|
-
quotaWeightAtUse?: number | undefined;
|
|
1272
|
-
}[];
|
|
1273
|
-
instanceId: string;
|
|
1274
|
-
worker: string;
|
|
1275
|
-
windowStartMs: number;
|
|
1276
|
-
windowEndMs: number;
|
|
1277
|
-
}>;
|
|
893
|
+
}, z.core.$strip>>;
|
|
894
|
+
}, z.core.$strip>;
|
|
1278
895
|
export type UsageReport = z.infer<typeof UsageReport>;
|
|
1279
896
|
/** 额度清零事件(EXPERT-REDESIGN §4-4,契约随 E1 冻结、E4 消费):申请刷新获批即写一条;5h/周双窗聚合
|
|
1280
897
|
* **只计最后一次 reset 之后**的 usage 报文(usage_report 是计费审计事实,全量留存,绝不删)。拒绝逻辑/
|
|
@@ -1282,25 +899,10 @@ export type UsageReport = z.infer<typeof UsageReport>;
|
|
|
1282
899
|
export declare const QuotaResetEvent: z.ZodObject<{
|
|
1283
900
|
id: z.ZodString;
|
|
1284
901
|
principal: z.ZodString;
|
|
1285
|
-
/** 清零生效时刻(ISO)——双窗聚合的下边界。 */
|
|
1286
902
|
reset_at: z.ZodString;
|
|
1287
|
-
/** 批准人(console username)。 */
|
|
1288
903
|
decided_by: z.ZodString;
|
|
1289
|
-
/** 关联的 quota_refresh_request id;admin 直接清零(无申请)时可缺。 */
|
|
1290
904
|
request_id: z.ZodOptional<z.ZodString>;
|
|
1291
|
-
},
|
|
1292
|
-
principal: string;
|
|
1293
|
-
id: string;
|
|
1294
|
-
reset_at: string;
|
|
1295
|
-
decided_by: string;
|
|
1296
|
-
request_id?: string | undefined;
|
|
1297
|
-
}, {
|
|
1298
|
-
principal: string;
|
|
1299
|
-
id: string;
|
|
1300
|
-
reset_at: string;
|
|
1301
|
-
decided_by: string;
|
|
1302
|
-
request_id?: string | undefined;
|
|
1303
|
-
}>;
|
|
905
|
+
}, z.core.$strip>;
|
|
1304
906
|
export type QuotaResetEvent = z.infer<typeof QuotaResetEvent>;
|
|
1305
907
|
export interface AggregatedPrincipalUsage {
|
|
1306
908
|
inputTokens: number;
|
|
@@ -1327,7 +929,7 @@ export interface AggregatedPrincipalUsage {
|
|
|
1327
929
|
weightedTokens: number;
|
|
1328
930
|
}>;
|
|
1329
931
|
}
|
|
1330
|
-
/**
|
|
932
|
+
/** Web-admin-side fold: per-principal totals ACROSS workers/instances (the "one user, many workers, one bill"
|
|
1331
933
|
* view). Pure — storage/dedup of replayed batches is the caller's concern (key batches by
|
|
1332
934
|
* worker+instanceId+windowStartMs). */
|
|
1333
935
|
export declare function aggregateUsage(reports: UsageReport[]): Record<string, AggregatedPrincipalUsage>;
|