@sema-agent/settings-schema 7.0.1 → 8.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.
Files changed (104) hide show
  1. package/CHANGELOG.md +207 -1
  2. package/README.md +60 -6
  3. package/conformance/VERSION +1 -1
  4. package/conformance/cases/c01-entry-salvage-then-typo/expected.json +5 -0
  5. package/conformance/cases/c02-hosts-clamp-stacked/expected.json +5 -0
  6. package/conformance/cases/c03-literal-dotted-top-key/expected.json +41 -0
  7. package/conformance/cases/c04-all-bad-collection-not-salvaged/expected.json +5 -0
  8. package/conformance/cases/c05-gate-domain-bad-entry-throws/expected.json +5 -0
  9. package/conformance/cases/c06-legacy-governance-lift/expected.json +12 -0
  10. package/conformance/cases/c07-hooks-is-not-a-config-domain/expected.json +5 -0
  11. package/conformance/cases/c08-limits-unknown-carried-64/expected.json +77 -0
  12. package/conformance/cases/c09-limits-unknown-carried-65/expected.json +78 -0
  13. package/conformance/cases/c10-record-key-secret-redacted-twice/expected.json +5 -0
  14. package/conformance/cases/c11-unicode-unknown-keys/expected.json +14 -0
  15. package/conformance/cases/c12-prototype-name-top-keys/expected.json +5 -0
  16. package/conformance/cases/c13-models-field-null/expected.json +5 -0
  17. package/conformance/cases/c14-models-field-empty-array/expected.json +16 -0
  18. package/conformance/cases/c15-models-field-number/expected.json +5 -0
  19. package/conformance/cases/c16-deep-nesting-205/expected.json +14 -0
  20. package/conformance/cases/c17-empty-domain-file/expected.json +10 -0
  21. package/conformance/cases/c18-wire-envelope-not-an-object/expected.json +10 -0
  22. package/conformance/cases/c19-wire-envelope-no-version/expected.json +10 -0
  23. package/conformance/cases/c20-governance-absent-lifts/expected.json +12 -0
  24. package/conformance/cases/c21-governance-empty-object-wins/expected.json +13 -0
  25. package/conformance/cases/c22-governance-null-throws/expected.json +5 -0
  26. package/conformance/cases/c23-mixed-validity-models/expected.json +5 -0
  27. package/conformance/cases/c24-valid-entries-with-unknown-keys/expected.json +29 -0
  28. package/conformance/cases/c25-published-roundtrip-zero-warnings/expected.json +33 -0
  29. package/conformance/cases/c26-hosts-array-1024/expected.json +7183 -0
  30. package/conformance/cases/c27-hosts-array-1025/expected.json +5 -0
  31. package/conformance/cases/c28-large-document-over-1mb/expected.json +14 -0
  32. package/conformance/cases/c29-salvage-then-domain-refine-fails/expected.json +5 -0
  33. package/conformance/cases/c30-version-triple-a-first/expected.json +29 -0
  34. package/conformance/cases/c31-version-triple-b-changed/expected.json +29 -0
  35. package/conformance/cases/c32-version-triple-a-restored/expected.json +29 -0
  36. package/conformance/cases/c33-limits-nested-carried-key/expected.json +17 -0
  37. package/conformance/cases/c34-skills-content-vs-manifest/expected.json +23 -0
  38. package/conformance/cases/c35-portable-domain-file-is-null/expected.json +5 -0
  39. package/conformance/cases/c36-skills-entry-without-content/expected.json +23 -0
  40. package/conformance/cases/c37-skills-field-null/expected.json +5 -0
  41. package/conformance/cases/c38-remote-exec-is-a-non-domain-file/expected.json +5 -0
  42. package/conformance/cases/c39-hidden-file-in-config-d/expected.json +5 -0
  43. package/conformance/cases/c40-s712-identity-only-what-reads-out/expected.json +5 -0
  44. package/conformance/cases/c41-s792-encoded-pem-identity/expected.json +5 -0
  45. package/conformance/cases/c42-s792-unknown-key-secret-redacted/expected.json +33 -0
  46. package/conformance/cases/c43-s792-form-and-deep-encoded-pem/expected.json +5 -0
  47. package/conformance/cases/c44-s792-utf8-escape-glued-token/expected.json +5 -0
  48. package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/expected.json +5 -0
  49. package/conformance/cases/c46-s811-host-name-url-userinfo-characterization/expected.json +5 -0
  50. package/conformance/cases/c47-s811-roles-key-url-userinfo-characterization/expected.json +5 -0
  51. package/conformance/cases/c48-s935-model-headers-transported/expected.json +59 -0
  52. package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/expected.json +5 -0
  53. package/conformance/cases/c50-s784-lane-host-legal/expected.json +44 -0
  54. package/conformance/cases/c51-s784-lane-e2b-legal/expected.json +46 -0
  55. package/conformance/cases/c52-s784-lane-k8s-legal/expected.json +48 -0
  56. package/conformance/cases/c53-s784-lane-ssh-legal/expected.json +48 -0
  57. package/conformance/cases/c54-s784-lane-adb-legal/expected.json +44 -0
  58. package/conformance/cases/c55-s784-lane-local-docker-legal/expected.json +45 -0
  59. package/conformance/cases/c56-s784-lane-device-legal/expected.json +51 -0
  60. package/conformance/cases/c57-s784-g1-enabled-no-lane/expected.json +5 -0
  61. package/conformance/cases/c58-s784-g2-multitenant-host/expected.json +5 -0
  62. package/conformance/cases/c59-s784-g3-k8s-missing-image/expected.json +5 -0
  63. package/conformance/cases/c60-s784-g3-e2b-missing-key/expected.json +5 -0
  64. package/conformance/cases/c61-s784-g3-other-lane-key/expected.json +5 -0
  65. package/conformance/cases/c62-s784-g7a-session-tidb/expected.json +5 -0
  66. package/conformance/cases/c63-s784-host-two-replicas/expected.json +5 -0
  67. package/conformance/cases/c64-s784-multitenant-local-store/expected.json +5 -0
  68. package/conformance/cases/c65-s784-pg-without-db/expected.json +5 -0
  69. package/conformance/cases/c66-s784-memory-backend-mismatch/expected.json +5 -0
  70. package/conformance/cases/c67-s784-retired-field-loud/expected.json +5 -0
  71. package/conformance/cases/c68-s784-device-local-store/expected.json +5 -0
  72. package/conformance/cases/cc01-mcp-toolfaces-non-object/expected.json +54 -0
  73. package/conformance/cases/cc02-mcp-source-key-is-transported/expected.json +27 -0
  74. package/conformance/cases/cc03-model-compat-unknown-subkey/expected.json +33 -0
  75. package/conformance/cases/cli01-model-name-nfc-vs-nfd/expected.json +5 -0
  76. package/conformance/cases/cli02-control-and-zero-width-chars/expected.json +5 -0
  77. package/conformance/cases/cli03-credential-pasted-into-unknown-key/expected.json +22 -0
  78. package/conformance/cases/cli04-env-value-with-newline/expected.json +46 -0
  79. package/conformance/cases/cli05-long-and-prefix-colliding-names/expected.json +53 -0
  80. package/conformance/cases/t01-absent-domain-key-not-null/expected.json +5 -0
  81. package/conformance/cases/t02-entry-salvage-vs-whole-domain-refusal/expected.json +5 -0
  82. package/conformance/cases/t03-prototype-name-key-not-swallowed/expected.json +29 -0
  83. package/conformance/cases/t04-secret-literal-never-in-warning-value/expected.json +5 -0
  84. package/conformance/cases/z01-every-domain-empty-object/expected.json +64 -0
  85. package/conformance/cases/z02-every-domain-unknown-key/expected.json +66 -0
  86. package/dist/api/auth-bridge.d.ts +2 -2
  87. package/dist/cli.js +58 -26
  88. package/dist/config-fns.js +7 -1
  89. package/dist/conformance/contract-adapter.d.ts +4 -2
  90. package/dist/conformance/contract-adapter.js +21 -7
  91. package/dist/conformance/corpus.js +12 -7
  92. package/dist/conformance/derive.d.ts +26 -0
  93. package/dist/conformance/derive.js +25 -2
  94. package/dist/conformance/index.d.ts +4 -3
  95. package/dist/conformance/index.js +4 -3
  96. package/dist/conformance/run.d.ts +3 -3
  97. package/dist/conformance/run.js +87 -17
  98. package/dist/conformance/types.d.ts +57 -17
  99. package/dist/conformance/types.js +4 -2
  100. package/dist/fleet.d.ts +417 -3
  101. package/dist/fleet.js +287 -12
  102. package/dist/hooks.d.ts +4 -4
  103. package/dist/types.js +3 -1
  104. package/package.json +2 -1
