@evoclock/pi-agentic-driver 0.9.1 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.agentic-driver/router.defaults.json +64 -0
  2. package/LICENSE +11 -8
  3. package/README.md +92 -32
  4. package/extensions/attended-authority-guard.ts +1 -1
  5. package/extensions/code-phage.js +1 -1
  6. package/extensions/herdr-communication.ts +1 -1
  7. package/extensions/herdr-dispatch.ts +1 -1
  8. package/extensions/herdr-lifecycle.ts +1 -1
  9. package/extensions/linux-microvm.ts +1 -1
  10. package/extensions/pulse.ts +33 -2
  11. package/extensions/task-board.ts +1 -1
  12. package/lib/adapters/diff-scope.mjs +1 -1
  13. package/lib/adapters/evidence.mjs +1 -1
  14. package/lib/adapters/narrative.mjs +1 -1
  15. package/lib/adapters/review-feedback.mjs +1 -1
  16. package/lib/adapters/visualization.mjs +1 -1
  17. package/lib/code-phage-core.mjs +1 -1
  18. package/lib/python_ast_metrics.py +1 -1
  19. package/lib/typescript_ast_metrics.mjs +1 -1
  20. package/package.json +12 -2
  21. package/scripts/aidr_writing_review.js +1 -1
  22. package/scripts/enforcement/attended_authority_guard.js +1 -1
  23. package/scripts/enforcement/guest_containment_taxonomy.v1.json +1 -1
  24. package/scripts/enforcement/herdr_async_dispatch_pi.js +1 -1
  25. package/scripts/enforcement/herdr_communication_pi.js +13 -8
  26. package/scripts/enforcement/herdr_lifecycle_pi.js +3 -3
  27. package/scripts/enforcement/linux_microvm_cutover_pi.js +1 -1
  28. package/scripts/enforcement/native_tui_context.js +1 -1
  29. package/scripts/enforcement/pulse_core_pi.js +38 -14
  30. package/scripts/enforcement/pulse_scheduler_pi.js +188 -25
  31. package/scripts/enforcement/router_engine_pi.js +483 -0
  32. package/scripts/enforcement/router_ranking_pi.js +146 -0
  33. package/scripts/enforcement/router_replacement_pi.js +98 -0
  34. package/scripts/enforcement/router_runtime_pi.js +299 -0
  35. package/scripts/enforcement/router_schemas_pi.js +604 -0
  36. package/scripts/enforcement/router_store_pi.js +438 -0
  37. package/scripts/enforcement/task_board_core_pi.js +255 -44
  38. package/skills/pulse/SKILL.md +1 -1
