opencode-model-price-intel 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -72,7 +72,7 @@ list the same plugin twice.
72
72
  | --- | --- | --- |
73
73
  | `decorateNames` | `true` | Append the price/score summary to `/models` names. The `/model-pricing` dialog still works when this is off. |
74
74
  | `costFormat` | `"detailed"` | `"detailed"` renders `in $2.5 out $15 · cache read $0.25 write $0`; `"compact"` renders `$2.5/$15/$0.25/$0`. |
75
- | `aaApiKey` | unset | Artificial Analysis API key. Falls back to `ARTIFICIALANALYSIS_API_KEY`. Without either, the public model-list page is used. |
75
+ | `aaApiKey` | unset | Artificial Analysis API key. Falls back to `ARTIFICIALANALYSIS_API_KEY`. Without either, the public leaderboard page is used. |
76
76
  | `refreshHours` | `12` | Score cache TTL and refresh interval, clamped to `1..596` hours. |
77
77
 
78
78
  Invalid option values log one warning and fall back to the defaults.
@@ -110,10 +110,12 @@ scrolls vertically (and horizontally when the window is narrow).
110
110
  - Prices come from `Model.Info.cost` in the live OpenCode registry. The plugin
111
111
  does not bundle a price table and does not convert currencies.
112
112
  - Scores are fetched from the Artificial Analysis Free Data API when a key is
113
- configured, otherwise from the public model-list page.
114
- - A bundled v5.0 snapshot (captured 2026-10-31) is registered before any
113
+ configured, otherwise from the public leaderboard page. The index version
114
+ (including patch releases such as v5.0.1) is read from the live payload, so
115
+ new releases are adopted automatically.
116
+ - A bundled v5.0.1 snapshot (captured 2026-11-14) is registered before any
115
117
  network or storage work, so the first render is immediate. It is only merged
116
- with live data from the same **major** index version.
118
+ with live data from the exact same index version.
117
119
  - The last good result is cached in OpenCode plugin storage for `refreshHours`.
118
120
  A cache older than 30 days is ignored.
119
121
  - The model registry reloads only when the score data actually changes.
@@ -128,8 +130,8 @@ scrolls vertically (and horizontally when the window is narrow).
128
130
  the model **name**. Set `decorateNames: false` to leave names untouched.
129
131
  - Prices are catalog prices, not your invoice. Providers can add surcharges,
130
132
  discounts, or regional pricing that the registry does not carry.
131
- - AA scores are comparative within one index major version. The plugin never
132
- mixes a v5 snapshot into a v6 result.
133
+ - AA scores are comparative within one index version. The plugin never mixes
134
+ scores from different versions, including patch releases.
133
135
  - The plugin only decorates the model list and renders the pricing dialog. It
134
136
  does not route requests or change model selection.
135
137
  - The `/model-pricing` dialog requires the TUI. Other clients still get the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-model-price-intel",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "OpenCode 2 plugin that adds input/output/cache prices and Artificial Analysis Intelligence Index scores to /models, plus a full /model-pricing TUI dialog.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,54 +1,48 @@
1
1
  /**
2
- * Bundled snapshot of the Artificial Analysis Intelligence Index (v5.0).
2
+ * Bundled snapshot of the Artificial Analysis Intelligence Index (v5.0.1).
3
3
  *
4
- * FALLBACK ONLY. The plugin fetches fresh scores from artificialanalysis.ai
5
- * when it refreshes (scores change as new evaluations land). This snapshot
6
- * (captured 2026-10-31) is used when the live fetch fails, and only when the
7
- * live index has the same major version.
4
+ * FALLBACK ONLY. Regenerate with `node scripts/regenerate-snapshot.ts`.
5
+ * Captured 2026-11-14 from the public leaderboard page.
6
+ * See SNAPSHOT-NOTICE.md for provenance and attribution.
8
7
  *
9
- * Canonical keys are `matchKey(name)` values: parenthesized qualifiers such as
10
- * "(Max)" are stripped and punctuation removed. OpenCode catalog IDs whose
11
- * digit order differs from the AA display name are documented in
12
- * `AA_SNAPSHOT_ALIASES` (rather than being silent extra snapshot keys).
13
- *
14
- * Regenerate with `node scripts/regenerate-snapshot.ts`; see SNAPSHOT-NOTICE.md.
15
- *
16
- * Source: https://artificialanalysis.ai/ (Intelligence Index v5.0).
17
- * Attribution required by Artificial Analysis terms.
8
+ * Canonical keys are `matchKey(name)` values; OpenCode ID-order aliases are
9
+ * documented in AA_SNAPSHOT_ALIASES. Do not mix scores across index versions.
18
10
  */
19
11
 
20
12
  export interface AAScore {
21
- /** Intelligence Index score, or null when not scored in v5.0. */
13
+ /** Intelligence Index score, or null when not scored in this version. */
22
14
  score: number | null
23
15
  /** Name of the exact AA entry matched (includes effort variant). */
24
16
  name: string
25
- /** Caveats, e.g. effort variant or historical v4.x scores. */
17
+ /** Caveats, e.g. effort variant or historical scores. */
26
18
  note?: string
27
19
  }
28
20
 
29
- export const AA_SNAPSHOT_VERSION = "5.0"
21
+ export const AA_SNAPSHOT_VERSION = "5.0.1"
30
22
  /** Kept for callers that imported the original constant. */
31
23
  export const AA_INDEX_VERSION = AA_SNAPSHOT_VERSION
32
- export const AA_SNAPSHOT_DATE = "2026-10-31"
24
+ export const AA_SNAPSHOT_DATE = "2026-11-14"
33
25
  /** Capture timestamp (UTC midnight on the capture date), not "now". */
34
26
  export const AA_SNAPSHOT_CAPTURED_AT = `${AA_SNAPSHOT_DATE}T00:00:00.000Z`
35
27
 
36
- /**
37
- * Documented OpenCode-ID aliases -> canonical snapshot key.
38
- *
39
- * - `claude-haiku-4-5` normalizes to `claudehaiku45`, while AA's display name
40
- * "Claude 4.5 Haiku" normalizes to `claude45haiku`.
41
- * - `claude-sonnet-4-5` has the same ID-order/name-order mismatch.
42
- * - `muse-wonder-1.0` normalizes to `musewonder10`; the scored AA entry is
43
- * "Muse Wonder (Max)" (`musewonder`).
44
- */
28
+ /** Documented OpenCode-ID aliases -> canonical snapshot key. */
45
29
  export const AA_SNAPSHOT_ALIASES: Readonly<Record<string, string>> = Object.freeze({
46
- claudehaiku45: "claude45haiku",
47
- claudesonnet45: "claude45sonnet",
48
- musewonder10: "musewonder",
30
+ "claudehaiku45": "claude45haiku",
31
+ "claudesonnet45": "claude45sonnet",
32
+ "deepseekv4pro": "deepseekv4pro0813",
33
+ "musewonder10": "musewonder",
34
+ "nemotron3ultra": "nemotron3ultra550ba55b",
49
35
  })
50
36
 