package/dist/fleet.d.ts CHANGED
@@ -51,7 +51,8 @@ export declare function drainVerdict(s: DrainSnapshot): DrainVerdict;
51
51
  * {@link readRestartSignal} — {@link InstanceHealth} keeps carrying it untyped, see there). One mint point, two exits —
52
52
  * never a second judgement.
53
53
  *
54
- * Shape = what the server already ships on /health (`restart: { reasons, version, since }`). PRESENCE is the signal:
54
+ * Shape = what the server already ships on /health (`restart: { reasons, version, since }`, plus the optional `withheld`
55
+ * qualifier below). PRESENCE is the signal:
55
56
  * present ⇔ a restart is required, absent ⇔ not; there is deliberately no `required` boolean inside (a boolean next
56
57
  * to presence is redundant, and two spellings of one fact drift).
57
58
  * - `reasons` — which restart-to-apply slices differ from the value baked at boot (the vocabulary is the engine's;
@@ -60,19 +61,26 @@ export declare function drainVerdict(s: DrainSnapshot): DrainVerdict;
60
61
  * later publishes, so a "one restart per version" dedupe bounds restarts per config version, not per reason set).
61
62
  * Judged exactly as `readEffectiveWire` judges a config version (a finite number) — one quantity, one rule.
62
63
  * - `since` — epoch ms when this restart first became required (stable across refreshes while `reasons` holds).
64
+ * - `withheld` — optional: the ENGINE's own verdict that this beat must not be handed to a new generation ("a restart is
65
+ * required, but replacing this instance right now would lose state it holds"). Absent ≡ `false` — an explicit `false`
66
+ * on the wire reads the same as the key being absent, and a reader never mints the key when the producer did not send
67
+ * it. It is a qualifier INSIDE the restart signal, never a signal of its own: it exists only where `restart` does.
63
68
  */
