@kici-dev/orchestrator 0.4.0 → 0.6.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 (183) hide show
  1. package/dist/__test-helpers__/fake-scaler-state-store.d.ts +24 -0
  2. package/dist/__test-helpers__/mock-db-query.d.ts +130 -0
  3. package/dist/__test-helpers__/mock-db.d.ts +27 -0
  4. package/dist/agent/agent-version.d.ts +34 -0
  5. package/dist/agent/dispatcher.d.ts +151 -13
  6. package/dist/agent/host-roster.d.ts +37 -5
  7. package/dist/agent/registry.d.ts +2 -0
  8. package/dist/app.d.ts +56 -7
  9. package/dist/approvals/apply-decision.d.ts +67 -19
  10. package/dist/approvals/held-run-release-wiring.d.ts +45 -0
  11. package/dist/approvals/independent-wiring.d.ts +55 -0
  12. package/dist/approvals/resume-router.d.ts +9 -0
  13. package/dist/approvals/triggerer-subject.d.ts +37 -0
  14. package/dist/cache/dep-cache.d.ts +54 -10
  15. package/dist/cache/global-eval-round-cache.d.ts +95 -0
  16. package/dist/cache/index.d.ts +3 -0
  17. package/dist/cache/pending-global-evals.d.ts +42 -0
  18. package/dist/cache/pending-inits.d.ts +10 -0
  19. package/dist/cli/api-client.d.ts +18 -0
  20. package/dist/cli/commands/cluster-settings.d.ts +52 -3
  21. package/dist/cli/commands/diagnose.d.ts +6 -0
  22. package/dist/cli/commands/held-run.d.ts +58 -0
  23. package/dist/cli/commands/runs.d.ts +1 -0
  24. package/dist/cli/commands/trust-policy.d.ts +117 -12
  25. package/dist/cli/kici-admin.d.ts +10 -0
  26. package/dist/cli.js +2770 -938
  27. package/dist/cluster/cluster-identity.d.ts +3 -3
  28. package/dist/cluster/cluster-settings-reader.d.ts +59 -1
  29. package/dist/cluster/coordinator.d.ts +44 -0
  30. package/dist/cluster/index.d.ts +4 -0
  31. package/dist/cluster/peer-handler.d.ts +19 -0
  32. package/dist/cluster/peer-registry.d.ts +17 -0
  33. package/dist/cluster/plan-headroom-store.d.ts +26 -0
  34. package/dist/cluster/worker-eviction.d.ts +69 -0
  35. package/dist/cold-store/load-event-log-range.d.ts +10 -3
  36. package/dist/cold-store/load-secret-audit-log-range.d.ts +4 -3
  37. package/dist/cold-store/tables/secret-audit-log.d.ts +4 -3
  38. package/dist/config.d.ts +48 -21
  39. package/dist/content-requirements-cache.d.ts +55 -0
  40. package/dist/contexts/held-runs.d.ts +123 -16
  41. package/dist/contexts/protection/aggregate.d.ts +3 -2
  42. package/dist/contexts/protection/branch-gate.d.ts +15 -0
  43. package/dist/contexts/protection/concurrency-gate.d.ts +8 -2
  44. package/dist/contexts/protection/pipeline.d.ts +18 -0
  45. package/dist/contexts/release-queued-holds.d.ts +35 -0
  46. package/dist/dashboard/attestation-filters.d.ts +8 -0
  47. package/dist/dashboard/handler.d.ts +6 -24
  48. package/dist/db/migrations/109_cluster_settings_cache_knobs.d.ts +4 -0
  49. package/dist/db/migrations/110_cluster_settings_global_eval_knobs.d.ts +4 -0
  50. package/dist/db/migrations/111_cluster_settings_global_eval_wait.d.ts +4 -0
  51. package/dist/db/migrations/112_execution_runs_workflow_repo.d.ts +4 -0
  52. package/dist/db/migrations/113_execution_runs_workflow_repo_index.d.ts +30 -0
  53. package/dist/db/migrations/114_ingest_queue_claim.d.ts +4 -0
  54. package/dist/db/migrations/115_global_workflows_cluster_switch.d.ts +9 -0
  55. package/dist/db/migrations/116_org_plan_headroom.d.ts +4 -0
  56. package/dist/db/migrations/117_invoke_gate_proxy_jobs.d.ts +4 -0
  57. package/dist/db/migrations/118_invoke_gate_scheduling.d.ts +4 -0
  58. package/dist/db/migrations/119_scaler_ha_ownership.d.ts +4 -0
  59. package/dist/db/migrations/120_cluster_settings_scaler_reaper.d.ts +4 -0
  60. package/dist/db/migrations/121_org_settings_allow_untrusted_dockerfile_builds.d.ts +19 -0
  61. package/dist/db/migrations/122_host_roster_scaler_managed.d.ts +25 -0
  62. package/dist/db/migrations/123_workflow_registrations_default_branch.d.ts +24 -0
  63. package/dist/db/migrations/124_cluster_settings_provision_backoff.d.ts +4 -0
  64. package/dist/db/migrations/125_org_trust_directory.d.ts +30 -0
  65. package/dist/db/migrations/126_held_runs_posted_pending_check.d.ts +28 -0
  66. package/dist/db/migrations/127_org_trust_policy_approval_expiry_seconds.d.ts +26 -0
  67. package/dist/db/migrations/128_execution_runs_global_eval_round.d.ts +23 -0
  68. package/dist/db/migrations/129_execution_runs_dispatch_routing_key.d.ts +24 -0
  69. package/dist/db/migrations/130_scaler_provision_outcomes.d.ts +32 -0
  70. package/dist/db/types.d.ts +428 -12
  71. package/dist/entry-helpers.d.ts +28 -0
  72. package/dist/events/event-emitter.d.ts +22 -0
  73. package/dist/events/event-router.d.ts +87 -1
  74. package/dist/events/types.d.ts +2 -23
  75. package/dist/git/credential-broker.d.ts +94 -0
  76. package/dist/git/inherited-secret.d.ts +24 -0
  77. package/dist/git/installation-token.d.ts +38 -0
  78. package/dist/git/job-context.d.ts +13 -0
  79. package/dist/index.js +324 -84
  80. package/dist/metrics/agent-metrics-aggregator.d.ts +4 -4
  81. package/dist/metrics/prometheus.d.ts +170 -3
  82. package/dist/oidc/oidc-mint-registration.d.ts +7 -3
  83. package/dist/orchestrator-core.d.ts +91 -24
  84. package/dist/pipeline/content-filter.d.ts +71 -0
  85. package/dist/pipeline/direct-ingress-deps.d.ts +100 -0
  86. package/dist/pipeline/dispatch-matched-workflow.d.ts +521 -11
  87. package/dist/pipeline/global-eval-round.d.ts +307 -0
  88. package/dist/pipeline/internal-event-pipeline.d.ts +140 -0
  89. package/dist/pipeline/invoke-gate.d.ts +164 -0
  90. package/dist/pipeline/job-contexts.d.ts +16 -17
  91. package/dist/pipeline/needs-scheduler.d.ts +13 -0
  92. package/dist/pipeline/process-webhook.d.ts +126 -31
  93. package/dist/pipeline/processor.d.ts +282 -23
  94. package/dist/pipeline/rerun.d.ts +21 -0
  95. package/dist/pipeline/resume-workflow.d.ts +40 -7
  96. package/dist/pipeline/route-or-dispatch-jobs.d.ts +15 -0
  97. package/dist/pipeline/security-hold-check.d.ts +217 -0
  98. package/dist/pipeline/test-pipeline.d.ts +12 -0
  99. package/dist/pipeline/undispatched-hold-checks.d.ts +23 -0
  100. package/dist/pipeline/webhook-payload-store.d.ts +20 -0
  101. package/dist/policy/dashboard-write-policy-listener.d.ts +62 -0
  102. package/dist/policy/dashboard-write-policy.d.ts +14 -0
  103. package/dist/provenance/backfill-run.d.ts +10 -1
  104. package/dist/provider-registry.d.ts +47 -4
  105. package/dist/providers/github/check-status-poster.d.ts +33 -3
  106. package/dist/providers/github/commit-message.d.ts +20 -0
  107. package/dist/providers/github/file-contents.d.ts +40 -0
  108. package/dist/providers/github/index.d.ts +2 -2
  109. package/dist/providers/github/normalizer.d.ts +3 -2
  110. package/dist/providers/local/normalizer.d.ts +3 -5
  111. package/dist/providers/universal-git/config.d.ts +2 -0
  112. package/dist/providers/universal-git/index.d.ts +11 -2
  113. package/dist/providers/universal-git/normalizer.d.ts +10 -0
  114. package/dist/queue/cleanup.d.ts +7 -1
  115. package/dist/queue/job-queue.d.ts +112 -7
  116. package/dist/queue/terminalize-unroutable.d.ts +13 -0
  117. package/dist/registration/extractor.d.ts +3 -3
  118. package/dist/registration/registration-index.d.ts +7 -0
  119. package/dist/registration/registration-run-match.d.ts +47 -0
  120. package/dist/registration/registration-store.d.ts +22 -0
  121. package/dist/reporting/check-run-reporter.d.ts +146 -2
  122. package/dist/reporting/check-run-summary.d.ts +25 -1
  123. package/dist/reporting/execution-tracker.d.ts +324 -9
  124. package/dist/reporting/log-chunk-sink.d.ts +8 -5
  125. package/dist/reporting/run-aggregator.d.ts +4 -14
  126. package/dist/reporting/step-display-order.d.ts +43 -0
  127. package/dist/routes/admin-held-runs.d.ts +142 -0
  128. package/dist/routes/admin-org-settings.d.ts +5 -0
  129. package/dist/routes/admin-trust-policy.d.ts +34 -6
  130. package/dist/routes/admin.d.ts +15 -0
  131. package/dist/scaler/backend-factory.d.ts +55 -0
  132. package/dist/scaler/bare-metal-backend.d.ts +43 -5
  133. package/dist/scaler/claim-store.d.ts +119 -0
  134. package/dist/scaler/config.d.ts +4 -0
  135. package/dist/scaler/container-backend.d.ts +18 -4
  136. package/dist/scaler/container-routing.d.ts +23 -0
  137. package/dist/scaler/container-spawn.d.ts +28 -0
  138. package/dist/scaler/event-backend.d.ts +124 -0
  139. package/dist/scaler/event-provision-reaper.d.ts +350 -0
  140. package/dist/scaler/failure-tracker.d.ts +1 -1
  141. package/dist/scaler/firecracker-backend.d.ts +33 -6
  142. package/dist/scaler/index.d.ts +11 -1
  143. package/dist/scaler/label-matcher.d.ts +4 -3
  144. package/dist/scaler/manager.d.ts +905 -76
  145. package/dist/scaler/resolve-container-auth.d.ts +45 -0
  146. package/dist/scaler/scaler-events.d.ts +11 -0
  147. package/dist/scaler/scaler-state-store.d.ts +294 -10
  148. package/dist/scaler/types.d.ts +131 -13
  149. package/dist/scaler/warm-pool.d.ts +132 -29
  150. package/dist/security/comment-handler.d.ts +50 -11
  151. package/dist/security/global-workflow-policy.d.ts +52 -12
  152. package/dist/security/identity-link.d.ts +60 -0
  153. package/dist/security/lock-source.d.ts +11 -13
  154. package/dist/security/reduced-privilege-note.d.ts +59 -0
  155. package/dist/security/trust-directory-store.d.ts +233 -0
  156. package/dist/security/trust-policy-gate.d.ts +94 -59
  157. package/dist/security/trust-policy-store.d.ts +3 -0
  158. package/dist/security/trust-resolver.d.ts +14 -98
  159. package/dist/server.d.ts +11 -1
  160. package/dist/server.js +56329 -42234
  161. package/dist/stale-detector/gate-deadline-detector.d.ts +52 -0
  162. package/dist/stale-detector/stale-run-detector.d.ts +61 -3
  163. package/dist/standalone.js +37619 -23570
  164. package/dist/webhook/ingest-accept.d.ts +70 -0
  165. package/dist/webhook/ingest-overflow-buffer.d.ts +35 -4
  166. package/dist/webhook/ingest-overflow-replayer.d.ts +50 -6
  167. package/dist/worker/in-memory-job-queue.d.ts +1 -1
  168. package/dist/ws/agent-handler.d.ts +31 -4
  169. package/dist/ws/dashboard-context-handler.d.ts +28 -4
  170. package/dist/ws/dashboard-global-workflows-handler.d.ts +36 -9
  171. package/dist/ws/execution-status-frame.d.ts +32 -0
  172. package/dist/ws/failure-messages.d.ts +2 -0
  173. package/dist/ws/git-credential-relay.d.ts +39 -0
  174. package/dist/ws/oidc-token-relay.d.ts +6 -11
  175. package/dist/ws/platform-client.d.ts +68 -9
  176. package/dist/ws/test-relay-handlers.d.ts +35 -10
  177. package/installer-image-digests.json +3 -3
  178. package/package.json +24 -19
  179. package/sbom.spdx.json +868 -821
  180. package/dist/approvals/team-membership-lookup.d.ts +0 -13
  181. package/dist/pipeline/inline-eval.d.ts +0 -44
  182. package/dist/providers/github/contributor-resolver.d.ts +0 -30
  183. package/dist/security/contributor-cache.d.ts +0 -83
