@linxiraos/pi-catalog 1.1.7 → 1.1.9

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 (308) hide show
  1. package/CHANGELOG.md +7 -8
  2. package/README.md +9 -8
  3. package/package.json +96 -100
  4. package/src/build.ts +165 -48
  5. package/src/cline-pass-model-id.ts +11 -0
  6. package/src/compat/anthropic.ts +15 -136
  7. package/src/compat/auth-ids.ts +157 -0
  8. package/src/compat/auth.ts +60 -0
  9. package/src/compat/axes.ts +301 -0
  10. package/src/compat/behavior.ts +207 -0
  11. package/src/compat/cascade.ts +230 -0
  12. package/src/{variant-collapse.ts → compat/collapse.ts} +381 -773
  13. package/src/compat/delegation.ts +37 -0
  14. package/src/compat/openai.ts +17 -848
  15. package/src/compat/resolve.ts +1231 -0
  16. package/src/compat/revision.ts +154 -0
  17. package/src/compat/rules/README.md +315 -0
  18. package/src/compat/rules/auth/_order.kdl +13 -0
  19. package/src/compat/rules/auth/abliteration.kdl +9 -0
  20. package/src/compat/rules/auth/aiand.kdl +9 -0
  21. package/src/compat/rules/auth/aimlapi.kdl +3 -0
  22. package/src/compat/rules/auth/alibaba-coding-plan.kdl +5 -0
  23. package/src/compat/rules/auth/alibaba-token-plan.kdl +11 -0
  24. package/src/compat/rules/auth/amazon-bedrock.kdl +5 -0
  25. package/src/compat/rules/auth/anthropic.kdl +53 -0
  26. package/src/compat/rules/auth/azure.kdl +3 -0
  27. package/src/compat/rules/auth/baseten.kdl +9 -0
  28. package/src/compat/rules/auth/bedrock-mantle.kdl +5 -0
  29. package/src/compat/rules/auth/cerebras.kdl +9 -0
  30. package/src/compat/rules/auth/cline-pass.kdl +12 -0
  31. package/src/compat/rules/auth/cloudflare-ai-gateway.kdl +4 -0
  32. package/src/compat/rules/auth/coreweave.kdl +9 -0
  33. package/src/compat/rules/auth/cursor.kdl +5 -0
  34. package/src/compat/rules/auth/deepinfra.kdl +11 -0
  35. package/src/compat/rules/auth/deepseek.kdl +10 -0
  36. package/src/compat/rules/auth/devin.kdl +28 -0
  37. package/src/compat/rules/auth/exa.kdl +9 -0
  38. package/src/compat/rules/auth/firepass.kdl +14 -0
  39. package/src/compat/rules/auth/fireworks.kdl +16 -0
  40. package/src/compat/rules/auth/github-copilot.kdl +7 -0
  41. package/src/compat/rules/auth/gitlab-duo-agent.kdl +23 -0
  42. package/src/compat/rules/auth/gitlab-duo.kdl +24 -0
  43. package/src/compat/rules/auth/gmi-cloud.kdl +9 -0
  44. package/src/compat/rules/auth/google-antigravity.kdl +28 -0
  45. package/src/compat/rules/auth/google-gemini-cli.kdl +26 -0
  46. package/src/compat/rules/auth/google-vertex.kdl +5 -0
  47. package/src/compat/rules/auth/google.kdl +3 -0
  48. package/src/compat/rules/auth/groq.kdl +3 -0
  49. package/src/compat/rules/auth/huggingface.kdl +9 -0
  50. package/src/compat/rules/auth/kagi.kdl +9 -0
  51. package/src/compat/rules/auth/kilo.kdl +4 -0
  52. package/src/compat/rules/auth/kimi-code.kdl +18 -0
  53. package/src/compat/rules/auth/litellm.kdl +8 -0
  54. package/src/compat/rules/auth/llama.cpp.kdl +10 -0
  55. package/src/compat/rules/auth/lm-studio.kdl +7 -0
  56. package/src/compat/rules/auth/meta.kdl +9 -0
  57. package/src/compat/rules/auth/minimax-code-cn.kdl +9 -0
  58. package/src/compat/rules/auth/minimax-code.kdl +9 -0
  59. package/src/compat/rules/auth/minimax.kdl +3 -0
  60. package/src/compat/rules/auth/mistral.kdl +3 -0
  61. package/src/compat/rules/auth/moonshot.kdl +9 -0
  62. package/src/compat/rules/auth/nanogpt.kdl +9 -0
  63. package/src/compat/rules/auth/novita.kdl +12 -0
  64. package/src/compat/rules/auth/nvidia.kdl +11 -0
  65. package/src/compat/rules/auth/ollama-cloud.kdl +8 -0
  66. package/src/compat/rules/auth/ollama.kdl +9 -0
  67. package/src/compat/rules/auth/openai-codex-device.kdl +20 -0
  68. package/src/compat/rules/auth/openai-codex.kdl +33 -0
  69. package/src/compat/rules/auth/openai.kdl +3 -0
  70. package/src/compat/rules/auth/opencode-go.kdl +10 -0
  71. package/src/compat/rules/auth/opencode-zen.kdl +9 -0
  72. package/src/compat/rules/auth/openrouter.kdl +32 -0
  73. package/src/compat/rules/auth/parallel.kdl +9 -0
  74. package/src/compat/rules/auth/perplexity.kdl +7 -0
  75. package/src/compat/rules/auth/qianfan.kdl +9 -0
  76. package/src/compat/rules/auth/qwen-portal.kdl +9 -0
  77. package/src/compat/rules/auth/sakana.kdl +9 -0
  78. package/src/compat/rules/auth/siliconflow-cn.kdl +9 -0
  79. package/src/compat/rules/auth/siliconflow.kdl +9 -0
  80. package/src/compat/rules/auth/synthetic.kdl +9 -0
  81. package/src/compat/rules/auth/tavily.kdl +9 -0
  82. package/src/compat/rules/auth/together.kdl +14 -0
  83. package/src/compat/rules/auth/umans.kdl +9 -0
  84. package/src/compat/rules/auth/venice.kdl +9 -0
  85. package/src/compat/rules/auth/vercel-ai-gateway.kdl +8 -0
  86. package/src/compat/rules/auth/vllm.kdl +9 -0
  87. package/src/compat/rules/auth/wafer-serverless.kdl +11 -0
  88. package/src/compat/rules/auth/xai-oauth.kdl +27 -0
  89. package/src/compat/rules/auth/xai.kdl +9 -0
  90. package/src/compat/rules/auth/xiaomi-token-plan-ams.kdl +9 -0
  91. package/src/compat/rules/auth/xiaomi-token-plan-cn.kdl +9 -0
  92. package/src/compat/rules/auth/xiaomi-token-plan-sgp.kdl +9 -0
  93. package/src/compat/rules/auth/xiaomi.kdl +6 -0
  94. package/src/compat/rules/auth/yolo-auto.kdl +11 -0
  95. package/src/compat/rules/auth/zai-coding-plan.kdl +33 -0
  96. package/src/compat/rules/auth/zai.kdl +9 -0
  97. package/src/compat/rules/auth/zenmux.kdl +9 -0
  98. package/src/compat/rules/auth/zhipu-coding-plan.kdl +9 -0
  99. package/src/compat/rules/classes/amazon.kdl +21 -0
  100. package/src/compat/rules/classes/anthropic.kdl +285 -0
  101. package/src/compat/rules/classes/baidu.kdl +9 -0
  102. package/src/compat/rules/classes/bytedance.kdl +9 -0
  103. package/src/compat/rules/classes/cohere.kdl +7 -0
  104. package/src/compat/rules/classes/deepseek.kdl +80 -0
  105. package/src/compat/rules/classes/gemini.kdl +107 -0
  106. package/src/compat/rules/classes/gemma.kdl +14 -0
  107. package/src/compat/rules/classes/glm.kdl +115 -0
  108. package/src/compat/rules/classes/gpt-oss.kdl +16 -0
  109. package/src/compat/rules/classes/kimi.kdl +113 -0
  110. package/src/compat/rules/classes/meta.kdl +15 -0
  111. package/src/compat/rules/classes/mimo.kdl +32 -0
  112. package/src/compat/rules/classes/minimax.kdl +79 -0
  113. package/src/compat/rules/classes/mistral.kdl +19 -0
  114. package/src/compat/rules/classes/openai.kdl +58 -0
  115. package/src/compat/rules/classes/qwen.kdl +72 -0
  116. package/src/compat/rules/classes/stepfun.kdl +10 -0
  117. package/src/compat/rules/classes/xai.kdl +45 -0
  118. package/src/compat/rules/providers/abliteration.kdl +8 -0
  119. package/src/compat/rules/providers/aiand.kdl +19 -0
  120. package/src/compat/rules/providers/aimlapi.kdl +24 -0
  121. package/src/compat/rules/providers/alibaba-coding-plan.kdl +20 -0
  122. package/src/compat/rules/providers/alibaba-token-plan.kdl +35 -0
  123. package/src/compat/rules/providers/amazon-bedrock.kdl +36 -0
  124. package/src/compat/rules/providers/anthropic.kdl +24 -0
  125. package/src/compat/rules/providers/azure.kdl +19 -0
  126. package/src/compat/rules/providers/baseten.kdl +19 -0
  127. package/src/compat/rules/providers/bedrock-mantle.kdl +15 -0
  128. package/src/compat/rules/providers/cerebras.kdl +19 -0
  129. package/src/compat/rules/providers/cline-pass.kdl +8 -0
  130. package/src/compat/rules/providers/cloudflare-ai-gateway.kdl +26 -0
  131. package/src/compat/rules/providers/coreweave.kdl +16 -0
  132. package/src/compat/rules/providers/cursor.kdl +52 -0
  133. package/src/compat/rules/providers/deepseek.kdl +21 -0
  134. package/src/compat/rules/providers/firepass.kdl +15 -0
  135. package/src/compat/rules/providers/fireworks.kdl +43 -0
  136. package/src/compat/rules/providers/github-copilot.kdl +62 -0
  137. package/src/compat/rules/providers/gitlab-duo.kdl +24 -0
  138. package/src/compat/rules/providers/gmi-cloud.kdl +8 -0
  139. package/src/compat/rules/providers/google-antigravity.kdl +59 -0
  140. package/src/compat/rules/providers/google-gemini-cli.kdl +12 -0
  141. package/src/compat/rules/providers/google-vertex.kdl +25 -0
  142. package/src/compat/rules/providers/google.kdl +17 -0
  143. package/src/compat/rules/providers/groq.kdl +8 -0
  144. package/src/compat/rules/providers/huggingface.kdl +24 -0
  145. package/src/compat/rules/providers/kilo.kdl +51 -0
  146. package/src/compat/rules/providers/kimi-code.kdl +37 -0
  147. package/src/compat/rules/providers/llama.cpp.kdl +6 -0
  148. package/src/compat/rules/providers/lm-studio.kdl +6 -0
  149. package/src/compat/rules/providers/meta.kdl +10 -0
  150. package/src/compat/rules/providers/minimax-code-cn.kdl +13 -0
  151. package/src/compat/rules/providers/minimax-code.kdl +13 -0
  152. package/src/compat/rules/providers/minimax.kdl +6 -0
  153. package/src/compat/rules/providers/mistral.kdl +8 -0
  154. package/src/compat/rules/providers/moonshot.kdl +36 -0
  155. package/src/compat/rules/providers/nanogpt.kdl +100 -0
  156. package/src/compat/rules/providers/novita.kdl +34 -0
  157. package/src/compat/rules/providers/nvidia.kdl +27 -0
  158. package/src/compat/rules/providers/ollama-cloud.kdl +20 -0
  159. package/src/compat/rules/providers/ollama.kdl +12 -0
  160. package/src/compat/rules/providers/openai-codex.kdl +121 -0
  161. package/src/compat/rules/providers/openai.kdl +82 -0
  162. package/src/compat/rules/providers/opencode-go.kdl +97 -0
  163. package/src/compat/rules/providers/opencode-zen.kdl +103 -0
  164. package/src/compat/rules/providers/openrouter.kdl +66 -0
  165. package/src/compat/rules/providers/qianfan.kdl +8 -0
  166. package/src/compat/rules/providers/sakana.kdl +10 -0
  167. package/src/compat/rules/providers/synthetic.kdl +16 -0
  168. package/src/compat/rules/providers/together.kdl +22 -0
  169. package/src/compat/rules/providers/umans.kdl +28 -0
  170. package/src/compat/rules/providers/venice.kdl +46 -0
  171. package/src/compat/rules/providers/vercel-ai-gateway.kdl +73 -0
  172. package/src/compat/rules/providers/vllm.kdl +8 -0
  173. package/src/compat/rules/providers/wafer-serverless.kdl +35 -0
  174. package/src/compat/rules/providers/xai-oauth.kdl +48 -0
  175. package/src/compat/rules/providers/xai.kdl +50 -0
  176. package/src/compat/rules/providers/xiaomi-token-plan-ams.kdl +9 -0
  177. package/src/compat/rules/providers/xiaomi-token-plan-cn.kdl +9 -0
  178. package/src/compat/rules/providers/xiaomi-token-plan-sgp.kdl +9 -0
  179. package/src/compat/rules/providers/xiaomi.kdl +22 -0
  180. package/src/compat/rules/providers/yolo-auto.kdl +16 -0
  181. package/src/compat/rules/providers/zai.kdl +36 -0
  182. package/src/compat/rules/providers/zenmux.kdl +108 -0
  183. package/src/compat/rules/providers/zhipu-coding-plan.kdl +49 -0
  184. package/src/compat/rules/runtime/behavior.kdl +254 -0
  185. package/src/compat/rules/taxonomy/_collapse.kdl +747 -0
  186. package/src/compat/rules/taxonomy/_discovery.kdl +60 -0
  187. package/src/compat/rules/taxonomy/ai21.kdl +6 -0
  188. package/src/compat/rules/taxonomy/amazon.kdl +8 -0
  189. package/src/compat/rules/taxonomy/anthropic.kdl +17 -0
  190. package/src/compat/rules/taxonomy/baidu.kdl +5 -0
  191. package/src/compat/rules/taxonomy/bytedance.kdl +5 -0
  192. package/src/compat/rules/taxonomy/cohere.kdl +6 -0
  193. package/src/compat/rules/taxonomy/deepseek.kdl +18 -0
  194. package/src/compat/rules/taxonomy/gemini.kdl +9 -0
  195. package/src/compat/rules/taxonomy/gemma.kdl +3 -0
  196. package/src/compat/rules/taxonomy/glm.kdl +17 -0
  197. package/src/compat/rules/taxonomy/gpt-oss.kdl +11 -0
  198. package/src/compat/rules/taxonomy/kimi.kdl +20 -0
  199. package/src/compat/rules/taxonomy/meta.kdl +10 -0
  200. package/src/compat/rules/taxonomy/mimo.kdl +7 -0
  201. package/src/compat/rules/taxonomy/minimax.kdl +9 -0
  202. package/src/compat/rules/taxonomy/mistral.kdl +7 -0
  203. package/src/compat/rules/taxonomy/openai.kdl +43 -0
  204. package/src/compat/rules/taxonomy/qwen.kdl +33 -0
  205. package/src/compat/rules/taxonomy/stepfun.kdl +5 -0
  206. package/src/compat/rules/taxonomy/unknown.kdl +3 -0
  207. package/src/compat/rules/taxonomy/xai.kdl +12 -0
  208. package/src/compat/rules.d.json.ts +5 -0
  209. package/src/compat/rules.json +16984 -0
  210. package/src/compat/taxonomy.ts +452 -0
  211. package/src/compat/types.ts +588 -0
  212. package/src/discovery/antigravity.ts +5 -6
  213. package/src/discovery/codex.ts +22 -7
  214. package/src/discovery/cursor-proto.ts +7 -7
  215. package/src/discovery/cursor.ts +47 -58
  216. package/src/discovery/devin-proto.ts +613 -0
  217. package/src/discovery/devin.ts +358 -52
  218. package/src/discovery/gemini-cli.ts +14 -11
  219. package/src/discovery/gemini.ts +2 -0
  220. package/src/identity/dialect.ts +2 -2
  221. package/src/identity/id.ts +12 -0
  222. package/src/identity/index.ts +3 -3
  223. package/src/identity/metrics.ts +165 -0
  224. package/src/identity/reference.ts +37 -5
  225. package/src/index.ts +1 -1
  226. package/src/model-manager.ts +19 -6
  227. package/src/model-thinking.ts +6 -810
  228. package/src/model-tokenizer.ts +37 -44
  229. package/src/models.json +89584 -22358
  230. package/src/models.ts +6 -1
  231. package/src/provider-models/bundled-references.ts +3 -1
  232. package/src/provider-models/cline-pass.ts +226 -0
  233. package/src/provider-models/descriptors.ts +18 -0
  234. package/src/provider-models/google.ts +14 -5
  235. package/src/provider-models/models-dev-policies.ts +4 -29
  236. package/src/provider-models/ollama.ts +15 -2
  237. package/src/provider-models/openai-compat.ts +798 -520
  238. package/src/provider-models/special.ts +211 -12
  239. package/src/types.ts +214 -11
  240. package/src/wire/cline-pass.ts +35 -0
  241. package/src/wire/codex.ts +23 -2
  242. package/src/wire/devin-proto.ts +30 -0
  243. package/src/wire/devin.ts +72 -0
  244. package/src/wire/github-copilot.ts +49 -7
  245. package/THIRD-PARTY-NOTICES.txt +0 -22909
  246. package/dist/types/build.d.ts +0 -19
  247. package/dist/types/compat/anthropic.d.ts +0 -18
  248. package/dist/types/compat/apply.d.ts +0 -7
  249. package/dist/types/compat/bedrock.d.ts +0 -3
  250. package/dist/types/compat/devin.d.ts +0 -9
  251. package/dist/types/compat/openai.d.ts +0 -28
  252. package/dist/types/discovery/antigravity.d.ts +0 -70
  253. package/dist/types/discovery/codex.d.ts +0 -36
  254. package/dist/types/discovery/cursor-proto.d.ts +0 -5368
  255. package/dist/types/discovery/cursor.d.ts +0 -29
  256. package/dist/types/discovery/devin-proto.d.ts +0 -1549
  257. package/dist/types/discovery/devin.d.ts +0 -24
  258. package/dist/types/discovery/gemini-cli.d.ts +0 -33
  259. package/dist/types/discovery/gemini.d.ts +0 -25
  260. package/dist/types/discovery/gitlab-duo-workflow.d.ts +0 -30
  261. package/dist/types/discovery/index.d.ts +0 -7
  262. package/dist/types/discovery/openai-compatible.d.ts +0 -89
  263. package/dist/types/discovery/protobuf.d.ts +0 -102
  264. package/dist/types/effort.d.ts +0 -10
  265. package/dist/types/fireworks-model-id.d.ts +0 -23
  266. package/dist/types/hosts.d.ts +0 -169
  267. package/dist/types/identity/bundled.d.ts +0 -3
  268. package/dist/types/identity/classify.d.ts +0 -69
  269. package/dist/types/identity/dialect.d.ts +0 -3
  270. package/dist/types/identity/family.d.ts +0 -243
  271. package/dist/types/identity/id.d.ts +0 -12
  272. package/dist/types/identity/index.d.ts +0 -8
  273. package/dist/types/identity/markers.d.ts +0 -4
  274. package/dist/types/identity/priority.d.ts +0 -1
  275. package/dist/types/identity/reference.d.ts +0 -29
  276. package/dist/types/index.d.ts +0 -19
  277. package/dist/types/model-cache.d.ts +0 -23
  278. package/dist/types/model-manager.d.ts +0 -93
  279. package/dist/types/model-thinking.d.ts +0 -81
  280. package/dist/types/model-tokenizer.d.ts +0 -8
  281. package/dist/types/models.d.ts +0 -17
  282. package/dist/types/openai-pricing.d.ts +0 -17
  283. package/dist/types/provider-models/bundled-references.d.ts +0 -13
  284. package/dist/types/provider-models/cache-provider-id.d.ts +0 -11
  285. package/dist/types/provider-models/descriptor-types.d.ts +0 -76
  286. package/dist/types/provider-models/descriptors.d.ts +0 -524
  287. package/dist/types/provider-models/google.d.ts +0 -29
  288. package/dist/types/provider-models/index.d.ts +0 -7
  289. package/dist/types/provider-models/models-dev-policies.d.ts +0 -8
  290. package/dist/types/provider-models/ollama.d.ts +0 -22
  291. package/dist/types/provider-models/openai-compat.d.ts +0 -703
  292. package/dist/types/provider-models/special.d.ts +0 -53
  293. package/dist/types/types.d.ts +0 -879
  294. package/dist/types/utils.d.ts +0 -20
  295. package/dist/types/variant-collapse.d.ts +0 -146
  296. package/dist/types/wire/alibaba-token-plan.d.ts +0 -22
  297. package/dist/types/wire/cloudflare-ai-gateway.d.ts +0 -18
  298. package/dist/types/wire/codex.d.ts +0 -66
  299. package/dist/types/wire/coreweave.d.ts +0 -12
  300. package/dist/types/wire/gemini-headers.d.ts +0 -62
  301. package/dist/types/wire/github-copilot.d.ts +0 -47
  302. package/dist/types/wire/image-fetchers.d.ts +0 -76
  303. package/src/compat/bedrock.ts +0 -153
  304. package/src/compat/devin.ts +0 -12
  305. package/src/identity/classify.ts +0 -242
  306. package/src/identity/family.ts +0 -496
  307. package/src/identity/markers.ts +0 -49
  308. package/src/openai-pricing.ts +0 -29
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Runtime compat cascade: resolves per-axis wire/thinking/catalog assignments
3
+ * for one structured model target from the compiled rule tree.
4
+ *
5
+ * Faithful port of the o2 reference resolver (`cascade.rs`): rules are
6
+ * conjunctions over `(class, provider, family, revision, models)`; per axis
7
+ * the matching rule with the greatest `(model-selector exactness,
8
+ * constrained-dimension count, priority)` tuple wins, and an equal-tuple
9
+ * same-axis contest throws {@link AmbiguousOverlapError}. Declaration and
10
+ * file order are never semantic.
11
+ */
12
+ import { parseRevision, type Revision, type RevisionTerm, revisionSatisfies } from "./revision";
13
+ import rules from "./rules.json";
14
+ import type { CompiledCascade, CompiledRule, CompiledSelector, ResolvedAxes, ResolveTarget } from "./types";
15
+
16
+ /**
17
+ * Two equal-rank rules contest one axis for a target. CI-time for bundled
18
+ * targets (the parity sweep exercises every catalog row); fix with an
19
+ * explicit `priority=` in the KDL, never in code.
20
+ */
21
+ export class AmbiguousOverlapError extends Error {
22
+ constructor(
23
+ readonly provider: string,
24
+ readonly model: string,
25
+ readonly axis: string,
26
+ readonly first: string,
27
+ readonly second: string,
28
+ ) {
29
+ super(
30
+ `ambiguous overlap for \`${provider}/${model}\` on axis \`${axis}\`: rules \`${first}\` and \`${second}\` tie; add an explicit priority`,
31
+ );
32
+ this.name = "AmbiguousOverlapError";
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Anchored `*`-wildcard match; both sides must be pre-lowercased. `*` spans
38
+ * any substring; non-wildcard text stays anchored in order.
39
+ */
40
+ export function globMatch(pattern: string, value: string): boolean {
41
+ const segments = pattern.split("*");
42
+ if (segments.length === 1) return value === pattern;
43
+ const head = segments[0];
44
+ if (!value.startsWith(head)) return false;
45
+ let remainder = value.slice(head.length);
46
+ for (let i = 1; i < segments.length - 1; i++) {
47
+ const segment = segments[i];
48
+ if (!segment) continue;
49
+ const found = remainder.indexOf(segment);
50
+ if (found === -1) return false;
51
+ remainder = remainder.slice(found + segment.length);
52
+ }
53
+ const last = segments[segments.length - 1];
54
+ return last === "" || remainder.endsWith(last);
55
+ }
56
+
57
+ interface IndexedRule {
58
+ compiled: CompiledRule;
59
+ revision?: RevisionTerm[];
60
+ priority: number;
61
+ dimensions: number;
62
+ hasExactEffortsRule: boolean;
63
+ }
64
+
65
+ let ruleIndex: IndexedRule[] | undefined;
66
+
67
+ function buildRuleIndex(cascade: CompiledCascade): IndexedRule[] {
68
+ return cascade.rules.map(compiled => {
69
+ const revision = compiled.revision?.map(term => {
70
+ const parsed = parseRevision(term.revision);
71
+ if (!parsed) throw new Error(`invalid compiled revision term in ${compiled.source}`);
72
+ return { op: term.op, revision: parsed } satisfies RevisionTerm;
73
+ });
74
+ const dimensions =
75
+ Number(compiled.class !== undefined) +
76
+ Number(compiled.providers !== undefined) +
77
+ Number(compiled.family !== undefined) +
78
+ Number(compiled.revision !== undefined) +
79
+ Number(compiled.models !== undefined);
80
+ return {
81
+ compiled,
82
+ revision,
83
+ priority: compiled.priority ?? 0,
84
+ dimensions,
85
+ hasExactEffortsRule: compiled.thinking !== undefined && "efforts" in compiled.thinking,
86
+ };
87
+ });
88
+ }
89
+
90
+ function getRuleIndex(): IndexedRule[] {
91
+ ruleIndex ??= buildRuleIndex(rules.cascade);
92
+ return ruleIndex;
93
+ }
94
+
95
+ function selectorMatches(selector: CompiledSelector, model: string, modelLower: string): boolean {
96
+ switch (selector.kind) {
97
+ case "exact":
98
+ return selector.value === model;
99
+ case "glob":
100
+ return globMatch(selector.value, modelLower);
101
+ case "token": {
102
+ for (const part of modelLower.split(/[^a-z0-9]+/)) {
103
+ if (part === selector.value) return true;
104
+ }
105
+ return false;
106
+ }
107
+ }
108
+ }
109
+
110
+ /** `(exactness, dimensions, priority)` when the rule matches, else undefined. */
111
+ function rankRule(
112
+ rule: IndexedRule,
113
+ target: ResolveTarget,
114
+ revision: Revision | undefined,
115
+ modelLower: string,
116
+ ): readonly [number, number, number] | undefined {
117
+ const { compiled } = rule;
118
+ if (compiled.class !== undefined && compiled.class !== target.class) return undefined;
119
+ if (compiled.providers !== undefined && !compiled.providers.includes(target.provider)) return undefined;
120
+ if (compiled.family !== undefined && compiled.family !== target.family) return undefined;
121
+ if (rule.revision !== undefined && (!revision || !revisionSatisfies(revision, rule.revision))) return undefined;
122
+ let exactness = 0;
123
+ if (compiled.models !== undefined) {
124
+ let best = -1;
125
+ for (const selector of compiled.models) {
126
+ if (!selectorMatches(selector, target.model, modelLower)) continue;
127
+ const value = selector.kind === "exact" ? 2 : 1;
128
+ if (value > best) best = value;
129
+ }
130
+ if (best < 0) return undefined;
131
+ exactness = best;
132
+ }
133
+ return [exactness, rule.dimensions, rule.priority];
134
+ }
135
+
136
+ type WinnerTable = Record<string, { rank: readonly [number, number, number]; rule: IndexedRule }>;
137
+
138
+ function rankCompare(a: readonly [number, number, number], b: readonly [number, number, number]): number {
139
+ return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
140
+ }
141
+
142
+ function contest(
143
+ winners: WinnerTable,
144
+ axes: Record<string, unknown> | undefined,
145
+ rank: readonly [number, number, number],
146
+ rule: IndexedRule,
147
+ target: ResolveTarget,
148
+ ): void {
149
+ if (!axes) return;
150
+ for (const axis in axes) {
151
+ const held = winners[axis];
152
+ if (held) {
153
+ const order = rankCompare(held.rank, rank);
154
+ if (order === 0) {
155
+ throw new AmbiguousOverlapError(
156
+ target.provider,
157
+ target.model,
158
+ axis,
159
+ held.rule.compiled.source,
160
+ rule.compiled.source,
161
+ );
162
+ }
163
+ if (order > 0) continue;
164
+ }
165
+ winners[axis] = { rank, rule };
166
+ }
167
+ }
168
+
169
+ function collect(winners: WinnerTable, pick: (rule: CompiledRule) => Record<string, unknown> | undefined) {
170
+ const out: Record<string, unknown> = {};
171
+ for (const axis in winners) {
172
+ out[axis] = pick(winners[axis].rule.compiled)?.[axis];
173
+ }
174
+ return out;
175
+ }
176
+
177
+ /**
178
+ * Resolves wire, thinking, and catalog assignments for one structured target.
179
+ *
180
+ * Thinking axes are gated on `target.reasoning`, except that an exact model
181
+ * selector declaring `thinking-efforts` upgrades the target (a reviewed
182
+ * correction to stale capability metadata). Family and revision selectors
183
+ * never match targets missing that identity rank. Unmatched targets resolve
184
+ * to empty maps.
185
+ *
186
+ * @throws AmbiguousOverlapError when two equal-rank rules contest one axis.
187
+ */
188
+ export function resolveCascade(target: ResolveTarget): ResolvedAxes {
189
+ return resolveOverIndex(getRuleIndex(), target);
190
+ }
191
+
192
+ /**
193
+ * Resolves a target against an arbitrary compiled cascade (test seam and
194
+ * scratch evaluations); `resolveCascade` delegates here with the bundled
195
+ * rule index.
196
+ */
197
+ export function resolveCascadeRules(cascade: CompiledCascade, target: ResolveTarget): ResolvedAxes {
198
+ return resolveOverIndex(buildRuleIndex(cascade), target);
199
+ }
200
+
201
+ function resolveOverIndex(index: IndexedRule[], target: ResolveTarget): ResolvedAxes {
202
+ const modelLower = target.model.toLowerCase();
203
+ const revision = target.revision === undefined ? undefined : parseRevision(target.revision);
204
+ const wire: WinnerTable = {};
205
+ const thinking: WinnerTable = {};
206
+ const catalog: WinnerTable = {};
207
+ let reasoning = target.reasoning;
208
+ if (!reasoning) {
209
+ for (const rule of index) {
210
+ if (!rule.hasExactEffortsRule) continue;
211
+ const rank = rankRule(rule, target, revision, modelLower);
212
+ if (rank && rank[0] === 2) {
213
+ reasoning = true;
214
+ break;
215
+ }
216
+ }
217
+ }
218
+ for (const rule of index) {
219
+ const rank = rankRule(rule, target, revision, modelLower);
220
+ if (!rank) continue;
221
+ contest(wire, rule.compiled.wire, rank, rule, target);
222
+ contest(catalog, rule.compiled.catalog, rank, rule, target);
223
+ if (reasoning) contest(thinking, rule.compiled.thinking, rank, rule, target);
224
+ }
225
+ return {
226
+ wire: collect(wire, rule => rule.wire),
227
+ thinking: collect(thinking, rule => rule.thinking),
228
+ catalog: collect(catalog, rule => rule.catalog),
229
+ };
230
+ }