@@ -0,0 +1,604 @@
1
+ // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ // Capacity/policy router schemas (ROUTER_DESIGN_TASK68 §2-§4): the closed
5
+ // seat record, the closed router configuration, and the normalized
6
+ // quota-observation record. Every shape is exactKeys-closed, every enum value
7
+ // comes from a closed registry, and every validator fails closed on unknown
8
+ // or missing fields.
9
+ //
10
+ // Pure, side-effect-free: no file I/O beyond what the caller passes in. The
11
+ // config digest is SHA-256 over the canonical JSON form (the same canonical
12
+ // JSON used by the board writer).
13
+
14
+ import { canonicalJsonString, sha256Hex } from "./task_board_core_pi.js";
15
+ import { realpathSync, writeFileSync, renameSync, readFileSync, unlinkSync, lstatSync, openSync, closeSync, constants } from "node:fs";
16
+ import { dirname, relative, resolve } from "node:path";
17
+ import { randomUUID } from "node:crypto";
18
+ export { canonicalJsonString, sha256Hex };
19
+
20
+ export const SEAT_SCHEMA = "agentic-driver.seat.v1";
21
+ export const ROUTER_CONFIG_SCHEMA = "agentic-driver.router-config.v1";
22
+ export const QUOTA_OBSERVATION_SCHEMA = "agentic-driver.quota-observation.v1";
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Closed registries (§2). Model names and placement are CONFIGURATION DATA,
26
+ // never code enums: only the registries below are closed in code, and each
27
+ // is versioned through the config record itself.
28
+ // ---------------------------------------------------------------------------
29
+
30
+ export const SEAT_KINDS = Object.freeze(["local", "hosted-api", "subscription"]);
31
+ export const PROVIDERS = Object.freeze([
32
+ "llama-server", "merge-gateway", "vercel-ai-gateway", "anthropic", "openai",
33
+ ]);
34
+ export const CAPABILITIES = Object.freeze([
35
+ "implement", "review", "final-verification", "design", "research",
36
+ ]);
37
+ export const CONTAINMENT_TIERS = Object.freeze(["testudo", "docker-policy", "none"]);
38
+ export const COST_CLASSES = Object.freeze(["free", "low", "medium", "high"]);
39
+ export const QUOTA_UNITS = Object.freeze(["messages", "tokens", "time", "credits"]);
40
+ export const CONFIDENCE_LEVELS = Object.freeze(["high", "medium", "low"]);
41
+ export const SOURCE_STATUSES = Object.freeze(["ok", "stale", "parse-error", "unknown"]);
42
+ export const EFFORT_LEVELS = Object.freeze(["low", "default", "high"]);
43
+ export const PHASES = Object.freeze(["plan", "implement", "review", "escalate"]);
44
+ export const RESERVE_SCOPES = Object.freeze(["account-window", "account", "pool"]);
45
+ export const COST_POLICIES = Object.freeze(["free-first", "cheapest", "none"]);
46
+ export const RANKING_FALLBACKS = Object.freeze(["preference-order"]);
47
+
48
+ const SEAT_ID_RE = /^[a-z0-9-]+$/;
49
+ const SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
50
+ const ISO_TS_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
51
+ const ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
52
+ const HEX64_RE = /^[0-9a-f]{64}$/;
53
+
54
+ function isPlainObject(value) {
55
+ return value !== null && typeof value === "object" && !Array.isArray(value);
56
+ }
57
+
58
+ function exactKeys(value, keys) {
59
+ const actual = Object.keys(value ?? {}).sort();
60
+ const expected = [...keys].sort();
61
+ return actual.length === expected.length && actual.every((key, i) => key === expected[i]);
62
+ }
63
+
64
+ function isNonNegInt(value) {
65
+ return Number.isInteger(value) && value >= 0;
66
+ }
67
+
68
+ function isPosInt(value) {
69
+ return Number.isInteger(value) && value >= 1;
70
+ }
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // §2 Seat record: agentic-driver.seat.v1
74
+ // ---------------------------------------------------------------------------
75
+
76
+ export function wellFormedSeat(seat, { endpoints = null } = {}) {
77
+ if (!isPlainObject(seat) || !exactKeys(seat, [
78
+ "schema", "seatId", "kind", "provider", "accountId", "endpointRef",
79
+ "model", "capabilities", "containmentTier", "maxConcurrency",
80
+ "costClass", "quotaCollector", "clusterMembership", "enabled", "deprecated",
81
+ ])) {
82
+ return { ok: false, reason: "seat record has unknown or missing fields (exactKeys closed)" };
83
+ }
84
+ if (seat.schema !== SEAT_SCHEMA) return { ok: false, reason: `seat schema must be ${SEAT_SCHEMA}` };
85
+ if (typeof seat.seatId !== "string" || !SEAT_ID_RE.test(seat.seatId) || seat.seatId.length > 64) {
86
+ return { ok: false, reason: `seatId "${seat.seatId}" must match ${SEAT_ID_RE.source} (max 64 chars)` };
87
+ }
88
+ if (!SEAT_KINDS.includes(seat.kind)) {
89
+ return { ok: false, reason: `seat kind "${seat.kind}" is not one of ${SEAT_KINDS.join(", ")}` };
90
+ }
91
+ if (!PROVIDERS.includes(seat.provider)) {
92
+ return { ok: false, reason: `seat provider "${seat.provider}" is not in the closed provider registry` };
93
+ }
94
+ // accountId is REQUIRED iff kind is subscription (§2).
95
+ if (seat.kind === "subscription") {
96
+ if (typeof seat.accountId !== "string" || seat.accountId === "") {
97
+ return { ok: false, reason: `subscription seat "${seat.seatId}" requires a non-empty accountId` };
98
+ }
99
+ } else if (seat.accountId !== null) {
100
+ return { ok: false, reason: `non-subscription seat "${seat.seatId}" must have accountId: null` };
101
+ }
102
+ if (typeof seat.endpointRef !== "string" || !SLUG_RE.test(seat.endpointRef)) {
103
+ return { ok: false, reason: `seat endpointRef "${seat.endpointRef}" must be a named endpoint slug` };
104
+ }
105
+ if (endpoints !== null && !isPlainObject(endpoints?.[seat.endpointRef])) {
106
+ return { ok: false, reason: `seat endpointRef "${seat.endpointRef}" does not resolve in endpoints{}` };
107
+ }
108
+ if (typeof seat.model !== "string" || seat.model === "" || seat.model.length > 128) {
109
+ return { ok: false, reason: `seat model must be a non-empty string (max 128 chars)` };
110
+ }
111
+ if (!Array.isArray(seat.capabilities) || seat.capabilities.length === 0
112
+ || !seat.capabilities.every((c) => CAPABILITIES.includes(c))) {
113
+ return { ok: false, reason: `seat capabilities must be a non-empty subset of the closed capability registry` };
114
+ }
115
+ if (!CONTAINMENT_TIERS.includes(seat.containmentTier)) {
116
+ return { ok: false, reason: `seat containmentTier "${seat.containmentTier}" is not one of ${CONTAINMENT_TIERS.join(", ")}` };
117
+ }
118
+ if (!isPosInt(seat.maxConcurrency) || seat.maxConcurrency > 32) {
119
+ return { ok: false, reason: `seat maxConcurrency must be an integer from 1 through 32` };
120
+ }
121
+ if (!COST_CLASSES.includes(seat.costClass)) {
122
+ return { ok: false, reason: `seat costClass "${seat.costClass}" is not one of ${COST_CLASSES.join(", ")}` };
123
+ }
124
+ if (seat.quotaCollector !== null && (typeof seat.quotaCollector !== "string" || !SLUG_RE.test(seat.quotaCollector))) {
125
+ return { ok: false, reason: `seat quotaCollector must be a collector slug or null` };
126
+ }
127
+ if (seat.clusterMembership !== null) {
128
+ const cm = seat.clusterMembership;
129
+ if (!isPlainObject(cm) || !exactKeys(cm, ["clusterId", "nodes", "spansNodes"])) {
130
+ return { ok: false, reason: `clusterMembership must have exactly {clusterId, nodes, spansNodes}` };
131
+ }
132
+ if (typeof cm.clusterId !== "string" || !SLUG_RE.test(cm.clusterId)) {
133
+ return { ok: false, reason: `clusterMembership.clusterId must be a slug` };
134
+ }
135
+ if (!Array.isArray(cm.nodes) || cm.nodes.length === 0
136
+ || !cm.nodes.every((n) => typeof n === "string" && SLUG_RE.test(n))) {
137
+ return { ok: false, reason: `clusterMembership.nodes must be a non-empty list of endpoint slugs` };
138
+ }
139
+ if (endpoints !== null && !cm.nodes.every((n) => isPlainObject(endpoints?.[n]))) {
140
+ return { ok: false, reason: `clusterMembership node "${cm.nodes.find((n) => !isPlainObject(endpoints?.[n]))}" does not resolve in endpoints{}` };
141
+ }
142
+ if (typeof cm.spansNodes !== "boolean") {
143
+ return { ok: false, reason: `clusterMembership.spansNodes must be a boolean` };
144
+ }
145
+ }
146
+ if (typeof seat.enabled !== "boolean") {
147
+ return { ok: false, reason: `seat enabled must be a boolean` };
148
+ }
149
+ if (seat.deprecated !== null && (typeof seat.deprecated !== "string" || !ISO_DATE_RE.test(seat.deprecated))) {
150
+ return { ok: false, reason: `seat deprecated must be an ISO yyyy-mm-dd date or null` };
151
+ }
152
+ return { ok: true, reason: null };
153
+ }
154
+
155
+ // ---------------------------------------------------------------------------
156
+ // §3 Router configuration: agentic-driver.router-config.v1
157
+ // ---------------------------------------------------------------------------
158
+
159
+ const ENDPOINT_FIELDS = Object.freeze(["url", "kind"]);
160
+ const MODEL_FIELDS = Object.freeze(["aliases", "contextWindow", "capabilities"]);
161
+ const COLLECTOR_FIELDS = Object.freeze(["adapter", "cachePath", "ttlSeconds", "accounts"]);
162
+ const ELIGIBILITY_FIELDS = Object.freeze(["rules", "reserve"]);
163
+ const RESERVE_FIELDS = Object.freeze([
164
+ "floorPercent", "scope", "coldStartFraction", "estimateSamples",
165
+ "estimateMinSamples", "estimateOutlierSigma", "ownerInteractiveOverride",
166
+ ]);
167
+ const PREFERENCE_FIELDS = Object.freeze(["order", "costPolicy"]);
168
+ const PREFERENCE_ORDER_FIELDS = Object.freeze(["seatId"]);
169
+ const RANKING_FIELDS = Object.freeze(["enabled", "janusUrl", "maxCandidates", "fallback"]);
170
+ const LOCAL_HEALTH_FIELDS = Object.freeze(["probeSeconds", "warmStateTracking"]);
171
+ const SECRET_REF_FIELDS = Object.freeze(["keychainService"]);
172
+
173
+ export function computeConfigDigest(config) {
174
+ return sha256Hex(canonicalJsonString(config));
175
+ }
176
+
177
+ // Closed eligibility rule ids (§6.1, ordered). The config may only reorder
178
+ // within this closed list's semantics — the engine evaluates the canonical
179
+ // order regardless; the list exists so a config can disable nothing and
180
+ // rename nothing. An empty list means "all eight rules, in order".
181
+ export const ELIGIBILITY_RULES = Object.freeze([
182
+ "seat-enabled",
183
+ "board-policy-route",
184
+ "capability-match",
185
+ "containment-tier",
186
+ "endpoint-health",
187
+ "model-installed",
188
+ "quota-eligibility",
189
+ "global-capacity",
190
+ ]);
191
+
192
+ export function validateRouterConfig(config) {
193
+ if (!isPlainObject(config)) return { ok: false, errors: ["router config must be an object"] };
194
+ if (!exactKeys(config, [
195
+ "schema", "revision", "endpoints", "seats", "models", "collectors",
196
+ "eligibility", "preferences", "ranking", "localHealth", "secrets",
197
+ ])) {
198
+ return { ok: false, errors: ["router config has unknown or missing fields (exactKeys closed)"] };
199
+ }
200
+ const errors = [];
201
+ if (config.schema !== ROUTER_CONFIG_SCHEMA) {
202
+ errors.push(`config schema must be ${ROUTER_CONFIG_SCHEMA}`);
203
+ }
204
+ if (!isPosInt(config.revision)) {
205
+ errors.push("config revision must be a positive integer");
206
+ }
207
+ // endpoints{}: named, closed-shape endpoint records. URLs are validated
208
+ // structurally (http/https); there is no unrestricted-URL escape hatch.
209
+ if (!isPlainObject(config.endpoints)) {
210
+ errors.push("endpoints must be an object keyed by endpoint slug");
211
+ } else {
212
+ for (const [name, endpoint] of Object.entries(config.endpoints)) {
213
+ if (!SLUG_RE.test(name)) {
214
+ errors.push(`endpoint name "${name}" must be a slug`);
215
+ continue;
216
+ }
217
+ if (!isPlainObject(endpoint) || !exactKeys(endpoint, ENDPOINT_FIELDS)) {
218
+ errors.push(`endpoint "${name}" must have exactly {url, kind}`);
219
+ continue;
220
+ }
221
+ if (typeof endpoint.url !== "string"
222
+ || !/^https?:\/\/[A-Za-z0-9._~:/?#[\]@!$&'()*+,;=%-]+$/.test(endpoint.url)) {
223
+ errors.push(`endpoint "${name}" url must be an http(s) URL`);
224
+ }
225
+ if (!PROVIDERS.includes(endpoint.kind)) {
226
+ errors.push(`endpoint "${name}" kind "${endpoint.kind}" is not in the closed provider registry`);
227
+ }
228
+ }
229
+ }
230
+ // seats[]: unique seatIds, well-formed records, resolvable endpointRefs.
231
+ if (!Array.isArray(config.seats) || config.seats.length === 0) {
232
+ errors.push("seats must be a non-empty list");
233
+ } else {
234
+ const seen = new Set();
235
+ for (const seat of config.seats) {
236
+ const check = wellFormedSeat(seat, { endpoints: config.endpoints });
237
+ if (!check.ok) {
238
+ errors.push(`seat ${seat?.seatId ?? "(unidentified)"}: ${check.reason}`);
239
+ continue;
240
+ }
241
+ if (seen.has(seat.seatId)) errors.push(`duplicate seatId "${seat.seatId}"`);
242
+ seen.add(seat.seatId);
243
+ if (!isPlainObject(config.models?.[seat.model])) {
244
+ errors.push(`seat "${seat.seatId}" model "${seat.model}" does not resolve in models{}`);
245
+ }
246
+ if (config.endpoints?.[seat.endpointRef]?.kind !== seat.provider) {
247
+ errors.push(`seat "${seat.seatId}" provider does not match endpoint kind`);
248
+ }
249
+ if (seat.quotaCollector !== null && !isPlainObject(config.collectors?.[seat.quotaCollector])) {
250
+ errors.push(`seat "${seat.seatId}" quotaCollector "${seat.quotaCollector}" does not resolve in collectors{}`);
251
+ }
252
+ }
253
+ }
254
+ // models{}: closed-shape model records.
255
+ if (!isPlainObject(config.models)) {
256
+ errors.push("models must be an object keyed by model id");
257
+ } else {
258
+ for (const [id, model] of Object.entries(config.models)) {
259
+ if (typeof id !== "string" || id === "" || id.length > 128) {
260
+ errors.push("model ids must be non-empty strings (max 128 chars)");
261
+ continue;
262
+ }
263
+ if (!isPlainObject(model) || !exactKeys(model, MODEL_FIELDS)) {
264
+ errors.push(`model "${id}" must have exactly {aliases, contextWindow, capabilities}`);
265
+ continue;
266
+ }
267
+ if (!Array.isArray(model.aliases) || !model.aliases.every((a) => typeof a === "string" && a !== "")) {
268
+ errors.push(`model "${id}" aliases must be a list of strings`);
269
+ }
270
+ if (!isPosInt(model.contextWindow)) {
271
+ errors.push(`model "${id}" contextWindow must be a positive integer`);
272
+ }
273
+ if (!Array.isArray(model.capabilities) || !model.capabilities.every((c) => CAPABILITIES.includes(c))) {
274
+ errors.push(`model "${id}" capabilities must be a subset of the closed capability registry`);
275
+ }
276
+ }
277
+ }
278
+ // collectors{}: adapter id, contained cache path, positive TTL, resolvable
279
+ // account slugs.
280
+ if (!isPlainObject(config.collectors)) {
281
+ errors.push("collectors must be an object keyed by collector id");
282
+ } else {
283
+ for (const [id, collector] of Object.entries(config.collectors)) {
284
+ if (!SLUG_RE.test(id)) {
285
+ errors.push(`collector id "${id}" must be a slug`);
286
+ continue;
287
+ }
288
+ if (!isPlainObject(collector) || !exactKeys(collector, COLLECTOR_FIELDS)) {
289
+ errors.push(`collector "${id}" must have exactly {adapter, cachePath, ttlSeconds, accounts}`);
290
+ continue;
291
+ }
292
+ if (typeof collector.adapter !== "string" || !SLUG_RE.test(collector.adapter)) {
293
+ errors.push(`collector "${id}" adapter must be a slug`);
294
+ }
295
+ if (typeof collector.cachePath !== "string" || collector.cachePath === ""
296
+ || collector.cachePath.includes("..")) {
297
+ errors.push(`collector "${id}" cachePath must be a non-empty path without traversal`);
298
+ }
299
+ if (!isPosInt(collector.ttlSeconds)) {
300
+ errors.push(`collector "${id}" ttlSeconds must be a positive integer`);
301
+ }
302
+ if (!Array.isArray(collector.accounts) || collector.accounts.length === 0
303
+ || !collector.accounts.every((a) => typeof a === "string" && SLUG_RE.test(a))) {
304
+ errors.push(`collector "${id}" accounts must be a non-empty list of account slugs`);
305
+ } else {
306
+ const expected = new Set((config.seats ?? []).filter((s) => s?.quotaCollector === id && s?.kind === "subscription").map((s) => s.accountId));
307
+ const actual = new Set(collector.accounts);
308
+ if (expected.size !== actual.size || [...expected].some((a) => !actual.has(a))) {
309
+ errors.push(`collector "${id}" accounts must exactly match its subscription seats`);
310
+ }
311
+ }
312
+ }
313
+ }
314
+ // eligibility{}: closed reserve parameters; rules is a closed list (empty
315
+ // = all eight, in canonical order).
316
+ if (!isPlainObject(config.eligibility) || !exactKeys(config.eligibility, ELIGIBILITY_FIELDS)) {
317
+ errors.push("eligibility must have exactly {rules, reserve}");
318
+ } else {
319
+ if (!Array.isArray(config.eligibility.rules)
320
+ || !config.eligibility.rules.every((r) => ELIGIBILITY_RULES.includes(r))) {
321
+ errors.push(`eligibility.rules must be a subset of the closed rule registry (${ELIGIBILITY_RULES.join(", ")})`);
322
+ }
323
+ const reserve = config.eligibility.reserve;
324
+ if (!isPlainObject(reserve) || !exactKeys(reserve, RESERVE_FIELDS)) {
325
+ errors.push("eligibility.reserve must have exactly {floorPercent, scope, coldStartFraction, estimateSamples, estimateMinSamples, estimateOutlierSigma, ownerInteractiveOverride}");
326
+ } else {
327
+ if (!Number.isFinite(reserve.floorPercent) || reserve.floorPercent < 0 || reserve.floorPercent > 100) {
328
+ errors.push("eligibility.reserve.floorPercent must be a number from 0 through 100");
329
+ }
330
+ if (!RESERVE_SCOPES.includes(reserve.scope)) {
331
+ errors.push(`eligibility.reserve.scope must be one of ${RESERVE_SCOPES.join(", ")}`);
332
+ }
333
+ if (!Number.isFinite(reserve.coldStartFraction) || reserve.coldStartFraction <= 0 || reserve.coldStartFraction > 1) {
334
+ errors.push("eligibility.reserve.coldStartFraction must be a number from >0 through 1");
335
+ }
336
+ if (!isPosInt(reserve.estimateSamples) || reserve.estimateSamples > 1000) {
337
+ errors.push("eligibility.reserve.estimateSamples must be a positive integer (max 1000)");
338
+ }
339
+ if (!isPosInt(reserve.estimateMinSamples) || reserve.estimateMinSamples > reserve.estimateSamples) {
340
+ errors.push("eligibility.reserve.estimateMinSamples must be a positive integer <= estimateSamples");
341
+ }
342
+ if (!Number.isFinite(reserve.estimateOutlierSigma) || reserve.estimateOutlierSigma <= 0) {
343
+ errors.push("eligibility.reserve.estimateOutlierSigma must be a positive number");
344
+ }
345
+ if (typeof reserve.ownerInteractiveOverride !== "boolean") {
346
+ errors.push("eligibility.reserve.ownerInteractiveOverride must be a boolean");
347
+ }
348
+ }
349
+ }
350
+ // preferences{}: resolvable seatIds in a deterministic order.
351
+ if (!isPlainObject(config.preferences) || !exactKeys(config.preferences, PREFERENCE_FIELDS)) {
352
+ errors.push("preferences must have exactly {order, costPolicy}");
353
+ } else {
354
+ if (!Array.isArray(config.preferences.order)) {
355
+ errors.push("preferences.order must be a list");
356
+ } else {
357
+ const seatIds = new Set((Array.isArray(config.seats) ? config.seats : []).map((s) => s?.seatId));
358
+ const seenOrder = new Set();
359
+ for (const entry of config.preferences.order) {
360
+ if (!isPlainObject(entry) || !exactKeys(entry, PREFERENCE_ORDER_FIELDS)) {
361
+ errors.push("preferences.order entries must have exactly {seatId}");
362
+ continue;
363
+ }
364
+ if (!seatIds.has(entry.seatId)) {
365
+ errors.push(`preferences.order seatId "${entry.seatId}" does not resolve in seats{}`);
366
+ }
367
+ if (seenOrder.has(entry.seatId)) {
368
+ errors.push(`preferences.order seatId "${entry.seatId}" appears more than once`);
369
+ }
370
+ seenOrder.add(entry.seatId);
371
+ }
372
+ }
373
+ if (!COST_POLICIES.includes(config.preferences.costPolicy)) {
374
+ errors.push(`preferences.costPolicy must be one of ${COST_POLICIES.join(", ")}`);
375
+ }
376
+ }
377
+ // ranking{}: bounded candidates; fallback is the single supported value.
378
+ if (!isPlainObject(config.ranking) || !exactKeys(config.ranking, RANKING_FIELDS)) {
379
+ errors.push("ranking must have exactly {enabled, janusUrl, maxCandidates, fallback}");
380
+ } else {
381
+ if (typeof config.ranking.enabled !== "boolean") {
382
+ errors.push("ranking.enabled must be a boolean");
383
+ }
384
+ if (typeof config.ranking.janusUrl !== "string"
385
+ || !/^https?:\/\/[A-Za-z0-9._~:/?#[\]@!$&'()*+,;=%-]+$/.test(config.ranking.janusUrl)) {
386
+ errors.push("ranking.janusUrl must be an http(s) URL");
387
+ }
388
+ if (!isPosInt(config.ranking.maxCandidates) || config.ranking.maxCandidates > 8) {
389
+ errors.push("ranking.maxCandidates must be a positive integer (max 8)");
390
+ }
391
+ if (!RANKING_FALLBACKS.includes(config.ranking.fallback)) {
392
+ errors.push(`ranking.fallback must be one of ${RANKING_FALLBACKS.join(", ")}`);
393
+ }
394
+ }
395
+ // localHealth{}: probe interval and warm-state tracking flag.
396
+ if (!isPlainObject(config.localHealth) || !exactKeys(config.localHealth, LOCAL_HEALTH_FIELDS)) {
397
+ errors.push("localHealth must have exactly {probeSeconds, warmStateTracking}");
398
+ } else {
399
+ if (!isPosInt(config.localHealth.probeSeconds)) {
400
+ errors.push("localHealth.probeSeconds must be a positive integer");
401
+ }
402
+ if (typeof config.localHealth.warmStateTracking !== "boolean") {
403
+ errors.push("localHealth.warmStateTracking must be a boolean");
404
+ }
405
+ }
406
+ // secrets{}: by reference only — a keychain service name per provider.
407
+ if (!isPlainObject(config.secrets)) {
408
+ errors.push("secrets must be an object keyed by provider id");
409
+ } else {
410
+ for (const [provider, ref] of Object.entries(config.secrets)) {
411
+ if (!PROVIDERS.includes(provider)) {
412
+ errors.push(`secrets key "${provider}" is not in the closed provider registry`);
413
+ continue;
414
+ }
415
+ if (!isPlainObject(ref) || !exactKeys(ref, SECRET_REF_FIELDS)
416
+ || typeof ref.keychainService !== "string" || ref.keychainService === "") {
417
+ errors.push(`secrets["${provider}"] must have exactly {keychainService} with a non-empty service name`);
418
+ }
419
+ }
420
+ }
421
+ return { ok: errors.length === 0, errors };
422
+ }
423
+
424
+ // Precedence merge (§3): user profile > repository defaults. The profile is
425
+ // a partial config; its present sections replace the defaults' sections
426
+ // wholesale (never deep-merged — ambiguity fails closed by construction).
427
+ // The revision of the merged config is the profile's revision when present,
428
+ // else the defaults' revision. Returns null on any structural failure.
429
+ export function mergeRouterConfig(defaults, profile) {
430
+ if (profile === null || profile === undefined) {
431
+ const check = validateRouterConfig(defaults);
432
+ return check.ok ? { ...defaults } : null;
433
+ }
434
+ if (!isPlainObject(profile)) return null;
435
+ const merged = { ...defaults };
436
+ for (const key of Object.keys(profile)) {
437
+ if (key === "schema") continue; // schema identity is fixed
438
+ if (!["revision", "endpoints", "seats", "models", "collectors", "eligibility",
439
+ "preferences", "ranking", "localHealth", "secrets"].includes(key)) {
440
+ return null; // unknown profile section fails closed
441
+ }
442
+ merged[key] = profile[key];
443
+ }
444
+ const check = validateRouterConfig(merged);
445
+ return check.ok ? merged : null;
446
+ }
447
+
448
+ // ---------------------------------------------------------------------------
449
+ // §4 Quota observation: agentic-driver.quota-observation.v1
450
+ // ---------------------------------------------------------------------------
451
+
452
+ const QUOTA_WINDOW_FIELDS = Object.freeze(["unit", "total", "used", "remaining", "resetAt", "derived"]);
453
+ const CONCURRENCY_FIELDS = Object.freeze(["active", "limit"]);
454
+
455
+ export function wellFormedQuotaObservation(observation) {
456
+ if (!isPlainObject(observation) || !exactKeys(observation, [
457
+ "schema", "collector", "schemaVersion", "seatId", "accountId", "capturedAt",
458
+ "expiresAt", "generation", "quotaWindows", "concurrency", "confidence",
459
+ "sourceStatus", "parseErrors",
460
+ ])) {
461
+ return { ok: false, reason: "quota observation has unknown or missing fields (exactKeys closed)" };
462
+ }
463
+ if (observation.schema !== QUOTA_OBSERVATION_SCHEMA) {
464
+ return { ok: false, reason: `observation schema must be ${QUOTA_OBSERVATION_SCHEMA}` };
465
+ }
466
+ if (typeof observation.collector !== "string" || !SLUG_RE.test(observation.collector)) {
467
+ return { ok: false, reason: "observation collector must be a slug" };
468
+ }
469
+ if (!isPosInt(observation.schemaVersion)) {
470
+ return { ok: false, reason: "observation schemaVersion must be a positive integer" };
471
+ }
472
+ if (typeof observation.seatId !== "string" || !SEAT_ID_RE.test(observation.seatId)) {
473
+ return { ok: false, reason: "observation seatId must be a seat slug" };
474
+ }
475
+ if (typeof observation.accountId !== "string" || observation.accountId === "") {
476
+ return { ok: false, reason: "observation accountId is required (explicit seat→account mapping)" };
477
+ }
478
+ if (!ISO_TS_RE.test(observation.capturedAt) || !ISO_TS_RE.test(observation.expiresAt)) {
479
+ return { ok: false, reason: "observation capturedAt/expiresAt must be ISO timestamps" };
480
+ }
481
+ if (Date.parse(observation.expiresAt) < Date.parse(observation.capturedAt)) {
482
+ return { ok: false, reason: "observation expiresAt must not precede capturedAt" };
483
+ }
484
+ if (!isNonNegInt(observation.generation)) {
485
+ return { ok: false, reason: "observation generation must be a non-negative integer" };
486
+ }
487
+ if (!Array.isArray(observation.quotaWindows)) {
488
+ return { ok: false, reason: "observation quotaWindows must be a list" };
489
+ }
490
+ for (const window of observation.quotaWindows) {
491
+ if (!isPlainObject(window) || !exactKeys(window, QUOTA_WINDOW_FIELDS)) {
492
+ return { ok: false, reason: "quota window must have exactly {unit, total, used, remaining, resetAt, derived}" };
493
+ }
494
+ if (!QUOTA_UNITS.includes(window.unit)) {
495
+ return { ok: false, reason: `quota window unit "${window.unit}" is not one of ${QUOTA_UNITS.join(", ")}` };
496
+ }
497
+ if (!isNonNegInt(window.total) || !isNonNegInt(window.used) || !isNonNegInt(window.remaining)) {
498
+ return { ok: false, reason: "quota window total/used/remaining must be non-negative integers" };
499
+ }
500
+ // §4: total is explicit and never derived — validation requires
501
+ // total >= used + remaining within the unit.
502
+ if (window.total < window.used + window.remaining) {
503
+ return { ok: false, reason: `quota window unit "${window.unit}" violates total >= used + remaining` };
504
+ }
505
+ if (window.resetAt !== null && !ISO_TS_RE.test(window.resetAt)) {
506
+ return { ok: false, reason: "quota window resetAt must be an ISO timestamp or null" };
507
+ }
508
+ if (typeof window.derived !== "boolean") {
509
+ return { ok: false, reason: "quota window derived must be a boolean" };
510
+ }
511
+ }
512
+ const concurrency = observation.concurrency;
513
+ if (concurrency !== null) {
514
+ if (!isPlainObject(concurrency) || !exactKeys(concurrency, CONCURRENCY_FIELDS)
515
+ || !isNonNegInt(concurrency.active) || !isPosInt(concurrency.limit)
516
+ || concurrency.active > concurrency.limit) {
517
+ return { ok: false, reason: "observation concurrency must be {active, limit} with 0 <= active <= limit" };
518
+ }
519
+ }
520
+ if (!CONFIDENCE_LEVELS.includes(observation.confidence)) {
521
+ return { ok: false, reason: `observation confidence must be one of ${CONFIDENCE_LEVELS.join(", ")}` };
522
+ }
523
+ if (!SOURCE_STATUSES.includes(observation.sourceStatus)) {
524
+ return { ok: false, reason: `observation sourceStatus must be one of ${SOURCE_STATUSES.join(", ")}` };
525
+ }
526
+ if (!Array.isArray(observation.parseErrors)
527
+ || !observation.parseErrors.every((e) => typeof e === "string" && e.length <= 512)) {
528
+ return { ok: false, reason: "observation parseErrors must be a list of strings (max 512 chars each)" };
529
+ }
530
+ return { ok: true, reason: null };
531
+ }
532
+
533
+ // §4 freshness: an observation is fresh iff now < expiresAt AND
534
+ // sourceStatus === "ok". Evaluated at read time, never trusted from the file.
535
+ export function isObservationFresh(observation, now) {
536
+ if (!isPlainObject(observation)) return false;
537
+ if (observation.sourceStatus !== "ok") return false;
538
+ const at = typeof now === "string" ? Date.parse(now) : now;
539
+ const expiry = Date.parse(observation?.expiresAt);
540
+ return Number.isFinite(at) && Number.isFinite(expiry) && at < expiry;
541
+ }
542
+
543
+ // §4 failure vs zero: collector failure (stale/parse-error/unknown) is
544
+ // distinct from legitimate zero capacity (ok with remaining: 0). Returns
545
+ // "failure" | "zero" | "available".
546
+ export function observationCapacityState(observation, now) {
547
+ if (!isPlainObject(observation)) return "failure";
548
+ if (observation.sourceStatus !== "ok") return "failure";
549
+ if (!isObservationFresh(observation, now)) return "failure";
550
+ const windows = Array.isArray(observation.quotaWindows) ? observation.quotaWindows : [];
551
+ if (windows.length === 0) return "available";
552
+ const anyZero = windows.every((w) => w.remaining === 0);
553
+ return anyZero ? "zero" : "available";
554
+ }
555
+
556
+ // ---------------------------------------------------------------------------
557
+ // §4 cache-root containment: cache paths resolve under the configured trusted
558
+ // root; traversal fails closed.
559
+ // ---------------------------------------------------------------------------
560
+
561
+ export function cachePathContained(cachePath, trustedRoot) {
562
+ if (typeof cachePath !== "string" || cachePath === "" || typeof trustedRoot !== "string" || trustedRoot === "" || cachePath.includes("\0")) {
563
+ return { ok: false, reason: "cache path and trusted root are required and must not contain null bytes" };
564
+ }
565
+ try {
566
+ const root = realpathSync(trustedRoot);
567
+ // Resolve the parent, not the final file (which may not exist yet). This
568
+ // detects symlinks in every existing path component.
569
+ const parent = realpathSync(dirname(resolve(cachePath)));
570
+ const finalPath = resolve(parent, cachePath.split("/").at(-1));
571
+ const rel = relative(root, finalPath);
572
+ if (rel === "" || rel.startsWith("..") || rel.startsWith("/")) throw new Error("escape");
573
+ try {
574
+ if (lstatSync(finalPath).isSymbolicLink()) throw new Error("final component is a symlink");
575
+ } catch (error) {
576
+ if (error?.code !== "ENOENT") throw error;
577
+ }
578
+ return { ok: true, reason: null, path: finalPath };
579
+ } catch {
580
+ return { ok: false, reason: "cache path does not resolve under the trusted root (fails closed)" };
581
+ }
582
+ }
583
+
584
+ export function writeCollectorCacheAtomic({ cachePath, trustedRoot, value }) {
585
+ const check = cachePathContained(cachePath, trustedRoot);
586
+ if (!check.ok) throw new Error(check.reason);
587
+ const tmp = `${check.path}.tmp-${randomUUID()}`;
588
+ try { writeFileSync(tmp, `${JSON.stringify(value)}\n`, { encoding: "utf8", mode: 0o600 }); renameSync(tmp, check.path); }
589
+ catch (error) { try { unlinkSync(tmp); } catch {} throw error; }
590
+ return check.path;
591
+ }
592
+
593
+ export function readCollectorCache({ cachePath, trustedRoot }) {
594
+ const check = cachePathContained(cachePath, trustedRoot);
595
+ if (!check.ok) return { ok: false, reason: check.reason };
596
+ let fd = null;
597
+ try {
598
+ fd = openSync(check.path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
599
+ const value = JSON.parse(readFileSync(fd, "utf8"));
600
+ const valid = wellFormedQuotaObservation(value);
601
+ return valid.ok ? { ok: true, observation: value } : { ok: false, reason: valid.reason };
602
+ } catch (error) { return { ok: false, reason: String(error?.message || error) }; }
603
+ finally { if (fd !== null) try { closeSync(fd); } catch {} }
604
+ }