@@ -23,9 +23,9 @@ export interface ClusterIdentityDeps {
23
23
  */
24
24
  storagePrefix?: string;
25
25
  /**
26
- * E2E escape hatch — when true, validateS3Sentinel logs a warning and
27
- * returns early. Mirrors the orchestrator's
28
- * `config.skipS3SentinelValidation` (KICI_SKIP_S3_SENTINEL_VALIDATION).
26
+ * Test-only escape hatch — when true, validateS3Sentinel logs a warning and
27
+ * returns early. Supplied only by the build-time test double's injected
28
+ * fault-injection policy; the shipped orchestrator always validates.
29
29
  */
30
30
  skipSentinelValidation?: boolean;
31
31
  }
@@ -1,9 +1,52 @@
1
1
  import type { Kysely } from 'kysely';
2
2
  import type { Database } from '../db/types.js';
3
3
  /** Numeric columns on cluster_settings readable via {@link ClusterSettingsReader}. */
4
- export type ClusterNumberColumn = 'max_github_payload_bytes' | 'event_log_max_payload_bytes' | 'lock_file_max_bytes' | 'webhook_dedup_ttl_ms' | 'contributor_cache_ttl_ms' | 'event_router_event_ttl_seconds' | 'event_router_max_dispatch_attempts' | 'queue_max_depth' | 'reroute_flap_grace_ms' | 'max_fanout_hosts' | 'event_router_rate_limit_per_workflow_per_minute' | 'cache_max_tarball_bytes' | 'cache_ttl_days' | 'check_run_tracking_ttl_days' | 'concurrency_wait_timeout_ms' | 'agent_token_ttl_ms' | 'ownership_db_check_timeout_ms' | 'unroutable_grace_ms';
4
+ export type ClusterNumberColumn = 'max_github_payload_bytes' | 'event_log_max_payload_bytes' | 'lock_file_max_bytes' | 'webhook_dedup_ttl_ms'
5
+ /** @deprecated Readable, but no call site reads it. Removed at v1.0.0. */
6
+ | 'contributor_cache_ttl_ms' | 'event_router_event_ttl_seconds' | 'event_router_max_dispatch_attempts' | 'queue_max_depth' | 'reroute_flap_grace_ms' | 'max_fanout_hosts' | 'event_router_rate_limit_per_workflow_per_minute' | 'cache_max_tarball_bytes' | 'cache_ttl_days' | 'lockfile_cache_max' | 'lockfile_cache_max_bytes' | 'lockfile_cache_ttl_ms' | 'content_cache_max' | 'content_cache_max_bytes' | 'content_cache_ttl_ms' | 'global_eval_round_timeout_ms' | 'global_eval_candidate_timeout_ms' | 'global_eval_cache_max' | 'global_eval_wait_timeout_ms' | 'check_run_tracking_ttl_days' | 'concurrency_wait_timeout_ms' | 'agent_token_ttl_ms' | 'ownership_db_check_timeout_ms' | 'unroutable_grace_ms' | 'ingest_overflow_claim_timeout_ms' | 'scaler_reap_interval_ms' | 'scaler_reap_stranded_timeout_ms' | 'scaler_reap_reattempt_interval_ms' | 'scaler_claim_retention_ms' | 'scaler_provision_backoff_base_ms' | 'scaler_provision_backoff_max_ms' | 'scaler_provision_max_consecutive_failures';
5
7
  /** Text columns on cluster_settings readable via {@link ClusterSettingsReader}. */
