@centerforagenticai/pi-multi-account 0.1.1

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 (124) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +29 -0
  3. package/README.md +999 -0
  4. package/config/models/pi-multi-account.v1.json +32 -0
  5. package/config/subscription-plans.v1.json +122 -0
  6. package/package.json +76 -0
  7. package/packages/pi-anthropic-oauth/LICENSE +21 -0
  8. package/packages/pi-anthropic-oauth/package.json +54 -0
  9. package/packages/pi-anthropic-oauth/src/auth.ts +396 -0
  10. package/packages/pi-anthropic-oauth/src/context.ts +116 -0
  11. package/packages/pi-anthropic-oauth/src/convert.ts +303 -0
  12. package/packages/pi-anthropic-oauth/src/index.ts +37 -0
  13. package/packages/pi-anthropic-oauth/src/prompt.ts +137 -0
  14. package/packages/pi-anthropic-oauth/src/stream.ts +476 -0
  15. package/packages/pi-antigravity/LICENSE +21 -0
  16. package/packages/pi-antigravity/package.json +77 -0
  17. package/packages/pi-antigravity/src/auth/index.ts +14 -0
  18. package/packages/pi-antigravity/src/auth/oauth.ts +442 -0
  19. package/packages/pi-antigravity/src/client/client.ts +561 -0
  20. package/packages/pi-antigravity/src/client/index.ts +1 -0
  21. package/packages/pi-antigravity/src/context.ts +110 -0
  22. package/packages/pi-antigravity/src/diagnostics/diagnostics.ts +96 -0
  23. package/packages/pi-antigravity/src/diagnostics/index.ts +1 -0
  24. package/packages/pi-antigravity/src/image/image.ts +336 -0
  25. package/packages/pi-antigravity/src/image/index.ts +1 -0
  26. package/packages/pi-antigravity/src/index.ts +280 -0
  27. package/packages/pi-antigravity/src/models/discovery.ts +154 -0
  28. package/packages/pi-antigravity/src/models/grouping.ts +424 -0
  29. package/packages/pi-antigravity/src/models/index.ts +3 -0
  30. package/packages/pi-antigravity/src/models/models.ts +500 -0
  31. package/packages/pi-antigravity/src/stream/index.ts +1 -0
  32. package/packages/pi-antigravity/src/stream/stream.ts +1478 -0
  33. package/packages/pi-antigravity/src/types/enums.ts +42 -0
  34. package/packages/pi-antigravity/src/types/index.ts +2 -0
  35. package/packages/pi-antigravity/src/types/types.ts +292 -0
  36. package/packages/pi-antigravity/src/usage/index.ts +1 -0
  37. package/packages/pi-antigravity/src/usage/usage.ts +416 -0
  38. package/packages/pi-antigravity/src/utils/http.ts +91 -0
  39. package/packages/pi-antigravity/src/utils/index.ts +3 -0
  40. package/packages/pi-antigravity/src/utils/security.ts +73 -0
  41. package/packages/pi-antigravity/src/utils/util.ts +132 -0
  42. package/scripts/multi-account.mjs +44 -0
  43. package/src/account-labels.ts +223 -0
  44. package/src/account-plan-assignment.ts +340 -0
  45. package/src/account-rate-history.ts +372 -0
  46. package/src/anthropic-adaptive-stream.ts +531 -0
  47. package/src/anthropic-alias-stream.ts +140 -0
  48. package/src/anthropic-context-compat.ts +80 -0
  49. package/src/api-pricing.ts +579 -0
  50. package/src/bounded-file-lines.ts +97 -0
  51. package/src/catalog-rebinding.ts +177 -0
  52. package/src/catalog-registration-probe.ts +111 -0
  53. package/src/codex-adapter.ts +345 -0
  54. package/src/codex-model-defaults.ts +785 -0
  55. package/src/command-completions.ts +404 -0
  56. package/src/commands.ts +2000 -0
  57. package/src/compaction.ts +14 -0
  58. package/src/config.ts +1317 -0
  59. package/src/continuation.ts +569 -0
  60. package/src/cooldowns.ts +110 -0
  61. package/src/cost-digest-store.ts +332 -0
  62. package/src/cost-digest.ts +1044 -0
  63. package/src/cost-history.ts +251 -0
  64. package/src/cost-period-closer.ts +160 -0
  65. package/src/cost-report-json.ts +318 -0
  66. package/src/cost-report-reader.ts +368 -0
  67. package/src/cost-report-render.ts +207 -0
  68. package/src/cost-report.ts +1104 -0
  69. package/src/coverage-attestation.ts +397 -0
  70. package/src/credential-lifecycle.ts +169 -0
  71. package/src/credential-refresh.ts +248 -0
  72. package/src/declaration-notice-marker.ts +238 -0
  73. package/src/diagnostic-store.ts +276 -0
  74. package/src/diagnostics.ts +309 -0
  75. package/src/discovery.ts +471 -0
  76. package/src/duration.ts +13 -0
  77. package/src/error-classification.ts +256 -0
  78. package/src/fuzzy.ts +15 -0
  79. package/src/group-policy.ts +81 -0
  80. package/src/history-store.ts +897 -0
  81. package/src/index.ts +5572 -0
  82. package/src/lifecycle.ts +378 -0
  83. package/src/logical-dispatch.ts +279 -0
  84. package/src/logical-model-selector.ts +254 -0
  85. package/src/logical-model-switcher.ts +430 -0
  86. package/src/logical-provider-attribution.ts +544 -0
  87. package/src/logical-provider.ts +1237 -0
  88. package/src/logical-route-indicator.ts +215 -0
  89. package/src/machine-lease.ts +445 -0
  90. package/src/model-support.ts +66 -0
  91. package/src/models-declaration.ts +1091 -0
  92. package/src/openai-adapter.ts +117 -0
  93. package/src/openrouter-budget.ts +304 -0
  94. package/src/openrouter-fallback.ts +146 -0
  95. package/src/period-boundaries.ts +376 -0
  96. package/src/pi-anthropic-oauth.d.ts +6 -0
  97. package/src/preflight.ts +253 -0
  98. package/src/pricing-cache.ts +235 -0
  99. package/src/project-identity.ts +100 -0
  100. package/src/provider-registration.ts +942 -0
  101. package/src/rate-formula.ts +163 -0
  102. package/src/recovery-engine.ts +853 -0
  103. package/src/recovery-output.ts +837 -0
  104. package/src/recovery-plan.ts +239 -0
  105. package/src/report-range.ts +203 -0
  106. package/src/route-resolver.ts +789 -0
  107. package/src/routing-config-transaction.ts +232 -0
  108. package/src/routing.ts +1163 -0
  109. package/src/runtime-state.ts +630 -0
  110. package/src/session-account-groups.ts +284 -0
  111. package/src/session-restore.ts +287 -0
  112. package/src/shared-usage.ts +1392 -0
  113. package/src/standalone-cli.ts +720 -0
  114. package/src/status-view.ts +578 -0
  115. package/src/subscription-plan-catalog.ts +346 -0
  116. package/src/tier-model-resolver.ts +46 -0
  117. package/src/upstream-anthropic.ts +315 -0
  118. package/src/upstream-antigravity.ts +327 -0
  119. package/src/usage-fetch.ts +1634 -0
  120. package/src/usage.ts +1026 -0
  121. package/src/vendor.ts +87 -0
  122. package/src/warmer.ts +231 -0
  123. package/src/watchdog.ts +219 -0
  124. package/src/window-history.ts +270 -0