64
69
  export declare const RestartSignal: z.ZodObject<{
65
70
  reasons: z.ZodArray<z.ZodString, "many">;
66
71
  version: z.ZodNumber;
67
72
  since: z.ZodNumber;
73
+ withheld: z.ZodOptional<z.ZodBoolean>;
68
74
  }, "strip", z.ZodTypeAny, {
69
75
  reasons: string[];
70
76
  version: number;
71
77
  since: number;
78
+ withheld?: boolean | undefined;
72
79
  }, {
73
80
  reasons: string[];
74
81
  version: number;
75
82
  since: number;
83
+ withheld?: boolean | undefined;
76
84
  }>;
77
85
  export type RestartSignal = z.infer<typeof RestartSignal>;
78
86
  /**
@@ -211,7 +219,11 @@ export declare const InstanceHealth: z.ZodObject<{
211
219
  drainingSince: z.ZodOptional<z.ZodNumber>;
212
220
  }, z.ZodTypeAny, "passthrough">>;
213
221
  export type InstanceHealth = z.infer<typeof InstanceHealth>;
214
- /** Readiness = the routability predicate (k8s readiness probe, gateway health filter, shell seam switch). */
222
+ /** Readiness = the routability predicate (k8s readiness probe, gateway health filter, shell seam switch).
223
+ * Its input is the boolean-`draining` dialect of {@link InstanceHealth}. An {@link EngineSignal} types `draining` as an
224
+ * object, so it does not typecheck here (on purpose: `!== true` would read an object-valued `draining` as "ready").
225
+ * The next major retires the boolean pair (`draining` / `drainingSince`) and gives both this predicate and
226
+ * {@link isRoutable} `EngineSignal` as their input. */
215
227
  export declare function isReadyForTraffic(h: Pick<InstanceHealth, "draining">): boolean;
216
228
  /** Heartbeat cadence the announcement declares (center judges staleness against the DECLARED cadence, so a
217
229
  * slow-heartbeat worker is not falsely reaped). */
@@ -242,14 +254,17 @@ export declare const InstanceAnnouncement: z.ZodObject<{
242
254
  reasons: z.ZodArray<z.ZodString, "many">;
243
255
  version: z.ZodNumber;
244
256
  since: z.ZodNumber;
257
+ withheld: z.ZodOptional<z.ZodBoolean>;
245
258
  }, "strip", z.ZodTypeAny, {
246
259
  reasons: string[];
247
260
  version: number;
248
261
  since: number;
262
+ withheld?: boolean | undefined;
249
263
  }, {
250
264
  reasons: string[];
251
265
  version: number;
252
266
  since: number;
267
+ withheld?: boolean | undefined;
253
268
  }>>;
254
269
  /** S-768: present ⇔ this instance has at least one reportable model refusal — the SAME {@link ModelRefusalsWire} /health
255
270
  * carries, minted by the same getter; read it with {@link readModelRefusals}`(announcement)` (never off this field
@@ -289,6 +304,7 @@ export declare const InstanceAnnouncement: z.ZodObject<{
289
304
  reasons: string[];
290
305
  version: number;
291
306
  since: number;
307
+ withheld?: boolean | undefined;
292
308
  } | undefined;
293
309
  modelRefusals?: {
294
310
  entries: Record<string, string>;
@@ -305,6 +321,7 @@ export declare const InstanceAnnouncement: z.ZodObject<{
305
321
  reasons: string[];
306
322
  version: number;
307
323
  since: number;
324
+ withheld?: boolean | undefined;
308
325
  } | undefined;
309
326
  modelRefusals?: unknown;
310
327
  heartbeatMs?: number | undefined;
@@ -346,14 +363,17 @@ export declare const InstanceRegistration: z.ZodObject<{
346
363
  reasons: z.ZodArray<z.ZodString, "many">;
347
364
  version: z.ZodNumber;
348
365
  since: z.ZodNumber;
366
+ withheld: z.ZodOptional<z.ZodBoolean>;
349
367
  }, "strip", z.ZodTypeAny, {
350
368
  reasons: string[];
351
369
  version: number;
352
370
  since: number;
371
+ withheld?: boolean | undefined;
353
372
  }, {
354
373
  reasons: string[];
355
374
  version: number;
356
375
  since: number;
376
+ withheld?: boolean | undefined;
357
377
  }>>;
358
378
  /** S-768: present ⇔ this instance has at least one reportable model refusal — the SAME {@link ModelRefusalsWire} /health
359
379
  * carries, minted by the same getter; read it with {@link readModelRefusals}`(announcement)` (never off this field
@@ -399,6 +419,7 @@ export declare const InstanceRegistration: z.ZodObject<{
399
419
  reasons: string[];
400
420
  version: number;
401
421
  since: number;
422
+ withheld?: boolean | undefined;
402
423
  } | undefined;
403
424
  modelRefusals?: {
404
425
  entries: Record<string, string>;
@@ -418,6 +439,7 @@ export declare const InstanceRegistration: z.ZodObject<{
418
439
  reasons: string[];
419
440
  version: number;
420
441
  since: number;
442
+ withheld?: boolean | undefined;
421
443
  } | undefined;
422
444
  modelRefusals?: unknown;
423
445
  heartbeatMs?: number | undefined;
@@ -427,9 +449,400 @@ export type InstanceRegistration = z.infer<typeof InstanceRegistration>;
427
449
  /** Silent past the declared cadence × multiplier ⇒ stale (auto-removed from the routable set). */