6
8
  export type ClusterStringColumn = 'dashboard_verified_issuer';
9
+ /** Boolean columns on cluster_settings readable via {@link ClusterSettingsReader}. */
10
+ export type ClusterBooleanColumn = 'global_workflows_enabled';
11
+ /**
12
+ * Ceiling for the three LRU entry-count knobs — `lockfile_cache_max`,
13
+ * `content_cache_max`, and `global_eval_cache_max`.
14
+ *
15
+ * This is a boot-safety bound, not a policy preference. The underlying LRU
16
+ * allocates its index arrays eagerly from `max` — several typed arrays plus the
17
+ * TTL and size arrays the caches ask for — so the cost is paid at construction
18
+ * with zero entries cached. Measured on the shipped version: an empty cache at
19
+ * `max` 5,000,000 costs ~191 MB, and at 5,000,000,000 the constructor throws
20
+ * `RangeError: Invalid array length`.
21
+ *
22
+ * That throw is what makes an unbounded knob dangerous rather than merely
23
+ * wasteful. All three caches are built inside `bootstrapOrchestrator`, so a bad
24
+ * stored value crashes the orchestrator before its admin API is listening —
25
+ * and the admin API is the only way `kici-admin cluster-settings` can reach the
26
+ * stored value. The knob would brick the very tool needed to un-brick it.
27
+ *
28
+ * 100,000 is 200x the shipped default of 500 and costs ~4 MB per empty cache,
29
+ * so it is far above any plausible operator setting while keeping boot bounded.
30
+ */
31
+ export declare const CACHE_MAX_ENTRIES_CEILING = 100000;
32
+ /**
33
+ * Bound a stored entry-count knob to something the LRU constructor survives.
34
+ *
35
+ * Applied at the read site rather than only at the write site, because the
36
+ * write-side validation cannot reach a value that is already in the database —
37
+ * set before the ceiling shipped, or written by any path other than the admin
38
+ * route. Clamping here is what actually guarantees a stored value cannot
39
+ * prevent boot.
40
+ *
41
+ * A value that is not a usable positive count (NaN, non-finite, below 1) falls
42
+ * back to the configured default rather than clamping to 1: a 1-entry cache
43
+ * thrashes silently, which is harder to diagnose than simply ignoring garbage.
44
+ * The fallback is floored and bounded by the ceiling on the same path as the
45
+ * stored value, so neither a fractional nor an above-ceiling configured default
46
+ * can reach the LRU constructor as a non-integer or unclamped max — the
47
+ * constructor rejects both, and either would prevent boot.
48
+ */
49
+ export declare function clampCacheMaxEntries(value: number, fallback: number): number;
7
50
  /**
8
51
  * A knob read that keeps "the operator never set this" separate from "we could
9
52
  * not read it".
@@ -64,6 +107,21 @@ export declare class ClusterSettingsReader {
64
107
  * An empty stored string is unset, matching {@link getString}.
65
108
  */