@@ -0,0 +1,1091 @@
1
+ /**
2
+ * Closed derivation of the machine-global `unified` model declaration.
3
+ *
4
+ * Complete live physical models enter this pure boundary. Only named safe
5
+ * capabilities leave it. The module touches no disk, credentials, or runtime
6
+ * state, and every mutable descendant in a returned row is an independent copy.
7
+ */
8
+ import { isDeepStrictEqual } from "node:util";
9
+ import type {
10
+ AnthropicMessagesCompat,
11
+ Api,
12
+ Model,
13
+ ModelCost,
14
+ ModelCostTier,
15
+ ModelThinkingLevel,
16
+ OpenAIResponsesCompat,
17
+ ThinkingLevelMap,
18
+ } from "@earendil-works/pi-ai";
19
+ import { GOOGLE_ANTIGRAVITY_API } from "./upstream-antigravity.js";
20
+
21
+ /** Provider id and custom API id. Both are exactly this string. */
22
+ export const LOGICAL_PROVIDER_ID = "unified";
23
+ /** Provider name rendered by Pi for the logical provider. */
24
+ export const LOGICAL_PROVIDER_DISPLAY_NAME = "unified";
25
+ /** Reserved placeholder host. The logical provider never dials it. */
26
+ export const DECLARATION_BASE_URL = "https://unused.invalid";
27
+ /** Fixed composition placeholder. This is not a credential. */
28
+ export const DECLARATION_PLACEHOLDER_KEY = "declaration-local-key";
29
+ /** Managed families in declaration order. */
30
+ export const DECLARED_FAMILIES = [
31
+ "anthropic",
32
+ "openai-codex",
33
+ "google-antigravity",
34
+ ] as const;
35
+
36
+ export type DeclaredFamily = (typeof DECLARED_FAMILIES)[number];
37
+
38
+ export type ModelDeclarationCatalogs = {
39
+ readonly anthropic: readonly Model<"anthropic-messages">[];
40
+ readonly "openai-codex": readonly Model<"openai-codex-responses">[];
41
+ /** Optional until the composition root supplies a live Antigravity catalog. */
42
+ readonly "google-antigravity"?: readonly Model<typeof GOOGLE_ANTIGRAVITY_API>[];
43
+ readonly [family: string]: readonly Model<Api>[] | undefined;
44
+ };
45
+
46
+ type AnthropicLogicalCompat = Omit<
47
+ AnthropicMessagesCompat,
48
+ "allowedFallbackModels"
49
+ >;
50
+ type LogicalCompat = AnthropicLogicalCompat | OpenAIResponsesCompat;
51
+
52
+ export interface ModelDeclarationRow {
53
+ id: string;
54
+ name: string;
55
+ api: typeof LOGICAL_PROVIDER_ID;
56
+ baseUrl: typeof DECLARATION_BASE_URL;
57
+ reasoning: boolean;
58
+ thinkingLevelMap?: ThinkingLevelMap;
59
+ input: ("text" | "image")[];
60
+ cost: ModelCost;
61
+ contextWindow: number;
62
+ maxTokens: number;
63
+ samplingParams?: Partial<Record<SamplingParamKey, number>>;
64
+ compat?: LogicalCompat;
65
+ }
66
+
67
+ export interface BuiltDeclaration {
68
+ models: ModelDeclarationRow[];
69
+ diagnostics: string[];
70
+ }
71
+
72
+ export interface ModelDeclarationBuildOptions {
73
+ /** Applied only after a Codex catalog row has passed closed projection. */
74
+ readonly codexContextWindowByModelId?: ReadonlyMap<string, number>;
75
+ }
76
+
77
+ export type ManagedLogicalModelSource =
78
+ | {
79
+ readonly family: "anthropic";
80
+ readonly model: Model<"anthropic-messages">;
81
+ }
82
+ | {
83
+ readonly family: "openai-codex";
84
+ readonly model: Model<"openai-codex-responses">;
85
+ }
86
+ | {
87
+ readonly family: "google-antigravity";
88
+ readonly model: Model<typeof GOOGLE_ANTIGRAVITY_API>;
89
+ };
90
+
91
+ const MODEL_FIELD_DISPOSITION = {
92
+ id: "copy",
93
+ name: "copy",
94
+ api: "override",
95
+ provider: "exclude",
96
+ baseUrl: "override",
97
+ reasoning: "copy",
98
+ thinkingLevelMap: "copy",
99
+ input: "copy",
100
+ cost: "copy",
101
+ contextWindow: "copy",
102
+ maxTokens: "copy",
103
+ samplingParams: "copy",
104
+ headers: "exclude",
105
+ compat: "copy",
106
+ } as const satisfies Record<
107
+ keyof Model<Api>,
108
+ "copy" | "exclude" | "override"
109
+ >;
110
+
111
+ const ANTHROPIC_COMPAT_DISPOSITION = {
112
+ supportsEagerToolInputStreaming: "copy",
113
+ supportsLongCacheRetention: "copy",
114
+ sendSessionAffinityHeaders: "copy",
115
+ supportsCacheControlOnTools: "copy",
116
+ supportsTemperature: "copy",
117
+ forceAdaptiveThinking: "copy",
118
+ allowEmptySignature: "copy",
119
+ supportsStrictTools: "copy",
120
+ allowedFallbackModels: "exclude",
121
+ supportsToolReferences: "copy",
122
+ } as const satisfies Record<
123
+ keyof AnthropicMessagesCompat,
124
+ "copy" | "exclude"
125
+ >;
126
+
127
+ const CODEX_COMPAT_DISPOSITION = {
128
+ supportsDeveloperRole: "copy",
129
+ sessionAffinityFormat: "copy",
130
+ supportsLongCacheRetention: "copy",
131
+ supportsStrictMode: "copy",
132
+ supportsOpenAIGrammarTools: "copy",
133
+ supportsAdditionalTools: "copy",
134
+ supportsToolSearch: "copy",
135
+ supportsExplicitPromptCacheMode: "copy",
136
+ } as const satisfies Record<keyof OpenAIResponsesCompat, "copy">;
137
+
138
+ const THINKING_LEVEL_DISPOSITION = {
139
+ off: "copy",
140
+ minimal: "copy",
141
+ low: "copy",
142
+ medium: "copy",
143
+ high: "copy",
144
+ xhigh: "copy",
145
+ max: "copy",
146
+ } as const satisfies Record<ModelThinkingLevel, "copy">;
147
+
148
+ const COST_DISPOSITION = {
149
+ input: "copy",
150
+ output: "copy",
151
+ cacheRead: "copy",
152
+ cacheWrite: "copy",
153
+ tiers: "copy",
154
+ } as const satisfies Record<keyof ModelCost, "copy">;
155
+
156
+ const COST_TIER_DISPOSITION = {
157
+ input: "copy",
158
+ output: "copy",
159
+ cacheRead: "copy",
160
+ cacheWrite: "copy",
161
+ inputTokensAbove: "copy",
162
+ } as const satisfies Record<keyof ModelCostTier, "copy">;
163
+
164
+ const SAMPLING_PARAM_KEYS = [
165
+ "top_p",
166
+ "top_k",
167
+ "min_p",
168
+ "repetition_penalty",
169
+ ] as const;
170
+ type SamplingParamKey = (typeof SAMPLING_PARAM_KEYS)[number];
171
+ const SAMPLING_PARAM_KEY_SET = new Set<string>(SAMPLING_PARAM_KEYS);
172
+ const SESSION_AFFINITY_FORMATS = new Set<string>([
173
+ "openai",
174
+ "openai-nosession",
175
+ "openrouter",
176
+ ]);
177
+ const MAX_AMBIGUITY_DIAGNOSTICS = 20;
178
+
179
+ export class ManagedModelProjectionError extends Error {
180
+ readonly family: DeclaredFamily;
181
+ readonly fieldPath: string;
182
+
183
+ constructor(family: DeclaredFamily, fieldPath: string) {
184
+ super(`The ${family} managed model has invalid capability data at ${fieldPath}.`);
185
+ this.name = "ManagedModelProjectionError";
186
+ this.family = family;
187
+ this.fieldPath = fieldPath;
188
+ }
189
+ }
190
+
191
+ function rejectProjection(
192
+ family: DeclaredFamily,
193
+ fieldPath: string,
194
+ ): never {
195
+ throw new ManagedModelProjectionError(family, fieldPath);
196
+ }
197
+
198
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
199
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
200
+ return false;
201
+ }
202
+ const prototype: unknown = (() => {
203
+ try {
204
+ return Object.getPrototypeOf(value);
205
+ } catch {
206
+ return undefined;
207
+ }
208
+ })();
209
+ return prototype === Object.prototype || prototype === null;
210
+ }
211
+
212
+ function isNonEmptyString(value: unknown): value is string {
213
+ return typeof value === "string" && value.length > 0;
214
+ }
215
+
216
+ function isRequiredReasoning(value: unknown): value is boolean {
217
+ return typeof value === "boolean";
218
+ }
219
+
220
+ function isOptionalCompatBoolean(value: unknown): value is boolean {
221
+ return typeof value === "boolean";
222
+ }
223
+
224
+ function isRequiredFiniteCostRate(value: unknown): value is number {
225
+ return typeof value === "number" && Number.isFinite(value);
226
+ }
227
+
228
+ function isRequiredFiniteTierMember(value: unknown): value is number {
229
+ return typeof value === "number" && Number.isFinite(value);
230
+ }
231
+
232
+ function isPositiveFiniteLimit(value: unknown): value is number {
233
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
234
+ }
235
+
236
+ function isFiniteSamplingValue(value: unknown): value is number {
237
+ return typeof value === "number" && Number.isFinite(value);
238
+ }
239
+
240
+
241
+ type CapturedOwnDataProperties = ReadonlyMap<string, PropertyDescriptor>;
242
+
243
+ function requiredCapturedValue(
244
+ properties: CapturedOwnDataProperties,
245
+ key: string,
246
+ family: DeclaredFamily,
247
+ fieldPath: string,
248
+ ): unknown {
249
+ const descriptor = properties.get(key);
250
+ if (descriptor === undefined) return rejectProjection(family, fieldPath);
251
+ return descriptor.value;
252
+ }
253
+
254
+ function optionalCapturedValue(
255
+ properties: CapturedOwnDataProperties,
256
+ key: string,
257
+ ): unknown {
258
+ return properties.get(key)?.value;
259
+ }
260
+
261
+ function projectRequiredString(
262
+ value: unknown,
263
+ family: DeclaredFamily,
264
+ fieldPath: string,
265
+ ): string {
266
+ if (!isNonEmptyString(value)) return rejectProjection(family, fieldPath);
267
+ return value;
268
+ }
269
+
270
+ function projectRequiredBoolean(
271
+ value: unknown,
272
+ family: DeclaredFamily,
273
+ fieldPath: string,
274
+ ): boolean {
275
+ if (!isRequiredReasoning(value)) return rejectProjection(family, fieldPath);
276
+ return value;
277
+ }
278
+
279
+ function exactCapturedKeys(
280
+ properties: CapturedOwnDataProperties,
281
+ allowed: ReadonlySet<string>,
282
+ family: DeclaredFamily,
283
+ fieldPath: string,
284
+ ): void {
285
+ for (const key of properties.keys()) {
286
+ if (!allowed.has(key)) return rejectProjection(family, `${fieldPath}.${key}`);
287
+ }
288
+ }
289
+
290
+ function optionalThinkingLevelValue(
291
+ properties: CapturedOwnDataProperties,
292
+ key: ModelThinkingLevel,
293
+ family: DeclaredFamily,
294
+ ): string | null | undefined {
295
+ if (!properties.has(key)) return undefined;
296
+ const member = requiredCapturedValue(properties, key, family, `thinkingLevelMap.${key}`);
297
+ if (member !== null && typeof member !== "string") {
298
+ return rejectProjection(family, `thinkingLevelMap.${key}`);
299
+ }
300
+ return member;
301
+ }
302
+
303
+ function projectThinkingLevelMap(
304
+ value: unknown,
305
+ family: DeclaredFamily,
306
+ ): ThinkingLevelMap | undefined {
307
+ if (value === undefined) return undefined;
308
+ if (!isPlainObject(value)) return rejectProjection(family, "thinkingLevelMap");
309
+ const properties = assertOwnDataProperties(value, family, "thinkingLevelMap");
310
+ exactCapturedKeys(
311
+ properties,
312
+ new Set(Object.keys(THINKING_LEVEL_DISPOSITION)),
313
+ family,
314
+ "thinkingLevelMap",
315
+ );
316
+ const projected: ThinkingLevelMap = {};
317
+ const off = optionalThinkingLevelValue(properties, "off", family);
318
+ if (off !== undefined) projected.off = off;
319
+ const minimal = optionalThinkingLevelValue(properties, "minimal", family);
320
+ if (minimal !== undefined) projected.minimal = minimal;
321
+ const low = optionalThinkingLevelValue(properties, "low", family);
322
+ if (low !== undefined) projected.low = low;
323
+ const medium = optionalThinkingLevelValue(properties, "medium", family);
324
+ if (medium !== undefined) projected.medium = medium;
325
+ const high = optionalThinkingLevelValue(properties, "high", family);
326
+ if (high !== undefined) projected.high = high;
327
+ const xhigh = optionalThinkingLevelValue(properties, "xhigh", family);
328
+ if (xhigh !== undefined) projected.xhigh = xhigh;
329
+ const max = optionalThinkingLevelValue(properties, "max", family);
330
+ if (max !== undefined) projected.max = max;
331
+ return projected;
332
+ }
333
+
334
+ function capturedArrayLength(
335
+ properties: CapturedOwnDataProperties,
336
+ family: DeclaredFamily,
337
+ fieldPath: string,
338
+ ): number {
339
+ const length = requiredCapturedValue(properties, "length", family, fieldPath);
340
+ if (!Number.isSafeInteger(length) || typeof length !== "number" || length < 0) {
341
+ return rejectProjection(family, fieldPath);
342
+ }
343
+ return length;
344
+ }
345
+
346
+ function projectInput(value: unknown, family: DeclaredFamily): ("text" | "image")[] {
347
+ if (!Array.isArray(value)) return rejectProjection(family, "input");
348
+ const properties = assertOwnDataProperties(value, family, "input");
349
+ const length = capturedArrayLength(properties, family, "input");
350
+ const projected: ("text" | "image")[] = [];
351
+ for (let index = 0; index < length; index += 1) {
352
+ const member = requiredCapturedValue(properties, String(index), family, "input");
353
+ if (member !== "text" && member !== "image") {
354
+ return rejectProjection(family, "input");
355
+ }
356
+ projected.push(member);
357
+ }
358
+ return projected;
359
+ }
360
+
361
+ function projectRequiredInput(
362
+ properties: CapturedOwnDataProperties,
363
+ family: DeclaredFamily,
364
+ ): ("text" | "image")[] {
365
+ return projectInput(requiredCapturedValue(properties, "input", family, "input"), family);
366
+ }
367
+
368
+ function projectCostTiers(
369
+ value: unknown,
370
+ family: DeclaredFamily,
371
+ ): ModelCostTier[] {
372
+ if (!Array.isArray(value)) return rejectProjection(family, "cost.tiers");
373
+ const arrayProperties = assertOwnDataProperties(value, family, "cost.tiers");
374
+ const length = capturedArrayLength(arrayProperties, family, "cost.tiers");
375
+ const projected: ModelCostTier[] = [];
376
+ for (let index = 0; index < length; index += 1) {
377
+ const entry = requiredCapturedValue(
378
+ arrayProperties,
379
+ String(index),
380
+ family,
381
+ "cost.tiers",
382
+ );
383
+ if (!isPlainObject(entry)) return rejectProjection(family, "cost.tiers");
384
+ const properties = assertOwnDataProperties(entry, family, "cost.tiers", [
385
+ "input",
386
+ "output",
387
+ "cacheRead",
388
+ "cacheWrite",
389
+ "inputTokensAbove",
390
+ ]);
391
+ exactCapturedKeys(
392
+ properties,
393
+ new Set(Object.keys(COST_TIER_DISPOSITION)),
394
+ family,
395
+ "cost.tiers",
396
+ );
397
+ const input = requiredCapturedValue(properties, "input", family, "cost.tiers.input");
398
+ const output = requiredCapturedValue(properties, "output", family, "cost.tiers.output");
399
+ const cacheRead = requiredCapturedValue(
400
+ properties,
401
+ "cacheRead",
402
+ family,
403
+ "cost.tiers.cacheRead",
404
+ );
405
+ const cacheWrite = requiredCapturedValue(
406
+ properties,
407
+ "cacheWrite",
408
+ family,
409
+ "cost.tiers.cacheWrite",
410
+ );
411
+ const inputTokensAbove = requiredCapturedValue(
412
+ properties,
413
+ "inputTokensAbove",
414
+ family,
415
+ "cost.tiers.inputTokensAbove",
416
+ );
417
+ if (!isRequiredFiniteTierMember(input)) {
418
+ return rejectProjection(family, "cost.tiers.input");
419
+ }
420
+ if (!isRequiredFiniteTierMember(output)) {
421
+ return rejectProjection(family, "cost.tiers.output");
422
+ }
423
+ if (!isRequiredFiniteTierMember(cacheRead)) {
424
+ return rejectProjection(family, "cost.tiers.cacheRead");
425
+ }
426
+ if (!isRequiredFiniteTierMember(cacheWrite)) {
427
+ return rejectProjection(family, "cost.tiers.cacheWrite");
428
+ }
429
+ if (!isRequiredFiniteTierMember(inputTokensAbove)) {
430
+ return rejectProjection(family, "cost.tiers.inputTokensAbove");
431
+ }
432
+ projected.push({ input, output, cacheRead, cacheWrite, inputTokensAbove });
433
+ }
434
+ return projected;
435
+ }
436
+
437
+ function projectCost(value: unknown, family: DeclaredFamily): ModelCost {
438
+ if (!isPlainObject(value)) return rejectProjection(family, "cost");
439
+ const properties = assertOwnDataProperties(value, family, "cost", [
440
+ "input",
441
+ "output",
442
+ "cacheRead",
443
+ "cacheWrite",
444
+ ]);
445
+ exactCapturedKeys(
446
+ properties,
447
+ new Set(Object.keys(COST_DISPOSITION)),
448
+ family,
449
+ "cost",
450
+ );
451
+ const input = requiredCapturedValue(properties, "input", family, "cost.input");
452
+ const output = requiredCapturedValue(properties, "output", family, "cost.output");
453
+ const cacheRead = requiredCapturedValue(properties, "cacheRead", family, "cost.cacheRead");
454
+ const cacheWrite = requiredCapturedValue(
455
+ properties,
456
+ "cacheWrite",
457
+ family,
458
+ "cost.cacheWrite",
459
+ );
460
+ if (!isRequiredFiniteCostRate(input)) return rejectProjection(family, "cost.input");
461
+ if (!isRequiredFiniteCostRate(output)) return rejectProjection(family, "cost.output");
462
+ if (!isRequiredFiniteCostRate(cacheRead)) return rejectProjection(family, "cost.cacheRead");
463
+ if (!isRequiredFiniteCostRate(cacheWrite)) return rejectProjection(family, "cost.cacheWrite");
464
+ const projected: ModelCost = { input, output, cacheRead, cacheWrite };
465
+ if (properties.has("tiers")) {
466
+ const tiers = optionalCapturedValue(properties, "tiers");
467
+ if (tiers !== undefined) projected.tiers = projectCostTiers(tiers, family);
468
+ }
469
+ return projected;
470
+ }
471
+
472
+ function projectRequiredCost(
473
+ properties: CapturedOwnDataProperties,
474
+ family: DeclaredFamily,
475
+ ): ModelCost {
476
+ return projectCost(requiredCapturedValue(properties, "cost", family, "cost"), family);
477
+ }
478
+
479
+ function projectPositiveLimit(
480
+ value: unknown,
481
+ family: DeclaredFamily,
482
+ fieldPath: "contextWindow" | "maxTokens",
483
+ ): number {
484
+ if (!isPositiveFiniteLimit(value)) return rejectProjection(family, fieldPath);
485
+ return value;
486
+ }
487
+
488
+ function projectSamplingParams(
489
+ value: unknown,
490
+ family: DeclaredFamily,
491
+ ): Partial<Record<SamplingParamKey, number>> | undefined {
492
+ if (value === undefined) return undefined;
493
+ if (!isPlainObject(value)) return rejectProjection(family, "samplingParams");
494
+ const properties = assertOwnDataProperties(value, family, "samplingParams");
495
+ exactCapturedKeys(properties, SAMPLING_PARAM_KEY_SET, family, "samplingParams");
496
+ const projected: Partial<Record<SamplingParamKey, number>> = {};
497
+ for (const key of properties.keys()) {
498
+ const member = requiredCapturedValue(properties, key, family, `samplingParams.${key}`);
499
+ if (!isFiniteSamplingValue(member)) {
500
+ return rejectProjection(family, `samplingParams.${key}`);
501
+ }
502
+ if (key === "top_p") projected.top_p = member;
503
+ else if (key === "top_k") projected.top_k = member;
504
+ else if (key === "min_p") projected.min_p = member;
505
+ else if (key === "repetition_penalty") projected.repetition_penalty = member;
506
+ }
507
+ return projected;
508
+ }
509
+
510
+ function projectOptionalCompatBoolean(
511
+ value: unknown,
512
+ family: DeclaredFamily,
513
+ fieldPath: string,
514
+ ): boolean {
515
+ if (!isOptionalCompatBoolean(value)) return rejectProjection(family, fieldPath);
516
+ return value;
517
+ }
518
+
519
+ function optionalCompatBoolean(
520
+ properties: CapturedOwnDataProperties,
521
+ key: string,
522
+ family: DeclaredFamily,
523
+ ): boolean | undefined {
524
+ if (!properties.has(key)) return undefined;
525
+ return projectOptionalCompatBoolean(
526
+ requiredCapturedValue(properties, key, family, `compat.${key}`),
527
+ family,
528
+ `compat.${key}`,
529
+ );
530
+ }
531
+
532
+ function projectAnthropicCompat(
533
+ properties: CapturedOwnDataProperties,
534
+ ): AnthropicLogicalCompat {
535
+ const projected: AnthropicLogicalCompat = {};
536
+ const eager = optionalCompatBoolean(properties, "supportsEagerToolInputStreaming", "anthropic");
537
+ if (eager !== undefined) projected.supportsEagerToolInputStreaming = eager;
538
+ const longCache = optionalCompatBoolean(properties, "supportsLongCacheRetention", "anthropic");
539
+ if (longCache !== undefined) projected.supportsLongCacheRetention = longCache;
540
+ const affinity = optionalCompatBoolean(properties, "sendSessionAffinityHeaders", "anthropic");
541
+ if (affinity !== undefined) projected.sendSessionAffinityHeaders = affinity;
542
+ const toolCache = optionalCompatBoolean(properties, "supportsCacheControlOnTools", "anthropic");
543
+ if (toolCache !== undefined) projected.supportsCacheControlOnTools = toolCache;
544
+ const temperature = optionalCompatBoolean(properties, "supportsTemperature", "anthropic");
545
+ if (temperature !== undefined) projected.supportsTemperature = temperature;
546
+ const adaptive = optionalCompatBoolean(properties, "forceAdaptiveThinking", "anthropic");
547
+ if (adaptive !== undefined) projected.forceAdaptiveThinking = adaptive;
548
+ const emptySignature = optionalCompatBoolean(properties, "allowEmptySignature", "anthropic");
549
+ if (emptySignature !== undefined) projected.allowEmptySignature = emptySignature;
550
+ const strictTools = optionalCompatBoolean(properties, "supportsStrictTools", "anthropic");
551
+ if (strictTools !== undefined) projected.supportsStrictTools = strictTools;
552
+ const toolReferences = optionalCompatBoolean(properties, "supportsToolReferences", "anthropic");
553
+ if (toolReferences !== undefined) projected.supportsToolReferences = toolReferences;
554
+ return projected;
555
+ }
556
+
557
+ function isSessionAffinityFormat(
558
+ value: unknown,
559
+ ): value is NonNullable<OpenAIResponsesCompat["sessionAffinityFormat"]> {
560
+ return typeof value === "string" && SESSION_AFFINITY_FORMATS.has(value);
561
+ }
562
+
563
+ function projectCodexCompat(
564
+ properties: CapturedOwnDataProperties,
565
+ ): OpenAIResponsesCompat {
566
+ const projected: OpenAIResponsesCompat = {};
567
+ const developerRole = optionalCompatBoolean(properties, "supportsDeveloperRole", "openai-codex");
568
+ if (developerRole !== undefined) projected.supportsDeveloperRole = developerRole;
569
+ if (properties.has("sessionAffinityFormat")) {
570
+ const affinity = requiredCapturedValue(
571
+ properties,
572
+ "sessionAffinityFormat",
573
+ "openai-codex",
574
+ "compat.sessionAffinityFormat",
575
+ );
576
+ if (!isSessionAffinityFormat(affinity)) {
577
+ return rejectProjection("openai-codex", "compat.sessionAffinityFormat");
578
+ }
579
+ projected.sessionAffinityFormat = affinity;
580
+ }
581
+ const longCache = optionalCompatBoolean(properties, "supportsLongCacheRetention", "openai-codex");
582
+ if (longCache !== undefined) projected.supportsLongCacheRetention = longCache;
583
+ const strictMode = optionalCompatBoolean(properties, "supportsStrictMode", "openai-codex");
584
+ if (strictMode !== undefined) projected.supportsStrictMode = strictMode;
585
+ const grammarTools = optionalCompatBoolean(properties, "supportsOpenAIGrammarTools", "openai-codex");
586
+ if (grammarTools !== undefined) projected.supportsOpenAIGrammarTools = grammarTools;
587
+ const additionalTools = optionalCompatBoolean(properties, "supportsAdditionalTools", "openai-codex");
588
+ if (additionalTools !== undefined) projected.supportsAdditionalTools = additionalTools;
589
+ const toolSearch = optionalCompatBoolean(properties, "supportsToolSearch", "openai-codex");
590
+ if (toolSearch !== undefined) projected.supportsToolSearch = toolSearch;
591
+ const promptCache = optionalCompatBoolean(
592
+ properties,
593
+ "supportsExplicitPromptCacheMode",
594
+ "openai-codex",
595
+ );
596
+ if (promptCache !== undefined) projected.supportsExplicitPromptCacheMode = promptCache;
597
+ return projected;
598
+ }
599
+
600
+ function captureCompatProperties(
601
+ value: unknown,
602
+ family: DeclaredFamily,
603
+ ): CapturedOwnDataProperties | undefined {
604
+ if (value === undefined) return undefined;
605
+ if (value === null || Array.isArray(value) || typeof value !== "object") {
606
+ return rejectProjection(family, "compat");
607
+ }
608
+ if (!isPlainObject(value)) return rejectProjection(family, "compat");
609
+ return assertOwnDataProperties(value, family, "compat");
610
+ }
611
+
612
+ function projectCapturedCompat(
613
+ properties: CapturedOwnDataProperties | undefined,
614
+ family: DeclaredFamily,
615
+ ): ModelDeclarationRow["compat"] {
616
+ if (properties === undefined) return undefined;
617
+ // Antigravity models carry no compat overrides; `Model<typeof
618
+ // GOOGLE_ANTIGRAVITY_API>["compat"]` types the field `never`, so any presence
619
+ // here is a foreign shape that must fail closed rather than being silently
620
+ // treated as an Anthropic or Codex override.
621
+ if (family === "google-antigravity") return rejectProjection(family, "compat");
622
+ return family === "anthropic"
623
+ ? projectAnthropicCompat(properties)
624
+ : projectCodexCompat(properties);
625
+ }
626
+
627
+ function projectCompat(
628
+ value: unknown,
629
+ family: DeclaredFamily,
630
+ ): ModelDeclarationRow["compat"] {
631
+ return projectCapturedCompat(captureCompatProperties(value, family), family);
632
+ }
633
+
634
+ function sourceRecord(
635
+ model: unknown,
636
+ family: DeclaredFamily,
637
+ ): CapturedOwnDataProperties {
638
+ if (!isPlainObject(model)) return rejectProjection(family, "model");
639
+ return assertOwnDataProperties(model, family, "model", [
640
+ "id",
641
+ "name",
642
+ "api",
643
+ "provider",
644
+ "baseUrl",
645
+ "reasoning",
646
+ "input",
647
+ "cost",
648
+ "contextWindow",
649
+ "maxTokens",
650
+ ]);
651
+ }
652
+
653
+ /** Project one complete managed physical model into one closed logical row. */
654
+ export function projectManagedLogicalModel(
655
+ source: ManagedLogicalModelSource,
656
+ ): ModelDeclarationRow {
657
+ const sourceProperties = assertOwnDataProperties(source, "anthropic", "source", [
658
+ "family",
659
+ "model",
660
+ ]);
661
+ const familyValue = requiredCapturedValue(
662
+ sourceProperties,
663
+ "family",
664
+ "anthropic",
665
+ "family",
666
+ );
667
+ if (
668
+ familyValue !== "anthropic" &&
669
+ familyValue !== "openai-codex" &&
670
+ familyValue !== "google-antigravity"
671
+ ) {
672
+ return rejectProjection("anthropic", "family");
673
+ }
674
+ const family = familyValue;
675
+ const model = requiredCapturedValue(sourceProperties, "model", family, "model");
676
+ const raw = sourceRecord(model, family);
677
+ const api = requiredCapturedValue(raw, "api", family, "api");
678
+ if (
679
+ (family === "anthropic" && api !== "anthropic-messages") ||
680
+ (family === "openai-codex" && api !== "openai-codex-responses") ||
681
+ (family === "google-antigravity" && api !== GOOGLE_ANTIGRAVITY_API)
682
+ ) {
683
+ return rejectProjection(family, "api");
684
+ }
685
+ const projected: Partial<ModelDeclarationRow> = {};
686
+ projected.id = projectRequiredString(
687
+ requiredCapturedValue(raw, "id", family, "id"),
688
+ family,
689
+ "id",
690
+ );
691
+ projected.name = projectRequiredString(
692
+ requiredCapturedValue(raw, "name", family, "name"),
693
+ family,
694
+ "name",
695
+ );
696
+ projected.reasoning = projectRequiredBoolean(
697
+ requiredCapturedValue(raw, "reasoning", family, "reasoning"),
698
+ family,
699
+ "reasoning",
700
+ );
701
+ const thinkingLevelMap = projectThinkingLevelMap(
702
+ optionalCapturedValue(raw, "thinkingLevelMap"),
703
+ family,
704
+ );
705
+ if (thinkingLevelMap !== undefined) projected.thinkingLevelMap = thinkingLevelMap;
706
+ projected.input = projectRequiredInput(raw, family);
707
+ projected.cost = projectRequiredCost(raw, family);
708
+ projected.contextWindow = projectPositiveLimit(
709
+ requiredCapturedValue(raw, "contextWindow", family, "contextWindow"),
710
+ family,
711
+ "contextWindow",
712
+ );
713
+ projected.maxTokens = projectPositiveLimit(
714
+ requiredCapturedValue(raw, "maxTokens", family, "maxTokens"),
715
+ family,
716
+ "maxTokens",
717
+ );
718
+ const samplingParams = projectSamplingParams(
719
+ optionalCapturedValue(raw, "samplingParams"),
720
+ family,
721
+ );
722
+ if (samplingParams !== undefined) projected.samplingParams = samplingParams;
723
+ const compat = projectCompat(optionalCapturedValue(raw, "compat"), family);
724
+ if (compat !== undefined) projected.compat = compat;
725
+ projected.api = LOGICAL_PROVIDER_ID;
726
+ projected.baseUrl = DECLARATION_BASE_URL;
727
+ return assertProjectedManagedModel(projected, family);
728
+ }
729
+
730
+ const PROJECTED_ROW_KEYS = new Set([
731
+ "id",
732
+ "name",
733
+ "api",
734
+ "baseUrl",
735
+ "reasoning",
736
+ "thinkingLevelMap",
737
+ "input",
738
+ "cost",
739
+ "contextWindow",
740
+ "maxTokens",
741
+ "samplingParams",
742
+ "compat",
743
+ ]);
744
+ const ANTHROPIC_ONLY_COMPAT_KEYS = new Set(
745
+ Object.keys(ANTHROPIC_COMPAT_DISPOSITION).filter(
746
+ (key) =>
747
+ key !== "allowedFallbackModels" &&
748
+ !Object.hasOwn(CODEX_COMPAT_DISPOSITION, key),
749
+ ),
750
+ );
751
+ const CODEX_ONLY_COMPAT_KEYS = new Set(
752
+ Object.keys(CODEX_COMPAT_DISPOSITION).filter(
753
+ (key) => !Object.hasOwn(ANTHROPIC_COMPAT_DISPOSITION, key),
754
+ ),
755
+ );
756
+
757
+ function inferProjectedFamily(
758
+ compat: CapturedOwnDataProperties | undefined,
759
+ ): DeclaredFamily {
760
+ if (compat === undefined) return "anthropic";
761
+ let anthropic = false;
762
+ let codex = false;
763
+ for (const key of compat.keys()) {
764
+ if (ANTHROPIC_ONLY_COMPAT_KEYS.has(key)) anthropic = true;
765
+ if (CODEX_ONLY_COMPAT_KEYS.has(key)) codex = true;
766
+ }
767
+ if (anthropic && codex) return rejectProjection("anthropic", "compat");
768
+ return codex ? "openai-codex" : "anthropic";
769
+ }
770
+
771
+ function isCanonicalArrayIndex(key: string): boolean {
772
+ const index = Number(key);
773
+ return (
774
+ Number.isInteger(index) &&
775
+ index >= 0 &&
776
+ index < 2 ** 32 - 1 &&
777
+ String(index) === key
778
+ );
779
+ }
780
+
781
+ function isOrdinaryRetainedDataProperty(
782
+ container: object,
783
+ key: string | symbol,
784
+ descriptor: PropertyDescriptor,
785
+ ): boolean {
786
+ if (typeof key !== "string") return false;
787
+ if (!Array.isArray(container)) return descriptor.enumerable === true;
788
+ if (key === "length") return descriptor.enumerable === false;
789
+ return descriptor.enumerable === true && isCanonicalArrayIndex(key);
790
+ }
791
+
792
+ function assertOwnDataProperties(
793
+ value: object,
794
+ family: DeclaredFamily,
795
+ fieldPath: string,
796
+ requiredKeys: readonly string[] = [],
797
+ ): CapturedOwnDataProperties {
798
+ let keys: (string | symbol)[];
799
+ try {
800
+ keys = Reflect.ownKeys(value);
801
+ } catch {
802
+ return rejectProjection(family, fieldPath);
803
+ }
804
+ const properties = new Map<string, PropertyDescriptor>();
805
+ for (const key of keys) {
806
+ let descriptor: PropertyDescriptor | undefined;
807
+ try {
808
+ descriptor = Reflect.getOwnPropertyDescriptor(value, key);
809
+ } catch {
810
+ return rejectProjection(family, fieldPath);
811
+ }
812
+ if (
813
+ typeof key !== "string" ||
814
+ descriptor === undefined ||
815
+ !Object.hasOwn(descriptor, "value") ||
816
+ !isOrdinaryRetainedDataProperty(value, key, descriptor)
817
+ ) {
818
+ return rejectProjection(family, fieldPath);
819
+ }
820
+ properties.set(key, descriptor);
821
+ }
822
+ for (const key of requiredKeys) {
823
+ if (!properties.has(key)) return rejectProjection(family, `${fieldPath}.${key}`);
824
+ }
825
+ return properties;
826
+ }
827
+
828
+ function compatAllowedKeys(family: DeclaredFamily): ReadonlySet<string> {
829
+ switch (family) {
830
+ case "anthropic":
831
+ return new Set(Object.keys(ANTHROPIC_COMPAT_DISPOSITION));
832
+ case "openai-codex":
833
+ return new Set(Object.keys(CODEX_COMPAT_DISPOSITION));
834
+ case "google-antigravity":
835
+ return new Set();
836
+ }
837
+ }
838
+
839
+ function validateProjectedManagedModel(
840
+ value: unknown,
841
+ familyHint?: DeclaredFamily,
842
+ ): asserts value is ModelDeclarationRow {
843
+ const errorFamily = familyHint ?? "anthropic";
844
+ if (!isPlainObject(value)) return rejectProjection(errorFamily, "model");
845
+ const properties = assertOwnDataProperties(value, errorFamily, "model", [
846
+ "id",
847
+ "name",
848
+ "api",
849
+ "baseUrl",
850
+ "reasoning",
851
+ "input",
852
+ "cost",
853
+ "contextWindow",
854
+ "maxTokens",
855
+ ]);
856
+ const compat = captureCompatProperties(optionalCapturedValue(properties, "compat"), errorFamily);
857
+ const family = familyHint ?? inferProjectedFamily(compat);
858
+ if (compat !== undefined) {
859
+ exactCapturedKeys(compat, compatAllowedKeys(family), family, "compat");
860
+ }
861
+ exactCapturedKeys(properties, PROJECTED_ROW_KEYS, family, "model");
862
+ projectRequiredString(requiredCapturedValue(properties, "id", family, "id"), family, "id");
863
+ projectRequiredString(
864
+ requiredCapturedValue(properties, "name", family, "name"),
865
+ family,
866
+ "name",
867
+ );
868
+ projectRequiredBoolean(
869
+ requiredCapturedValue(properties, "reasoning", family, "reasoning"),
870
+ family,
871
+ "reasoning",
872
+ );
873
+ projectThinkingLevelMap(optionalCapturedValue(properties, "thinkingLevelMap"), family);
874
+ projectRequiredInput(properties, family);
875
+ projectRequiredCost(properties, family);
876
+ projectPositiveLimit(
877
+ requiredCapturedValue(properties, "contextWindow", family, "contextWindow"),
878
+ family,
879
+ "contextWindow",
880
+ );
881
+ projectPositiveLimit(
882
+ requiredCapturedValue(properties, "maxTokens", family, "maxTokens"),
883
+ family,
884
+ "maxTokens",
885
+ );
886
+ projectSamplingParams(optionalCapturedValue(properties, "samplingParams"), family);
887
+ if (compat?.has("allowedFallbackModels")) {
888
+ return rejectProjection(family, "compat.allowedFallbackModels");
889
+ }
890
+ projectCapturedCompat(compat, family);
891
+ if (requiredCapturedValue(properties, "api", family, "api") !== LOGICAL_PROVIDER_ID) {
892
+ return rejectProjection(family, "api");
893
+ }
894
+ if (
895
+ requiredCapturedValue(properties, "baseUrl", family, "baseUrl") !==
896
+ DECLARATION_BASE_URL
897
+ ) {
898
+ return rejectProjection(family, "baseUrl");
899
+ }
900
+ }
901
+
902
+ /** Validate one projected row in place and return the same object. */
903
+ export function assertProjectedManagedModel(
904
+ value: unknown,
905
+ familyHint?: DeclaredFamily,
906
+ ): ModelDeclarationRow {
907
+ validateProjectedManagedModel(value, familyHint);
908
+ return value;
909
+ }
910
+
911
+ function freezeIfObject(value: unknown): void {
912
+ if (typeof value === "object" && value !== null) Object.freeze(value);
913
+ }
914
+
915
+ function freezeProjectedManagedModel(row: ModelDeclarationRow): void {
916
+ const properties = assertOwnDataProperties(row, "anthropic", "model");
917
+ freezeIfObject(optionalCapturedValue(properties, "thinkingLevelMap"));
918
+ freezeIfObject(requiredCapturedValue(properties, "input", "anthropic", "input"));
919
+ const cost = requiredCapturedValue(properties, "cost", "anthropic", "cost");
920
+ if (!isPlainObject(cost)) return rejectProjection("anthropic", "cost");
921
+ const costProperties = assertOwnDataProperties(cost, "anthropic", "cost");
922
+ const tiers = optionalCapturedValue(costProperties, "tiers");
923
+ if (tiers !== undefined) {
924
+ if (!Array.isArray(tiers)) return rejectProjection("anthropic", "cost.tiers");
925
+ const tierProperties = assertOwnDataProperties(tiers, "anthropic", "cost.tiers");
926
+ const length = capturedArrayLength(tierProperties, "anthropic", "cost.tiers");
927
+ for (let index = 0; index < length; index += 1) {
928
+ freezeIfObject(
929
+ requiredCapturedValue(
930
+ tierProperties,
931
+ String(index),
932
+ "anthropic",
933
+ "cost.tiers",
934
+ ),
935
+ );
936
+ }
937
+ Object.freeze(tiers);
938
+ }
939
+ Object.freeze(cost);
940
+ freezeIfObject(optionalCapturedValue(properties, "samplingParams"));
941
+ freezeIfObject(optionalCapturedValue(properties, "compat"));
942
+ Object.freeze(row);
943
+ }
944
+
945
+ function validateProjectedManagedModels(
946
+ projectedModels: unknown[],
947
+ ): asserts projectedModels is ModelDeclarationRow[] {
948
+ const properties = assertOwnDataProperties(projectedModels, "anthropic", "models");
949
+ const length = capturedArrayLength(properties, "anthropic", "models");
950
+ for (let index = 0; index < length; index += 1) {
951
+ const row = requiredCapturedValue(properties, String(index), "anthropic", "models");
952
+ validateProjectedManagedModel(row);
953
+ freezeProjectedManagedModel(row);
954
+ }
955
+ }
956
+
957
+ /** Validate and deeply freeze projected registration input in place. */
958
+ export function assertProjectedManagedModels(
959
+ projectedModels: unknown[],
960
+ ): ModelDeclarationRow[] {
961
+ validateProjectedManagedModels(projectedModels);
962
+ Object.freeze(projectedModels);
963
+ return projectedModels;
964
+ }
965
+
966
+ function isDeclaredFamily(value: string): value is DeclaredFamily {
967
+ return (DECLARED_FAMILIES as readonly string[]).includes(value);
968
+ }
969
+
970
+ function applyProjectedCodexContextWindow(
971
+ row: ModelDeclarationRow,
972
+ options: ModelDeclarationBuildOptions,
973
+ ): ModelDeclarationRow {
974
+ const contextWindow = options.codexContextWindowByModelId?.get(row.id);
975
+ if (contextWindow === undefined) return row;
976
+ row.contextWindow = projectPositiveLimit(
977
+ contextWindow,
978
+ "openai-codex",
979
+ "contextWindow",
980
+ );
981
+ return row;
982
+ }
983
+
984
+ /** Build ordered, unambiguous rows from complete family-keyed live catalogs. */
985
+ export function buildModelDeclaration(
986
+ catalogs: ModelDeclarationCatalogs,
987
+ options: ModelDeclarationBuildOptions = {},
988
+ ): BuiltDeclaration {
989
+ const projectedByFamily = new Map<DeclaredFamily, ModelDeclarationRow[]>();
990
+ for (const family of DECLARED_FAMILIES) {
991
+ const rows: ModelDeclarationRow[] = [];
992
+ if (family === "anthropic") {
993
+ for (const model of catalogs.anthropic) {
994
+ rows.push(projectManagedLogicalModel({ family, model }));
995
+ }
996
+ } else if (family === "openai-codex") {
997
+ for (const model of catalogs["openai-codex"]) {
998
+ const projected = projectManagedLogicalModel({ family, model });
999
+ rows.push(applyProjectedCodexContextWindow(projected, options));
1000
+ }
1001
+ } else {
1002
+ for (const model of catalogs["google-antigravity"] ?? []) {
1003
+ rows.push(projectManagedLogicalModel({ family, model }));
1004
+ }
1005
+ }
1006
+ projectedByFamily.set(family, rows);
1007
+ }
1008
+
1009
+ const owners = new Map<string, Set<DeclaredFamily>>();
1010
+ for (const family of DECLARED_FAMILIES) {
1011
+ for (const row of projectedByFamily.get(family) ?? []) {
1012
+ const existing = owners.get(row.id);
1013
+ if (existing === undefined) owners.set(row.id, new Set([family]));
1014
+ else existing.add(family);
1015
+ }
1016
+ }
1017
+
1018
+ const models: ModelDeclarationRow[] = [];
1019
+ const emitted = new Set<string>();
1020
+ for (const family of DECLARED_FAMILIES) {
1021
+ for (const row of projectedByFamily.get(family) ?? []) {
1022
+ if (emitted.has(row.id)) continue;
1023
+ const owningFamilies = owners.get(row.id);
1024
+ if (owningFamilies === undefined || owningFamilies.size !== 1) continue;
1025
+ emitted.add(row.id);
1026
+ models.push(row);
1027
+ }
1028
+ }
1029
+
1030
+ const ambiguous = [...owners]
1031
+ .filter(([, families]) => families.size > 1)
1032
+ .map(([id]) => id)
1033
+ .sort();
1034
+ const diagnostics = ambiguous
1035
+ .slice(0, MAX_AMBIGUITY_DIAGNOSTICS)
1036
+ .map(
1037
+ (id) =>
1038
+ `omitted ambiguous model id ${id}: served by more than one managed family, so no family can be chosen for it`,
1039
+ );
1040
+ const overflow = ambiguous.length - MAX_AMBIGUITY_DIAGNOSTICS;
1041
+ if (overflow > 0) {
1042
+ diagnostics.push(
1043
+ `omitted ${overflow} further ambiguous model ids beyond the first ${MAX_AMBIGUITY_DIAGNOSTICS}`,
1044
+ );
1045
+ }
1046
+ for (const key of Object.keys(catalogs)) {
1047
+ if (!isDeclaredFamily(key)) {
1048
+ diagnostics.push(`ignored unknown catalog family ${key}`);
1049
+ }
1050
+ }
1051
+ return { models, diagnostics };
1052
+ }
1053
+
1054
+ export type InstalledDeclarationStatus =
1055
+ | "absent"
1056
+ | "unreadable"
1057
+ | "mismatched"
1058
+ | "matched";
1059
+
1060
+ /** Compare complete validated managed rows in their declared order. */
1061
+ export function inspectInstalledDeclaration(
1062
+ parsed: unknown,
1063
+ expectedModels: readonly ModelDeclarationRow[],
1064
+ ): InstalledDeclarationStatus {
1065
+ if (!isPlainObject(parsed)) return "unreadable";
1066
+ const providers = parsed.providers;
1067
+ if (providers === undefined) return "absent";
1068
+ if (!isPlainObject(providers)) return "unreadable";
1069
+ const declaration = providers[LOGICAL_PROVIDER_ID];
1070
+ if (declaration === undefined) return "absent";
1071
+ if (!isPlainObject(declaration)) return "unreadable";
1072
+ if (
1073
+ declaration.api !== LOGICAL_PROVIDER_ID ||
1074
+ declaration.baseUrl !== DECLARATION_BASE_URL ||
1075
+ declaration.name !== LOGICAL_PROVIDER_DISPLAY_NAME ||
1076
+ typeof declaration.apiKey !== "string" ||
1077
+ declaration.apiKey === "" ||
1078
+ !Array.isArray(declaration.models)
1079
+ ) {
1080
+ return "mismatched";
1081
+ }
1082
+ try {
1083
+ for (const row of expectedModels) assertProjectedManagedModel(row);
1084
+ for (const row of declaration.models) assertProjectedManagedModel(row);
1085
+ } catch {
1086
+ return "mismatched";
1087
+ }
1088
+ return isDeepStrictEqual(declaration.models, expectedModels)
1089
+ ? "matched"
1090
+ : "mismatched";
1091
+ }