model-eol 0.2.4 → 0.4.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.
@@ -0,0 +1,145 @@
1
+ name: model-eol bot
2
+
3
+ on:
4
+ schedule:
5
+ - cron: '17 4 * * 1'
6
+ workflow_dispatch:
7
+
8
+ concurrency:
9
+ group: model-eol-bot
10
+ cancel-in-progress: false
11
+
12
+ env:
13
+ # Major-line pin: receives compatible fixes and refreshed bundled feeds.
14
+ MODEL_EOL_PACKAGE: model-eol@0
15
+
16
+ jobs:
17
+ plan:
18
+ name: build trusted plan
19
+ runs-on: ubuntu-latest
20
+ permissions:
21
+ contents: read
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ with:
25
+ persist-credentials: false
26
+ - uses: actions/setup-node@v4
27
+ with:
28
+ node-version: 20
29
+ - name: Build the versioned plan
30
+ run: |
31
+ mkdir -p "$RUNNER_TEMP/model-eol-plan"
32
+ MODEL_EOL_VERSION="$(npm view "$MODEL_EOL_PACKAGE" version --json | node -e 'let s="";process.stdin.on("data",c=>s+=c).on("end",()=>{const v=JSON.parse(s);const r=Array.isArray(v)?v.at(-1):v;if(typeof r!=="string")process.exit(1);process.stdout.write(r)})')"
33
+ if [[ ! "$MODEL_EOL_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
34
+ echo "refusing invalid resolved model-eol version: $MODEL_EOL_VERSION" >&2
35
+ exit 1
36
+ fi
37
+ printf '%s\n' "$MODEL_EOL_VERSION" > "$RUNNER_TEMP/model-eol-plan/model-eol-version"
38
+ PLAN_ARGS=(plan .)
39
+ if [ ! -f .model-eol.json ]; then PLAN_ARGS+=(--days 90 --scope direct); fi
40
+ npx --yes --package="model-eol@$MODEL_EOL_VERSION" model-eol "${PLAN_ARGS[@]}" > "$RUNNER_TEMP/model-eol-plan/model-eol-plan.json"
41
+ - name: Upload plan
42
+ uses: actions/upload-artifact@v4
43
+ with:
44
+ name: model-eol-plan
45
+ path: |
46
+ ${{ runner.temp }}/model-eol-plan/model-eol-plan.json
47
+ ${{ runner.temp }}/model-eol-plan/model-eol-version
48
+ if-no-files-found: error
49
+
50
+ evaluate:
51
+ name: evaluate isolated migrations
52
+ needs: plan
53
+ runs-on: ubuntu-latest
54
+ permissions:
55
+ contents: read
56
+ steps:
57
+ # Provider credentials exist only in this read-only job. The evaluator
58
+ # forwards only names explicitly allowed by eval.pass_env.
59
+ - uses: actions/checkout@v4
60
+ with:
61
+ persist-credentials: false
62
+ - uses: actions/setup-node@v4
63
+ with:
64
+ node-version: 20
65
+ - name: Download trusted plan
66
+ uses: actions/download-artifact@v4
67
+ with:
68
+ name: model-eol-plan
69
+ path: ${{ runner.temp }}/model-eol-plan
70
+ - name: Apply and evaluate each migration independently
71
+ env:
72
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
73
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
74
+ GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
75
+ GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
76
+ # A non-empty repository variable overrides eval.command. Otherwise
77
+ # the strictly validated command in .model-eol.json is used.
78
+ MODEL_EOL_EVAL_COMMAND: ${{ vars.MODEL_EOL_EVAL_COMMAND }}
79
+ run: |
80
+ mkdir -p "$RUNNER_TEMP/model-eol-eval"
81
+ MODEL_EOL_VERSION="$(cat "$RUNNER_TEMP/model-eol-plan/model-eol-version")"
82
+ if [[ ! "$MODEL_EOL_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
83
+ echo "refusing invalid resolved model-eol version: $MODEL_EOL_VERSION" >&2
84
+ exit 1
85
+ fi
86
+ npx --yes --package="model-eol@$MODEL_EOL_VERSION" model-eol-bot evaluate \
87
+ --target-dir . \
88
+ --plan-file "$RUNNER_TEMP/model-eol-plan/model-eol-plan.json" \
89
+ --output-file "$RUNNER_TEMP/model-eol-eval/model-eol-eval-results.json"
90
+ - name: Upload bounded eval results
91
+ uses: actions/upload-artifact@v4
92
+ with:
93
+ name: model-eol-eval-results
94
+ path: ${{ runner.temp }}/model-eol-eval/model-eol-eval-results.json
95
+ if-no-files-found: error
96
+
97
+ publish:
98
+ name: reconcile migration work
99
+ needs: [plan, evaluate]
100
+ runs-on: ubuntu-latest
101
+ permissions:
102
+ contents: write
103
+ pull-requests: write
104
+ issues: write
105
+ steps:
106
+ - uses: actions/checkout@v4
107
+ with:
108
+ # model-eol-bot authenticates its own temporary HTTPS clones and
109
+ # pushes with GITHUB_TOKEN; no checkout credential is persisted.
110
+ persist-credentials: false
111
+ - uses: actions/setup-node@v4
112
+ with:
113
+ node-version: 20
114
+ - name: Download trusted plan
115
+ uses: actions/download-artifact@v4
116
+ with:
117
+ name: model-eol-plan
118
+ path: ${{ runner.temp }}/model-eol-plan
119
+ - name: Download eval results
120
+ uses: actions/download-artifact@v4
121
+ with:
122
+ name: model-eol-eval-results
123
+ path: ${{ runner.temp }}/model-eol-eval
124
+ - name: Publish and reconcile model-eol work
125
+ env:
126
+ GITHUB_TOKEN: ${{ secrets.MODEL_EOL_BOT_TOKEN || secrets.GITHUB_TOKEN }}
127
+ MODEL_EOL_PLAN_FILE: ${{ runner.temp }}/model-eol-plan/model-eol-plan.json
128
+ MODEL_EOL_EVAL_RESULTS_FILE: ${{ runner.temp }}/model-eol-eval/model-eol-eval-results.json
129
+ # The write job receives the non-secret command text only so it can
130
+ # verify the eval configuration digest. It receives no provider keys.
131
+ MODEL_EOL_EVAL_COMMAND: ${{ vars.MODEL_EOL_EVAL_COMMAND }}
132
+ MODEL_EOL_TOKEN_KIND: ${{ secrets.MODEL_EOL_BOT_TOKEN && 'external-token' || 'github-token' }}
133
+ run: |
134
+ MODEL_EOL_VERSION="$(cat "$RUNNER_TEMP/model-eol-plan/model-eol-version")"
135
+ if [[ ! "$MODEL_EOL_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
136
+ echo "refusing invalid resolved model-eol version: $MODEL_EOL_VERSION" >&2
137
+ exit 1
138
+ fi
139
+ npx --yes --package="model-eol@$MODEL_EOL_VERSION" model-eol-bot --repo "${{ github.repository }}" --target-dir .
140
+
141
+ # A PR made with GITHUB_TOKEN does not trigger pull_request workflows. For
142
+ # required checks, store a narrowly scoped fine-grained PAT or GitHub App token
143
+ # as MODEL_EOL_BOT_TOKEN. Keep provider keys out of the publish job.
144
+ # Set feeds.allow_vendored_fallback: true only for degraded report-only runs.
145
+ # Independent concurrent invocations need external mutual exclusion.
package/check.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  // model-eol reference CLI - zero dependencies.
3
3
  //
4
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]
5
+ // node check.mjs [paths...] [--config FILE] [--days N] [--feeds DIR] [--via DISTRIBUTOR] [--scope all|direct] [--json] [--include-docs] [--allow-incomplete]
6
6
  //
7
7
  // New inventory modes:
8
8
  // node check.mjs inventory [paths...] [--json|--format json|cyclonedx|text]
@@ -14,11 +14,23 @@
14
14
  // exit codes: check/alert 0 = clear, 1 = finding, 2 = usage error;
15
15
  // apply 0 = all items applied, 1 = item refused, 2 = usage or plan error.
16
16
 
17
+ import fs from 'node:fs'
17
18
  import path from 'node:path'
18
19
 
19
20
  import { applyPlan } from './lib/apply.mjs'
20
21
  import { parseCliArgs } from './lib/cli.mjs'
21
- import { findingFromRef, isBad, loadFeeds } from './lib/feeds.mjs'
22
+ import {
23
+ CLI_DEFAULT_CONFIG,
24
+ DEFAULT_CONFIG,
25
+ configForRepoPath,
26
+ configuredVias,
27
+ loadConfig,
28
+ mappedRoutesForRepoPath,
29
+ normalizeConfig,
30
+ routeForRepoReference,
31
+ } from './lib/config.mjs'
32
+ import { BUILTIN_CHANNELS, findingFromRef, isBad, loadFeeds } from './lib/feeds.mjs'
33
+ import { normalizeRepoPath } from './lib/glob.mjs'
22
34
  import { buildPlan } from './lib/plan.mjs'
23
35
  import {
24
36
  buildAlert,
@@ -32,7 +44,53 @@ import {
32
44
  formatInventoryCycloneDX,
33
45
  formatSchedule,
34
46
  } from './lib/reports.mjs'
35
- import { addedLinesForTargets, filterFindingsToChanged, incompleteScanNotes, scanTargets } from './lib/scanner.mjs'
47
+ import { addedLinesForTargets, filterFindingsToChanged, gitRootFor, incompleteScanNotes, scanTargets } from './lib/scanner.mjs'
48
+
49
+ const rootForTarget = target => {
50
+ const absolute = path.resolve(target)
51
+ let stat
52
+ try {
53
+ stat = fs.lstatSync(absolute)
54
+ } catch {
55
+ return null
56
+ }
57
+ let resolved = absolute
58
+ try {
59
+ resolved = fs.realpathSync(absolute)
60
+ } catch {
61
+ }
62
+ try {
63
+ const gitRoot = gitRootFor(resolved)
64
+ if (gitRoot) return gitRoot
65
+ } catch {
66
+ }
67
+ return stat.isDirectory() ? resolved : path.dirname(resolved)
68
+ }
69
+
70
+ const configForTargets = ({ targets, explicit }) => {
71
+ const roots = [...new Set(targets.map(rootForTarget).filter(Boolean))]
72
+ if (explicit !== null) {
73
+ if (!explicit) throw new Error('--config must be a non-empty path')
74
+ return { file: path.resolve(explicit), roots }
75
+ }
76
+
77
+ const files = [...new Set(roots
78
+ .map(root => path.join(root, '.model-eol.json'))
79
+ .filter(file => fs.existsSync(file)))]
80
+ if (files.length > 1) {
81
+ throw new Error(`multiple .model-eol.json files found (${files.join(', ')}); use --config FILE to choose one`)
82
+ }
83
+ return { file: files[0] ?? null, roots }
84
+ }
85
+
86
+ const repoPathForFile = (file, roots) => {
87
+ const absolute = path.resolve(file)
88
+ const candidates = roots
89
+ .map(root => ({ root, relative: path.relative(root, absolute) }))
90
+ .filter(({ relative }) => relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))
91
+ .sort((a, b) => b.root.length - a.root.length)
92
+ return normalizeRepoPath(candidates[0]?.relative ?? absolute)
93
+ }
36
94
 
37
95
  const main = () => {
38
96
  const COMMANDS = new Set(['check', 'inventory', 'schedule', 'alert', 'plan', 'apply', 'help'])
@@ -46,6 +104,7 @@ try {
46
104
  args,
47
105
  options: {
48
106
  days: { type: 'string' },
107
+ config: { type: 'string' },
49
108
  feeds: { type: 'string' },
50
109
  via: { type: 'string' },
51
110
  scope: { type: 'string' },
@@ -69,10 +128,6 @@ try {
69
128
  const { values, positionals } = parsed
70
129
  if (values.help) command = 'help'
71
130
 
72
- const DAYS = Number(values.days ?? '90')
73
- const FEEDS_DIR = values.feeds ?? path.join(import.meta.dirname, 'feeds')
74
- const VIA = values.via ?? null // e.g. azure-ai-foundry, aws-bedrock
75
- const SCOPE = values.scope ?? 'all'
76
131
  const FORMAT = values.format ?? null
77
132
  const PLAN_FILE = values.plan ?? null
78
133
  const AS_JSON = values.json ?? false
@@ -86,12 +141,12 @@ if (command === 'help') {
86
141
  console.log(`model-eol
87
142
 
88
143
  Usage:
89
- node check.mjs [paths...] [--days N] [--feeds DIR] [--via DISTRIBUTOR] [--scope all|direct] [--json] [--include-docs] [--allow-incomplete]
90
- node check.mjs check [paths...] [--days N] [--scope all|direct] [--json] [--changed BASE_REF] [--allow-incomplete]
91
- node check.mjs inventory [paths...] [--json] [--format json|cyclonedx|text]
92
- node check.mjs schedule [paths...] [--days N] [--json]
93
- node check.mjs alert [paths...] [--days N] [--scope all|direct] [--format github|markdown|badge] [--json]
94
- node check.mjs plan [paths...] [--days N] [--scope all|direct] [--via DISTRIBUTOR] [--feeds DIR] [--allow-incomplete]
144
+ node check.mjs [paths...] [--config FILE] [--days N] [--feeds DIR] [--via DISTRIBUTOR] [--scope all|direct] [--json] [--include-docs] [--allow-incomplete]
145
+ node check.mjs check [paths...] [--config FILE] [--days N] [--scope all|direct] [--json] [--changed BASE_REF] [--allow-incomplete]
146
+ node check.mjs inventory [paths...] [--config FILE] [--json] [--format json|cyclonedx|text]
147
+ node check.mjs schedule [paths...] [--config FILE] [--days N] [--json]
148
+ node check.mjs alert [paths...] [--config FILE] [--days N] [--scope all|direct] [--format github|markdown|badge] [--json]
149
+ node check.mjs plan [paths...] [--config FILE] [--days N] [--scope all|direct] [--via DISTRIBUTOR] [--feeds DIR] [--allow-incomplete]
95
150
  node check.mjs apply --plan plan.json [--dry-run]
96
151
 
97
152
  Commands:
@@ -109,6 +164,10 @@ Scopes:
109
164
  all Check every tracked model ID found. This preserves the original behavior.
110
165
  direct Check direct API and generic model references; leave cloud/gateway refs in inventory.
111
166
 
167
+ Configuration:
168
+ .model-eol.json is loaded automatically from the target repository root.
169
+ --config FILE selects an explicit config. Explicit CLI flags override config values.
170
+
112
171
  Exit codes:
113
172
  0 Clean, or report generated successfully.
114
173
  1 Findings, alert errors, or refused apply items.
@@ -132,6 +191,23 @@ if (command === 'apply') {
132
191
  }
133
192
  }
134
193
 
194
+ let configLocation
195
+ let repositoryConfig
196
+ try {
197
+ configLocation = configForTargets({ targets, explicit: values.config ?? null })
198
+ repositoryConfig = configLocation.file
199
+ ? loadConfig(configLocation.file, { defaults: DEFAULT_CONFIG, allowMissing: false })
200
+ : normalizeConfig({}, { defaults: CLI_DEFAULT_CONFIG })
201
+ } catch (e) {
202
+ console.error(`failed to load repository config: ${e.message}`)
203
+ return 2
204
+ }
205
+
206
+ const DAYS = Number(values.days ?? repositoryConfig.days)
207
+ const FEEDS_DIR = values.feeds ?? path.join(import.meta.dirname, 'feeds')
208
+ const VIA = values.via ?? repositoryConfig.via // e.g. azure-ai-foundry, aws-bedrock
209
+ const SCOPE = values.scope ?? repositoryConfig.scope
210
+
135
211
  if (!Number.isFinite(DAYS) || !Number.isInteger(DAYS) || DAYS < 0) {
136
212
  console.error('--days must be a finite non-negative integer')
137
213
  return 2
@@ -165,6 +241,37 @@ if (feedData.entries.size === 0) {
165
241
  return 2
166
242
  }
167
243
 
244
+ const allowedVias = new Set([...BUILTIN_CHANNELS, ...feedData.vias])
245
+ const unknownVia = [values.via, ...configuredVias(repositoryConfig)]
246
+ .find(via => via && !allowedVias.has(via))
247
+ if (unknownVia) {
248
+ console.error(`unknown lifecycle channel "${unknownVia}"; known values: ${[...allowedVias].sort().join(', ')}`)
249
+ return 2
250
+ }
251
+ const unresolvedRoute = repositoryConfig.routes.find(route => route.model && !feedData.entries.has(route.model))
252
+ if (unresolvedRoute) {
253
+ console.error(`configured route model "${unresolvedRoute.model}" is not present in loaded feeds`)
254
+ return 2
255
+ }
256
+
257
+ const policyCache = new Map()
258
+ const policyForFile = file => {
259
+ const absolute = path.resolve(file)
260
+ if (!policyCache.has(absolute)) {
261
+ const repoPath = repoPathForFile(absolute, configLocation.roots)
262
+ policyCache.set(absolute, { ...configForRepoPath(repositoryConfig, repoPath), repoPath })
263
+ }
264
+ return policyCache.get(absolute)
265
+ }
266
+ const routeForReference = ({ file, matched, id }) => {
267
+ const policy = policyForFile(file)
268
+ return routeForRepoReference(repositoryConfig, policy.repoPath, { matched, id })
269
+ }
270
+ const mappedRoutesForFile = file => {
271
+ const policy = policyForFile(file)
272
+ return mappedRoutesForRepoPath(repositoryConfig, policy.repoPath)
273
+ }
274
+
168
275
  let scan
169
276
  try {
170
277
  scan = scanTargets({
@@ -172,6 +279,11 @@ try {
172
279
  entries: feedData.entries,
173
280
  keys: feedData.keys,
174
281
  includeDocs: INCLUDE_DOCS,
282
+ ignoreRoots: configLocation.roots,
283
+ ignoredFiles: configLocation.file ? [configLocation.file] : [],
284
+ policyForFile,
285
+ routeForReference,
286
+ mappedRoutesForFile,
175
287
  })
176
288
  } catch (e) {
177
289
  console.error(`scan failed: ${e.message}`)
@@ -189,10 +301,32 @@ if (incompleteNotes.length && (command === 'check' || command === 'plan')) {
189
301
  if (!ALLOW_INCOMPLETE) return 2
190
302
  }
191
303
 
192
- const findings = scan.modelRefs.map(ref => findingFromRef(ref, { days: DAYS, via: VIA }))
193
- const checkFindings = SCOPE === 'direct'
194
- ? findings.filter(f => f.usage === 'direct-api' || f.usage === 'model-reference')
195
- : findings
304
+ const findings = scan.modelRefs.map(ref => {
305
+ const policy = policyForFile(ref.file)
306
+ const route = routeForReference({ file: ref.file, matched: ref.matched, id: ref.id })
307
+ const days = values.days === undefined ? policy.days : DAYS
308
+ const scope = values.scope === undefined ? policy.scope : SCOPE
309
+ const via = values.via === undefined ? (route?.via ?? policy.via) : VIA
310
+ const finding = findingFromRef(ref, { days, via })
311
+ finding.effective_scope = scope
312
+ finding.requested_via = via
313
+ if (policy.override_indexes.length || route) {
314
+ finding.policy_provenance = {
315
+ override_indexes: policy.override_indexes,
316
+ route_index: route?.index ?? null,
317
+ }
318
+ }
319
+ return finding
320
+ })
321
+ for (const candidate of scan.candidateRefs) {
322
+ const policy = policyForFile(candidate.file)
323
+ candidate.effective_scope = values.scope === undefined ? policy.scope : SCOPE
324
+ if (policy.override_indexes.length) {
325
+ candidate.policy_provenance = { override_indexes: policy.override_indexes, route_index: null }
326
+ }
327
+ }
328
+ const checkFindings = findings.filter(finding =>
329
+ finding.effective_scope !== 'direct' || finding.usage === 'direct-api' || finding.usage === 'model-reference')
196
330
  let changedFindings = checkFindings
197
331
  if (CHANGED_BASE !== null) {
198
332
  try {
package/feeds/amazon.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "spec": "model-eol/0.1",
3
3
  "publisher": "amazon",
4
- "generated": "2026-08-03T21:43:59.800Z",
4
+ "generated": "2026-08-18T00:47:44.931Z",
5
5
  "source": "https://docs.aws.amazon.com/bedrock/latest/userguide/model-lifecycle.html",
6
6
  "models": [
7
7
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "spec": "model-eol/0.1",
3
3
  "publisher": "anthropic",
4
- "generated": "2026-08-03T21:43:59.800Z",
4
+ "generated": "2026-08-18T00:47:44.931Z",
5
5
  "source": "https://platform.claude.com/docs/en/about-claude/model-deprecations",
6
6
  "policy": {
7
7
  "min_notice_days": 60,
package/feeds/google.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "spec": "model-eol/0.1",
3
3
  "publisher": "google",
4
- "generated": "2026-08-03T21:43:59.800Z",
4
+ "generated": "2026-08-18T00:47:44.931Z",
5
5
  "source": "https://ai.google.dev/gemini-api/docs/deprecations",
6
6
  "models": [
7
7
  {
@@ -318,7 +318,13 @@
318
318
  },
319
319
  {
320
320
  "id": "gemini-embedding-2",
321
- "source": "https://ai.google.dev/gemini-api/docs/deprecations"
321
+ "source": "https://ai.google.dev/gemini-api/docs/deprecations",
322
+ "distributions": [
323
+ {
324
+ "via": "vertex-ai",
325
+ "source": "https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/model-versions"
326
+ }
327
+ ]
322
328
  },
323
329
  {
324
330
  "id": "gemini-embedding-001",
@@ -518,9 +524,7 @@
518
524
  "source": "https://ai.google.dev/gemini-api/docs/deprecations",
519
525
  "shutdown": "2026-08-31",
520
526
  "date_precision": "earliest",
521
- "replacement_options": [
522
- "gemini-robotics-er-2-preview"
523
- ]
527
+ "replacement": "gemini-robotics-er-2-preview"
524
528
  },
525
529
  {
526
530
  "id": "gemini-robotics-er-1.5-preview",
@@ -528,6 +532,85 @@
528
532
  "shutdown": "2026-04-30",
529
533
  "date_precision": "earliest",
530
534
  "replacement": "gemini-robotics-er-1.6-preview"
535
+ },
536
+ {
537
+ "id": "gemini-3.7-flash",
538
+ "source": "https://ai.google.dev/gemini-api/docs/deprecations",
539
+ "distributions": [
540
+ {
541
+ "via": "vertex-ai",
542
+ "source": "https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/model-versions"
543
+ }
544
+ ]
545
+ },
546
+ {
547
+ "id": "gemma-4-26b-a4b-it"
548
+ },
549
+ {
550
+ "id": "gemma-4-31b-it"
551
+ },
552
+ {
553
+ "id": "gemini-flash-latest"
554
+ },
555
+ {
556
+ "id": "gemini-flash-lite-latest"
557
+ },
558
+ {
559
+ "id": "gemini-pro-latest"
560
+ },
561
+ {
562
+ "id": "gemini-3.1-pro-preview-customtools"
563
+ },
564
+ {
565
+ "id": "nano-banana-pro-preview"
566
+ },
567
+ {
568
+ "id": "gemini-3.1-flash-lite-image",
569
+ "distributions": [
570
+ {
571
+ "via": "vertex-ai",
572
+ "source": "https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/model-versions"
573
+ }
574
+ ]
575
+ },
576
+ {
577
+ "id": "gemini-omni-flash-preview"
578
+ },
579
+ {
580
+ "id": "gemini-robotics-er-2-preview"
581
+ },
582
+ {
583
+ "id": "gemini-2.5-computer-use-preview-10-2025"
584
+ },
585
+ {
586
+ "id": "antigravity-preview-05-2026"
587
+ },
588
+ {
589
+ "id": "deep-research-max-preview-04-2026"
590
+ },
591
+ {
592
+ "id": "deep-research-preview-04-2026"
593
+ },
594
+ {
595
+ "id": "deep-research-pro-preview-12-2025"
596
+ },
597
+ {
598
+ "id": "gemini-embedding-2-preview"
599
+ },
600
+ {
601
+ "id": "aqa"
602
+ },
603
+ {
604
+ "id": "gemini-2.5-flash-native-audio-latest"
605
+ },
606
+ {
607
+ "id": "gemini-2.5-flash-native-audio-preview-09-2025"
608
+ },
609
+ {
610
+ "id": "gemini-robotics-er-2-streaming-preview"
611
+ },
612
+ {
613
+ "id": "gemini-3.5-live-translate-preview"
531
614
  }
532
615
  ]
533
616
  }
package/feeds/openai.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "spec": "model-eol/0.1",
3
3
  "publisher": "openai",
4
- "generated": "2026-08-03T21:43:59.800Z",
4
+ "generated": "2026-08-18T00:47:44.930Z",
5
5
  "source": "https://developers.openai.com/api/docs/deprecations",
6
6
  "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.",
7
7
  "models": [
@@ -308,35 +308,35 @@
308
308
  "source": "https://developers.openai.com/api/docs/deprecations",
309
309
  "announced": "2026-04-22",
310
310
  "shutdown": "2026-10-23",
311
- "replacement": "gpt-5.4-mini"
311
+ "replacement": "gpt-5.6-terra"
312
312
  },
313
313
  {
314
314
  "id": "ft-gpt-4",
315
315
  "source": "https://developers.openai.com/api/docs/deprecations",
316
316
  "announced": "2026-04-22",
317
317
  "shutdown": "2026-10-23",
318
- "replacement": "gpt-5.5"
318
+ "replacement": "gpt-5.6-sol"
319
319
  },
320
320
  {
321
321
  "id": "ft-gpt-4.1-nano-2025-04-14",
322
322
  "source": "https://developers.openai.com/api/docs/deprecations",
323
323
  "announced": "2026-04-22",
324
324
  "shutdown": "2026-10-23",
325
- "replacement": "gpt-5.4-nano"
325
+ "replacement": "gpt-5.6-luna"
326
326
  },
327
327
  {
328
328
  "id": "ft-babbage-002",
329
329
  "source": "https://developers.openai.com/api/docs/deprecations",
330
330
  "announced": "2026-04-22",
331
331
  "shutdown": "2026-10-23",
332
- "replacement": "gpt-5.4-mini"
332
+ "replacement": "gpt-5.6-terra"
333
333
  },
334
334
  {
335
335
  "id": "ft-davinci-002",
336
336
  "source": "https://developers.openai.com/api/docs/deprecations",
337
337
  "announced": "2026-04-22",
338
338
  "shutdown": "2026-10-23",
339
- "replacement": "gpt-5.4-mini"
339
+ "replacement": "gpt-5.6-terra"
340
340
  },
341
341
  {
342
342
  "id": "sora-2",
@@ -373,40 +373,28 @@
373
373
  "source": "https://developers.openai.com/api/docs/deprecations",
374
374
  "announced": "2025-09-26",
375
375
  "shutdown": "2026-09-28",
376
- "replacement_options": [
377
- "gpt-5.4-mini",
378
- "gpt-5-mini"
379
- ]
376
+ "replacement": "gpt-5.6-terra"
380
377
  },
381
378
  {
382
379
  "id": "babbage-002",
383
380
  "source": "https://developers.openai.com/api/docs/deprecations",
384
381
  "announced": "2025-09-26",
385
382
  "shutdown": "2026-09-28",
386
- "replacement_options": [
387
- "gpt-5.4-mini",
388
- "gpt-5-mini"
389
- ]
383
+ "replacement": "gpt-5.6-terra"
390
384
  },
391
385
  {
392
386
  "id": "davinci-002",
393
387
  "source": "https://developers.openai.com/api/docs/deprecations",
394
388
  "announced": "2025-09-26",
395
389
  "shutdown": "2026-09-28",
396
- "replacement_options": [
397
- "gpt-5.4-mini",
398
- "gpt-5-mini"
399
- ]
390
+ "replacement": "gpt-5.6-terra"
400
391
  },
401
392
  {
402
393
  "id": "gpt-3.5-turbo-1106",
403
394
  "source": "https://developers.openai.com/api/docs/deprecations",
404
395
  "announced": "2025-09-26",
405
396
  "shutdown": "2026-09-28",
406
- "replacement_options": [
407
- "gpt-5.4-mini",
408
- "gpt-5-mini"
409
- ]
397
+ "replacement": "gpt-5.6-terra"
410
398
  },
411
399
  {
412
400
  "id": "computer-use-preview-2025-03-11",
@@ -427,10 +415,7 @@
427
415
  },
428
416
  {
429
417
  "id": "gpt-4o-mini-tts-2025-03-20",
430
- "source": "https://developers.openai.com/api/docs/deprecations",
431
- "announced": "2026-04-22",
432
- "shutdown": "2026-07-23",
433
- "replacement": "gpt-4o-mini-tts-2025-12-15"
418
+ "source": "https://developers.openai.com/api/docs/deprecations"
434
419
  },
435
420
  {
436
421
  "id": "gpt-4o-search-preview-2025-03-11",