66
109
  tryGetString(column: ClusterStringColumn): Promise<ClusterSettingRead<string>>;
110
+ /**
111
+ * Resolve a fleet-wide boolean knob without collapsing a read failure into a
112
+ * default.
113
+ *
114
+ * There is deliberately no `getBoolean(column, fallback)` companion. The
115
+ * security gate that consumes this must tell the reader's outcomes apart
116
+ * itself: a stored `false` and a NULL both deny today, but they deny for
117
+ * different reasons, and `{ ok: false }` must deny regardless of what the
118
+ * configured default happens to be. A convenience wrapper that folded the
119
+ * failure into the default would be safe only for as long as that default
120
+ * stayed `false`. A caller that only needs a display value (the dashboard
121
+ * status badge) folds `{ ok: false }` into the default explicitly at its own
122
+ * site, where getting it wrong misreports a badge rather than opening a gate.
123
+ */
124
+ tryGetBoolean(column: ClusterBooleanColumn): Promise<ClusterSettingRead<boolean>>;
67
125
  /**
68
126
  * The cluster_settings row version last seen in the cache (0 if never loaded).
69
127
  *
@@ -90,6 +90,8 @@ export interface RunCoordinatorDeps {
90
90
  sendAndWaitAckViaHandler?: (targetInstanceId: string, msg: JobReroute, timeoutMs: number) => Promise<boolean>;
91
91
  /** Fallback: send a message via server-side (incoming) peer connection (fire-and-forget). */
92
92
  sendToPeerViaHandler?: (targetInstanceId: string, msg: PeerToPeerMessage) => boolean;
93
+ /** Close a peer's incoming socket by instanceId (used by worker eviction). */
94
+ closePeerViaHandler?: (targetInstanceId: string, code: number, reason: string) => void;
93
95
  ackTimeoutMs?: number;
94
96
  /** Stale peer timeout in ms. Default: 60000 (60s). */
95
97
  staleTimeoutMs?: number;