51
37
  export const AA_SNAPSHOT: Readonly<Record<string, AAScore>> = {
38
+ "agnes25protheta": {
39
+ score: 24.029012523359,
40
+ name: "Agnes 2.5 Pro Theta",
41
+ },
42
+ "apodex11": {
43
+ score: 13.6712884627289,
44
+ name: "Apodex 1.1",
45
+ },
52
46
  "beam10": {
53
47
  score: 17.7307282547205,
54
48
  name: "Beam 1.0",
@@ -56,246 +50,262 @@ export const AA_SNAPSHOT: Readonly<Record<string, AAScore>> = {
56
50
  "bigpickle": {
57
51
  score: null,
58
52
  name: "Big Pickle",
59
- note: "No Artificial Analysis Intelligence Index score found (v5.0 or earlier). Stealth model (per OpenCode docs); not listed on Artificial Analysis; no score found.",
53
+ note: "No Artificial Analysis Intelligence Index score found (v5.0.1 or earlier). Stealth model (per OpenCode docs); not listed on Artificial Analysis; no score found.",
54
+ },
55
+ "claude45haiku": {
56
+ score: null,
57
+ name: "Claude 4.5 Haiku",
58
+ note: "Not scored in v5.0.1. v4.2 score 22.5 (llmcompare snapshot 2026-10-12). OpenCode ID alias: claude-haiku-4-5.",
59
+ },
60
+ "claude45sonnet": {
61
+ score: null,
62
+ name: "Claude 4.5 Sonnet",
63
+ note: "Not scored in v5.0.1. v4.0-era score 43 (Reasoning, per AA Sonnet 4.6 article, Feb 2026). OpenCode ID alias: claude-sonnet-4-5.",
60
64
  },
61
65
  "claudefable5": {
62
66
  score: null,
63
67
  name: "Claude Fable 5",
64
- note: "Not scored in v5.0. v4.2 score 53.2 (llmcompare snapshot 2026-10-12); v4.1: 59.9 (with Opus 4.8 fallback).",
68
+ note: "Not scored in v5.0.1. v4.2 score 53.2 (llmcompare snapshot 2026-10-12); v4.1: 59.9 (with Opus 4.8 fallback).",
65
69
  },
66
70
  "claudefable51": {
67
- score: 49.0102124158081,
68
- name: "Claude Fable 5.1 (Max, Default Fallback)",
69
- note: "AA variant: (max, default fallback)",
70
- },
71
- "claude45haiku": {
72
71
  score: null,
73
- name: "Claude 4.5 Haiku",
74
- note: "Not scored in v5.0. v4.2 score 22.5 (llmcompare snapshot 2026-10-12). OpenCode ID alias: claude-haiku-4-5.",
72
+ name: "Claude Fable 5.1",
73
+ note: "Not scored in v5.0.1. AA variant: (max, default fallback)",
74
+ },
75
+ "claudefable52": {
76
+ score: 64.563595756626,
77
+ name: "Claude Fable 5.2 (Max, Default Fallback)",
78
+ note: "AA variant: (max, default fallback)",
75
79
  },
76
80
  "claudehaiku55": {
77
- score: 36.2345435880142,
81
+ score: 36.4845435880142,
78
82
  name: "Claude Haiku 5.5 (Max)",
79
83
  },
80
84
  "claudeopus45": {
81
85
  score: null,
82
86
  name: "Claude Opus 4.5",
83
- note: "Not scored in v5.0. v4.1 score 34.7.",
87
+ note: "Not scored in v5.0.1. v4.1 score 34.7.",
84
88
  },
85
89
  "claudeopus46": {
86
90
  score: null,
87
91
  name: "Claude Opus 4.6",
88
- note: "Not scored in v5.0. v4.1 score 37.8 (v4.0-era: 53 max).",
92
+ note: "Not scored in v5.0.1. v4.1 score 37.8 (v4.0-era: 53 max).",
89
93
  },
90
94
  "claudeopus47": {
91
95
  score: null,
92
96
  name: "Claude Opus 4.7",
93
- note: "Not scored in v5.0. v4.1 score 53.5; v4.0: 57.3.",
97
+ note: "Not scored in v5.0.1. v4.1 score 53.5; v4.0: 57.3.",
94
98
  },
95
99
  "claudeopus48": {
96
100
  score: null,
97
101
  name: "Claude Opus 4.8",
98
- note: "Not scored in v5.0. v4.2 score 42.0 (llmcompare snapshot 2026-10-12); v4.1: 55.7 (max); v4.0: 61.4.",
102
+ note: "Not scored in v5.0.1. v4.2 score 42.0 (llmcompare snapshot 2026-10-12); v4.1: 55.7 (max); v4.0: 61.4.",
99
103
  },
100
104
  "claudeopus5": {
101
105
  score: null,
102
106
  name: "Claude Opus 5",
103
- note: "Not scored in v5.0. v4.2 score 54.1 (llmcompare snapshot 2026-10-12).",
107
+ note: "Not scored in v5.0.1. v4.2 score 54.1 (llmcompare snapshot 2026-10-12).",
104
108
  },
105
109
  "claudeopus55": {
106
- score: 58.4060336078904,
110
+ score: 59.1060336078904,
107
111
  name: "Claude Opus 5.5 (Max, Default Fallback)",
108
112
  note: "AA variant: (max, default fallback)",
109
113
  },
110
- "claude45sonnet": {
111
- score: null,
112
- name: "Claude 4.5 Sonnet",
113
- note: "Not scored in v5.0. v4.0-era score 43 (Reasoning, per AA Sonnet 4.6 article, Feb 2026). OpenCode ID alias: claude-sonnet-4-5.",
114
- },
115
114
  "claudesonnet46": {
116
115
  score: null,
117
116
  name: "Claude Sonnet 4.6",
118
- note: "Not scored in v5.0. v4.2 score 30.5 (llmcompare snapshot 2026-10-12); v4.1: 35.9.",
117
+ note: "Not scored in v5.0.1. v4.2 score 30.5 (llmcompare snapshot 2026-10-12); v4.1: 35.9.",
119
118
  },
120
119
  "claudesonnet5": {
121
120
  score: null,
122
121
  name: "Claude Sonnet 5",
123
- note: "Not scored in v5.0. v4.2 score 45.1 (llmcompare snapshot 2026-10-12).",
122
+ note: "Not scored in v5.0.1. v4.2 score 45.1 (llmcompare snapshot 2026-10-12).",
124
123
  },
125
124
  "claudesonnet55": {
126
- score: 54.24133407109,
125
+ score: 54.54133407109,
127
126
  name: "Claude Sonnet 5.5 (Max, Default Fallback)",
128
127
  note: "AA variant: (max, default fallback)",
129
128
  },
129
+ "commanda": {
130
+ score: 6.24120025576958,
131
+ name: "Command A+",
132
+ },
130
133
  "deepseekv41flash": {
131
134
  score: null,
132
135
  name: "DeepSeek V4.1 Flash",
133
- note: "Base snapshot not scored in v5.0; v5.0 scores DeepSeek V4.1 Flash 1017 (Max) at 32.5. v4.2: 39.5 (llmcompare snapshot 2026-10-12).",
136
+ note: "Base snapshot entry is not scored in v5.0.1; the scored AA entry is DeepSeek V4.1 Flash 1017 (Max). v4.2: 39.5 (llmcompare snapshot 2026-10-12).",
134
137
  },
135
138
  "deepseekv41flash1017": {
136
- score: 32.4896393532027,
139
+ score: 32.5896393532027,
137
140
  name: "DeepSeek V4.1 Flash 1017 (Max)",
138
141
  },
139
- "deepseekv4pro": {
140
- score: 23.1,
141
- name: "DeepSeek V4 Pro",
142
- note: "v5.0 score; AA entry name: DeepSeek V4 Pro 0813 (Max).",
142
+ "deepseekv4pro0813": {
143
+ score: 23.0632981493003,
144
+ name: "DeepSeek V4 Pro 0813 (Max)",
143
145
  },
144
146
  "flint40b": {
145
147
  score: null,
146
148
  name: "Flint 40B",
147
- note: "No Artificial Analysis Intelligence Index score found (v5.0 or earlier). Not listed on Artificial Analysis; no score found.",
149
+ note: "No Artificial Analysis Intelligence Index score found (v5.0.1 or earlier). Not listed on Artificial Analysis; no score found.",
148
150
  },
149
151
  "gemini31pro": {
150
152
  score: null,
151
153
  name: "Gemini 3.1 Pro",
152
- note: "Not scored in v5.0. v4.2 score 36.7 (llmcompare snapshot 2026-10-12); v4.1: 46.5 (Preview).",
154
+ note: "Not scored in v5.0.1. v4.2 score 36.7 (llmcompare snapshot 2026-10-12); v4.1: 46.5 (Preview).",
153
155
  },
154
156
  "gemini35flash": {
155
157
  score: null,
156
158
  name: "Gemini 3.5 Flash",
157
- note: "Not scored in v5.0. v4.2 score 39.7 (llmcompare snapshot 2026-10-12); v4.1: 50.2 (high).",
159
+ note: "Not scored in v5.0.1. v4.2 score 39.7 (llmcompare snapshot 2026-10-12); v4.1: 50.2 (high).",
158
160
  },
159
161
  "gemini35flashlite": {
160
- score: 11.7,
162
+ score: 11.6563462044212,
161
163
  name: "Gemini 3.5 Flash-Lite",
162
- note: "v5.0 score; AA entry name: Gemini 3.5 Flash-Lite.",
163
164
  },
164
165
  "gemini36flash": {
165
166
  score: null,
166
167
  name: "Gemini 3.6 Flash",
167
- note: "Not scored in v5.0. v4.2 score 40.3 (llmcompare snapshot 2026-10-12).",
168
+ note: "Not scored in v5.0.1. v4.2 score 40.3 (llmcompare snapshot 2026-10-12).",
168
169
  },
169
170
  "gemini37flash": {
170
171
  score: null,
171
172
  name: "Gemini 3.7 Flash",
172
- note: "Not scored in v5.0. v4.2 score 45.2 (llmcompare snapshot 2026-10-12).",
173
+ note: "Not scored in v5.0.1. v4.2 score 45.2 (llmcompare snapshot 2026-10-12).",
173
174
  },
174
175
  "gemini38flash": {
175
- score: 30.5468119957291,
176
+ score: 30.8468119957291,
176
177
  name: "Gemini 3.8 Flash (High)",
177
178
  note: "AA variant: (high)",
178
179
  },
179
180
  "gemini3flash": {
180
181
  score: null,
181
182
  name: "Gemini 3 Flash",
182
- note: "Not scored in v5.0. v4.1 score 37.8 (Preview, Reasoning).",
183
+ note: "Not scored in v5.0.1. v4.1 score 37.8 (Preview, Reasoning).",
183
184
  },
184
185
  "gemini4argon": {
185
- score: 50.4,
186
- name: "Gemini 4 Argon",
187
- note: "v5.0 score; AA entry name: Gemini 4 Argon (High).",
186
+ score: 50.4121935905389,
187
+ name: "Gemini 4 Argon (High)",
188
+ note: "AA variant: (high)",
188
189
  },
189
190
  "gemini4carbon": {
190
- score: 59.425738829526,
191
+ score: 60.025738829526,
191
192
  name: "Gemini 4 Carbon (High)",
192
193
  note: "AA variant: (high)",
193
194
  },
195
+ "gemini4hydrogen": {
196
+ score: 53.9192287317542,
197
+ name: "Gemini 4 Hydrogen (Xhigh)",
198
+ note: "AA variant: (xhigh)",
199
+ },
200
+ "gemma431b": {
201
+ score: 4.38438284215374,
202
+ name: "Gemma 4 31B (Reasoning)",
203
+ note: "AA variant: (reasoning)",
204
+ },
194
205
  "glm5": {
195
206
  score: null,
196
- name: "GLM 5",
197
- note: "Not scored in v5.0. v4.1 score 39.5 (v4.0-era: 50).",
207
+ name: "GLM-5",
208
+ note: "Not scored in v5.0.1. v4.1 score 39.5 (v4.0-era: 50).",
198
209
  },
199
210
  "glm51": {
200
211
  score: null,
201
- name: "GLM 5.1",
202
- note: "Not scored in v5.0. v4.2 score 26.4 (llmcompare snapshot 2026-10-12); v4.1: 40.2.",
212
+ name: "GLM-5.1",
213
+ note: "Not scored in v5.0.1. v4.2 score 26.4 (llmcompare snapshot 2026-10-12); v4.1: 40.2.",
203
214
  },
204
215
  "glm52": {
205
216
  score: null,
206
- name: "GLM 5.2",
207
- note: "Not scored in v5.0. v4.2 score 34.0 (llmcompare snapshot 2026-10-12).",
217
+ name: "GLM-5.2",
218
+ note: "Not scored in v5.0.1. v4.2 score 34.0 (llmcompare snapshot 2026-10-12).",
208
219
  },
209
220
  "glm53": {
210
221
  score: 31.653381843882,
211
222
  name: "GLM-5.3 (Max)",
212
223
  },
213
224
  "glm53flash": {
214
- score: 27.8,
215
- name: "GLM 5.3 Flash",
216
- note: "v5.0 score; AA entry name: GLM-5.3-Flash.",
225
+ score: 27.803889160883,
226
+ name: "GLM-5.3-Flash",
217
227
  },
218
228
  "gpt5": {
219
229
  score: null,
220
230
  name: "GPT-5",
221
- note: "Not scored in v5.0. v4.1 score 36.1.",
231
+ note: "Not scored in v5.0.1. v4.1 score 36.1.",
222
232
  },
223
233
  "gpt51": {
224
234
  score: null,
225
235
  name: "GPT-5.1",
226
- note: "Not scored in v5.0. v4.1 score 38.9.",
236
+ note: "Not scored in v5.0.1. v4.1 score 38.9.",
227
237
  },
228
238
  "gpt52": {
229
239
  score: null,
230
240
  name: "GPT-5.2",
231
- note: "Not scored in v5.0. v4.1 score 42.2 (xhigh); v4.0-era report: 51.",
241
+ note: "Not scored in v5.0.1. v4.1 score 42.2 (xhigh); v4.0-era report: 51.",
232
242
  },
233
243
  "gpt53codex": {
234
244
  score: null,
235
- name: "GPT-5.3 Codex",
236
- note: "Not scored in v5.0. v4.1 score 44.3 (xhigh); v4.0-era report: 54.",
245
+ name: "GPT-5.3 Codex (Xhigh)",
246
+ note: "Not scored in v5.0.1. v4.1 score 44.3 (xhigh); v4.0-era report: 54.",
237
247
  },
238
248
  "gpt53codexspark": {
239
249
  score: null,
240
250
  name: "GPT-5.3 Codex Spark",
241
- note: "No Artificial Analysis Intelligence Index score found (v5.0 or earlier). Not listed on Artificial Analysis; no score found.",
251
+ note: "No Artificial Analysis Intelligence Index score found (v5.0.1 or earlier). Not listed on Artificial Analysis; no score found.",
242
252
  },
243
253
  "gpt54": {
244
254
  score: null,
245
255
  name: "GPT-5.4",
246
- note: "Not scored in v5.0. v4.1 score 51.4 (xhigh); v4.0-era report: 57 (tied with Gemini 3.1 Pro Preview).",
256
+ note: "Not scored in v5.0.1. v4.1 score 51.4 (xhigh); v4.0-era report: 57 (tied with Gemini 3.1 Pro Preview).",
247
257
  },
248
258
  "gpt54mini": {
249
259
  score: null,
250
260
  name: "GPT-5.4 mini",
251
- note: "Not scored in v5.0. v4.2 score 24.6 (llmcompare snapshot 2026-10-12).",
261
+ note: "Not scored in v5.0.1. v4.2 score 24.6 (llmcompare snapshot 2026-10-12).",
252
262
  },
253
263
  "gpt54nano": {
254
264
  score: null,
255
265
  name: "GPT-5.4 nano",
256
- note: "Not scored in v5.0. v4.2 score 21.2 (llmcompare snapshot 2026-10-12).",
266
+ note: "Not scored in v5.0.1. v4.2 score 21.2 (llmcompare snapshot 2026-10-12).",
257
267
  },
258
268
  "gpt54pro": {
259
269
  score: null,
260
270
  name: "GPT-5.4 Pro",
261
- note: "No Artificial Analysis Intelligence Index score found (v5.0 or earlier). AA lists GPT-5.4 Pro (Xhigh) but has not scored it; no historical score found.",
271
+ note: "No Artificial Analysis Intelligence Index score found (v5.0.1 or earlier). AA lists GPT-5.4 Pro (Xhigh) but has not scored it; no historical score found.",
262
272
  },
263
273
  "gpt55": {
264
274
  score: null,
265
275
  name: "GPT-5.5",
266
- note: "Not scored in v5.0. v4.2 score 38.6 (llmcompare snapshot 2026-10-12); v4.1: 54.8 (xhigh).",
276
+ note: "Not scored in v5.0.1. v4.2 score 38.6 (llmcompare snapshot 2026-10-12); v4.1: 54.8 (xhigh).",
267
277
  },
268
278
  "gpt55pro": {
269
279
  score: null,
270
- name: "GPT-5.5 Pro",
271
- note: "No Artificial Analysis Intelligence Index score found (v5.0 or earlier). AA lists GPT-5.5 Pro (Xhigh) but has never scored it; no historical score found.",
280
+ name: "GPT-5.5 Pro (Xhigh)",
281
+ note: "No Artificial Analysis Intelligence Index score found (v5.0.1 or earlier). AA lists GPT-5.5 Pro (Xhigh) but has never scored it; no historical score found.",
272
282
  },
273
283
  "gpt56luna": {
274
284
  score: null,
275
285
  name: "GPT-5.6 Luna",
276
- note: "Not scored in v5.0. v4.2 score 43.4 (llmcompare snapshot 2026-10-12).",
286
+ note: "Not scored in v5.0.1. v4.2 score 43.4 (llmcompare snapshot 2026-10-12).",
277
287
  },
278
288
  "gpt56sol": {
279
289
  score: null,
280
290
  name: "GPT-5.6 Sol",
281
- note: "Not scored in v5.0. v4.2 score 51.3 (llmcompare snapshot 2026-10-12).",
291
+ note: "Not scored in v5.0.1. v4.2 score 51.3 (llmcompare snapshot 2026-10-12).",
282
292
  },
283
293
  "gpt56terra": {
284
294
  score: null,
285
295
  name: "GPT-5.6 Terra",
286
- note: "Not scored in v5.0. v4.2 score 46.8 (llmcompare snapshot 2026-10-12).",
296
+ note: "Not scored in v5.0.1. v4.2 score 46.8 (llmcompare snapshot 2026-10-12).",
287
297
  },
288
298
  "gpt5nano": {
289
299
  score: null,
290
300
  name: "GPT-5 nano",
291
- note: "No Artificial Analysis Intelligence Index score found (v5.0 or earlier). AA lists GPT-5 nano but has not scored it; no historical score found.",
301
+ note: "No Artificial Analysis Intelligence Index score found (v5.0.1 or earlier). AA lists GPT-5 nano but has not scored it; no historical score found.",
292
302
  },
293
303
  "gpt61sol": {
294
- score: 54.5087338138148,
304
+ score: 54.6087338138148,
295
305
  name: "GPT-6.1 Sol (Max)",
296
306
  },
297
307
  "gpt61terra": {
298
- score: 50.9765188577015,
308
+ score: 51.3265188577015,
299
309
  name: "GPT-6.1 Terra (Max)",
300
310
  },
301
311
  "gpt6astra": {
@@ -303,110 +313,138 @@ export const AA_SNAPSHOT: Readonly<Record<string, AAScore>> = {
303
313
  name: "GPT-6 Astra (Max)",
304
314
  },
305
315
  "gpt6luna": {
306
- score: 27.7552004938153,
316
+ score: 27.8552004938153,
307
317
  name: "GPT-6 Luna (Max)",
308
318
  },
309
319
  "gpt6sol": {
310
320
  score: null,
311
- name: "GPT-6 Sol",
312
- note: "No Artificial Analysis Intelligence Index score found (v5.0 or earlier). AA lists GPT-6 Sol (Max) but has not scored it in v5.0; no v4.x score found.",
321
+ name: "GPT-6 Sol (Daybreak Blue, max)",
322
+ note: "No Artificial Analysis Intelligence Index score found (v5.0.1 or earlier). AA lists GPT-6 Sol (Max) but has not scored it in v5.0; no v4.x score found.",
323
+ },
324
+ "gptoss120b": {
325
+ score: 3.81981165137417,
326
+ name: "gpt-oss-120b (High)",
327
+ note: "AA variant: (high)",
328
+ },
329
+ "granite423b": {
330
+ score: 4.72268717901823,
331
+ name: "Granite 4.2 3B",
332
+ },
333
+ "granite428b": {
334
+ score: 5.09826495217051,
335
+ name: "Granite 4.2 8B",
313
336
  },
314
337
  "grok45": {
315
338
  score: null,
316
339
  name: "Grok 4.5",
317
- note: "Not scored in v5.0. v4.2 score 45.5 (llmcompare snapshot 2026-10-12).",
340
+ note: "Not scored in v5.0.1. v4.2 score 45.5 (llmcompare snapshot 2026-10-12).",
318
341
  },
319
342
  "grok46": {
320
343
  score: null,
321
344
  name: "Grok 4.6",
322
- note: "Not scored in v5.0. v4.2 score 50.6 (llmcompare snapshot 2026-10-12).",
345
+ note: "Not scored in v5.0.1. v4.2 score 50.6 (llmcompare snapshot 2026-10-12).",
323
346
  },
324
347
  "grok47": {
325
- score: 34.3350117067196,
348
+ score: 35.4850117067196,
326
349
  name: "Grok 4.7 (Xhigh)",
327
350
  note: "AA variant: (xhigh)",
328
351
  },
329
352
  "grokbuild01": {
330
353
  score: null,
331
354
  name: "Grok Build 0.1",
332
- note: "No Artificial Analysis Intelligence Index score found (v5.0 or earlier). AA lists Grok Build 0.1 0616 but has not scored it; no historical score found.",
355
+ note: "No Artificial Analysis Intelligence Index score found (v5.0.1 or earlier). AA lists Grok Build 0.1 0616 but has not scored it; no historical score found.",
333
356
  },
334
357
  "hy3": {
335
358
  score: null,
336
359
  name: "Hy3",
337
- note: "Not scored in v5.0. v4.2 score 25.8 (llmcompare snapshot 2026-10-12).",
360
+ note: "Not scored in v5.0.1. v4.2 score 25.8 (llmcompare snapshot 2026-10-12).",
338
361
  },
339
362
  "hy4preview": {
340
363
  score: null,
341
364
  name: "Hy4 preview",
342
- note: "No Artificial Analysis Intelligence Index score found (v5.0 or earlier). Not listed on Artificial Analysis; no score found.",
365
+ note: "No Artificial Analysis Intelligence Index score found (v5.0.1 or earlier). Not listed on Artificial Analysis; no score found.",
343
366
  },
344
367
  "icemancontributor": {
345
368
  score: null,
346
369
  name: "Iceman Contributor",
347
- note: "No Artificial Analysis Intelligence Index score found (v5.0 or earlier). Not listed on Artificial Analysis; no score found.",
370
+ note: "No Artificial Analysis Intelligence Index score found (v5.0.1 or earlier). Not listed on Artificial Analysis; no score found.",
348
371
  },
349
372
  "inkling": {
350
373
  score: 12.4781698715401,
351
374
  name: "Inkling (Xhigh)",
352
375
  note: "AA variant: (xhigh)",
353
376
  },
377
+ "inkling2small": {
378
+ score: 30.6286781498958,
379
+ name: "Inkling 2 Small",
380
+ },
381
+ "inklingsmall": {
382
+ score: 12.9101620926084,
383
+ name: "Inkling Small",
384
+ },
354
385
  "jev113": {
355
386
  score: null,
356
387
  name: "Jev 1.13",
357
- note: "No Artificial Analysis Intelligence Index score found (v5.0 or earlier). System One decision model (TypeSafe AI); not listed on Artificial Analysis; no score found.",
388
+ note: "No Artificial Analysis Intelligence Index score found (v5.0.1 or earlier). System One decision model (TypeSafe AI); not listed on Artificial Analysis; no score found.",
389
+ },
390
+ "jt41flash236ba21b": {
391
+ score: 18.952876906667,
392
+ name: "JT-4.1 Flash 236B A21B",
358
393
  },
359
394
  "k2horizon375ba23b": {
360
- score: 17.7702731734027,
395
+ score: 17.8202731734027,
361
396
  name: "K2 Horizon 375B A23B",
362
397
  },
398
+ "kexaone20": {
399
+ score: 7.56357436827474,
400
+ name: "K-EXAONE 2.0",
401
+ },
363
402
  "kimik25": {
364
403
  score: null,
365
404
  name: "Kimi K2.5",
366
- note: "Not scored in v5.0. v4.1 score 38.1 (v4.0-era: 47 Reasoning).",
405
+ note: "Not scored in v5.0.1. v4.1 score 38.1 (v4.0-era: 47 Reasoning).",
367
406
  },
368
407
  "kimik26": {
369
408
  score: null,
370
409
  name: "Kimi K2.6",
371
- note: "Not scored in v5.0. v4.2 score 27.5 (llmcompare snapshot 2026-10-12); v4.1: 42.8.",
410
+ note: "Not scored in v5.0.1. v4.2 score 27.5 (llmcompare snapshot 2026-10-12); v4.1: 42.8.",
372
411
  },
373
412
  "kimik27code": {
374
413
  score: null,
375
414
  name: "Kimi K2.7 Code",
376
- note: "Not scored in v5.0. v4.2 score 26.3 (llmcompare snapshot 2026-10-12).",
415
+ note: "Not scored in v5.0.1. v4.2 score 26.3 (llmcompare snapshot 2026-10-12).",
377
416
  },
378
417
  "kimik3": {
379
418
  score: null,
380
419
  name: "Kimi K3",
381
- note: "Not scored in v5.0 at max effort (only Kimi K3 (Low): 17.4). v4.2 score 50.2 (llmcompare snapshot 2026-10-12).",
420
+ note: "Not scored in v5.0.1 at max effort (only Kimi K3 (Low): 17.4). v4.2 score 50.2 (llmcompare snapshot 2026-10-12).",
382
421
  },
383
422
  "kimik31pro": {
384
- score: 46.0026236744189,
423
+ score: 46.4526236744189,
385
424
  name: "Kimi K3.1 Pro",
386
425
  },
387
426
  "ling31flash1019": {
388
- score: 29.470119369837,
427
+ score: 29.570119369837,
389
428
  name: "Ling 3.1 Flash 1019",
390
429
  },
391
430
  "longcat20": {
392
431
  score: null,
393
- name: "LongCat-2.0",
394
- note: "Not scored in v5.0. v4.2 score 19.7 (llmcompare snapshot 2026-10-12).",
432
+ name: "LongCat 2.0",
433
+ note: "Not scored in v5.0.1. v4.2 score 19.7 (llmcompare snapshot 2026-10-12).",
395
434
  },
396
435
  "mimov25": {
397
436
  score: null,
398
437
  name: "MiMo-V2.5",
399
- note: "Not scored in v5.0. v4.2 score 22.3 (llmcompare snapshot 2026-10-12).",
438
+ note: "Not scored in v5.0.1. v4.2 score 22.3 (llmcompare snapshot 2026-10-12).",
400
439
  },
401
440
  "mimov25pro": {
402
441
  score: null,
403
442
  name: "MiMo-V2.5-Pro",
404
- note: "Not scored in v5.0. v4.2 score 32.6 (llmcompare snapshot 2026-10-12); v4.1: 42.2.",
443
+ note: "Not scored in v5.0.1. v4.2 score 32.6 (llmcompare snapshot 2026-10-12); v4.1: 42.2.",
405
444
  },
406
445
  "mimov26flash": {
407
- score: 26.7,
446
+ score: 26.7276981444568,
408
447
  name: "MiMo-V2.6-Flash",
409
- note: "v5.0 score; AA entry name: MiMo-V2.6-Flash.",
410
448
  },
411
449
  "mimov26pro": {
412
450
  score: 29.8897833093321,
@@ -414,80 +452,107 @@ export const AA_SNAPSHOT: Readonly<Record<string, AAScore>> = {
414
452
  },
415
453
  "minimaxm25": {
416
454
  score: null,
417
- name: "MiniMax M2.5",
418
- note: "Not scored in v5.0. v4.0-era score 42 (per AA MiniMax M2.7 article, Mar 2026).",
455
+ name: "MiniMax-M2.5",
456
+ note: "Not scored in v5.0.1. v4.0-era score 42 (per AA MiniMax M2.7 article, Mar 2026).",
419
457
  },
420
458
  "minimaxm27": {
421
459
  score: null,
422
- name: "MiniMax M2.7",
423
- note: "Not scored in v5.0. v4.2 score 23.2 (llmcompare snapshot 2026-10-12); v4.1: 38.1; v4.0-era: 50.",
460
+ name: "MiniMax-M2.7",
461
+ note: "Not scored in v5.0.1. v4.2 score 23.2 (llmcompare snapshot 2026-10-12); v4.1: 38.1; v4.0-era: 50.",
424
462
  },
425
463
  "minimaxm3": {
426
- score: 18.7,
427
- name: "MiniMax M3",
428
- note: "v5.0 score; AA entry name: MiniMax-M3.",
464
+ score: 18.8789083859255,
465
+ name: "MiniMax-M3",
429
466
  },
430
467
  "minimaxm31flash": {
431
- score: 28.9095214290458,
468
+ score: 29.0595214290458,
432
469
  name: "MiniMax-M3.1-Flash",
433
470
  },
434
471
  "mistrallarge4": {
435
- score: 26.0249946754784,
472
+ score: 26.1249946754784,
436
473
  name: "Mistral Large 4 (High)",
437
474
  note: "AA variant: (high)",
438
475
  },
476
+ "mistralmedium35": {
477
+ score: 4.64508756526957,
478
+ name: "Mistral Medium 3.5",
479
+ },
480
+ "mistralsmall4": {
481
+ score: 4.29716249600478,
482
+ name: "Mistral Small 4 (Reasoning)",
483
+ note: "AA variant: (reasoning)",
484
+ },
485
+ "museember": {
486
+ score: 36.0212987864912,
487
+ name: "Muse Ember (Max)",
488
+ },
489
+ "museglimmer": {
490
+ score: 6.34128724913462,
491
+ name: "Muse Glimmer (High)",
492
+ note: "AA variant: (high)",
493
+ },
439
494
  "musespark12": {
440
495
  score: null,
441
496
  name: "Muse Spark 1.2",
442
- note: "Not scored in v5.0. v4.2 score 46.8 (llmcompare snapshot 2026-10-12).",
497
+ note: "Not scored in v5.0.1. v4.2 score 46.8 (llmcompare snapshot 2026-10-12).",
443
498
  },
444
499
  "musespark13": {
445
- score: 36.4,
500
+ score: null,
446
501
  name: "Muse Spark 1.3",
447
- note: "v5.0 score; AA entry name: Muse Spark 1.3 (Max).",
502
+ note: "Not scored in v5.0.1.",
448
503
  },
449
504
  "musesparkopen": {
450
- score: 24.6815868992913,
505
+ score: 24.8815868992913,
451
506
  name: "Muse Spark Open (Xhigh)",
452
507
  note: "AA variant: (xhigh)",
453
508
  },
454
509
  "musewonder": {
455
- score: 43.3248225998431,
510
+ score: 43.5748225998431,
456
511
  name: "Muse Wonder (Max)",
457
- note: "OpenCode ID alias: muse-wonder-1.0.",
458
512
  },
459
513
  "musewonderfull": {
460
- score: 49.7272123627021,
514
+ score: 50.0772123627021,
461
515
  name: "Muse Wonder Full (Max)",
462
516
  },
517
+ "nanbeige4333b": {
518
+ score: 20.4071119588083,
519
+ name: "Nanbeige4.3-33B",
520
+ },
463
521
  "nemotron35lightning": {
464
- score: 5.5,
522
+ score: 5.5346663903388,
465
523
  name: "Nemotron 3.5 Lightning",
466
- note: "v5.0 score; AA entry name: Nemotron 3.5 Lightning.",
467
524
  },
468
525
  "nemotron35supervl": {
469
526
  score: 13.6203422723141,
470
527
  name: "Nemotron 3.5 Super VL",
471
528
  },
472
- "nemotron3ultra": {
473
- score: 11.8,
474
- name: "Nemotron 3 Ultra",
475
- note: "v5.0 score; AA entry name: Nemotron 3 Ultra 550B A55B (Reasoning).",
529
+ "nemotron3ultra550ba55b": {
530
+ score: 11.7592726308177,
531
+ name: "Nemotron 3 Ultra 550B A55B (Reasoning)",
532
+ note: "AA variant: (reasoning)",
533
+ },
534
+ "northminicode": {
535
+ score: 3.42470262148493,
536
+ name: "North Mini Code",
537
+ },
538
+ "nvidianemotron3nano4b": {
539
+ score: 1.81584603785463,
540
+ name: "NVIDIA Nemotron 3 Nano 4B",
476
541
  },
477
542
  "qwen35plus": {
478
543
  score: null,
479
544
  name: "Qwen3.5 Plus",
480
- note: "No Artificial Analysis Intelligence Index score found (v5.0 or earlier). No 'Qwen3.5 Plus' on AA. Nearest entries: Qwen3.5 122B A10B (Reasoning) (v4.1 score 32.3) and Qwen3.5 Omni Plus (unscored).",
545
+ note: "No Artificial Analysis Intelligence Index score found (v5.0.1 or earlier). No 'Qwen3.5 Plus' on AA. Nearest entries: Qwen3.5 122B A10B (Reasoning) (v4.1 score 32.3) and Qwen3.5 Omni Plus (unscored).",
481
546
  },
482
547
  "qwen36plus": {
483
548
  score: null,
484
549
  name: "Qwen3.6 Plus",
485
- note: "Not scored in v5.0. v4.1 score 39.6.",
550
+ note: "Not scored in v5.0.1. v4.1 score 39.6.",
486
551
  },
487
552
  "qwen37plus": {
488
553
  score: null,
489
554
  name: "Qwen3.7 Plus",
490
- note: "Not scored in v5.0. v4.2 score 25.8 (llmcompare snapshot 2026-10-12); v4.1: 39.0.",
555
+ note: "Not scored in v5.0.1. v4.2 score 25.8 (llmcompare snapshot 2026-10-12); v4.1: 39.0.",
491
556
  },
492
557
  "qwen3827b": {
493
558
  score: 20.1068756031705,
@@ -497,19 +562,30 @@ export const AA_SNAPSHOT: Readonly<Record<string, AAScore>> = {
497
562
  "qwen38flash": {
498
563
  score: null,
499
564
  name: "Qwen3.8 Flash",
500
- note: "Not scored in v5.0. v4.2 score 39.9 for Qwen3.8 Flash Next (llmcompare snapshot 2026-10-12). Matched to AA model 'Qwen3.8-Flash-Next' (nearest Flash-tier entry).",
565
+ note: "Not scored in v5.0.1. v4.2 score 39.9 for Qwen3.8 Flash Next (llmcompare snapshot 2026-10-12). Matched to AA model 'Qwen3.8-Flash-Next' (nearest Flash-tier entry).",
566
+ },
567
+ "qwen38flashnext": {
568
+ score: 28.5750026655353,
569
+ name: "Qwen3.8-Flash-Next",
501
570
  },
502
571
  "qwen38max": {
503
- score: 33.5545627367888,
572
+ score: 33.6545627367888,
504
573
  name: "Qwen3.8 Max (0902)",
505
574
  note: "AA variant: (0902)",
506
575
  },
576
+ "solarpro4": {
577
+ score: 14.1589437644357,
578
+ name: "Solar Pro 4",
579
+ },
507
580
  "step5preview": {
508
581
  score: 28.5283090216016,
509
582
  name: "Step 5 Preview",
510
583
  },
584
+ "tencenthy4": {
585
+ score: 34.5552385392508,
586
+ name: "Tencent Hy4",
587
+ },
511
588
  }
512
589
 
513
- // The snapshot is shared process-wide; freeze it so accidental mutation cannot
514
- // change later lookups. `snapshotScores()` returns a frozen copy as well.
590
+ // Shared process-wide; freeze so accidental mutation cannot poison lookups.
515
591
  Object.freeze(AA_SNAPSHOT)
package/src/aa.ts CHANGED
@@ -2,9 +2,10 @@
2
2
  * Live Artificial Analysis Intelligence Index fetcher.
3
3
  *
4
4
  * Scores change as Artificial Analysis publishes new evaluations (the index is
5
- * versioned — currently v5.0), so this runs whenever the plugin refreshes,
6
- * with a cached copy kept in plugin storage and periodic refresh while the
7
- * session is open.
5
+ * versioned — v5.0.1 at the time of writing), so this runs whenever the plugin
6
+ * refreshes, with a cached copy kept in plugin storage and periodic refresh
7
+ * while the session is open. The version is read from the live payload rather
8
+ * than pinned, so a new release is adopted automatically.
8
9
  *
9
10
  * Two sources, in order:
10
11
  *
@@ -15,12 +16,14 @@
15
16
  * accepts any valid key (Free, Pro or Commercial). Attribution:
16
17
  * https://artificialanalysis.ai/
17
18
  *
18
- * 2. The public model-list page (https://artificialanalysis.ai/models), whose
19
- * embedded dataset (`initialModels` records in the RSC payload) carries the
20
- * `intelligenceIndex` score per model variant. Requires no key.
19
+ * 2. The public model leaderboard page
20
+ * (https://artificialanalysis.ai/leaderboards/models), whose embedded
21
+ * dataset carries the `intelligenceIndex` score per model variant. It is a
22
+ * superset of the model-list page and publishes the current index version.
23
+ * Requires no key.
21
24
  *
22
25
  * The bundled snapshot in `aa-snapshot.ts` is only used when both fail, and
23
- * only when its major index version matches the live data.
26
+ * only when its index version exactly matches the live data.
24
27
  */
25
28
 
26
29
  import {
@@ -33,7 +36,7 @@ import {
33
36
  type AAScore,
34
37
  } from "./aa-snapshot.ts"
35
38
 
36
- export const AA_MODELS_URL = "https://artificialanalysis.ai/models"
39
+ export const AA_MODELS_URL = "https://artificialanalysis.ai/leaderboards/models"
37
40
  /** Free-tier endpoint: accepts any valid API key; Pro-only route is not needed. */
38
41
  export const AA_API_URL = "https://artificialanalysis.ai/api/v2/language/models/free"
39
42
  /** Hard safety cap on API pagination (the Free quota is 100 requests/day). */
@@ -90,7 +93,7 @@ export interface AAAPIFetch {
90
93
  export interface AAPageFetch {
91
94
  records: AARecord[]
92
95
  version: string
93
- /** True when the page did not publish a version and 5.0 was assumed. */
96
+ /** True when the page did not publish a version and the bundled version was assumed. */
94
97
  versionAssumed: boolean
95
98
  }
96
99
 
@@ -144,15 +147,17 @@ function finiteNonNegative(value: unknown): number | null {
144
147
  return Number.isFinite(number) && number >= 0 ? number : null
145
148
  }
146
149
 
147
- /** Normalize AA's numeric (`5` / `5.1`) or string (`"5.0"`, `"v5"`) version. */
150
+ /** Normalize AA's numeric (`5` / `5.1`) or string (`"5.0"`, `"v5"`, `"5.0.1"`) version. */
148
151
  export function normalizeVersion(value: unknown, fallback = AA_INDEX_VERSION): string {
149
152
  if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
150
153
  const text = String(value)
151
154
  return text.includes(".") ? text : `${text}.0`
152
155
  }
153
156
  if (typeof value === "string") {
154
- const match = value.trim().match(/^v?(\d+)(?:\.(\d+))?/i)
155
- if (match) return `${match[1]}.${match[2] ?? "0"}`
157
+ const match = value.trim().match(/^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?/i)
158
+ if (match) {
159
+ return `${match[1]}.${match[2] ?? "0"}${match[3] !== undefined ? `.${match[3]}` : ""}`
160
+ }
156
161
  }
157
162
  return fallback
158
163
  }
@@ -160,26 +165,34 @@ export function normalizeVersion(value: unknown, fallback = AA_INDEX_VERSION): s
160
165
  function optionalVersion(value: unknown): string | undefined {
161
166
  if (value === undefined || value === null || value === "") return undefined
162
167
  if (typeof value === "number" && (!Number.isFinite(value) || value < 0)) return undefined
163
- if (typeof value === "string" && !/^v?\d+(?:\.\d+)?/i.test(value.trim())) return undefined
168
+ if (typeof value === "string" && !/^v?\d+(?:\.\d+){0,2}/i.test(value.trim())) return undefined
164
169
  return normalizeVersion(value)
165
170
  }
166
171
 
167
- function majorOf(version: string): string {
168
- return version.split(".")[0] ?? version
169
- }
170
-
171
172
  function countNumericScores(scores: Readonly<Record<string, AAScore>>): number {
172
173
  let count = 0
173
174
  for (const entry of Object.values(scores)) if (entry.score != null) count++
174
175
  return count
175
176
  }
176
177
 
178
+ /** Compare normalized index versions segment by segment (`5.0.1` > `5.0`). */
179
+ function compareVersions(a: string, b: string): number {
180
+ const left = normalizeVersion(a).split(".").map(Number)
181
+ const right = normalizeVersion(b).split(".").map(Number)
182
+ const length = Math.max(left.length, right.length)
183
+ for (let index = 0; index < length; index++) {
184
+ const difference = (left[index] ?? 0) - (right[index] ?? 0)
185
+ if (difference !== 0) return difference
186
+ }
187
+ return 0
188
+ }
189
+
177
190
  // ------------------------------------------------------------------ page
178
191
 
179
192
  /**
180
193
  * Company aggregate rows on AA's evaluations pages are shaped like model
181
- * records. The fallback only reads the model-list page, but filter these
182
- * explicitly as a second line of defence.
194
+ * records. The shared parser filters these explicitly as a second line of
195
+ * defence.
183
196
  */
184
197
  const COMPANY_AGGREGATES = new Set([
185
198
  "google",
@@ -338,9 +351,14 @@ export function parseAARecords(payload: string): AARecord[] {
338
351
  export function parsePageVersion(payload: string): string | undefined {
339
352
  const decoded = payload.includes("self.__next_f.push") ? decodeRSC(payload) : payload
340
353
  const patterns = [
341
- /"intelligenceIndexVersion"\s*:\s*"?v?(\d+(?:\.\d+)?)"?/i,
342
- /"intelligence_index_version"\s*:\s*"?v?(\d+(?:\.\d+)?)"?/i,
343
- /"indexVersion"\s*:\s*"?v?(\d+(?:\.\d+)?)"?/i,
354
+ /"intelligenceIndexVersion"\s*:\s*"?v?(\d+(?:\.\d+){0,2})"?/i,
355
+ /"intelligence_index_version"\s*:\s*"?v?(\d+(?:\.\d+){0,2})"?/i,
356
+ /"indexVersion"\s*:\s*"?v?(\d+(?:\.\d+){0,2})"?/i,
357
+ // Rendered copy and JSON-LD metadata say "Artificial Analysis
358
+ // Intelligence Index v5.0.1"; use it when no machine-readable version key
359
+ // is present.
360
+ /Artificial Analysis Intelligence Index\s+v?(\d+(?:\.\d+){0,2})/i,
361
+ /Intelligence Index\s+v(\d+(?:\.\d+){0,2})/i,
344
362
  ]
345
363
  for (const pattern of patterns) {
346
364
  const match = decoded.match(pattern)
@@ -461,18 +479,34 @@ export function lookupScore(
461
479
  modelName: string,
462
480
  ): AAScore | undefined {
463
481
  const keys = [...new Set([matchKey(modelID), matchKey(modelName)].filter((key) => key.length > 0))]
464
- const exactEntries: AAScore[] = []
482
+ const exactEntries: { key: string; entry: AAScore }[] = []
465
483
  for (const key of keys) {
466
484
  const entry = index[key]
467
485
  if (!entry) continue
468
486
  const duplicate = exactEntries.some(
469
- (existing) => existing === entry || (existing.name === entry.name && existing.score === entry.score),
487
+ ({ entry: existing }) =>
488
+ existing === entry || (existing.name === entry.name && existing.score === entry.score),
470
489
  )
471
- if (!duplicate) exactEntries.push(entry)
490
+ if (!duplicate) exactEntries.push({ key, entry })
491
+ }
492
+ // ID and display name can legitimately resolve to two entries, e.g. a
493
+ // checkpoint-scored model whose ID normalizes to the unscored base name
494
+ // ("deepseek-v4.1-flash") while the display name normalizes to the scored
495
+ // checkpoint ("DeepSeek V4.1 Flash 1017"). When exactly one of them carries
496
+ // a score and the keys are versions of the same name, use it; unrelated
497
+ // names or two distinct scores stay ambiguous.
498
+ if (exactEntries.length > 1) {
499
+ const scored = exactEntries.filter(({ entry }) => entry.score != null)
500
+ if (scored.length === 1) {
501
+ const winner = scored[0]
502
+ const related = exactEntries.every(
503
+ ({ key }) => key === winner.key || winner.key.startsWith(key) || key.startsWith(winner.key),
504
+ )
505
+ if (related) return winner.entry
506
+ }
507
+ return undefined
472
508
  }
473
- // ID and display name disagreeing is ambiguous: show AA n/a rather than guess.
474
- if (exactEntries.length > 1) return undefined
475
- const exact = exactEntries[0]
509
+ const exact = exactEntries[0]?.entry
476
510
  if (exact?.score != null) return exact
477
511
 
478
512
  const candidates = new Map<string, AAScore>()
@@ -490,6 +524,18 @@ export function lookupScore(
490
524
  candidates.set(candidate, entry)
491
525
  }
492
526
  }
527
+ // Some AA entries carry the vendor in the display name ("Tencent Hy4") while
528
+ // the OpenCode catalog uses the short name ("Hy4"). Accept a vendor-prefixed
529
+ // exact match too, but only for a known company prefix so unrelated model
530
+ // families cannot absorb each other's scores.
531
+ for (const key of keys) {
532
+ if (key.length < 3) continue
533
+ for (const [candidate, entry] of Object.entries(index)) {
534
+ if (candidate.length <= key.length || !candidate.endsWith(key)) continue
535
+ const prefix = candidate.slice(0, candidate.length - key.length)
536
+ if (COMPANY_AGGREGATES.has(prefix)) candidates.set(candidate, entry)
537
+ }
538
+ }
493
539
  // Multiple possible siblings are ambiguous: prefer no score over a guess.
494
540
  if (candidates.size > 1) return undefined
495
541
  const suffixed = [...candidates.values()][0]
@@ -535,7 +581,7 @@ function mergeWithSnapshot(
535
581
  let live = 0
536
582
  for (const entry of Object.values(index)) if (entry.score != null) live++
537
583
  let snapshot = 0
538
- if (majorOf(version) === majorOf(AA_SNAPSHOT_VERSION)) {
584
+ if (normalizeVersion(version) === normalizeVersion(AA_SNAPSHOT_VERSION)) {
539
585
  for (const [key, entry] of Object.entries(snapshotIndex())) {
540
586
  const existing = merged[key]
541
587
  if (existing?.score != null) continue
@@ -553,8 +599,10 @@ function mergeWithSnapshot(
553
599
 
554
600
  /**
555
601
  * Use the bundled snapshot to fill gaps the live data does not cover, but only
556
- * when the live index has the same major version. Live non-null scores always
557
- * win; snapshot-derived entries carry a `[snapshot]` note.
602
+ * when the live index is exactly the snapshot's version. Scores change between
603
+ * index updates (including patch releases), so older snapshot values must not
604
+ * be presented as current. Live non-null scores always win; snapshot-derived
605
+ * entries carry a `[snapshot]` note.
558
606
  */
559
607
  export function withSnapshotFallback(
560
608
  index: Readonly<Record<string, AAScore>>,
@@ -799,9 +847,10 @@ export async function fetchFromAPI(
799
847
  }
800
848
 
801
849
  /**
802
- * Scrape the embedded dataset from the public model-list page (no key needed).
803
- * Only the model-list page is used: the evaluations page contains company
804
- * aggregate rows and must never be treated as a model source.
850
+ * Scrape the embedded dataset from the public model leaderboard page (no key
851
+ * needed). The leaderboard is a superset of the model-list page: it carries
852
+ * every scored variant and the current index version. Company aggregate rows
853
+ * are filtered during parsing.
805
854
  */
806
855
  export async function fetchFromPages(
807
856
  fetchImpl: typeof fetch = fetch,
@@ -813,7 +862,7 @@ export async function fetchFromPages(
813
862
  retryDelayMs: options?.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS,
814
863
  }
815
864
  const response = await fetchWithRetry(fetchImpl, AA_MODELS_URL, {}, settings)
816
- if (!response.ok) throw new Error(`Artificial Analysis model page returned ${response.status}`)
865
+ if (!response.ok) throw new Error(`Artificial Analysis leaderboard page returned ${response.status}`)
817
866
  const html = await response.text()
818
867
  const decoded = decodeRSC(html)
819
868
  const records = parseAARecords(decoded)
@@ -827,9 +876,9 @@ export async function fetchFromPages(
827
876
 
828
877
  /**
829
878
  * Fetch current Intelligence Index scores. Tries the Free API when a key is
830
- * configured, then the public model-list page, then falls back to the bundled
879
+ * configured, then the public leaderboard page, then falls back to the bundled
831
880
  * snapshot. Every source completes before the snapshot is used, so a stalled
832
- * API cannot consume the page fallback's abort budget.
881
+ * API cannot consume the leaderboard fallback's abort budget.
833
882
  */
834
883
  export async function fetchAAScores(options?: AAFetchOptions): Promise<AAFetched> {
835
884
  const fetchImpl = options?.fetchImpl ?? fetch
@@ -844,7 +893,7 @@ export async function fetchAAScores(options?: AAFetchOptions): Promise<AAFetched
844
893
  if (apiKey) {
845
894
  try {
846
895
  const { records, version, pages } = await fetchFromAPI(apiKey, fetchImpl, settings)
847
- if (records.length > 0) {
896
+ if (records.length > 0 && compareVersions(version, AA_INDEX_VERSION) >= 0) {
848
897
  const { scores, coverage } = mergeWithSnapshot(indexRecords(records), version)
849
898
  log(
850
899
  `[model-price-intel] AA API (${pages} page${pages === 1 ? "" : "s"}): ${coverage.live} live + ${coverage.snapshot} snapshot scores`,
@@ -857,10 +906,16 @@ export async function fetchAAScores(options?: AAFetchOptions): Promise<AAFetched
857
906
  coverage,
858
907
  }
859
908
  }
860
- log("[model-price-intel] AA API returned no usable model records; falling back to the model page")
909
+ if (records.length > 0) {
910
+ log(
911
+ `[model-price-intel] AA API returned v${version}, older than the latest known v${AA_INDEX_VERSION}; falling back to the leaderboard page`,
912
+ )
913
+ } else {
914
+ log("[model-price-intel] AA API returned no usable model records; falling back to the leaderboard page")
915
+ }
861
916
  } catch (error) {
862
917
  log(
863
- `[model-price-intel] AA API failed (${error instanceof Error ? error.message : String(error)}); falling back to the model page`,
918
+ `[model-price-intel] AA API failed (${error instanceof Error ? error.message : String(error)}); falling back to the leaderboard page`,
864
919
  )
865
920
  }
866
921
  }
@@ -870,10 +925,10 @@ export async function fetchAAScores(options?: AAFetchOptions): Promise<AAFetched
870
925
  if (page.records.length > 0) {
871
926
  const { scores, coverage } = mergeWithSnapshot(indexRecords(page.records), page.version)
872
927
  const source = page.versionAssumed
873
- ? `artificialanalysis.ai model pages; assumed ${page.version} (page)`
874
- : "artificialanalysis.ai model pages"
928
+ ? `artificialanalysis.ai leaderboard page; assumed ${page.version}`
929
+ : "artificialanalysis.ai leaderboard page"
875
930
  log(
876
- `[model-price-intel] AA model page (v${page.version}${page.versionAssumed ? " assumed" : ""}): ${coverage.live} live + ${coverage.snapshot} snapshot scores`,
931
+ `[model-price-intel] AA leaderboard page (v${page.version}${page.versionAssumed ? " assumed" : ""}): ${coverage.live} live + ${coverage.snapshot} snapshot scores`,
877
932
  )
878
933
  return {
879
934
  scores,
@@ -883,10 +938,10 @@ export async function fetchAAScores(options?: AAFetchOptions): Promise<AAFetched
883
938
  coverage,
884
939
  }
885
940
  }
886
- log("[model-price-intel] AA model page contained no model records")
941
+ log("[model-price-intel] AA leaderboard page contained no model records")
887
942
  } catch (error) {
888
943
  log(
889
- `[model-price-intel] AA model page failed (${error instanceof Error ? error.message : String(error)}); using bundled snapshot`,
944
+ `[model-price-intel] AA leaderboard page failed (${error instanceof Error ? error.message : String(error)}); using bundled snapshot`,
890
945
  )
891
946
  }
892
947
 
package/src/index.ts CHANGED
@@ -43,7 +43,7 @@ import { pricingSegment, type CostFormat } from "./pricing.ts"
43
43
  import { ModelPricing } from "./rpc.ts"
44
44
  import { pricingData, renderTable, type TableModel } from "./table.ts"
45
45
 
46
- const CACHE_KEY = "aa-scores/v3"
46
+ const CACHE_KEY = "aa-scores/v4"
47
47
  const DEFAULT_REFRESH_HOURS = 12
48
48
  const MIN_REFRESH_HOURS = 1
49
49
  /** 596 h stays below the 2^31-1 ms setTimeout/setInterval ceiling. */
package/src/table.ts CHANGED
@@ -62,7 +62,7 @@ export const DEFAULT_TABLE_WIDTH = 108
62
62
 
63
63
  const TITLE = "Model pricing & Artificial Analysis Intelligence Index"
64
64
  const FOOTNOTE =
65
- "AA: * bundled v5 snapshot; ~ other match note (variant or entry name). Attribution: https://artificialanalysis.ai/"
65
+ "AA: * bundled snapshot; ~ other match note (variant or entry name). Attribution: https://artificialanalysis.ai/"
66
66
  const COLUMN_GAP = " "
67
67
 
68
68
  interface Column {