428
450
  export declare function isInstanceStale(r: Pick<InstanceRegistration, "lastSeenMs" | "heartbeatMs">, nowMs: number): boolean;
429
451
  /** Health fusion: routable ⇔ self-declared ready ∧ not draining ∧ not stale ∧ observer does not veto.
430
- * Two sources merge as an AND — EITHER source's "not ready" removes the instance (fail-closed). */
452
+ * Two sources merge as an AND — EITHER source's "not ready" removes the instance (fail-closed).
453
+ * Its input is the stored heartbeat row (boolean `draining`); it moves to {@link EngineSignal} in the same major as
454
+ * {@link isReadyForTraffic}. */
431
455
  export declare function isRoutable(r: InstanceRegistration, nowMs: number): boolean;
432
456
  export declare function routableInstances(rs: InstanceRegistration[], nowMs: number): InstanceRegistration[];
457
+ /**
458
+ * The engine's draining fact — ONE object wherever the engine says "I am draining": inside {@link EngineSignal}`.draining`
459
+ * and as the body of a {@link DrainReceipt}.
460
+ * - `actionId` — the action that put the engine into draining (idempotency key of the drain request).
461
+ * - `since` — epoch ms when draining began; it does not move when the same action repeats.
462
+ * - `deadlineAt` — epoch ms by which the engine stops on its own.
463
+ */
464
+ export declare const EngineDraining: z.ZodObject<{
465
+ actionId: z.ZodString;
466
+ since: z.ZodNumber;
467
+ deadlineAt: z.ZodNumber;
468
+ }, "strip", z.ZodTypeAny, {
469
+ since: number;
470
+ actionId: string;
471
+ deadlineAt: number;
472
+ }, {
473
+ since: number;
474
+ actionId: string;
475
+ deadlineAt: number;
476
+ }>;
477
+ export type EngineDraining = z.infer<typeof EngineDraining>;
478
+ export declare function isCanonicalGeneration(value: unknown): value is string;
479
+ /** Numeric order of two canonical generations: `-1` when `a` is older than `b`, `0` when equal, `1` when newer — `"9"` is
480
+ * older than `"10"`; any number of digits. Throws a `TypeError` on an input that is not canonical (a comparison that quietly
481
+ * returned some number would be a fence decision made on garbage); judge untrusted input with {@link isCanonicalGeneration}
482
+ * first — the wire shapes of this file already do. */
483
+ export declare function compareGeneration(a: string, b: string): -1 | 0 | 1;
484
+ /**
485
+ * Everything the ENGINE itself mints about its own state, as one object — the closed form of the engine-owned subset of the
486
+ * /health self-description ({@link InstanceHealth} is that self-description: an open record, `.passthrough()`, which also
487
+ * carries what the host adds — build, pid, port, store posture). One mint point; the heartbeat and `/health` carry the same
488
+ * keys with the same values, flat (`ready`, `draining`, `restart`, … sit directly on the body, no wrapper key), and
489
+ * {@link readEngineSignal} is the one reader of either.
490
+ *
491
+ * - `ready` — always said (never "absent while ready"): the engine can take new work right now.
492
+ * - `draining` — present ⇔ draining ({@link EngineDraining}). NOTE the dialect: {@link InstanceHealth}`.draining` and
493
+ * {@link InstanceAnnouncement}`.draining` are BOOLEANS (the 8.0 dialect, kept untouched); here it is an object. A body
494
+ * whose `draining` is a boolean is therefore not an `EngineSignal` and reads as `undefined`.
495
+ * - `restart` — the one {@link RestartSignal}, same schema object, no copy.
496
+ * - `generation` — echo of the env the config renderer set on this instance ({@link FLEET_GENERATION_ENV}), a canonical
497
+ * decimal string ({@link isCanonicalGeneration}). Absent ⇔ not a fleet member (a single-machine engine): it takes no lease
498
+ * and is not fenced.
499
+ * - `instanceId` — the engine's OWN process identity, minted by the engine every time the process starts (never read from
500
+ * env: replicas of one Deployment share one env, and a restarted container reuses its env, so env cannot tell two
501
+ * processes apart). It is present on a standalone engine too — only `generation` marks fleet membership.
502
+ * - `leaseUntil` — epoch ms until which the engine holds its serving lease, as the engine computed it from the last
503
+ * `ttlMs` it was granted ({@link ServingLeaseResponse}); absent when it holds none.
504
+ * - `configTargetVersion` / `configAppliedVersion` — the config-center version the engine was told to converge on, and the
505
+ * one it has applied (opaque strings; equal ⇔ converged).
506
+ *
507
+ * Reading: `EngineSignal` is parsed with the default strip — a whole /health body goes in, only these keys come out. A
508
+ * shape violation in ANY key makes the whole signal `undefined` (it is never "the rest of it"). `undefined` means "no
509
+ * signal this reader can say" — an engine that predates the keys, or a malformed body — and is NEVER "ready": a caller on
510
+ * the routing axis treats it as not routable unless it deliberately falls back to the boolean dialect of
511
+ * {@link InstanceHealth}.
512
+ */
513
+ export declare const EngineSignal: z.ZodObject<{
514
+ ready: z.ZodBoolean;
515
+ draining: z.ZodOptional<z.ZodObject<{
516
+ actionId: z.ZodString;
517
+ since: z.ZodNumber;
518
+ deadlineAt: z.ZodNumber;
519
+ }, "strip", z.ZodTypeAny, {
520
+ since: number;
521
+ actionId: string;
522
+ deadlineAt: number;
523
+ }, {
524
+ since: number;
525
+ actionId: string;
526
+ deadlineAt: number;
527
+ }>>;
528
+ restart: z.ZodOptional<z.ZodObject<{
529
+ reasons: z.ZodArray<z.ZodString, "many">;
530
+ version: z.ZodNumber;
531
+ since: z.ZodNumber;
532
+ withheld: z.ZodOptional<z.ZodBoolean>;
533
+ }, "strip", z.ZodTypeAny, {
534
+ reasons: string[];
535
+ version: number;
536
+ since: number;
537
+ withheld?: boolean | undefined;
538
+ }, {
539
+ reasons: string[];
540
+ version: number;
541
+ since: number;
542
+ withheld?: boolean | undefined;
543
+ }>>;
544
+ generation: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
545
+ instanceId: z.ZodOptional<z.ZodString>;
546
+ leaseUntil: z.ZodOptional<z.ZodNumber>;
547
+ configTargetVersion: z.ZodOptional<z.ZodString>;
548
+ configAppliedVersion: z.ZodOptional<z.ZodString>;
549
+ }, "strip", z.ZodTypeAny, {
550
+ ready: boolean;
551
+ draining?: {
552
+ since: number;
553
+ actionId: string;
554
+ deadlineAt: number;
555
+ } | undefined;
556
+ restart?: {
557
+ reasons: string[];
558
+ version: number;
559
+ since: number;
560
+ withheld?: boolean | undefined;
561
+ } | undefined;
562
+ instanceId?: string | undefined;
563
+ generation?: string | undefined;
564
+ leaseUntil?: number | undefined;
565
+ configTargetVersion?: string | undefined;
566
+ configAppliedVersion?: string | undefined;
567
+ }, {
568
+ ready: boolean;
569
+ draining?: {
570
+ since: number;
571
+ actionId: string;
572
+ deadlineAt: number;
573
+ } | undefined;
574
+ restart?: {
575
+ reasons: string[];
576
+ version: number;
577
+ since: number;
578
+ withheld?: boolean | undefined;
579
+ } | undefined;
580
+ instanceId?: string | undefined;
581
+ generation?: string | undefined;
582
+ leaseUntil?: number | undefined;
583
+ configTargetVersion?: string | undefined;
584
+ configAppliedVersion?: string | undefined;
585
+ }>;
586
+ export type EngineSignal = z.infer<typeof EngineSignal>;
587
+ /**
588
+ * The reader of {@link EngineSignal}, for either exit (a /health body or a heartbeat body — the keys are flat on both): the
589
+ * same guarded body reader as {@link readRestartSignal} and {@link readModelRefusals}, so the same odd input gets the same
590
+ * answer from all three. Shape mismatch, a missing `ready`, a non-object body, even a throwing accessor ⇒ `undefined` —
591
+ * never throws. See {@link EngineSignal} for what `undefined` means (not "ready").
592
+ */
593
+ export declare function readEngineSignal(health: unknown): EngineSignal | undefined;
594
+ /**
595
+ * The env name a fleet member's generation travels under — the config renderer sets it, the engine reads it back and echoes
596
+ * it as {@link EngineSignal}`.generation`. Both sides import THIS constant; neither spells the name. The value is the
597
+ * generation this instance belongs to (opaque string); absent from the engine's env ⇔ not a fleet member (the standalone
598
+ * form). There is deliberately NO env name for the instance id: an instance's identity is minted by the engine per process
599
+ * ({@link EngineSignal}`.instanceId`), and env cannot carry it (see there).
600
+ */
601
+ export declare const FLEET_GENERATION_ENV = "FLEET_GENERATION";
602
+ /**
603
+ * The serving lease — the ONE fence between a superseded generation and the traffic it must stop taking. A fleet-member
604
+ * engine renews it with the center; the center answers per the lifecycle object it holds for that instance. Three constants
605
+ * fix the clock, all in ms:
606
+ * - {@link SERVING_LEASE_TTL_DEFAULT_MS} — the lease length when the operator sets none (10 min).
607
+ * - {@link SERVING_LEASE_TTL_FLOOR_MS} — the shortest lease there is: a configured TTL below it is treated as it (1 min),
608
+ * the same way {@link DRAIN_GRACE_FLOOR_MS} protects a fat-fingered grace.
609
+ * - {@link SERVING_LEASE_DRIFT_MARGIN_MS} — the center's clock-skew allowance: it adds this to a lease's recorded expiry
610
+ * before it counts that lease as over. It is DERIVED from {@link DEFAULT_HEARTBEAT_MS} (one cadence, one number), never a
611
+ * second constant that could drift from it.
612
+ */
613
+ export declare const SERVING_LEASE_TTL_DEFAULT_MS = 600000;
614
+ export declare const SERVING_LEASE_TTL_FLOOR_MS = 60000;
615
+ export declare const SERVING_LEASE_DRIFT_MARGIN_MS = 30000;
616
+ /**
617
+ * The ONE place a configured lease TTL becomes a usable one: not a number (absent, `NaN`, infinite, another type) ⇒
618
+ * {@link SERVING_LEASE_TTL_DEFAULT_MS}; a number below {@link SERVING_LEASE_TTL_FLOOR_MS} (zero and negatives included) ⇒ the
619
+ * floor; otherwise the number, truncated to whole ms, with no ceiling. It is for the ISSUING side's configuration. A reply's
620
+ * `ttlMs` is used exactly as granted and is never clamped upward — an engine that stretched a short grant to the floor would
621
+ * hold the lease past what the center recorded, which is the one thing the fence exists to prevent.
622
+ */
623
+ export declare function clampServingLeaseTtlMs(configuredMs: number | undefined): number;
624
+ /**
625
+ * Engine → center: "may this instance keep serving?" The identity triple names the instance exactly — a lease is never
626
+ * asked for by worker name alone. `generation` is the env echo ({@link FLEET_GENERATION_ENV}); `instanceId` is the engine's
627
+ * own per-process identity ({@link EngineSignal}`.instanceId`), so a restarted process is a NEW instance to the center.
628
+ * `scope` is the tenant scope the worker lives in (absent = the single-scope deployment). Only fleet members ask: an engine
629
+ * without a generation (the standalone form) has no lease and sends nothing.
630
+ */
631
+ export declare const ServingLeaseRequest: z.ZodObject<{
632
+ worker: z.ZodString;
633
+ scope: z.ZodOptional<z.ZodString>;
634
+ generation: z.ZodEffects<z.ZodString, string, string>;
635
+ instanceId: z.ZodString;
636
+ }, "strip", z.ZodTypeAny, {
637
+ instanceId: string;
638
+ worker: string;
639
+ generation: string;
640
+ scope?: string | undefined;
641
+ }, {
642
+ instanceId: string;
643
+ worker: string;
644
+ generation: string;
645
+ scope?: string | undefined;
646
+ }>;
647
+ export type ServingLeaseRequest = z.infer<typeof ServingLeaseRequest>;
648
+ /**
649
+ * Why a lease is refused — a CLOSED set of exactly two words, owned here:
650
+ * - `superseded` — a newer generation of this worker has taken over; this instance is the old one.
651
+ * - `retired` — the center has retired this instance (or the worker it belongs to).
652
+ * Only {@link ServingLeaseResponse}'s `denied` arm carries a reason; `unknown` has none, by shape.
653
+ */
654
+ export declare const SERVING_LEASE_DENIED_REASONS: readonly ["superseded", "retired"];
655
+ export declare const ServingLeaseDeniedReason: z.ZodEnum<["superseded", "retired"]>;
656
+ export type ServingLeaseDeniedReason = z.infer<typeof ServingLeaseDeniedReason>;
657
+ /**
658
+ * Center → engine: the verdict, a CLOSED set of three words ({@link SERVING_LEASE_VERDICTS}) — the arms of one
659
+ * discriminated union, so a reader that switches on `verdict` is told by the compiler when a word is added.
660
+ * - `granted` — keep serving; `ttlMs` is how long the lease lasts, a DURATION, never a wall-clock instant: the engine counts
661
+ * it from the instant it SENT the request, on its own monotonic clock (`expiresAt = sendAt + ttlMs`), and the center keeps
662
+ * its own expiry from its store time — the two sides never compare clocks, so no clock agreement is needed.
663
+ * - `denied` — this instance must stop: `reason` is {@link ServingLeaseDeniedReason}. The ONLY arm that obliges an engine to
664
+ * stop serving, and it is issued on POSITIVE evidence only: the center keeps, per (scope, worker), a persisted generation
665
+ * floor that is never garbage-collected, and a request whose generation lies below it is `superseded`; `retired` likewise
666
+ * rests on a record the center holds. A center that merely holds no such record does not deny — it answers `unknown`.
667
+ * - `unknown` — the center holds no lifecycle object for this instance right now (restoring its store, migrating scopes, not
668
+ * yet claimed). It says NOTHING about whether the instance should serve, so it is not a kind of `denied` and absence of the
669
+ * object NEVER turns into a denial: it carries no reason, and an engine never drains on it. To the engine it is the same
670
+ * case as an unreachable center.
671
+ * This is the law for what a center SENDS. A reader does not apply it to `reason`: {@link readServingLeaseResponse} judges by
672
+ * the `verdict` key and keeps a `denied` a `denied` even when its reason is outside the set ({@link ServingLeaseReading}). A
673
+ * reply whose `verdict` itself is outside the three words (or a `granted` with no valid `ttlMs`) is not a verdict at all and
674
+ * reads as `undefined` — an unreachable center, never `denied`, never `granted`.
675
+ */
676
+ export declare const ServingLeaseResponse: z.ZodDiscriminatedUnion<"verdict", [z.ZodObject<{
677
+ verdict: z.ZodLiteral<"granted">;
678
+ ttlMs: z.ZodNumber;
679
+ }, "strip", z.ZodTypeAny, {
680
+ verdict: "granted";
681
+ ttlMs: number;
682
+ }, {
683
+ verdict: "granted";
684
+ ttlMs: number;
685
+ }>, z.ZodObject<{
686
+ verdict: z.ZodLiteral<"denied">;
687
+ reason: z.ZodEnum<["superseded", "retired"]>;
688
+ }, "strip", z.ZodTypeAny, {
689
+ reason: "superseded" | "retired";
690
+ verdict: "denied";
691
+ }, {
692
+ reason: "superseded" | "retired";
693
+ verdict: "denied";
694
+ }>, z.ZodObject<{
695
+ verdict: z.ZodLiteral<"unknown">;
696
+ }, "strip", z.ZodTypeAny, {
697
+ verdict: "unknown";
698
+ }, {
699
+ verdict: "unknown";
700
+ }>]>;
701
+ export type ServingLeaseResponse = z.infer<typeof ServingLeaseResponse>;
702
+ /** The reason word a READER puts in place of a `denied` reason it cannot place (outside {@link SERVING_LEASE_DENIED_REASONS},
703
+ * missing, not a string). Read-face only: a producer never sends it, and the strict {@link ServingLeaseResponse} refuses it. */
704
+ export declare const SERVING_LEASE_UNKNOWN_REASON = "unknown_reason";
705
+ /** The read face of {@link ServingLeaseResponse} — same three arms, with the `denied` arm tolerant about `reason`. */
706
+ declare const ServingLeaseReadFace: z.ZodDiscriminatedUnion<"verdict", [z.ZodObject<{
707
+ verdict: z.ZodLiteral<"granted">;
708
+ ttlMs: z.ZodNumber;
709
+ }, "strip", z.ZodTypeAny, {
710
+ verdict: "granted";
711
+ ttlMs: number;
712
+ }, {
713
+ verdict: "granted";
714
+ ttlMs: number;
715
+ }>, z.ZodObject<{
716
+ verdict: z.ZodLiteral<"denied">;
717
+ reason: z.ZodEffects<z.ZodUnknown, "superseded" | "retired" | "unknown_reason", unknown>;
718
+ }, "strip", z.ZodTypeAny, {
719
+ reason: "superseded" | "retired" | "unknown_reason";
720
+ verdict: "denied";
721
+ }, {
722
+ verdict: "denied";
723
+ reason?: unknown;
724
+ }>, z.ZodObject<{
725
+ verdict: z.ZodLiteral<"unknown">;
726
+ }, "strip", z.ZodTypeAny, {
727
+ verdict: "unknown";
728
+ }, {
729
+ verdict: "unknown";
730
+ }>]>;
731
+ /** The verdict words as a runtime list. `satisfies` bites a word the union does not have; the converse (a union arm missing
732
+ * from this list) is pinned by a test that reads the union's discriminators. */
733
+ export declare const SERVING_LEASE_VERDICTS: readonly ["granted", "denied", "unknown"];
734
+ /** Center side: the request body of a serving-lease renewal. Shape mismatch / non-object / throwing accessor ⇒ `undefined`,
735
+ * never throws (same guarded reader as every other read in this file). */
736
+ export declare function readServingLeaseRequest(body: unknown): ServingLeaseRequest | undefined;
737
+ /**
738
+ * What {@link readServingLeaseResponse} returns: {@link ServingLeaseResponse} plus the one thing a reader adds — a `denied`
739
+ * whose reason it could not place carries {@link SERVING_LEASE_UNKNOWN_REASON}. Two faces on purpose (the failure direction
740
+ * must match the meaning): the strict wire law says what a center may SEND (a closed two-word reason set); the reader judges
741
+ * by the `verdict` key alone, so a center that learns a third reason word cannot make an older engine read `denied` as
742
+ * `undefined` and keep serving. Branch on `verdict`; `reason` is for the log line.
743
+ */
744
+ export type ServingLeaseReading = z.output<typeof ServingLeaseReadFace>;
745
+ /** Engine side: the reply to a renewal, judged by its `verdict` key: `granted` needs a valid `ttlMs`, `denied` is `denied`
746
+ * whatever its `reason` (see {@link ServingLeaseReading}), `unknown` is `unknown`. A non-object, a throwing accessor, a
747
+ * `verdict` outside the three words or a `granted` without a valid `ttlMs` ⇒ `undefined`, never throws — and `undefined`
748
+ * is read like an unreachable center, never like `denied` and never like `granted`. */
749
+ export declare function readServingLeaseResponse(body: unknown): ServingLeaseReading | undefined;
750
+ /**
751
+ * What the CENTER says it can do: capability bits an engine reads before it starts using a center-side endpoint.
752
+ * - `servingLease` — the center answers serving-lease renewals ({@link ServingLeaseRequest} / {@link ServingLeaseResponse}).
753
+ *
754
+ * A bit is `true` or absent — one spelling for "not supported" (there is no `false`). Absent (an older center, a body without
755
+ * the key, a body that cannot be read) means the center does not support the lease, and the engine then neither renews a
756
+ * lease nor is fenced by one: this is the compatibility gate that lets an executor ship ahead of the center without pushing
757
+ * running engines into lease expiry. It is a gate, not a verdict — nothing in this shape says "the center refuses leases".
758
+ * A reader asks `readCenterCapabilities(body)?.servingLease === true` and starts the lease client only on that.
759
+ * Unknown bits are stripped (a newer center's extra bits never break an older reader); a known bit whose value is not `true`
760
+ * makes the whole read `undefined`, which reads exactly as absent.
761
+ * This package fixes the shape and the reader, not which center endpoint carries the object.
762
+ */
763
+ export declare const CenterCapabilities: z.ZodObject<{
764
+ servingLease: z.ZodOptional<z.ZodLiteral<true>>;
765
+ }, "strip", z.ZodTypeAny, {
766
+ servingLease?: true | undefined;
767
+ }, {
768
+ servingLease?: true | undefined;
769
+ }>;
770
+ export type CenterCapabilities = z.infer<typeof CenterCapabilities>;
771
+ /** Engine side: the center's capability object. Shape mismatch / non-object / throwing accessor ⇒ `undefined`, never throws
772
+ * (and `undefined` means "no capability said" — the same as every bit absent). */
773
+ export declare function readCenterCapabilities(body: unknown): CenterCapabilities | undefined;
774
+ /**
775
+ * The drain endpoint's request: "put this instance into draining, by this deadline". One persisted action, one validation,
776
+ * whether the center calls directly or an executor retries it. Two lanes, one shape: on the Docker lane (and for a direct
777
+ * call from the center) this HTTP request is how an instance is drained; on the Kubernetes lane no per-pod HTTP call is made
778
+ * (per-pod addressing and RBAC make it the wrong tool) — an instance is drained by its serving lease being denied
779
+ * ({@link ServingLeaseResponse}), after which the engine puts itself into draining.
780
+ * - `actionId` — idempotency key: the same id again echoes the first answer ({@link DrainReceipt}`.state` =
781
+ * `already_draining`) and changes nothing.
782
+ * - the identity — `scope?` / `worker` / `expectedGeneration?` / `instanceId?` — names WHICH instance the action is for. The
783
+ * engine compares it field by field against its own, absent against absent: an engine that is not a fleet member has no
784
+ * generation, so a request with no `expectedGeneration` is the standalone form, not a wildcard; `instanceId`, when given,
785
+ * is the engine's own per-process identity ({@link EngineSignal}`.instanceId`). A mismatch is the endpoint's refusal, not
786
+ * this shape's: this schema only says what a well-formed request looks like.
787
+ * - `deadlineAt` — epoch ms by which the engine stops on its own.
788
+ * - `term` — the claim term the sending executor holds for this action (center-issued, opaque); absent where no executor
789
+ * sits in between (a direct call, a hand-issued action).
790
+ */
791
+ export declare const DrainRequest: z.ZodObject<{
792
+ actionId: z.ZodString;
793
+ scope: z.ZodOptional<z.ZodString>;
794
+ worker: z.ZodString;
795
+ expectedGeneration: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
796
+ instanceId: z.ZodOptional<z.ZodString>;
797
+ deadlineAt: z.ZodNumber;
798
+ term: z.ZodOptional<z.ZodString>;
799
+ }, "strip", z.ZodTypeAny, {
800
+ worker: string;
801
+ actionId: string;
802
+ deadlineAt: number;
803
+ instanceId?: string | undefined;
804
+ scope?: string | undefined;
805
+ expectedGeneration?: string | undefined;
806
+ term?: string | undefined;
807
+ }, {
808
+ worker: string;
809
+ actionId: string;
810
+ deadlineAt: number;
811
+ instanceId?: string | undefined;
812
+ scope?: string | undefined;
813
+ expectedGeneration?: string | undefined;
814
+ term?: string | undefined;
815
+ }>;
816
+ export type DrainRequest = z.infer<typeof DrainRequest>;
817
+ /** What a drain request leaves the engine in: `draining` — this call put it into draining; `already_draining` — it already was
818
+ * (the same action repeated), and the receipt reports the first call's facts. */
819
+ export declare const DRAIN_RECEIPT_STATES: readonly ["draining", "already_draining"];
820
+ export declare const DrainReceiptState: z.ZodEnum<["draining", "already_draining"]>;
821
+ export type DrainReceiptState = z.infer<typeof DrainReceiptState>;
822
+ /** The drain endpoint's answer: the engine's draining fact ({@link EngineDraining} — the same object {@link EngineSignal}
823
+ * carries as `draining`, extended, not copied) plus how this call found the engine. */
824
+ export declare const DrainReceipt: z.ZodObject<{
825
+ actionId: z.ZodString;
826
+ since: z.ZodNumber;
827
+ deadlineAt: z.ZodNumber;
828
+ } & {
829
+ state: z.ZodEnum<["draining", "already_draining"]>;
830
+ }, "strip", z.ZodTypeAny, {
831
+ since: number;
832
+ actionId: string;
833
+ deadlineAt: number;
834
+ state: "draining" | "already_draining";
835
+ }, {
836
+ since: number;
837
+ actionId: string;
838
+ deadlineAt: number;
839
+ state: "draining" | "already_draining";
840
+ }>;
841
+ export type DrainReceipt = z.infer<typeof DrainReceipt>;
842
+ /** Engine side: the request body of the drain endpoint. Shape mismatch / non-object / throwing accessor ⇒ `undefined`, never throws. */
843
+ export declare function readDrainRequest(body: unknown): DrainRequest | undefined;
844
+ /** Caller side: the drain endpoint's answer. Shape mismatch / non-object / throwing accessor ⇒ `undefined`, never throws. */
845
+ export declare function readDrainReceipt(body: unknown): DrainReceipt | undefined;
433
846
  export declare const DISPATCH_STRATEGIES: readonly ["round-robin", "least-inflight", "consistent-hash"];
434
847
  export type DispatchStrategy = (typeof DISPATCH_STRATEGIES)[number];
435
848
  /** Per-worker dispatch policy (center-configured; the gateway is OPTIONAL — D3: TOC/self-maintained TOB go
@@ -749,3 +1162,4 @@ export interface AggregatedPrincipalUsage {
749
1162
  * view). Pure — storage/dedup of replayed batches is the caller's concern (key batches by
750
1163
  * worker+instanceId+windowStartMs). */
751
1164
  export declare function aggregateUsage(reports: UsageReport[]): Record<string, AggregatedPrincipalUsage>;
1165
+ export {};