@@ -137,7 +139,23 @@ export declare class RunCoordinator {
137
139
  private readonly nakTracker;
138
140
  /** Stale eviction timer handle. */
139
141
  private staleEvictionTimer;
142
+ /** Plan-ceiling worker eviction (drain-then-disconnect). */
143
+ private readonly workerEviction;
144
+ /** Per-peer "no in-flight rerouted jobs" callbacks, fired by worker drain. */
145
+ private readonly evictionIdleCallbacks;
146
+ private readonly closePeerViaHandler?;
140
147
  constructor(deps: RunCoordinatorDeps);
148
+ /** In-flight rerouted jobs currently attributed to a peer. */
149
+ private runningJobCountForPeer;
150
+ /** Fire a registered drain idle-callback once the peer has no in-flight jobs. */
151
+ private fireIdleIfDrained;
152
+ /**
153
+ * Reconcile this coordinator's workers against the pushed ceiling. Invoked on
154
+ * every `plan.headroom` frame: when `evictExcess` is set, the newest workers
155
+ * past `ceiling` are drained; otherwise any in-flight drain is cancelled and
156
+ * the worker restored, so a raised ceiling rescues it.
157
+ */
158
+ reconcileWorkerEviction(ceiling: number, evictExcess: boolean): void;
141
159
  /**
142
160
  * Route jobs for a webhook run. Claims jobs that local agents can handle,
143
161
  * reroutes the rest to peers with matching capacity.
@@ -268,6 +286,32 @@ export declare class RunCoordinator {
268
286
  * its worker is connected.
269
287
  */
270
288
  private trackReroutedJob;
289
+ /** Arm a spawn-window / reap timer that fires {@link handleRerouteSpawnTimeout}. */
290
+ private armRerouteWindow;
291
+ /**
292
+ * Whether the reroute target has actually started the job, read from the
293
+ * shared `execution_jobs` row.
294
+ *
295
+ * A read failure answers "not running" so a transient database fault cannot
296
+ * silently disable the backstop — the re-dispatch it then performs is the
297
+ * behavior that shipped before this check existed, and it is idempotent.
298
+ * Only the FIRST observation goes through this path: once `peerStarted`
299
+ * latches, a read fault can no longer bounce a running job.
300
+ */
301
+ private rerouteTargetIsRunning;
302
+ /**
303
+ * The peer owns the job, so the backstop must not re-dispatch it. Release the
304
+ * tracking entry once the shared row is terminal, otherwise re-arm the window
305
+ * as a reap poll.
306
+ *
307
+ * The entry cannot simply be dropped here: cancel propagation still needs it
308
+ * while the job runs. It also cannot simply be kept, because a peer
309
+ * COORDINATOR relays no terminal `job.progress` — the shared row IS its
310
+ * report — so nothing else would ever release it and `reroutedJobs` would
311
+ * grow for the lifetime of the process. A read fault re-arms (never releases),
312
+ * so a transient fault costs one more poll rather than tracking.
313
+ */
314
+ private reapOrDeferRerouteTracking;
271
315
  /**
272
316
  * Post-ACK spawn-window backstop. Fires when a rerouted job's peer accepted
273
317
  * but produced no progress within the window (Layer A), or immediately when a
@@ -21,6 +21,10 @@ export type { RaftPersistentState } from './raft-state.js';
21
21
  export { OrphanRecovery } from './orphan-recovery.js';
22
22
  export { RunCoordinator } from './coordinator.js';
23
23
  export type { RunContext, JobToRoute, RouteResult } from './coordinator.js';
24
+ export { PlanHeadroomStore } from './plan-headroom-store.js';
25
+ export type { StoredPlanHeadroom } from './plan-headroom-store.js';
26
+ export { WorkerEviction, selectEvictionTargets, WORKER_DRAIN_TIMEOUT_MS, } from './worker-eviction.js';
27
+ export type { EvictionHooks } from './worker-eviction.js';
24
28
  export { createClusterHealthRoutes } from './health-api.js';
25
29
  export type { ClusterHealthRoutesDeps } from './health-api.js';
26
30
  //# sourceMappingURL=index.d.ts.map
@@ -33,6 +33,14 @@ export interface PeerHandlerDeps {
33
33
  peerRegistry: PeerRegistry;
34
34
  /** Callback to get this orchestrator's local agent inventory for heartbeats. */
35
35
  getLocalInventory: () => Omit<PeerHeartbeat, 'type'>;
36
+ /**
37
+ * The Platform-pushed worker ceiling this coordinator enforces, or `null`
38
+ * when none was ever received (admit worker joins freely). Read at each
39
+ * worker-peer admission. Served from the persisted PlanHeadroomStore, so it
40
+ * survives a Platform disconnect — the coordinator keeps enforcing the last
41
+ * known ceiling rather than resetting to unlimited.
42
+ */
43
+ getWorkerCeiling?: () => Promise<number | null>;
36
44
  /** Heartbeat interval in ms. Default: 30000 (30s). */
37
45
  heartbeatIntervalMs?: number;
38
46
  /** Auth timeout in ms. Default: 15000 (15s). */
@@ -103,9 +111,20 @@ export interface PeerHandlerDeps {
103
111
  * Returns `handleConnection(ws, remoteIp?)` which should be called when a new
104
112
  * WebSocket connection is upgraded on the peer endpoint.
105
113
  */
114
+ /**
115
+ * Whether a joining peer may be admitted against the plan ceiling.
116
+ *
117
+ * Only workers are gated: a coordinator peer holds its own Platform connection
118
+ * and is counted there, and a peer advertising no role registers as a
119
+ * coordinator (see PeerRegistry.addPeer), so neither is gated here. A `null`
120
+ * ceiling — none ever received — admits freely. The ceiling is ABSOLUTE, so the
121
+ * join is admitted only while the currently-connected worker count is below it.
122
+ */
123
+ export declare function shouldAdmitWorker(role: string | undefined, ceiling: number | null, connectedWorkerCount: number): boolean;
106
124
  export declare function createPeerHandler(deps: PeerHandlerDeps): {
107
125
  handleConnection: (ws: PeerWsLike, remoteIp?: string) => void;
108
126
  sendToPeer: (targetInstanceId: string, msg: PeerToPeerMessage) => boolean;
127
+ closePeer: (targetInstanceId: string, code: number, reason: string) => void;
109
128
  sendAndWaitAck: (targetInstanceId: string, msg: JobReroute, timeoutMs?: number) => Promise<boolean>;
110
129
  sendConfigReloadAndWait: (targetInstanceId: string, msg: PeerConfigReload, timeoutMs?: number) => Promise<PeerConfigReloadResponse | null>;
111
130
  sendLogsCollectAndWait: (targetInstanceId: string, msg: PeerLogsCollectRequest, timeoutMs: number) => Promise<Buffer>;
@@ -35,6 +35,8 @@ export interface PeerInfo {
35
35
  routingKeys: string[];
36
36
  connected: boolean;
37
37
  lastHeartbeatAt: number;
38
+ /** When this peer first connected. Distinct from `lastHeartbeatAt`, which moves; this does not. */
39
+ connectedAt: number;
38
40
  agents: PeerAgentInfo[];
39
41
  draining: boolean;
40
42
  capabilities: PeerCapabilities;
@@ -71,6 +73,12 @@ export interface PeerRegistryOptions {
71
73
  onClusterSettingsVersionBehind?: (peerVersion: number) => void;
72
74
  /** Called when a peer transitions to disconnected state. */
73
75
  onPeerDisconnected?: (instanceId: string) => void;
76
+ /**
77
+ * Called when the connected-peer set changes (a fresh add or a
78
+ * markDisconnected), so a coordinator can re-report its `cluster.membership`
79
+ * snapshot to the Platform without waiting for the periodic interval.
80
+ */
81
+ onMembershipChange?: () => void;
74
82
  }
75
83
  export declare class PeerRegistry {
76
84
  private readonly peers;
@@ -81,6 +89,7 @@ export declare class PeerRegistry {
81
89
  private readonly onRegistryVersionBehind?;
82
90
  private readonly onClusterSettingsVersionBehind?;
83
91
  private readonly onPeerDisconnected?;
92
+ private readonly onMembershipChange?;
84
93
  constructor(options?: PeerRegistryOptions);
85
94
  /**
86
95
  * Update the local config version for comparison with peer heartbeats.
@@ -196,6 +205,14 @@ export declare class PeerRegistry {
196
205
  * Get all peers with role=worker.
197
206
  */
198
207
  getWorkerPeers(): PeerInfo[];
208
+ /**
209
+ * Currently-connected worker peers.
210
+ *
211
+ * A peer that advertises no role registers as a coordinator (see `addPeer`),
212
+ * so a worker predating the role field is not counted here. That under-counts,
213
+ * which errs permissive, and it self-heals as workers upgrade.
214
+ */
215
+ getConnectedWorkerPeers(): PeerInfo[];
199
216
  /**
200
217
  * Get all peers with role=coordinator.
201
218
  */
@@ -0,0 +1,26 @@
1
+ import type { Kysely } from 'kysely';
2
+ import type { PlanHeadroom } from '@kici-dev/engine';
3
+ import type { Database } from '../db/types.js';
4
+ /** The ceiling as last pushed by the Platform. */
5
+ export interface StoredPlanHeadroom {
6
+ maxWorkerPeers: number;
7
+ orgLimit: number;
8
+ orgTotal: number;
9
+ evictExcess: boolean;
10
+ updatedAt: Date;
11
+ }
12
+ /**
13
+ * The orchestrator's cache of the Platform-owned worker ceiling.
14
+ *
15
+ * Persisted so a coordinator restarting during a Platform outage keeps
16
+ * enforcing the last known ceiling instead of resetting to unlimited. Same
17
+ * ownership shape as `org_trust_policy`: the Platform writes it, the
18
+ * orchestrator only reads it back.
19
+ */
20
+ export declare class PlanHeadroomStore {
21
+ private readonly db;
22
+ constructor(db: Kysely<Database>);
23
+ read(): Promise<StoredPlanHeadroom | null>;
24
+ write(headroom: PlanHeadroom): Promise<void>;
25
+ }
26
+ //# sourceMappingURL=plan-headroom-store.d.ts.map
@@ -0,0 +1,69 @@
1
+ import type { PeerInfo, PeerRegistry } from './peer-registry.js';
2
+ /** How long a draining worker may hold in-flight jobs before it is disconnected anyway. */
3
+ export declare const WORKER_DRAIN_TIMEOUT_MS: number;
4
+ /**
5
+ * The coordinator-side operations eviction needs.
6
+ *
7
+ * Declared here rather than reached for on the coordinator directly: the
8
+ * coordinator owns dispatch and job attribution, but exposes no ineligibility
9
+ * or idle-notification surface, and widening its public API for one caller
10
+ * would couple eviction to it. A fake implementation makes this class unit
11
+ * testable without a running cluster.
12
+ */
13
+ export interface EvictionHooks {
14
+ /** Stop rerouting new jobs to this peer. */
15
+ markIneligible(instanceId: string): void;
16
+ /**
17
+ * Restore this peer to dispatch eligibility and drop any idle-notification
18
+ * registration. Called when a drain is cancelled (the ceiling rose so the
19
+ * peer is no longer excess) and after a drained peer is disconnected, so a
20
+ * later re-admission of the same instance starts dispatch-eligible.
21
+ */
22
+ clearIneligible(instanceId: string): void;
23
+ /** In-flight rerouted jobs currently attributed to this peer. */
24
+ runningJobCount(instanceId: string): number;
25
+ /** Invoke the callback once this peer has no in-flight rerouted jobs. */
26
+ onPeerIdle(instanceId: string, callback: () => void): void;
27
+ /** Close this peer's connection with the given code and reason. */
28
+ closePeer(instanceId: string, code: number, reason: string): void;
29
+ }
30
+ /**
31
+ * The workers to drain to bring this coordinator down to its ceiling.
32
+ *
33
+ * Newest-first by connect time: the joins that crossed the line are the ones
34
+ * that go, so long-running capacity is not shuffled arbitrarily.
35
+ */
36
+ export declare function selectEvictionTargets(workers: readonly PeerInfo[], ceiling: number): PeerInfo[];
37
+ /**
38
+ * Drain-then-disconnect this coordinator's excess workers.
39
+ *
40
+ * A target is first marked ineligible for new dispatch, then closed once its
41
+ * in-flight rerouted jobs reach terminal status — which the worker's durable
42
+ * outbox and `job.progress.ack` already guarantee — or after
43
+ * `WORKER_DRAIN_TIMEOUT_MS`, after which the coordinator's existing
44
+ * orphan-recovery guard handles whatever remains.
45
+ */
46
+ export declare class WorkerEviction {
47
+ private readonly peerRegistry;
48
+ private readonly hooks;
49
+ private readonly logger;
50
+ private readonly draining;
51
+ constructor(peerRegistry: PeerRegistry, hooks: EvictionHooks, logger: {
52
+ warn: (msg: string, meta?: unknown) => void;
53
+ });
54
+ /**
55
+ * Reconcile the connected worker set against the ceiling.
56
+ *
57
+ * When `evictExcess` is true, drain the newest workers past `ceiling`;
58
+ * otherwise the target set is empty. Any drain in flight for a worker that is
59
+ * no longer a target is CANCELLED — its timer cleared and its dispatch
60
+ * eligibility restored — so a raised ceiling (or the Platform clearing
61
+ * `evictExcess` once the org drops back under) rescues a worker instead of
62
+ * disconnecting it at the drain timeout.
63
+ */
64
+ reconcile(ceiling: number, evictExcess: boolean): void;
65
+ /** Stop an in-flight drain and restore the worker to dispatch eligibility. */
66
+ private cancelDrain;
67
+ private startDrain;
68
+ }
69
+ //# sourceMappingURL=worker-eviction.d.ts.map
@@ -13,10 +13,17 @@
13
13
  * dashboard's payload-detail view continues to resolve archived
14
14
  * deliveries identically to hot ones.
15
15
  */
16
- import { type Kysely, type Selectable } from 'kysely';
16
+ import { type Kysely } from 'kysely';
17
17
  import { type ColdStore } from '@kici-dev/shared';
18
- import type { Database, EventLogTable } from '../db/types.js';
19
- export type EventLogColdStoreRow = Selectable<EventLogTable>;
18
+ import type { Database } from '../db/types.js';
19
+ import type { EventLogColdStoreRow } from './tables/event-log.js';
20
+ /**
21
+ * Re-exported, not re-declared: the alias is defined beside the cold-store
22
+ * adapter that owns the table, and a second identical declaration here is what
23
+ * a homonym sweep flags. Kept as an export so this module's public surface is
24
+ * unchanged for its consumers.
25
+ */
26
+ export type { EventLogColdStoreRow };
20
27
  export interface LoadOrchEventLogRangeArgs {
21
28
  db: Kysely<Database>;
22
29
  coldStore: ColdStore | undefined;
@@ -15,10 +15,11 @@
15
15
  * rows after streaming. This is correct because cold chunks are
16
16
  * partitioned by `(routing_key, day)`, not by the secondary filters.
17
17
  */
18
- import { type Kysely, type Selectable } from 'kysely';
18
+ import { type Kysely } from 'kysely';
19
19
  import { type ColdStore } from '@kici-dev/shared';
20
- import type { Database, SecretAuditLogTable } from '../db/types.js';
21
- export type SecretAuditLogRow = Selectable<SecretAuditLogTable>;
20
+ import type { Database, SecretAuditLogRow } from '../db/types.js';
21
+ /** Re-exported rather than re-declared — see the note in `load-event-log-range`. */
22
+ export type { SecretAuditLogRow };
22
23
  export interface LoadSecretAuditLogRangeArgs {
23
24
  db: Kysely<Database>;
24
25
  coldStore: ColdStore | undefined;
@@ -13,10 +13,11 @@
13
13
  * synthetic prefix `__orchestrator__`. Mirrors the same pattern already
14
14
  * used for `event_log` payload keys when the routing key is missing.
15
15
  */
16
- import { type Kysely, type Selectable } from 'kysely';
16
+ import { type Kysely } from 'kysely';
17
17
  import { type ChunkCommitMetadata, type ColdRetention, type ColdStoreTableConfig, type EligiblePartition, type TableAdapter } from '@kici-dev/shared';
18
- import type { Database, SecretAuditLogTable } from '../../db/types.js';
19
- export type SecretAuditLogRow = Selectable<SecretAuditLogTable>;
18
+ import type { Database, SecretAuditLogRow } from '../../db/types.js';
19
+ /** Re-exported rather than re-declared — see the note in `load-event-log-range`. */
20
+ export type { SecretAuditLogRow };
20
21
  export interface SecretAuditLogAdapterOptions {
21
22
  overrides?: Partial<ColdStoreTableConfig>;
22
23
  }
package/dist/config.d.ts CHANGED
@@ -56,9 +56,17 @@ declare const configSchema: z.ZodObject<{
56
56
  lockfileCacheMax: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
57
57
  lockfileCacheTtlMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
58
58
  lockfileCacheMaxBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
59
+ contentCacheMax: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
60
+ contentCacheTtlMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
61
+ contentCacheMaxBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
62
+ globalEvalRoundTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
63
+ globalEvalCandidateTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
64
+ globalEvalCacheMax: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
65
+ globalEvalWaitTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
59
66
  queueMaxDepth: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
60
67
  queueTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
61
68
  unroutableGraceMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
69
+ globalWorkflowsEnabled: z.ZodPipe<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>, z.ZodTransform<boolean, string | boolean>>;
62
70
  queueBackpressureThreshold: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
63
71
  workerConcurrency: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
64
72
  concurrencyWaitTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
@@ -81,6 +89,7 @@ declare const configSchema: z.ZodObject<{
81
89
  ingestOverflowReplayIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
82
90
  ingestOverflowReplayBatch: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
83
91
  ingestOverflowMaxAttempts: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
92
+ ingestOverflowClaimTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
84
93
  rerouteSpawnWindowMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
85
94
  rerouteAckTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
86
95
  rerouteMaxHops: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
@@ -88,6 +97,13 @@ declare const configSchema: z.ZodObject<{
88
97
  scalerSpawnTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
89
98
  backupStalenessWarnHours: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
90
99
  scalerPendingSweepIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
100
+ scalerReapIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
101
+ scalerReapStrandedTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
102
+ scalerReapReattemptIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
103
+ scalerClaimRetentionMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
104
+ scalerProvisionBackoffBaseMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
105
+ scalerProvisionBackoffMaxMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
106
+ scalerProvisionMaxConsecutiveFailures: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
91
107
  cacheStorageType: z.ZodOptional<z.ZodEnum<{
92
108
  filesystem: "filesystem";
93
109
  s3: "s3";
@@ -168,12 +184,6 @@ declare const configSchema: z.ZodObject<{
168
184
  eventRouterRetryBaseBackoffMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
169
185
  eventRouterRetryMaxBackoffMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
170
186
  eventRouterRetryScanIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
171
- testMode: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
172
- testEventFailFirstN: z.ZodOptional<z.ZodString>;
173
- testMintDeferAudience: z.ZodOptional<z.ZodString>;
174
- testMintRejectAudience: z.ZodOptional<z.ZodString>;
175
- testRerunDelayMs: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
176
- testOmitDashboardRequestTypes: z.ZodOptional<z.ZodString>;
177
187
  eventLogMaxPayloadBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
178
188
  maxGithubPayloadBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
179
189
  lockFileMaxBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
@@ -193,7 +203,6 @@ declare const configSchema: z.ZodObject<{
193
203
  }>>;
194
204
  autoMigrate: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
195
205
  agentMaxReconnectDelayMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
196
- skipS3SentinelValidation: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
197
206
  otelExporterOtlpEndpoint: z.ZodOptional<z.ZodString>;
198
207
  clusterName: z.ZodOptional<z.ZodString>;
199
208
  cluster: z.ZodPrefault<z.ZodObject<{
@@ -250,9 +259,17 @@ export declare const packagingConfigSchema: z.ZodObject<{
250
259
  lockfileCacheMax: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
251
260
  lockfileCacheTtlMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
252
261
  lockfileCacheMaxBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
262
+ contentCacheMax: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
263
+ contentCacheTtlMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
264
+ contentCacheMaxBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
265
+ globalEvalRoundTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
266
+ globalEvalCandidateTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
267
+ globalEvalCacheMax: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
268
+ globalEvalWaitTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
253
269
  queueMaxDepth: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
254
270
  queueTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
255
271
  unroutableGraceMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
272
+ globalWorkflowsEnabled: z.ZodPipe<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>, z.ZodTransform<boolean, string | boolean>>;
256
273
  queueBackpressureThreshold: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
257
274
  workerConcurrency: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
258
275
  concurrencyWaitTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
@@ -275,6 +292,7 @@ export declare const packagingConfigSchema: z.ZodObject<{
275
292
  ingestOverflowReplayIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
276
293
  ingestOverflowReplayBatch: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
277
294
  ingestOverflowMaxAttempts: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
295
+ ingestOverflowClaimTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
278
296
  rerouteSpawnWindowMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
279
297
  rerouteAckTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
280
298
  rerouteMaxHops: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
@@ -282,6 +300,13 @@ export declare const packagingConfigSchema: z.ZodObject<{
282
300
  scalerSpawnTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
283
301
  backupStalenessWarnHours: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
284
302
  scalerPendingSweepIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
303
+ scalerReapIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
304
+ scalerReapStrandedTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
305
+ scalerReapReattemptIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
306
+ scalerClaimRetentionMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
307
+ scalerProvisionBackoffBaseMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
308
+ scalerProvisionBackoffMaxMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
309
+ scalerProvisionMaxConsecutiveFailures: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
285
310
  cacheStorageType: z.ZodOptional<z.ZodEnum<{
286
311
  filesystem: "filesystem";
287
312
  s3: "s3";
@@ -362,12 +387,6 @@ export declare const packagingConfigSchema: z.ZodObject<{
362
387
  eventRouterRetryBaseBackoffMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
363
388
  eventRouterRetryMaxBackoffMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
364
389
  eventRouterRetryScanIntervalMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
365
- testMode: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
366
- testEventFailFirstN: z.ZodOptional<z.ZodString>;
367
- testMintDeferAudience: z.ZodOptional<z.ZodString>;
368
- testMintRejectAudience: z.ZodOptional<z.ZodString>;
369
- testRerunDelayMs: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
370
- testOmitDashboardRequestTypes: z.ZodOptional<z.ZodString>;
371
390
  eventLogMaxPayloadBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
372
391
  maxGithubPayloadBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
373
392
  lockFileMaxBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
@@ -387,7 +406,6 @@ export declare const packagingConfigSchema: z.ZodObject<{
387
406
  }>>;
388
407
  autoMigrate: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
389
408
  agentMaxReconnectDelayMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
390
- skipS3SentinelValidation: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
391
409
  otelExporterOtlpEndpoint: z.ZodOptional<z.ZodString>;
392
410
  clusterName: z.ZodOptional<z.ZodString>;
393
411
  cluster: z.ZodPrefault<z.ZodObject<{
@@ -477,9 +495,17 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
477
495
  lockfileCacheMax: number;
478
496
  lockfileCacheTtlMs: number;
479
497
  lockfileCacheMaxBytes: number;
498
+ contentCacheMax: number;
499
+ contentCacheTtlMs: number;
500
+ contentCacheMaxBytes: number;
501
+ globalEvalRoundTimeoutMs: number;
502
+ globalEvalCandidateTimeoutMs: number;
503
+ globalEvalCacheMax: number;
504
+ globalEvalWaitTimeoutMs: number;
480
505
  queueMaxDepth: number;
481
506
  queueTimeoutMs: number;
482
507
  unroutableGraceMs: number;
508
+ globalWorkflowsEnabled: boolean;
483
509
  queueBackpressureThreshold: number;
484
510
  workerConcurrency: number;
485
511
  concurrencyWaitTimeoutMs: number;
@@ -499,6 +525,7 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
499
525
  ingestOverflowReplayIntervalMs: number;
500
526
  ingestOverflowReplayBatch: number;
501
527
  ingestOverflowMaxAttempts: number;
528
+ ingestOverflowClaimTimeoutMs: number;
502
529
  rerouteSpawnWindowMs: number;
503
530
  rerouteAckTimeoutMs: number;
504
531
  rerouteMaxHops: number;
@@ -506,6 +533,13 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
506
533
  scalerSpawnTimeoutMs: number;
507
534
  backupStalenessWarnHours: number;
508
535
  scalerPendingSweepIntervalMs: number;
536
+ scalerReapIntervalMs: number;
537
+ scalerReapStrandedTimeoutMs: number;
538
+ scalerReapReattemptIntervalMs: number;
539
+ scalerClaimRetentionMs: number;
540
+ scalerProvisionBackoffBaseMs: number;
541
+ scalerProvisionBackoffMaxMs: number;
542
+ scalerProvisionMaxConsecutiveFailures: number;
509
543
  cacheStorageType?: "filesystem" | "s3" | undefined;
510
544
  cacheStoragePath?: string | undefined;
511
545
  cacheStorageS3Bucket?: string | undefined;
@@ -568,12 +602,6 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
568
602
  eventRouterRetryBaseBackoffMs: number;
569
603
  eventRouterRetryMaxBackoffMs: number;
570
604
  eventRouterRetryScanIntervalMs: number;
571
- testMode: boolean;
572
- testEventFailFirstN?: string | undefined;
573
- testMintDeferAudience?: string | undefined;
574
- testMintRejectAudience?: string | undefined;
575
- testRerunDelayMs?: number | undefined;
576
- testOmitDashboardRequestTypes?: string | undefined;
577
605
  eventLogMaxPayloadBytes: number;
578
606
  maxGithubPayloadBytes: number;
579
607
  lockFileMaxBytes: number;
@@ -584,7 +612,6 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
584
612
  nodeEnv: "development" | "production" | "test";
585
613
  autoMigrate: boolean;
586
614
  agentMaxReconnectDelayMs: number;
587
- skipS3SentinelValidation: boolean;
588
615
  otelExporterOtlpEndpoint?: string | undefined;
589
616
  clusterName?: string | undefined;
590
617
  cluster: {
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Provider-agnostic LRU cache for source-file contents fetched at a ref.
3
+ *
4
+ * Wraps any {@link FileContentsFetcher} with an in-memory LRU cache keyed by
5
+ * `(provider, repo, sha, path)`. The key is content-addressable by
6
+ * construction: a commit SHA pins immutable bytes, so a cached entry for a
7
+ * `(repo, sha, path)` triple can never go stale within the SHA. When the caller
8
+ * passes a mutable ref (a branch name) instead of a SHA, staleness is bounded by
9
+ * the TTL, exactly as the lock-file cache handles the same case.
10
+ *
11
+ * Mirrors {@link ../lockfile-cache.ts LockFileCache}: same LRU + in-flight
12
+ * coalescing shape so N concurrent misses for one key run a single fetch.
13
+ */
14
+ import type { FileContentsFetcher } from '@kici-dev/engine';
15
+ /**
16
+ * One fetched file's presence + bytes. `present: true` with `bytes: undefined`
17
+ * is the size-limit case (the file exists but the provider returned no inline
18
+ * content, e.g. a GitHub file over 1 MiB) — callers MUST treat it as
19
+ * "content unavailable for matching", never as an absent or empty file.
20
+ */
21
+ export interface FileContentEntry {
22
+ readonly present: boolean;
23
+ readonly bytes?: string;
24
+ }
25
+ export declare class ContentRequirementsCache {
26
+ private readonly cache;
27
+ private readonly inFlight;
28
+ private hits;
29
+ private misses;
30
+ constructor(options: {
31
+ max: number;
32
+ ttl: number;
33
+ maxBytes?: number;
34
+ });
35
+ /**
36
+ * Fetch a file's contents at a ref, using the provided fetcher and LRU
37
+ * caching. Each distinct `(provider, repo, sha, path)` is fetched at most
38
+ * once; concurrent misses coalesce onto one fetch.
39
+ *
40
+ * @param fetcher - Provider-specific file-contents fetcher.
41
+ * @param repo - Provider repo identifier ("owner/repo").
42
+ * @param sha - Git ref (commit SHA, or a branch/tag name).
43
+ * @param path - Repo-relative file path.
44
+ */
45
+ get(fetcher: FileContentsFetcher, repo: string, sha: string, path: string): Promise<FileContentEntry>;
46
+ private fetchAndCache;
47
+ /** Cache statistics for metrics/monitoring. */
48
+ getStats(): {
49
+ hits: number;
50
+ misses: number;
51
+ size: number;
52
+ calculatedSize: number;
53
+ };
54
+ }
55
+ //# sourceMappingURL=content-requirements-cache.d.ts.map