jeo-code 0.7.56 → 0.7.58
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/CHANGELOG.md +29 -0
- package/README.ja.md +2 -2
- package/README.ko.md +2 -2
- package/README.md +2 -2
- package/README.zh.md +2 -2
- package/package.json +1 -1
- package/src/agent/config-schema.ts +14 -0
- package/src/agent/context-files.ts +25 -0
- package/src/agent/engine.ts +4 -2
- package/src/agent/prompt-router.ts +211 -34
- package/src/agent/state.ts +22 -1
- package/src/agent/subagents.ts +37 -3
- package/src/agent/tools.ts +169 -0
- package/src/ai/model-catalog-compat.ts +20 -4
- package/src/ai/model-catalog.ts +117 -83
- package/src/ai/model-discovery.ts +5 -1
- package/src/ai/model-manager.ts +17 -4
- package/src/auth/callback-server.ts +13 -3
- package/src/commands/doctor.ts +32 -6
- package/src/commands/launch.ts +1 -1
- package/src/tui/app.ts +2 -2
- package/src/tui/components/status.ts +1 -1
package/src/agent/state.ts
CHANGED
|
@@ -130,7 +130,14 @@ export interface Config {
|
|
|
130
130
|
* Model role tiers (gjc `--smol`/`--slow`/`--plan` parity). Each falls back to
|
|
131
131
|
* `defaultModel`. Env `JEO_SMOL_MODEL`/`JEO_SLOW_MODEL`/`JEO_PLAN_MODEL` fill gaps.
|
|
132
132
|
*/
|
|
133
|
-
roles?: {
|
|
133
|
+
roles?: {
|
|
134
|
+
smol?: string;
|
|
135
|
+
medium?: string;
|
|
136
|
+
high?: string;
|
|
137
|
+
xhigh?: string;
|
|
138
|
+
slow?: string;
|
|
139
|
+
plan?: string;
|
|
140
|
+
};
|
|
134
141
|
/** Prompt-content-based per-turn model routing (PromptRouter) — see prompt-router.ts.
|
|
135
142
|
* Opt-in (undefined/false = off). Never affects subagent/role-tier model resolution. */
|
|
136
143
|
routing?: {
|
|
@@ -139,8 +146,19 @@ export interface Config {
|
|
|
139
146
|
tiers?: {
|
|
140
147
|
trivial?: { model?: string; thinking?: "low" | "medium" | "high" | "xhigh" };
|
|
141
148
|
standard?: { model?: string; thinking?: "low" | "medium" | "high" | "xhigh" };
|
|
149
|
+
/** A single borderline-complex signal (see prompt-router.ts's `classifyPromptHeuristically`)
|
|
150
|
+
* — routes to `roles.high` by default instead of jumping straight to `complex`/xhigh. */
|
|
151
|
+
high?: { model?: string; thinking?: "low" | "medium" | "high" | "xhigh" };
|
|
142
152
|
complex?: { model?: string; thinking?: "low" | "medium" | "high" | "xhigh" };
|
|
143
153
|
};
|
|
154
|
+
/** Opt-in (default off): for `standard`/`high` tiers left unconfigured (no
|
|
155
|
+
* `routing.tiers.*.model`, no `roles.medium`/`roles.high`), pick a SESSION-STABLE
|
|
156
|
+
* model from the cross-provider equivalence pool (`tierModelPool`/`selectFromPool`)
|
|
157
|
+
* instead of falling straight to `defaultModel` — spreads different sessions across
|
|
158
|
+
* every credentialed provider's comparable-tier model so jeo actually captures each
|
|
159
|
+
* provider's respective strengths on comparably-priced work, rather than every
|
|
160
|
+
* session converging on one provider. */
|
|
161
|
+
crossProviderPool?: boolean;
|
|
144
162
|
};
|
|
145
163
|
gitAutoCommit?: boolean;
|
|
146
164
|
hooks?: HookConfig;
|
|
@@ -284,6 +302,9 @@ function withEnvOverlay(cfg: Config): Config {
|
|
|
284
302
|
lmstudioBaseUrl: cfg.lmstudioBaseUrl || process.env.LMSTUDIO_BASE_URL || "http://localhost:1234/v1",
|
|
285
303
|
roles: {
|
|
286
304
|
smol: cfg.roles?.smol || jeoEnv("SMOL_MODEL"),
|
|
305
|
+
medium: cfg.roles?.medium || jeoEnv("MEDIUM_MODEL"),
|
|
306
|
+
high: cfg.roles?.high || jeoEnv("HIGH_MODEL"),
|
|
307
|
+
xhigh: cfg.roles?.xhigh || jeoEnv("XHIGH_MODEL"),
|
|
287
308
|
slow: cfg.roles?.slow || jeoEnv("SLOW_MODEL"),
|
|
288
309
|
plan: cfg.roles?.plan || jeoEnv("PLAN_MODEL"),
|
|
289
310
|
},
|
package/src/agent/subagents.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { DEFAULT_TOOLS, TOOL_PROTOCOL, READONLY_TOOL_PROTOCOL, WORKING_DISCIPLINE, type ToolHandler } from "./engine";
|
|
13
13
|
import type { Config } from "./state";
|
|
14
|
+
import type { CatalogModel } from "../ai/model-catalog";
|
|
14
15
|
import architectPrompt from "../prompts/agents/architect.md" with { type: "text" };
|
|
15
16
|
import criticPrompt from "../prompts/agents/critic.md" with { type: "text" };
|
|
16
17
|
import executorPrompt from "../prompts/agents/executor.md" with { type: "text" };
|
|
@@ -158,9 +159,42 @@ export function defaultSubagentRole(): SubagentRole {
|
|
|
158
159
|
export type SubagentConfig = NonNullable<Config["subagents"]>;
|
|
159
160
|
|
|
160
161
|
/** Per-role model override → falls back to the global default model. */
|
|
161
|
-
export function resolveSubagentModel(
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
export function resolveSubagentModel(
|
|
163
|
+
roleId: string,
|
|
164
|
+
config: {
|
|
165
|
+
defaultModel: string;
|
|
166
|
+
subagents?: Config["subagents"];
|
|
167
|
+
roles?: Config["roles"];
|
|
168
|
+
providers?: Config["providers"];
|
|
169
|
+
oauth?: Config["oauth"];
|
|
170
|
+
},
|
|
171
|
+
): string {
|
|
172
|
+
const normalized = normalizeRoleId(roleId);
|
|
173
|
+
const entry = config.subagents?.[normalized];
|
|
174
|
+
if (entry?.model) return entry.model;
|
|
175
|
+
|
|
176
|
+
// Exception: load strongestCredentialed dynamically using require to prevent eager loading and circular dependency issues that break test mocking.
|
|
177
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
178
|
+
const { strongestCredentialed } = require("./prompt-router");
|
|
179
|
+
if (normalized === "executor") {
|
|
180
|
+
return config.roles?.xhigh || config.roles?.slow || strongestCredentialed(config) || config.defaultModel;
|
|
181
|
+
}
|
|
182
|
+
if (normalized === "architect") {
|
|
183
|
+
return config.roles?.xhigh || strongestCredentialed(config, (m: CatalogModel) => m.thinking.includes("xhigh") || m.thinking.includes("high")) || config.defaultModel;
|
|
184
|
+
}
|
|
185
|
+
if (normalized === "planner") {
|
|
186
|
+
return config.roles?.high || strongestCredentialed(config) || config.defaultModel;
|
|
187
|
+
}
|
|
188
|
+
if (normalized === "critic") {
|
|
189
|
+
return (
|
|
190
|
+
config.roles?.medium ||
|
|
191
|
+
config.roles?.smol ||
|
|
192
|
+
strongestCredentialed(config, (m: CatalogModel) => m.canonical === "o3-mini") ||
|
|
193
|
+
strongestCredentialed(config, (m: CatalogModel) => m.canonical === "gemini-2.5-flash") ||
|
|
194
|
+
config.defaultModel
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
return config.defaultModel;
|
|
164
198
|
}
|
|
165
199
|
|
|
166
200
|
/** Per-role step budget → config override, else the role default, else 15. */
|
package/src/agent/tools.ts
CHANGED
|
@@ -1130,3 +1130,172 @@ export async function deleteTool(
|
|
|
1130
1130
|
return { success: false, output: "", error: err.message };
|
|
1131
1131
|
}
|
|
1132
1132
|
}
|
|
1133
|
+
|
|
1134
|
+
|
|
1135
|
+
/**
|
|
1136
|
+
* Binary-search git history to find the commit that introduced a regression.
|
|
1137
|
+
* Uses `git bisect run <predicate>` to automate the search.
|
|
1138
|
+
* Exit codes: 0=good, non-zero=bad, 125=skip.
|
|
1139
|
+
*/
|
|
1140
|
+
export async function bisectTool(
|
|
1141
|
+
good: string,
|
|
1142
|
+
bad: string = "HEAD",
|
|
1143
|
+
run: string = "",
|
|
1144
|
+
invert: boolean = false,
|
|
1145
|
+
maxSteps: number = 40,
|
|
1146
|
+
stepTimeoutMs: number = 600000,
|
|
1147
|
+
cwd: string = process.cwd()
|
|
1148
|
+
): Promise<ToolResult> {
|
|
1149
|
+
try {
|
|
1150
|
+
if (!good || !good.trim()) {
|
|
1151
|
+
return { success: false, output: "", error: 'bisect requires "good" (a known-good commit)' };
|
|
1152
|
+
}
|
|
1153
|
+
if (!run || !run.trim()) {
|
|
1154
|
+
return { success: false, output: "", error: 'bisect requires "run" (a shell command to test)' };
|
|
1155
|
+
}
|
|
1156
|
+
if (maxSteps <= 0) {
|
|
1157
|
+
return { success: false, output: "", error: 'maxSteps must be > 0' };
|
|
1158
|
+
}
|
|
1159
|
+
if (stepTimeoutMs <= 0) {
|
|
1160
|
+
return { success: false, output: "", error: 'stepTimeoutMs must be > 0' };
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// Helper to run a command with timeout support and resilient error handling
|
|
1164
|
+
const runCmd = async (args: string[], timeoutMs?: number): Promise<{ code: number; out: string; err: string }> => {
|
|
1165
|
+
const timeout = timeoutMs || stepTimeoutMs;
|
|
1166
|
+
let timeoutOccurred = false;
|
|
1167
|
+
const timeoutHandle = setTimeout(() => {
|
|
1168
|
+
timeoutOccurred = true;
|
|
1169
|
+
}, timeout);
|
|
1170
|
+
|
|
1171
|
+
try {
|
|
1172
|
+
const proc = Bun.spawn(args, {
|
|
1173
|
+
cwd,
|
|
1174
|
+
stdout: "pipe",
|
|
1175
|
+
stderr: "pipe",
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
let out = "";
|
|
1179
|
+
let err = "";
|
|
1180
|
+
|
|
1181
|
+
const stdoutReader = (proc.stdout as ReadableStream<Uint8Array>).getReader();
|
|
1182
|
+
const stderrReader = (proc.stderr as ReadableStream<Uint8Array>).getReader();
|
|
1183
|
+
|
|
1184
|
+
// Read stdout with resilience: stream errors don't fail the entire call.
|
|
1185
|
+
// If a reader encounters an error, we continue with partial output, as
|
|
1186
|
+
// git bisect can still function with partial stdout/stderr.
|
|
1187
|
+
try {
|
|
1188
|
+
while (!timeoutOccurred) {
|
|
1189
|
+
const { done, value } = await stdoutReader.read();
|
|
1190
|
+
if (done) break;
|
|
1191
|
+
out += new TextDecoder().decode(value);
|
|
1192
|
+
}
|
|
1193
|
+
} catch (e) {
|
|
1194
|
+
// Stream read error; continue with partial output.
|
|
1195
|
+
// This is acceptable because git bisect may still be usable.
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
// Read stderr with resilience: same strategy as stdout.
|
|
1199
|
+
try {
|
|
1200
|
+
while (!timeoutOccurred) {
|
|
1201
|
+
const { done, value } = await stderrReader.read();
|
|
1202
|
+
if (done) break;
|
|
1203
|
+
err += new TextDecoder().decode(value);
|
|
1204
|
+
}
|
|
1205
|
+
} catch (e) {
|
|
1206
|
+
// Stream read error; continue with partial output.
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
if (timeoutOccurred) {
|
|
1210
|
+
return { code: 124, out, err: `Command timeout after ${timeout}ms` };
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
const code = await proc.exited;
|
|
1214
|
+
return { code: typeof code === 'number' ? code : 0, out, err };
|
|
1215
|
+
} finally {
|
|
1216
|
+
clearTimeout(timeoutHandle);
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
// Validate commits exist (quick validation with shorter timeout)
|
|
1221
|
+
const goodRes = await runCmd(["git", "rev-parse", good], 10000);
|
|
1222
|
+
const badRes = await runCmd(["git", "rev-parse", bad], 10000);
|
|
1223
|
+
|
|
1224
|
+
if (goodRes.code !== 0) {
|
|
1225
|
+
return { success: false, output: "", error: `Invalid or non-existent commit: ${good}` };
|
|
1226
|
+
}
|
|
1227
|
+
if (badRes.code !== 0) {
|
|
1228
|
+
return { success: false, output: "", error: `Invalid or non-existent commit: ${bad}` };
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
const goodSha = goodRes.out.trim();
|
|
1232
|
+
const badSha = badRes.out.trim();
|
|
1233
|
+
|
|
1234
|
+
// Start bisect session
|
|
1235
|
+
const initRes = await runCmd(["sh", "-c", `git bisect start "${badSha}" "${goodSha}"`], 10000);
|
|
1236
|
+
if (initRes.code !== 0) {
|
|
1237
|
+
return { success: false, output: "", error: `Failed to start bisect: ${initRes.err || initRes.out}` };
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// Run bisect with the predicate command.
|
|
1241
|
+
// Shell escaping: single quotes in sh -c context need special care.
|
|
1242
|
+
// 'text' -> '\''text'\'' (end quote, escaped quote, start quote, content, end quote, escaped quote, start quote)
|
|
1243
|
+
const escapedRun = run.replace(/'/g, "'\''");
|
|
1244
|
+
const runCmd2 = invert
|
|
1245
|
+
? `git bisect run sh -c '${escapedRun} && exit 1 || exit 0'`
|
|
1246
|
+
: `git bisect run sh -c '${escapedRun}'`;
|
|
1247
|
+
|
|
1248
|
+
const runRes = await runCmd(["sh", "-c", runCmd2]);
|
|
1249
|
+
|
|
1250
|
+
// Parse result: look for "XXXX is the first bad/good commit"
|
|
1251
|
+
const badCommitMatch = runRes.out.match(/^([0-9a-f]{7,40}) is the first (bad|good) commit$/m);
|
|
1252
|
+
let culprit: string | null = null;
|
|
1253
|
+
let concluded = false;
|
|
1254
|
+
|
|
1255
|
+
if (badCommitMatch) {
|
|
1256
|
+
culprit = badCommitMatch[1];
|
|
1257
|
+
concluded = true;
|
|
1258
|
+
} else if (runRes.out.includes("only 'skip'ped commits left")) {
|
|
1259
|
+
concluded = false;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// Cleanup: always attempt to reset bisect and working tree (best-effort).
|
|
1263
|
+
// Errors here are non-critical; if reset fails, the user can manually run
|
|
1264
|
+
// 'git bisect reset' to recover. We don't throw on cleanup errors.
|
|
1265
|
+
try {
|
|
1266
|
+
await runCmd(["git", "bisect", "reset"], 10000);
|
|
1267
|
+
} catch {}
|
|
1268
|
+
try {
|
|
1269
|
+
await runCmd(["git", "reset", "--hard"], 10000);
|
|
1270
|
+
} catch {}
|
|
1271
|
+
|
|
1272
|
+
if (!concluded) {
|
|
1273
|
+
return {
|
|
1274
|
+
success: false,
|
|
1275
|
+
output: `Bisect did not converge. Output:\n${runRes.out}`,
|
|
1276
|
+
error: "",
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
if (culprit) {
|
|
1281
|
+
// Get commit details (use shorter timeout for metadata lookup)
|
|
1282
|
+
const detailsRes = await runCmd(["git", "show", "-s", "--format=%aN|%ai|%s", culprit], 10000);
|
|
1283
|
+
const [author, date, subject] = detailsRes.out.trim().split("|");
|
|
1284
|
+
const message = [
|
|
1285
|
+
invert ? `First fixing commit: ${culprit}` : `First bad commit: ${culprit}`,
|
|
1286
|
+
author ? `Author: ${author}` : "",
|
|
1287
|
+
date ? `Date: ${date}` : "",
|
|
1288
|
+
subject ? `Subject: ${subject}` : "",
|
|
1289
|
+
]
|
|
1290
|
+
.filter(Boolean)
|
|
1291
|
+
.join("\n");
|
|
1292
|
+
|
|
1293
|
+
return { success: true, output: message };
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
return { success: false, output: "Bisect completed but found no culprit.", error: "" };
|
|
1297
|
+
} catch (err: any) {
|
|
1298
|
+
return { success: false, output: "", error: err.message || String(err) };
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
|
|
@@ -19,10 +19,18 @@ export interface ModelCatalogEntry {
|
|
|
19
19
|
recommended?: boolean;
|
|
20
20
|
note?: string;
|
|
21
21
|
company?: string;
|
|
22
|
+
/** Public release date, `"YYYY-MM"` — see `CatalogModel.releaseDate`. Drives
|
|
23
|
+
* `catalogForProvider`'s newest-first secondary sort. */
|
|
24
|
+
releaseDate?: string;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
/** Canonical ids
|
|
25
|
-
|
|
27
|
+
/** Canonical ids surfaced as a provider's recommended default — the current
|
|
28
|
+
* (2026-07 deep-research verified) general-availability flagship per provider,
|
|
29
|
+
* NOT necessarily the single most expensive/most-capable model (e.g. Anthropic's
|
|
30
|
+
* actual top tier, claude-fable-5, is pricier than the "recommended" sonnet-5 —
|
|
31
|
+
* recommended means "solid everyday default", not "strongest"). Re-verify and
|
|
32
|
+
* update whenever a provider ships a new default-tier release. */
|
|
33
|
+
const RECOMMENDED: Record<string, true> = { "claude-sonnet-5": true, "gpt-5.5": true, "gemini-3-flash": true, "antigravity/gpt-5.5": true, "qwen2.5": true };
|
|
26
34
|
|
|
27
35
|
export function normalizeModelId(id: string): string {
|
|
28
36
|
return (id ?? "").trim().toLowerCase();
|
|
@@ -35,9 +43,10 @@ function adapt(m: CatalogModel): ModelCatalogEntry {
|
|
|
35
43
|
provider: m.provider,
|
|
36
44
|
contextWindow: m.contextTokens,
|
|
37
45
|
reasoning,
|
|
38
|
-
recommended: RECOMMENDED
|
|
46
|
+
recommended: !!RECOMMENDED[m.canonical],
|
|
39
47
|
note: `${formatTokens(m.contextTokens)} ctx${reasoning ? ", reasoning" : ""}`,
|
|
40
48
|
company: m.company,
|
|
49
|
+
releaseDate: m.releaseDate,
|
|
41
50
|
};
|
|
42
51
|
}
|
|
43
52
|
|
|
@@ -50,12 +59,19 @@ export function findCatalogEntry(id: string): ModelCatalogEntry | undefined {
|
|
|
50
59
|
return hit ? adapt(hit) : undefined;
|
|
51
60
|
}
|
|
52
61
|
|
|
53
|
-
/** Entries for a provider, recommended first then by
|
|
62
|
+
/** Entries for a provider, recommended first, then NEWEST-FIRST by `releaseDate`
|
|
63
|
+
* (undated entries sort last, tiebroken by id) — this is the actual list order
|
|
64
|
+
* `/model`'s live picker and `jeo setup`'s recommendation prompt show a user. */
|
|
54
65
|
export function catalogForProvider(provider: ProviderName): ModelCatalogEntry[] {
|
|
55
66
|
return catalogByProvider(provider)
|
|
56
67
|
.map(adapt)
|
|
57
68
|
.sort((a, b) => {
|
|
58
69
|
if (!!b.recommended !== !!a.recommended) return b.recommended ? 1 : -1;
|
|
70
|
+
if (a.releaseDate !== b.releaseDate) {
|
|
71
|
+
if (!a.releaseDate) return 1;
|
|
72
|
+
if (!b.releaseDate) return -1;
|
|
73
|
+
return b.releaseDate.localeCompare(a.releaseDate);
|
|
74
|
+
}
|
|
59
75
|
return a.id.localeCompare(b.id);
|
|
60
76
|
});
|
|
61
77
|
}
|
package/src/ai/model-catalog.ts
CHANGED
|
@@ -29,35 +29,66 @@ export interface CatalogModel {
|
|
|
29
29
|
images: boolean;
|
|
30
30
|
/** Optional company override. */
|
|
31
31
|
company?: string;
|
|
32
|
+
/** True for models requiring separate approval/waitlist beyond the provider's
|
|
33
|
+
* base credential (e.g. Anthropic's invite-only Project Glasswing models) — a
|
|
34
|
+
* user with a valid API key/OAuth for the PROVIDER may still lack access to
|
|
35
|
+
* this SPECIFIC model. Auto-select (prompt-router.ts's pool/single-winner
|
|
36
|
+
* selection) excludes these; explicit `/model` or `routing.tiers.*.model`
|
|
37
|
+
* can still target them by id for approved accounts. */
|
|
38
|
+
limitedAvailability?: boolean;
|
|
39
|
+
/** Public release date, `"YYYY-MM"` (lexicographically sortable) — deep-research
|
|
40
|
+
* sourced from each provider's own announcement. Drives newest-first catalog
|
|
41
|
+
* ordering and recency-based tiebreaks in prompt-router.ts's ranking functions
|
|
42
|
+
* (`compareStrengthAscending`, `cheapestCredentialed`, `strongestCredentialed`)
|
|
43
|
+
* so a genuinely newer same-tier model wins a tie instead of an arbitrary
|
|
44
|
+
* alphabetical pick. Omitted (undefined) for ids with no confirmed public
|
|
45
|
+
* date — these sort AFTER every dated entry within their block, never before,
|
|
46
|
+
* so an unconfirmed date can never silently outrank a verified-newer model. */
|
|
47
|
+
releaseDate?: string;
|
|
32
48
|
}
|
|
33
49
|
|
|
34
50
|
const FULL: ThinkLevel[] = ["low", "medium", "high", "xhigh"];
|
|
35
51
|
const STD: ThinkLevel[] = ["low", "medium", "high"];
|
|
36
52
|
|
|
53
|
+
// [canonical model id, release YYYY-MM] tuples, deep-research sourced (Anthropic/
|
|
54
|
+
// OpenAI/Google base-model announcement dates — antigravity is a hosting layer over
|
|
55
|
+
// the SAME underlying models, not a separate release). Newest-first per family below;
|
|
56
|
+
// -thinking/-high/-low suffixes are reasoning-effort variants of the SAME release.
|
|
37
57
|
export const ANTIGRAVITY_MODELS = [
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"claude-opus-4-8",
|
|
42
|
-
"claude-opus-4-8-thinking",
|
|
43
|
-
"claude-
|
|
44
|
-
"claude-
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"gemini-3-flash",
|
|
49
|
-
"gemini-3-pro-high",
|
|
50
|
-
"gemini-3-pro-low",
|
|
51
|
-
"
|
|
52
|
-
"gemini-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
58
|
+
["gpt-5.5", "2026-04"],
|
|
59
|
+
["gemini-3.1-pro-high", "2026-02"],
|
|
60
|
+
["gemini-3.1-pro-low", "2026-02"],
|
|
61
|
+
["claude-opus-4-8", "2026-05"],
|
|
62
|
+
["claude-opus-4-8-thinking", "2026-05"],
|
|
63
|
+
["claude-opus-4-7", "2026-04"],
|
|
64
|
+
["claude-opus-4-7-thinking", "2026-04"],
|
|
65
|
+
["claude-sonnet-4-6", "2026-02"],
|
|
66
|
+
["claude-sonnet-4-6-thinking", "2026-02"],
|
|
67
|
+
["claude-opus-4-6-thinking", "2026-02"],
|
|
68
|
+
["gemini-3-flash", "2025-12"],
|
|
69
|
+
["gemini-3-pro-high", "2025-11"],
|
|
70
|
+
["gemini-3-pro-low", "2025-11"],
|
|
71
|
+
["gpt-oss-120b-medium", "2025-08"],
|
|
72
|
+
["gemini-2.5-flash", "2025-06"],
|
|
73
|
+
["gemini-2.5-flash-thinking", "2025-06"],
|
|
74
|
+
["gemini-2.5-pro", "2025-03"],
|
|
55
75
|
] as const;
|
|
56
76
|
|
|
57
|
-
/** A curated set of common public models with their documented capabilities.
|
|
77
|
+
/** A curated set of common public models with their documented capabilities.
|
|
78
|
+
* Ordered NEWEST-FIRST within each provider block per `releaseDate`
|
|
79
|
+
* (deep-research sourced from each provider's own announcement — see the
|
|
80
|
+
* v0.7.58 CHANGELOG entry for the full per-model source list). Purely for
|
|
81
|
+
* human readability / "what's current" at a glance — routing correctness
|
|
82
|
+
* does not depend on array order: `prompt-router.ts`'s ranking functions
|
|
83
|
+
* (`cheapestCredentialed`/`strongestCredentialed`/`compareStrengthAscending`)
|
|
84
|
+
* now tiebreak on `releaseDate` directly rather than relying on position. */
|
|
58
85
|
export const MODEL_CATALOG: readonly CatalogModel[] = [
|
|
59
|
-
// Anthropic
|
|
60
|
-
{ canonical: "claude-
|
|
86
|
+
// Anthropic (newest first: sonnet-5 > fable-5 > opus-4-8 > opus-4-7 > sonnet-4-6 > opus-4-6 > haiku-4-5)
|
|
87
|
+
{ canonical: "claude-sonnet-5", provider: "anthropic", providerModel: "claude-sonnet-5", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true, releaseDate: "2026-06" },
|
|
88
|
+
// Fable 5 = Anthropic's most capable widely-released model (adaptive thinking always on);
|
|
89
|
+
// Mythos 5 is limited-availability (Project Glasswing) but callable by id for approved accounts.
|
|
90
|
+
{ canonical: "claude-fable-5", provider: "anthropic", providerModel: "claude-fable-5", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true, releaseDate: "2026-06" },
|
|
91
|
+
{ canonical: "claude-mythos-5", provider: "anthropic", providerModel: "claude-mythos-5", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true, limitedAvailability: true, releaseDate: "2026-06" },
|
|
61
92
|
// NOTE: opus 4.6+ use Anthropic ADAPTIVE thinking (type:"adaptive" + output_config.effort).
|
|
62
93
|
// opus 4.7/4.8 OMIT visible thought unless the request opts into `display: "summarized"` —
|
|
63
94
|
// anthropic.ts sets that on the adaptive transport so reasoning streams again (gjc parity).
|
|
@@ -65,50 +96,54 @@ export const MODEL_CATALOG: readonly CatalogModel[] = [
|
|
|
65
96
|
// The 4.6 generation onward is 1M context / 128k sync max output (Anthropic docs:
|
|
66
97
|
// Opus 4.8 & Sonnet 5 comparison table; Opus 4.6/4.7, Sonnet 4.6 share the gen's
|
|
67
98
|
// dateless-snapshot 1M/128k + 300k batch-output beta). Dated pre-4.6 ids keep 200k/64k.
|
|
68
|
-
{ canonical: "claude-opus-4-
|
|
69
|
-
{ canonical: "claude-opus-4-7", provider: "anthropic", providerModel: "claude-opus-4-7", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true },
|
|
70
|
-
{ canonical: "claude-
|
|
71
|
-
{ canonical: "claude-
|
|
72
|
-
{ canonical: "claude-
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
{ canonical: "
|
|
76
|
-
{ canonical: "
|
|
77
|
-
|
|
78
|
-
{ canonical: "gpt-
|
|
79
|
-
{ canonical: "
|
|
80
|
-
{ canonical: "gpt-
|
|
81
|
-
{ canonical: "
|
|
82
|
-
|
|
83
|
-
{ canonical: "
|
|
84
|
-
{ canonical: "
|
|
85
|
-
{ canonical: "
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
{ canonical: "
|
|
89
|
-
{ canonical: "
|
|
90
|
-
{ canonical: "
|
|
91
|
-
|
|
92
|
-
{ canonical: "kimi-k2-0711-preview", provider: "kimi", providerModel: "kimi-k2-0711-preview", contextTokens: 128_000, maxOutputTokens: 16_384, thinking: [], images: false },
|
|
93
|
-
{ canonical: "kimi-thinking-preview", provider: "kimi", providerModel: "kimi-thinking-preview", contextTokens: 128_000, maxOutputTokens: 32_000, thinking: FULL, images: true },
|
|
94
|
-
{ canonical: "kimi-latest", provider: "kimi", providerModel: "kimi-latest", contextTokens: 128_000, maxOutputTokens: 16_384, thinking: [], images: true },
|
|
95
|
-
{ canonical: "moonshot-v1-128k", provider: "kimi", providerModel: "moonshot-v1-128k", contextTokens: 128_000, maxOutputTokens: 16_384, thinking: [], images: false },
|
|
99
|
+
{ canonical: "claude-opus-4-8", provider: "anthropic", providerModel: "claude-opus-4-8", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true, releaseDate: "2026-05" },
|
|
100
|
+
{ canonical: "claude-opus-4-7", provider: "anthropic", providerModel: "claude-opus-4-7", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true, releaseDate: "2026-04" },
|
|
101
|
+
{ canonical: "claude-sonnet-4-6", provider: "anthropic", providerModel: "claude-sonnet-4-6", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true, releaseDate: "2026-02" },
|
|
102
|
+
{ canonical: "claude-opus-4-6", provider: "anthropic", providerModel: "claude-opus-4-6", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true, releaseDate: "2026-02" },
|
|
103
|
+
{ canonical: "claude-haiku-4-5", provider: "anthropic", providerModel: "claude-haiku-4-5-20251001", contextTokens: 200_000, maxOutputTokens: 64_000, thinking: FULL, images: true, releaseDate: "2025-10" },
|
|
104
|
+
// OpenAI (newest first: gpt-5.5 > gpt-5.4 > o4-mini/o3/gpt-4.1 > o3-mini > gpt-4o-mini > gpt-4o)
|
|
105
|
+
{ canonical: "gpt-5.5", provider: "openai", providerModel: "gpt-5.5", contextTokens: 400_000, maxOutputTokens: 128_000, thinking: FULL, images: true, releaseDate: "2026-04" },
|
|
106
|
+
{ canonical: "gpt-5.4", provider: "openai", providerModel: "gpt-5.4", contextTokens: 400_000, maxOutputTokens: 128_000, thinking: FULL, images: true, releaseDate: "2026-03" },
|
|
107
|
+
{ canonical: "o4-mini", provider: "openai", providerModel: "o4-mini", contextTokens: 200_000, maxOutputTokens: 100_000, thinking: STD, images: true, releaseDate: "2025-04" },
|
|
108
|
+
{ canonical: "o3", provider: "openai", providerModel: "o3", contextTokens: 200_000, maxOutputTokens: 100_000, thinking: STD, images: true, releaseDate: "2025-04" },
|
|
109
|
+
{ canonical: "gpt-4.1", provider: "openai", providerModel: "gpt-4.1", contextTokens: 1_000_000, maxOutputTokens: 32_768, thinking: [], images: true, releaseDate: "2025-04" },
|
|
110
|
+
{ canonical: "o3-mini", provider: "openai", providerModel: "o3-mini", contextTokens: 200_000, maxOutputTokens: 100_000, thinking: STD, images: false, releaseDate: "2025-01" },
|
|
111
|
+
{ canonical: "gpt-4o-mini", provider: "openai", providerModel: "gpt-4o-mini", contextTokens: 128_000, maxOutputTokens: 16_384, thinking: [], images: true, releaseDate: "2024-07" },
|
|
112
|
+
{ canonical: "gpt-4o", provider: "openai", providerModel: "gpt-4o", contextTokens: 128_000, maxOutputTokens: 16_384, thinking: [], images: true, releaseDate: "2024-05" },
|
|
113
|
+
// xAI (Grok) — OpenAI-compatible at https://api.x.ai/v1 (XAI_API_KEY). Newest first.
|
|
114
|
+
{ canonical: "grok-4.3", provider: "xai", providerModel: "grok-4.3", contextTokens: 256_000, maxOutputTokens: 64_000, thinking: FULL, images: true, releaseDate: "2026-04" },
|
|
115
|
+
{ canonical: "grok-4-fast-reasoning", provider: "xai", providerModel: "grok-4-fast-reasoning", contextTokens: 2_000_000, maxOutputTokens: 64_000, thinking: FULL, images: true, releaseDate: "2025-09" },
|
|
116
|
+
{ canonical: "grok-4-fast-non-reasoning", provider: "xai", providerModel: "grok-4-fast-non-reasoning", contextTokens: 2_000_000, maxOutputTokens: 64_000, thinking: [], images: true, releaseDate: "2025-09" },
|
|
117
|
+
{ canonical: "grok-code-fast-1", provider: "xai", providerModel: "grok-code-fast-1", contextTokens: 256_000, maxOutputTokens: 64_000, thinking: FULL, images: false, releaseDate: "2025-08" },
|
|
118
|
+
// Kimi (Moonshot) — OpenAI-compatible at https://api.moonshot.ai/v1 (KIMI_API_KEY). Newest first.
|
|
119
|
+
{ canonical: "kimi-k2-0711-preview", provider: "kimi", providerModel: "kimi-k2-0711-preview", contextTokens: 128_000, maxOutputTokens: 16_384, thinking: [], images: false, releaseDate: "2025-07" },
|
|
120
|
+
{ canonical: "kimi-thinking-preview", provider: "kimi", providerModel: "kimi-thinking-preview", contextTokens: 128_000, maxOutputTokens: 32_000, thinking: FULL, images: true, releaseDate: "2025-05" },
|
|
121
|
+
{ canonical: "kimi-latest", provider: "kimi", providerModel: "kimi-latest", contextTokens: 128_000, maxOutputTokens: 16_384, thinking: [], images: true, releaseDate: "2025-02" },
|
|
122
|
+
{ canonical: "moonshot-v1-128k", provider: "kimi", providerModel: "moonshot-v1-128k", contextTokens: 128_000, maxOutputTokens: 16_384, thinking: [], images: false, releaseDate: "2024-01" },
|
|
96
123
|
// Kimi Code (Moonshot coding subscription, device-code OAuth) — Anthropic-compatible
|
|
97
124
|
// at https://api.kimi.com/coding. Provider-qualified (`kimi/…`) to avoid canonical
|
|
98
125
|
// collisions (kimi-k2.5 also exists under tencent). gjc catalog parity: kimi-code.
|
|
99
|
-
|
|
100
|
-
{ canonical: "kimi-k2.7-code", provider: "kimi", providerModel: "kimi/kimi-k2.7-code", contextTokens: 262_144, maxOutputTokens: 65_536, thinking: FULL, images: true, company: "Moonshot (Kimi Code)" },
|
|
101
|
-
{ canonical: "kimi
|
|
102
|
-
{ canonical: "kimi/kimi-k2
|
|
103
|
-
{ canonical: "kimi/kimi-k2", provider: "kimi", providerModel: "kimi/kimi-k2", contextTokens: 262_144, maxOutputTokens:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
{ canonical: "gemini-
|
|
107
|
-
{ canonical: "gemini-
|
|
108
|
-
{ canonical: "gemini-
|
|
126
|
+
// Newest first.
|
|
127
|
+
{ canonical: "kimi-k2.7-code", provider: "kimi", providerModel: "kimi/kimi-k2.7-code", contextTokens: 262_144, maxOutputTokens: 65_536, thinking: FULL, images: true, company: "Moonshot (Kimi Code)", releaseDate: "2026-06" },
|
|
128
|
+
{ canonical: "kimi-for-coding", provider: "kimi", providerModel: "kimi/kimi-for-coding", contextTokens: 262_144, maxOutputTokens: 32_000, thinking: FULL, images: true, company: "Moonshot (Kimi Code)", releaseDate: "2026-01" }, // [INFERENCE] alias-registration date approximated from Kimi CLI's 2026-01 launch — no primary-source changelog entry found
|
|
129
|
+
{ canonical: "kimi/kimi-k2.5", provider: "kimi", providerModel: "kimi/kimi-k2.5", contextTokens: 262_144, maxOutputTokens: 65_536, thinking: FULL, images: true, company: "Moonshot (Kimi Code)", releaseDate: "2026-01" },
|
|
130
|
+
{ canonical: "kimi/kimi-k2-turbo-preview", provider: "kimi", providerModel: "kimi/kimi-k2-turbo-preview", contextTokens: 262_144, maxOutputTokens: 32_000, thinking: FULL, images: false, company: "Moonshot (Kimi Code)", releaseDate: "2025-08" },
|
|
131
|
+
{ canonical: "kimi/kimi-k2", provider: "kimi", providerModel: "kimi/kimi-k2", contextTokens: 262_144, maxOutputTokens: 262_144, thinking: [], images: false, company: "Moonshot (Kimi Code)", releaseDate: "2025-07" },
|
|
132
|
+
// Google (newest first: gemini-3.1-pro > gemini-3-* > gemini-2.5-* > gemini-2.0-flash > gemini-1.5-pro)
|
|
133
|
+
{ canonical: "gemini-3.1-pro-high", provider: "gemini", providerModel: "gemini-3.1-pro-high", contextTokens: 1_000_000, maxOutputTokens: 65_536, thinking: FULL, images: true, releaseDate: "2026-02" },
|
|
134
|
+
{ canonical: "gemini-3.1-pro-low", provider: "gemini", providerModel: "gemini-3.1-pro-low", contextTokens: 1_000_000, maxOutputTokens: 65_536, thinking: FULL, images: true, releaseDate: "2026-02" },
|
|
135
|
+
{ canonical: "gemini-3-flash", provider: "gemini", providerModel: "gemini-3-flash", contextTokens: 1_000_000, maxOutputTokens: 65_536, thinking: FULL, images: true, releaseDate: "2025-12" },
|
|
136
|
+
{ canonical: "gemini-3-pro-high", provider: "gemini", providerModel: "gemini-3-pro-high", contextTokens: 1_000_000, maxOutputTokens: 65_536, thinking: FULL, images: true, releaseDate: "2025-11" },
|
|
137
|
+
{ canonical: "gemini-3-pro-low", provider: "gemini", providerModel: "gemini-3-pro-low", contextTokens: 1_000_000, maxOutputTokens: 65_536, thinking: FULL, images: true, releaseDate: "2025-11" },
|
|
138
|
+
{ canonical: "gemini-2.5-flash", provider: "gemini", providerModel: "gemini-2.5-flash", contextTokens: 1_000_000, maxOutputTokens: 65_536, thinking: STD, images: true, releaseDate: "2025-06" },
|
|
139
|
+
{ canonical: "gemini-2.5-pro", provider: "gemini", providerModel: "gemini-2.5-pro", contextTokens: 1_000_000, maxOutputTokens: 65_536, thinking: STD, images: true, releaseDate: "2025-03" },
|
|
140
|
+
{ canonical: "gemini-2.0-flash", provider: "gemini", providerModel: "gemini-2.0-flash", contextTokens: 1_000_000, maxOutputTokens: 8_192, thinking: [], images: true, releaseDate: "2025-02" },
|
|
141
|
+
{ canonical: "gemini-1.5-pro", provider: "gemini", providerModel: "gemini-1.5-pro", contextTokens: 1_000_000, maxOutputTokens: 8_192, thinking: [], images: true, releaseDate: "2024-02" },
|
|
109
142
|
// Google Antigravity / Gemini CLI (Cloud Code Assist) — provider-qualified to avoid
|
|
110
|
-
// collisions with public Gemini, Anthropic, and OpenAI/Codex ids.
|
|
111
|
-
|
|
143
|
+
// collisions with public Gemini, Anthropic, and OpenAI/Codex ids. A hosting layer
|
|
144
|
+
// over the SAME underlying models researched above, not a separate release —
|
|
145
|
+
// releaseDate comes from the [id, date] tuple, not re-derived.
|
|
146
|
+
...ANTIGRAVITY_MODELS.map(([id, releaseDate]): CatalogModel => ({
|
|
112
147
|
canonical: `antigravity/${id}`,
|
|
113
148
|
provider: "antigravity",
|
|
114
149
|
providerModel: id,
|
|
@@ -117,31 +152,30 @@ export const MODEL_CATALOG: readonly CatalogModel[] = [
|
|
|
117
152
|
thinking: id.includes("thinking") || id.includes("-high") || id.includes("-low") || id.includes("gemini-3") || id.startsWith("gpt-5") ? FULL : STD,
|
|
118
153
|
images: !id.includes("gpt-oss"),
|
|
119
154
|
company: id.includes("claude") ? "Anthropic via Antigravity" : id.includes("gpt") ? "OpenAI via Antigravity" : "Google Antigravity",
|
|
155
|
+
releaseDate,
|
|
120
156
|
})),
|
|
121
|
-
// Tencent
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
{ canonical: "
|
|
126
|
-
{ canonical: "
|
|
127
|
-
{ canonical: "deepseek-
|
|
128
|
-
|
|
129
|
-
{ canonical: "
|
|
130
|
-
{ canonical: "
|
|
131
|
-
{ canonical: "
|
|
132
|
-
|
|
133
|
-
{ canonical: "
|
|
134
|
-
{ canonical: "
|
|
135
|
-
{ canonical: "glm-5", provider: "tencent", providerModel: "glm-5", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent" },
|
|
136
|
-
{ canonical: "glm-5-turbo", provider: "tencent", providerModel: "glm-5-turbo", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent" },
|
|
137
|
-
{ canonical: "
|
|
138
|
-
|
|
139
|
-
{ canonical: "kimi-k2.
|
|
140
|
-
{ canonical: "
|
|
141
|
-
// Tencent — Hunyuan MT
|
|
142
|
-
{ canonical: "hy-mt2-plus", provider: "tencent", providerModel: "hy-mt2-plus", contextTokens: 128_000, maxOutputTokens: 8192, thinking: STD, images: false, company: "Tencent" },
|
|
157
|
+
// Tencent — hosts the same underlying models under its own cloud brand; dates
|
|
158
|
+
// below are the ORIGINAL developer's release (DeepSeek/MiniMax/Zhipu/Moonshot),
|
|
159
|
+
// not Tencent's hosting-catalog appearance date, except where the id itself pins
|
|
160
|
+
// a Tencent-specific snapshot (the two `-2026XX` suffixed DeepSeek ids). Newest first overall.
|
|
161
|
+
{ canonical: "glm-5.2", provider: "tencent", providerModel: "glm-5.2", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-06" },
|
|
162
|
+
{ canonical: "hy-mt2-plus", provider: "tencent", providerModel: "hy-mt2-plus", contextTokens: 128_000, maxOutputTokens: 8192, thinking: STD, images: false, company: "Tencent", releaseDate: "2026-06" },
|
|
163
|
+
{ canonical: "deepseek-v4-pro-202606", provider: "tencent", providerModel: "deepseek-v4-pro-202606", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-06" },
|
|
164
|
+
{ canonical: "minimax-m3", provider: "tencent", providerModel: "minimax-m3", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-06" },
|
|
165
|
+
{ canonical: "deepseek-v4-flash-202605", provider: "tencent", providerModel: "deepseek-v4-flash-202605", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-05" },
|
|
166
|
+
{ canonical: "glm-5v-turbo", provider: "tencent", providerModel: "glm-5v-turbo", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: true, company: "Tencent", releaseDate: "2026-04" },
|
|
167
|
+
{ canonical: "deepseek-v4-pro", provider: "tencent", providerModel: "deepseek-v4-pro", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-04" },
|
|
168
|
+
{ canonical: "deepseek-v4-flash", provider: "tencent", providerModel: "deepseek-v4-flash", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-04" },
|
|
169
|
+
{ canonical: "kimi-k2.6", provider: "tencent", providerModel: "kimi-k2.6", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-04" },
|
|
170
|
+
{ canonical: "minimax-m2.7", provider: "tencent", providerModel: "minimax-m2.7", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-03" },
|
|
171
|
+
{ canonical: "glm-5.1", provider: "tencent", providerModel: "glm-5.1", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-03" },
|
|
172
|
+
{ canonical: "glm-5-turbo", provider: "tencent", providerModel: "glm-5-turbo", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-03" },
|
|
173
|
+
{ canonical: "minimax-m2.5", provider: "tencent", providerModel: "minimax-m2.5", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-02" },
|
|
174
|
+
{ canonical: "glm-5", provider: "tencent", providerModel: "glm-5", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-02" },
|
|
175
|
+
{ canonical: "kimi-k2.5", provider: "tencent", providerModel: "kimi-k2.5", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2026-01" },
|
|
176
|
+
{ canonical: "deepseek-v3.2", provider: "tencent", providerModel: "deepseek-v3.2", contextTokens: 128_000, maxOutputTokens: 8192, thinking: FULL, images: false, company: "Tencent", releaseDate: "2025-12" },
|
|
143
177
|
// Ollama (local)
|
|
144
|
-
{ canonical: "qwen2.5", provider: "ollama", providerModel: "ollama/qwen2.5:0.5b", contextTokens: 32_768, maxOutputTokens: 8_192, thinking: [], images: false },
|
|
178
|
+
{ canonical: "qwen2.5", provider: "ollama", providerModel: "ollama/qwen2.5:0.5b", contextTokens: 32_768, maxOutputTokens: 8_192, thinking: [], images: false, releaseDate: "2024-09" },
|
|
145
179
|
];
|
|
146
180
|
|
|
147
181
|
/**
|
|
@@ -60,7 +60,11 @@ const ANTIGRAVITY_MODEL_DENYLIST = new Set([
|
|
|
60
60
|
|
|
61
61
|
function anthropicHeaders(cred: Credential): Record<string, string> {
|
|
62
62
|
if (cred.kind === "oauth") {
|
|
63
|
-
|
|
63
|
+
// NOTE: The oauth-2025-04-20 beta is for the messages endpoint, not the models endpoint.
|
|
64
|
+
// The models endpoint does not support this beta, so we omit it here to avoid
|
|
65
|
+
// potential 400/403 errors. The fallback to catalog ensures Anthropic models
|
|
66
|
+
// are always available even if the live endpoint fails.
|
|
67
|
+
return { authorization: `Bearer ${cred.token}`, "anthropic-version": "2023-06-01" };
|
|
64
68
|
}
|
|
65
69
|
if (cred.kind === "api_key") {
|
|
66
70
|
return { "x-api-key": cred.token, "anthropic-version": "2023-06-01" };
|