model-eol 0.2.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.
package/check.mjs ADDED
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env node
2
+ // model-eol reference CLI - zero dependencies.
3
+ //
4
+ // Default mode preserves the original CI gate:
5
+ // node check.mjs [paths...] [--days N] [--feeds DIR] [--via DISTRIBUTOR] [--scope all|direct] [--json] [--include-docs] [--allow-incomplete]
6
+ //
7
+ // New inventory modes:
8
+ // node check.mjs inventory [paths...] [--json|--format json|cyclonedx|text]
9
+ // node check.mjs schedule [paths...] [--days N] [--json]
10
+ // node check.mjs alert [paths...] [--format github|markdown|badge] [--json]
11
+ // node check.mjs plan [paths...] [--days N] [--scope all|direct] [--via DISTRIBUTOR] [--feeds DIR]
12
+ // node check.mjs apply --plan plan.json [--dry-run]
13
+ //
14
+ // exit codes: check/alert 0 = clear, 1 = finding, 2 = usage error;
15
+ // apply 0 = all items applied, 1 = item refused, 2 = usage or plan error.
16
+
17
+ import path from 'node:path'
18
+
19
+ import { applyPlan } from './lib/apply.mjs'
20
+ import { parseCliArgs } from './lib/cli.mjs'
21
+ import { findingFromRef, isBad, loadFeeds } from './lib/feeds.mjs'
22
+ import { buildPlan } from './lib/plan.mjs'
23
+ import {
24
+ buildAlert,
25
+ buildInventory,
26
+ buildSchedule,
27
+ formatAlertGithub,
28
+ formatAlertBadge,
29
+ formatAlertMarkdown,
30
+ formatCheck,
31
+ formatInventory,
32
+ formatInventoryCycloneDX,
33
+ formatSchedule,
34
+ } from './lib/reports.mjs'
35
+ import { addedLinesForTargets, filterFindingsToChanged, incompleteScanNotes, scanTargets } from './lib/scanner.mjs'
36
+
37
+ const COMMANDS = new Set(['check', 'inventory', 'schedule', 'alert', 'plan', 'apply', 'help'])
38
+ const args = process.argv.slice(2)
39
+
40
+ if (args[0] === '--help' || args[0] === '-h') args[0] = 'help'
41
+ let command = COMMANDS.has(args[0]) ? args.shift() : 'check'
42
+ let parsed
43
+ try {
44
+ parsed = parseCliArgs({
45
+ args,
46
+ options: {
47
+ days: { type: 'string' },
48
+ feeds: { type: 'string' },
49
+ via: { type: 'string' },
50
+ scope: { type: 'string' },
51
+ format: { type: 'string' },
52
+ plan: { type: 'string' },
53
+ json: { type: 'boolean' },
54
+ 'include-docs': { type: 'boolean' },
55
+ 'allow-incomplete': { type: 'boolean' },
56
+ 'dry-run': { type: 'boolean' },
57
+ changed: { type: 'string' },
58
+ help: { type: 'boolean', short: 'h' },
59
+ },
60
+ allowPositionals: true,
61
+ help: 'node check.mjs --help',
62
+ })
63
+ } catch (error) {
64
+ console.error(error.message)
65
+ process.exit(2)
66
+ }
67
+
68
+ const { values, positionals } = parsed
69
+ if (values.help) command = 'help'
70
+
71
+ const DAYS = Number(values.days ?? '90')
72
+ const FEEDS_DIR = values.feeds ?? path.join(import.meta.dirname, 'feeds')
73
+ const VIA = values.via ?? null // e.g. azure-ai-foundry, aws-bedrock
74
+ const SCOPE = values.scope ?? 'all'
75
+ const FORMAT = values.format ?? null
76
+ const PLAN_FILE = values.plan ?? null
77
+ const AS_JSON = values.json ?? false
78
+ const INCLUDE_DOCS = values['include-docs'] ?? false
79
+ const ALLOW_INCOMPLETE = values['allow-incomplete'] ?? false
80
+ const DRY_RUN = values['dry-run'] ?? false
81
+ const CHANGED_BASE = values.changed ?? null
82
+ const targets = positionals.length ? positionals : ['.']
83
+
84
+ if (command === 'help') {
85
+ console.log(`model-eol
86
+
87
+ Usage:
88
+ node check.mjs [paths...] [--days N] [--feeds DIR] [--via DISTRIBUTOR] [--scope all|direct] [--json] [--include-docs] [--allow-incomplete]
89
+ node check.mjs check [paths...] [--days N] [--scope all|direct] [--json] [--changed BASE_REF] [--allow-incomplete]
90
+ node check.mjs inventory [paths...] [--json] [--format json|cyclonedx|text]
91
+ node check.mjs schedule [paths...] [--days N] [--json]
92
+ node check.mjs alert [paths...] [--days N] [--scope all|direct] [--format github|markdown|badge] [--json]
93
+ node check.mjs plan [paths...] [--days N] [--scope all|direct] [--via DISTRIBUTOR] [--feeds DIR] [--allow-incomplete]
94
+ node check.mjs apply --plan plan.json [--dry-run]
95
+
96
+ Commands:
97
+ check Fail when tracked model IDs are retired or retiring within --days.
98
+ With --changed BASE_REF, answer the PR CI question: "this PR ADDS a
99
+ dependency on a model that already has a death date".
100
+ inventory List tracked model references plus direct/cloud/gateway integration hints.
101
+ --format json is the machine-readable default; --format cyclonedx emits a CycloneDX 1.6 ML-BOM.
102
+ schedule Show the retirement schedule for tracked references in the target.
103
+ alert Emit GitHub Actions annotations, Markdown, or a Shields badge JSON and fail on errors.
104
+ plan Emit a JSON migration plan with only safely patchable direct API references.
105
+ apply Apply a plan with line-hash and occurrence checks; use --dry-run to preview.
106
+
107
+ Scopes:
108
+ all Check every tracked model ID found. This preserves the original behavior.
109
+ direct Check direct API and generic model references; leave cloud/gateway refs in inventory.
110
+ `)
111
+ process.exit(0)
112
+ }
113
+
114
+ if (command === 'apply') {
115
+ if (!PLAN_FILE || positionals.length) {
116
+ console.error('apply requires --plan plan.json and accepts no target paths')
117
+ process.exit(2)
118
+ }
119
+ try {
120
+ const result = applyPlan({ planPath: PLAN_FILE, dryRun: DRY_RUN, rootDir: process.cwd() })
121
+ process.exit(result.failed ? 1 : 0)
122
+ } catch (e) {
123
+ console.error(`failed to apply plan ${PLAN_FILE}: ${e.message}`)
124
+ process.exit(2)
125
+ }
126
+ }
127
+
128
+ if (!Number.isFinite(DAYS) || !Number.isInteger(DAYS) || DAYS < 0) {
129
+ console.error('--days must be a finite non-negative integer')
130
+ process.exit(2)
131
+ }
132
+ if (!['all', 'direct'].includes(SCOPE)) {
133
+ console.error('--scope must be all or direct')
134
+ process.exit(2)
135
+ }
136
+ if (CHANGED_BASE !== null && command !== 'check') {
137
+ console.error('--changed is only supported by the check command')
138
+ process.exit(2)
139
+ }
140
+ if (command === 'alert' && FORMAT !== null && !['github', 'markdown', 'badge', 'json'].includes(FORMAT)) {
141
+ console.error('--format must be github, markdown, badge, or json for alert')
142
+ process.exit(2)
143
+ }
144
+ if (command === 'inventory' && FORMAT !== null && !['json', 'cyclonedx', 'text'].includes(FORMAT)) {
145
+ console.error('--format must be json, cyclonedx, or text for inventory')
146
+ process.exit(2)
147
+ }
148
+
149
+ let feedData
150
+ try {
151
+ feedData = loadFeeds(FEEDS_DIR)
152
+ } catch (e) {
153
+ console.error(`failed to load feeds from ${FEEDS_DIR}: ${e.message}`)
154
+ process.exit(2)
155
+ }
156
+ if (feedData.entries.size === 0) {
157
+ console.error(`no feed entries loaded from ${FEEDS_DIR}`)
158
+ process.exit(2)
159
+ }
160
+
161
+ let scan
162
+ try {
163
+ scan = scanTargets({
164
+ targets,
165
+ entries: feedData.entries,
166
+ keys: feedData.keys,
167
+ includeDocs: INCLUDE_DOCS,
168
+ })
169
+ } catch (e) {
170
+ console.error(`scan failed: ${e.message}`)
171
+ process.exit(2)
172
+ }
173
+
174
+ const incompleteNotes = incompleteScanNotes(scan.notes)
175
+ if (incompleteNotes.length && (command === 'check' || command === 'plan')) {
176
+ const label = ALLOW_INCOMPLETE ? 'warning: incomplete scan allowed' : 'INCOMPLETE SCAN'
177
+ console.error(`model-eol: ${label}; ${incompleteNotes.length} coverage-loss note(s) recorded`)
178
+ for (const note of incompleteNotes) {
179
+ const location = note.file ? ` (${note.file})` : ''
180
+ console.error(` ${note.reason}${location}${note.message ? `: ${note.message}` : ''}`)
181
+ }
182
+ if (!ALLOW_INCOMPLETE) process.exit(2)
183
+ }
184
+
185
+ const findings = scan.modelRefs.map(ref => findingFromRef(ref, { days: DAYS, via: VIA }))
186
+ const checkFindings = SCOPE === 'direct'
187
+ ? findings.filter(f => f.usage === 'direct-api' || f.usage === 'model-reference')
188
+ : findings
189
+ const changedFindings = CHANGED_BASE === null
190
+ ? checkFindings
191
+ : (() => {
192
+ try {
193
+ return filterFindingsToChanged(checkFindings, addedLinesForTargets(targets, CHANGED_BASE))
194
+ } catch (e) {
195
+ console.error(`--changed failed: ${e.message}`)
196
+ process.exit(2)
197
+ }
198
+ })()
199
+ const bad = changedFindings.filter(isBad)
200
+ const inventory = () => buildInventory({ scan, findings, days: DAYS, via: VIA, scope: SCOPE, targets })
201
+ const schedule = () => buildSchedule(inventory())
202
+ const alert = () => buildAlert(schedule())
203
+
204
+ if (command === 'plan') {
205
+ console.log(JSON.stringify(buildPlan({
206
+ scan,
207
+ findings,
208
+ entries: feedData.entries,
209
+ days: DAYS,
210
+ via: VIA,
211
+ scope: SCOPE,
212
+ }), null, 2))
213
+ process.exit(0)
214
+ }
215
+
216
+ if (command === 'check') {
217
+ if (AS_JSON) {
218
+ console.log(JSON.stringify({ threshold_days: DAYS, distributor: VIA, scope: SCOPE, scan_notes: scan.notes, findings: changedFindings }, null, 2))
219
+ } else {
220
+ console.log(formatCheck({ findings: changedFindings, bad, scannedFiles: scan.files.length, days: DAYS, scope: SCOPE }))
221
+ }
222
+ process.exit(bad.length ? 1 : 0)
223
+ }
224
+
225
+ if (command === 'inventory') {
226
+ const inv = inventory()
227
+ const inventoryFormat = FORMAT ?? 'json'
228
+ if (inventoryFormat === 'cyclonedx') {
229
+ console.log(JSON.stringify(formatInventoryCycloneDX(inv), null, 2))
230
+ } else if (AS_JSON || inventoryFormat === 'json') {
231
+ console.log(JSON.stringify(inv, null, 2))
232
+ } else {
233
+ console.log(formatInventory(inv, DAYS))
234
+ }
235
+ process.exit(0)
236
+ }
237
+
238
+ if (command === 'schedule') {
239
+ const sched = schedule()
240
+ if (AS_JSON) {
241
+ console.log(JSON.stringify(sched, null, 2))
242
+ } else {
243
+ console.log(formatSchedule(sched, DAYS))
244
+ }
245
+ process.exit(0)
246
+ }
247
+
248
+ if (command === 'alert') {
249
+ const payload = alert()
250
+ if (AS_JSON || FORMAT === 'json') {
251
+ console.log(JSON.stringify(payload, null, 2))
252
+ } else if (FORMAT === 'badge') {
253
+ console.log(JSON.stringify(formatAlertBadge(payload), null, 2))
254
+ } else if (FORMAT === 'markdown') {
255
+ console.log(formatAlertMarkdown(payload, DAYS))
256
+ } else {
257
+ console.log(formatAlertGithub(payload, DAYS))
258
+ }
259
+ process.exit(payload.errors.length ? 1 : 0)
260
+ }
261
+
262
+ console.error(`unknown command: ${command}`)
263
+ process.exit(2)
@@ -0,0 +1,64 @@
1
+ {
2
+ "spec": "model-eol/0.1",
3
+ "publisher": "amazon",
4
+ "generated": "2026-08-03T14:35:06.544Z",
5
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html",
6
+ "models": [
7
+ {
8
+ "id": "nova-canvas",
9
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html",
10
+ "announced": "2026-03-30",
11
+ "shutdown": "2026-09-30",
12
+ "distributions": [
13
+ {
14
+ "via": "aws-bedrock",
15
+ "announced": "2026-03-30",
16
+ "shutdown": "2026-09-30",
17
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html"
18
+ }
19
+ ]
20
+ },
21
+ {
22
+ "id": "nova-reel",
23
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html",
24
+ "announced": "2026-03-30",
25
+ "shutdown": "2026-09-30",
26
+ "distributions": [
27
+ {
28
+ "via": "aws-bedrock",
29
+ "announced": "2026-03-30",
30
+ "shutdown": "2026-09-30",
31
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html"
32
+ }
33
+ ]
34
+ },
35
+ {
36
+ "id": "nova-premier",
37
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html",
38
+ "announced": "2026-03-13",
39
+ "shutdown": "2026-09-14",
40
+ "distributions": [
41
+ {
42
+ "via": "aws-bedrock",
43
+ "announced": "2026-03-13",
44
+ "shutdown": "2026-09-14",
45
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html"
46
+ }
47
+ ]
48
+ },
49
+ {
50
+ "id": "nova-sonic",
51
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html",
52
+ "announced": "2026-03-13",
53
+ "shutdown": "2026-09-14",
54
+ "distributions": [
55
+ {
56
+ "via": "aws-bedrock",
57
+ "announced": "2026-03-13",
58
+ "shutdown": "2026-09-14",
59
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html"
60
+ }
61
+ ]
62
+ }
63
+ ]
64
+ }
@@ -0,0 +1,252 @@
1
+ {
2
+ "spec": "model-eol/0.1",
3
+ "publisher": "anthropic",
4
+ "generated": "2026-08-03T14:35:06.963Z",
5
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
6
+ "policy": {
7
+ "min_notice_days": 60,
8
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations"
9
+ },
10
+ "note": "Community-maintained seed dataset, compiled 2026-07-25 from the provider's deprecations page. Verify against the source before acting; this file demonstrates the format. Anthropic policy floor: >=60 days notice for publicly released models. Weights of public models are preserved per the Nov 2025 commitments - preservation is not availability.",
11
+ "models": [
12
+ {
13
+ "id": "claude-3-7-sonnet-20250219",
14
+ "aliases": [
15
+ "claude-3-7-sonnet"
16
+ ],
17
+ "distributions": [
18
+ {
19
+ "via": "aws-bedrock",
20
+ "announced": "2026-01-30",
21
+ "shutdown": "2026-07-30",
22
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html"
23
+ }
24
+ ],
25
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
26
+ "announced": "2025-10-28",
27
+ "shutdown": "2026-02-19",
28
+ "replacement": "claude-sonnet-4-6"
29
+ },
30
+ {
31
+ "id": "claude-3-5-haiku-20241022",
32
+ "aliases": [
33
+ "claude-3-5-haiku"
34
+ ],
35
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
36
+ "announced": "2025-12-19",
37
+ "shutdown": "2026-02-19",
38
+ "replacement": "claude-haiku-4-5-20251001"
39
+ },
40
+ {
41
+ "id": "claude-3-haiku-20240307",
42
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
43
+ "distributions": [
44
+ {
45
+ "via": "aws-bedrock",
46
+ "announced": "2026-03-10",
47
+ "shutdown": "2026-09-10",
48
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html"
49
+ }
50
+ ],
51
+ "announced": "2026-02-19",
52
+ "shutdown": "2026-04-20",
53
+ "replacement": "claude-haiku-4-5-20251001"
54
+ },
55
+ {
56
+ "id": "claude-3-opus-20240229",
57
+ "aliases": [
58
+ "claude-opus-3"
59
+ ],
60
+ "notes": "Subject of Anthropic's deprecation-commitments follow-up post.",
61
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
62
+ "announced": "2025-06-30",
63
+ "shutdown": "2026-01-05",
64
+ "replacement": "claude-opus-4-8"
65
+ },
66
+ {
67
+ "id": "claude-sonnet-4-20250514",
68
+ "aliases": [
69
+ "claude-sonnet-4"
70
+ ],
71
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
72
+ "distributions": [
73
+ {
74
+ "via": "aws-bedrock",
75
+ "announced": "2026-04-14",
76
+ "shutdown": "2026-10-14",
77
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html"
78
+ }
79
+ ],
80
+ "announced": "2026-04-14",
81
+ "shutdown": "2026-06-15",
82
+ "replacement": "claude-sonnet-4-6"
83
+ },
84
+ {
85
+ "id": "claude-opus-4-20250514",
86
+ "aliases": [
87
+ "claude-opus-4"
88
+ ],
89
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
90
+ "announced": "2026-04-14",
91
+ "shutdown": "2026-06-15",
92
+ "replacement": "claude-opus-4-8"
93
+ },
94
+ {
95
+ "id": "claude-opus-4-1-20250805",
96
+ "aliases": [
97
+ "claude-opus-4-1"
98
+ ],
99
+ "notes": "Upcoming at dataset compile time - eleven days out. This is the entry a CI check should be failing on today.",
100
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
101
+ "distributions": [
102
+ {
103
+ "via": "aws-bedrock",
104
+ "announced": "2026-07-08",
105
+ "shutdown": "2027-01-08",
106
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html"
107
+ }
108
+ ],
109
+ "announced": "2026-06-05",
110
+ "shutdown": "2026-08-05",
111
+ "replacement": "claude-opus-4-8"
112
+ },
113
+ {
114
+ "id": "claude-3-5-sonnet-20240620",
115
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
116
+ "distributions": [
117
+ {
118
+ "via": "aws-bedrock",
119
+ "announced": "2026-01-30",
120
+ "shutdown": "2026-07-30",
121
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html"
122
+ }
123
+ ],
124
+ "announced": "2025-08-13",
125
+ "shutdown": "2025-10-28",
126
+ "replacement": "claude-sonnet-4-6"
127
+ },
128
+ {
129
+ "id": "claude-3-5-sonnet-20241022",
130
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
131
+ "distributions": [
132
+ {
133
+ "via": "aws-bedrock",
134
+ "announced": "2026-01-30",
135
+ "shutdown": "2026-07-30",
136
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html"
137
+ }
138
+ ],
139
+ "announced": "2025-08-13",
140
+ "shutdown": "2025-10-28",
141
+ "replacement": "claude-sonnet-4-6"
142
+ },
143
+ {
144
+ "id": "claude-2.0",
145
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
146
+ "announced": "2025-01-21",
147
+ "shutdown": "2025-07-21",
148
+ "replacement": "claude-opus-4-8"
149
+ },
150
+ {
151
+ "id": "claude-2.1",
152
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
153
+ "announced": "2025-01-21",
154
+ "shutdown": "2025-07-21",
155
+ "replacement": "claude-opus-4-8"
156
+ },
157
+ {
158
+ "id": "claude-3-sonnet-20240229",
159
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
160
+ "distributions": [
161
+ {
162
+ "via": "aws-bedrock",
163
+ "announced": "2026-01-30",
164
+ "shutdown": "2026-07-30",
165
+ "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html"
166
+ }
167
+ ],
168
+ "announced": "2025-01-21",
169
+ "shutdown": "2025-07-21",
170
+ "replacement": "claude-sonnet-4-6"
171
+ },
172
+ {
173
+ "id": "claude-1.0",
174
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
175
+ "announced": "2024-09-04",
176
+ "shutdown": "2024-11-06",
177
+ "replacement": "claude-haiku-4-5-20251001"
178
+ },
179
+ {
180
+ "id": "claude-1.1",
181
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
182
+ "announced": "2024-09-04",
183
+ "shutdown": "2024-11-06",
184
+ "replacement": "claude-haiku-4-5-20251001"
185
+ },
186
+ {
187
+ "id": "claude-1.2",
188
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
189
+ "announced": "2024-09-04",
190
+ "shutdown": "2024-11-06",
191
+ "replacement": "claude-haiku-4-5-20251001"
192
+ },
193
+ {
194
+ "id": "claude-1.3",
195
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
196
+ "announced": "2024-09-04",
197
+ "shutdown": "2024-11-06",
198
+ "replacement": "claude-haiku-4-5-20251001"
199
+ },
200
+ {
201
+ "id": "claude-instant-1.0",
202
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
203
+ "announced": "2024-09-04",
204
+ "shutdown": "2024-11-06",
205
+ "replacement": "claude-haiku-4-5-20251001"
206
+ },
207
+ {
208
+ "id": "claude-instant-1.1",
209
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
210
+ "announced": "2024-09-04",
211
+ "shutdown": "2024-11-06",
212
+ "replacement": "claude-haiku-4-5-20251001"
213
+ },
214
+ {
215
+ "id": "claude-instant-1.2",
216
+ "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
217
+ "announced": "2024-09-04",
218
+ "shutdown": "2024-11-06",
219
+ "replacement": "claude-haiku-4-5-20251001"
220
+ },
221
+ {
222
+ "id": "claude-opus-5"
223
+ },
224
+ {
225
+ "id": "claude-sonnet-5"
226
+ },
227
+ {
228
+ "id": "claude-fable-5"
229
+ },
230
+ {
231
+ "id": "claude-opus-4-8"
232
+ },
233
+ {
234
+ "id": "claude-opus-4-7"
235
+ },
236
+ {
237
+ "id": "claude-sonnet-4-6"
238
+ },
239
+ {
240
+ "id": "claude-opus-4-6"
241
+ },
242
+ {
243
+ "id": "claude-opus-4-5-20251101"
244
+ },
245
+ {
246
+ "id": "claude-haiku-4-5-20251001"
247
+ },
248
+ {
249
+ "id": "claude-sonnet-4-5-20250929"
250
+ }
251
+ ]
252
+ }