dsh-plugin-subscription-oauth 0.8.14 → 0.8.15
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/lib/auth/provider-controller.js +21 -17
- package/lib/auth/tokens.js +1 -1
- package/lib/client/FastModeButton.d.ts +50 -0
- package/lib/client/FastModeButton.js +94 -0
- package/lib/client/UsageChip.js +2 -1
- package/lib/client/index.d.ts +14 -1
- package/lib/client/index.js +46 -2
- package/lib/client/styles.d.ts +8 -0
- package/lib/client/styles.js +61 -0
- package/lib/client.js +205 -3
- package/lib/client.js.map +1 -1
- package/lib/index.js +2 -0
- package/lib/protocol.d.ts +13 -1
- package/lib/providers/antigravity-catalog.d.ts +29 -9
- package/lib/providers/antigravity-catalog.js +212 -56
- package/lib/providers/antigravity.d.ts +2 -0
- package/lib/providers/antigravity.js +25 -3
- package/lib/providers/grok.d.ts +109 -3
- package/lib/providers/grok.js +258 -34
- package/lib/providers/kimi-provider.js +2 -2
- package/lib/providers/kimi.d.ts +22 -2
- package/lib/providers/kimi.js +72 -22
- package/lib/translate/antigravity.js +3 -3
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -243,6 +243,8 @@ export function apply(ctx, config = {}) {
|
|
|
243
243
|
};
|
|
244
244
|
registerSubscriptionRpc(ctx, registry, balanceContext);
|
|
245
245
|
// Warm each route's live catalog for an install that is already signed in.
|
|
246
|
+
// Discovery uses the stored token as-is (no resolve, no refresh), so a dead grant
|
|
247
|
+
// is skipped silently rather than opening the browser.
|
|
246
248
|
for (const id of registry.ids()) {
|
|
247
249
|
void registry.discover(id, false).catch((error) => {
|
|
248
250
|
onWarn(`${id} startup discovery failed: ${error instanceof Error ? error.message : String(error)}`);
|
package/lib/protocol.d.ts
CHANGED
|
@@ -324,7 +324,7 @@ export interface RpcMap {
|
|
|
324
324
|
value: CcSwitchBalanceResult;
|
|
325
325
|
};
|
|
326
326
|
'model-variants': {
|
|
327
|
-
payload:
|
|
327
|
+
payload: ModelVariantsRequest;
|
|
328
328
|
value: ModelVariantsResult;
|
|
329
329
|
};
|
|
330
330
|
}
|
|
@@ -357,11 +357,23 @@ export type BalanceResult =
|
|
|
357
357
|
export interface ModelVariants {
|
|
358
358
|
/** Fast/lightspeed variant wire id, when the provider reported one. */
|
|
359
359
|
fast?: string;
|
|
360
|
+
/**
|
|
361
|
+
* The base model the fast variant belongs to.
|
|
362
|
+
*
|
|
363
|
+
* Sent with `fast` so the picker button can switch back off when the selection is
|
|
364
|
+
* already the fast variant, which is not itself a listed row.
|
|
365
|
+
*/
|
|
366
|
+
fastBase?: string;
|
|
360
367
|
/** Image-generation model wire id, when the provider reported one. */
|
|
361
368
|
image?: string;
|
|
362
369
|
}
|
|
363
370
|
/** Result of `model-variants`: the variants for one model id. */
|
|
364
371
|
export type ModelVariantsResult = ModelVariants | undefined;
|
|
372
|
+
/** Payload of `model-variants`. */
|
|
373
|
+
export interface ModelVariantsRequest {
|
|
374
|
+
provider: string;
|
|
375
|
+
id: string;
|
|
376
|
+
}
|
|
365
377
|
/** Payload of `deepseek-balance`. */
|
|
366
378
|
export interface DeepSeekBalanceRequest {
|
|
367
379
|
/** Client metadata the host's account service expects. */
|
|
@@ -14,20 +14,40 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { UsageWindow } from '../protocol.js';
|
|
16
16
|
import type { CatalogEntry } from './catalog.js';
|
|
17
|
-
/** True when a model id names
|
|
18
|
-
export declare function
|
|
19
|
-
/**
|
|
20
|
-
|
|
17
|
+
/** True when a model id names an image-generation model. */
|
|
18
|
+
export declare function isImageModel(id: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The wire id one gemini flash family and effort level map to when no live listing
|
|
21
|
+
* has been parsed yet.
|
|
22
|
+
*
|
|
23
|
+
* The live listing is the authority (see `wireIdFor`); this is the fallback that
|
|
24
|
+
* keeps a cold start — a persisted selection used before discovery ran — from
|
|
25
|
+
* sending a bare family id the endpoint does not serve. The service names tiers
|
|
26
|
+
* inconsistently: the High tier of 3.5 Flash is `gemini-3.5-flash-high` in some
|
|
27
|
+
* groups and `gemini-3.5-flash-agent` in others; 3.8 Flash is
|
|
28
|
+
* `gemini-3.8-flash-tiered`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function geminiTierId(base: string, effort: string): string;
|
|
21
31
|
/** Antigravity's own reasoning ladder, per model family. */
|
|
22
32
|
export declare function antigravityEffortsFor(model: string): readonly string[];
|
|
33
|
+
/**
|
|
34
|
+
* The wire id one picker row and effort level must dispatch.
|
|
35
|
+
*
|
|
36
|
+
* @param rowId - the row the picker offered.
|
|
37
|
+
* @param effort - the chosen level, when the caller picked one.
|
|
38
|
+
* @returns the wire id, or undefined when no live listing covers this row.
|
|
39
|
+
*/
|
|
40
|
+
export declare function wireIdFor(rowId: string, effort?: string): string | undefined;
|
|
41
|
+
/** Collapse families into one row each, as magpie does. */
|
|
42
|
+
export declare function collapseFamilies(raw: CatalogEntry[]): CatalogEntry[];
|
|
23
43
|
/**
|
|
24
44
|
* Read the agent-callable catalog from a `fetchAvailableModels` response.
|
|
25
45
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
46
|
+
* The response's own grouping decides which ids the agent envelope may call; this
|
|
47
|
+
* removes the surfaces it cannot (tabs, commit messages, transcription, standalone
|
|
48
|
+
* image generation) and keeps the response's own order. Tiers of one family then
|
|
49
|
+
* collapse into the single picker row that serves them, with a wire-id map so the
|
|
50
|
+
* request still names an id the account listed (see `wireIdFor`).
|
|
31
51
|
*
|
|
32
52
|
* @param payload - the decoded response.
|
|
33
53
|
* @returns the catalog entries, in the response's order.
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* @module dsh-plugin-subscription-oauth/providers/antigravity-catalog
|
|
14
14
|
*/
|
|
15
|
+
import { baseCandidatesOf } from './grok.js';
|
|
15
16
|
/** Effort rungs that fold into one picker row. */
|
|
16
17
|
const EFFORTS = ['low', 'medium', 'high'];
|
|
17
18
|
/** True for a plain object usable as a JSON record. */
|
|
@@ -48,19 +49,33 @@ function modelInfo(raw) {
|
|
|
48
49
|
...raw.supportsImages === true ? { supportsImages: true } : {},
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
|
-
/**
|
|
52
|
-
const
|
|
53
|
-
/** True when a model id names
|
|
54
|
-
export function
|
|
55
|
-
return
|
|
52
|
+
/** Model ids that are image-generation models, never picker rows. */
|
|
53
|
+
const IMAGE_MODEL = /(?:^|[-_])(?:image|imagine|draw|pic|photo)(?:[-_]|$)/i;
|
|
54
|
+
/** True when a model id names an image-generation model. */
|
|
55
|
+
export function isImageModel(id) {
|
|
56
|
+
return IMAGE_MODEL.test(id);
|
|
56
57
|
}
|
|
57
|
-
/**
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
/**
|
|
59
|
+
* The wire id one gemini flash family and effort level map to when no live listing
|
|
60
|
+
* has been parsed yet.
|
|
61
|
+
*
|
|
62
|
+
* The live listing is the authority (see `wireIdFor`); this is the fallback that
|
|
63
|
+
* keeps a cold start — a persisted selection used before discovery ran — from
|
|
64
|
+
* sending a bare family id the endpoint does not serve. The service names tiers
|
|
65
|
+
* inconsistently: the High tier of 3.5 Flash is `gemini-3.5-flash-high` in some
|
|
66
|
+
* groups and `gemini-3.5-flash-agent` in others; 3.8 Flash is
|
|
67
|
+
* `gemini-3.8-flash-tiered`.
|
|
68
|
+
*/
|
|
69
|
+
export function geminiTierId(base, effort) {
|
|
70
|
+
if (effort === 'high') {
|
|
71
|
+
if (base === 'gemini-3.5-flash')
|
|
72
|
+
return 'gemini-3.5-flash-agent';
|
|
73
|
+
if (base === 'gemini-3.8-flash')
|
|
74
|
+
return 'gemini-3.8-flash-tiered';
|
|
75
|
+
if (base === 'gemini-3-flash')
|
|
76
|
+
return 'gemini-3-flash-agent';
|
|
77
|
+
}
|
|
78
|
+
return `${base}-${effort}`;
|
|
64
79
|
}
|
|
65
80
|
/** Antigravity's own reasoning ladder, per model family. */
|
|
66
81
|
export function antigravityEffortsFor(model) {
|
|
@@ -72,6 +87,180 @@ export function antigravityEffortsFor(model) {
|
|
|
72
87
|
return ['low', 'medium', 'high'];
|
|
73
88
|
return [];
|
|
74
89
|
}
|
|
90
|
+
// ── magpie-style family collapse ─────────────────────────────────────────
|
|
91
|
+
/** One effort word of Antigravity's ids and the level it means. */
|
|
92
|
+
const EFFORT_WORDS = [
|
|
93
|
+
{ word: 'extra-high', level: 'xhigh' },
|
|
94
|
+
{ word: 'extra-low', level: 'minimal' },
|
|
95
|
+
{ word: 'xhigh', level: 'xhigh' },
|
|
96
|
+
{ word: 'minimal', level: 'minimal' },
|
|
97
|
+
// A flash family's High tier is spelled `-high`, `-tiered` or `-agent` depending on
|
|
98
|
+
// the generation, so all three mean the same rung of the ladder.
|
|
99
|
+
{ word: 'tiered', level: 'high' },
|
|
100
|
+
{ word: 'agent', level: 'high' },
|
|
101
|
+
{ word: 'low', level: 'low' },
|
|
102
|
+
{ word: 'medium', level: 'medium' },
|
|
103
|
+
{ word: 'high', level: 'high' },
|
|
104
|
+
{ word: 'max', level: 'max' },
|
|
105
|
+
];
|
|
106
|
+
/** A ladder's canonical order. */
|
|
107
|
+
const LEVEL_ORDER = ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
108
|
+
/** An id's family and the level its effort word says, '' when it has none. */
|
|
109
|
+
function splitEffortId(id) {
|
|
110
|
+
for (const { word, level } of EFFORT_WORDS) {
|
|
111
|
+
if (id.endsWith(`-${word}`)) {
|
|
112
|
+
const base = id.slice(0, id.length - word.length - 1);
|
|
113
|
+
if (base.length > 0)
|
|
114
|
+
return { family: base, effort: level };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return { family: id, effort: '' };
|
|
118
|
+
}
|
|
119
|
+
/** The level a display name's bracketed suffix says ("Gemini 3.7 Flash (High)" → high). */
|
|
120
|
+
function nameEffort(name) {
|
|
121
|
+
const open = name.lastIndexOf(' (');
|
|
122
|
+
if (open < 0 || !name.endsWith(')'))
|
|
123
|
+
return { base: name, effort: '' };
|
|
124
|
+
const inner = name.slice(open + 2, -1).toLowerCase();
|
|
125
|
+
let effort = '';
|
|
126
|
+
if (inner === 'minimal' || inner === 'extra low')
|
|
127
|
+
effort = 'minimal';
|
|
128
|
+
else if (inner === 'low')
|
|
129
|
+
effort = 'low';
|
|
130
|
+
else if (inner === 'medium')
|
|
131
|
+
effort = 'medium';
|
|
132
|
+
else if (inner === 'high')
|
|
133
|
+
effort = 'high';
|
|
134
|
+
else if (inner === 'extra high' || inner === 'xhigh')
|
|
135
|
+
effort = 'xhigh';
|
|
136
|
+
if (effort === '')
|
|
137
|
+
return { base: name, effort: '' };
|
|
138
|
+
return { base: name.slice(0, open).trim(), effort };
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Group raw entries into the families the service names.
|
|
142
|
+
*
|
|
143
|
+
* Grouping follows the display name, not the id, because the service names the family
|
|
144
|
+
* and lets the wire id drift: the High tier of Gemini 3.5 Flash is
|
|
145
|
+
* `gemini-3-flash-agent` — an id that names a different generation than the name it
|
|
146
|
+
* carries. A level is stated by the name's own bracket when it has one, and by the
|
|
147
|
+
* id's effort word otherwise; the family a variant belongs to is the longest listed
|
|
148
|
+
* prefix of its own id, so `gemini-3.5-flash-low` folds into whichever listed row its
|
|
149
|
+
* prefix names with no hand-written family map.
|
|
150
|
+
*
|
|
151
|
+
* @param raw - one entry per wire id the listing exposed.
|
|
152
|
+
* @returns the families, in the order their first member appeared.
|
|
153
|
+
*/
|
|
154
|
+
function familiesOf(raw) {
|
|
155
|
+
const byName = new Map();
|
|
156
|
+
const order = [];
|
|
157
|
+
const listed = new Set(raw.map((entry) => entry.id));
|
|
158
|
+
const familyOf = (id, fallback) => {
|
|
159
|
+
for (const base of baseCandidatesOf(id)) {
|
|
160
|
+
if (listed.has(base))
|
|
161
|
+
return base;
|
|
162
|
+
}
|
|
163
|
+
return fallback;
|
|
164
|
+
};
|
|
165
|
+
for (const entry of raw) {
|
|
166
|
+
const { base: name, effort: named } = nameEffort(entry.name);
|
|
167
|
+
const { family: idBase, effort: idLevel } = splitEffortId(entry.id);
|
|
168
|
+
// The family's key: the name's bracket-free base, when the name states a level, so
|
|
169
|
+
// "Gemini 3.5 Flash (Medium)" is always that family even if no listed id starts
|
|
170
|
+
// with `gemini-3.5-flash`; otherwise the longest listed prefix of the id itself.
|
|
171
|
+
const familyBase = named !== '' ? name : (idBase !== entry.id ? familyOf(entry.id, name) : name);
|
|
172
|
+
let f = byName.get(familyBase);
|
|
173
|
+
if (f === undefined) {
|
|
174
|
+
f = { name: familyBase, variants: [], base: '' };
|
|
175
|
+
byName.set(familyBase, f);
|
|
176
|
+
order.push(f);
|
|
177
|
+
}
|
|
178
|
+
if (f.base === '' && idBase !== entry.id)
|
|
179
|
+
f.base = idBase;
|
|
180
|
+
f.variants.push({
|
|
181
|
+
id: entry.id,
|
|
182
|
+
contextWindow: entry.contextWindow,
|
|
183
|
+
inputModalities: entry.inputModalities ?? ['text'],
|
|
184
|
+
level: named !== '' ? named : idLevel,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return order;
|
|
188
|
+
}
|
|
189
|
+
/** The rungs a family defaults to, best first, when no variant is the bare model. */
|
|
190
|
+
const DEFAULT_ORDER = ['xhigh', 'high', 'medium', 'low', 'minimal', 'max'];
|
|
191
|
+
/** The variant whose id the family's row carries: the bare model, else its top rung. */
|
|
192
|
+
function defaultVariant(f) {
|
|
193
|
+
const bare = f.variants.find((v) => v.level === '');
|
|
194
|
+
if (bare !== undefined)
|
|
195
|
+
return bare;
|
|
196
|
+
for (const level of DEFAULT_ORDER) {
|
|
197
|
+
const found = f.variants.find((v) => v.level === level);
|
|
198
|
+
if (found !== undefined)
|
|
199
|
+
return found;
|
|
200
|
+
}
|
|
201
|
+
return f.variants[0];
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* The wire id each picker row must send per effort level, as the last parsed family
|
|
205
|
+
* listing reported it.
|
|
206
|
+
*
|
|
207
|
+
* Collapsing gives the picker one row per family, but the endpoint only serves the
|
|
208
|
+
* suffixed ids the listing named: the High tier of Gemini 3.5 Flash is
|
|
209
|
+
* `gemini-3-flash-agent`, and `gpt-oss-120b` only exists as `gpt-oss-120b-medium`.
|
|
210
|
+
* Nothing here is invented — a level the listing did not name falls back to the
|
|
211
|
+
* family's own default id, and only a tiered gemini family may synthesise the tier
|
|
212
|
+
* spelling the endpoint accepts (see `geminiTierId`).
|
|
213
|
+
*/
|
|
214
|
+
const wireIds = new Map();
|
|
215
|
+
/**
|
|
216
|
+
* The wire id one picker row and effort level must dispatch.
|
|
217
|
+
*
|
|
218
|
+
* @param rowId - the row the picker offered.
|
|
219
|
+
* @param effort - the chosen level, when the caller picked one.
|
|
220
|
+
* @returns the wire id, or undefined when no live listing covers this row.
|
|
221
|
+
*/
|
|
222
|
+
export function wireIdFor(rowId, effort) {
|
|
223
|
+
const row = wireIds.get(rowId);
|
|
224
|
+
if (row === undefined)
|
|
225
|
+
return undefined;
|
|
226
|
+
const chosen = effort === undefined ? undefined : row.levels.get(effort);
|
|
227
|
+
if (chosen !== undefined)
|
|
228
|
+
return chosen;
|
|
229
|
+
// A tiered gemini family the listing exposed only one way still takes its own tier
|
|
230
|
+
// spelling for the three levels this build knows (`gemini-3.8-flash` + low →
|
|
231
|
+
// `gemini-3.8-flash-low`). Nothing else is synthesised.
|
|
232
|
+
const tiered = effort === 'low' || effort === 'medium' || effort === 'high';
|
|
233
|
+
if (tiered && row.base !== '' && row.base !== rowId)
|
|
234
|
+
return geminiTierId(row.base, effort);
|
|
235
|
+
return row.levels.get('') ?? rowId;
|
|
236
|
+
}
|
|
237
|
+
/** Collapse families into one row each, as magpie does. */
|
|
238
|
+
export function collapseFamilies(raw) {
|
|
239
|
+
const out = [];
|
|
240
|
+
for (const f of familiesOf(raw)) {
|
|
241
|
+
const levels = new Map();
|
|
242
|
+
for (const v of f.variants)
|
|
243
|
+
if (!levels.has(v.level))
|
|
244
|
+
levels.set(v.level, v.id);
|
|
245
|
+
const def = defaultVariant(f);
|
|
246
|
+
levels.set('', def.id);
|
|
247
|
+
wireIds.set(def.id, { levels, base: f.base });
|
|
248
|
+
const aliases = f.variants.map((v) => v.id).filter((id) => id !== def.id);
|
|
249
|
+
out.push({
|
|
250
|
+
// The row keeps an id the account listed, so a selection made from it stays
|
|
251
|
+
// dispatchable; the name is what the picker shows.
|
|
252
|
+
id: def.id,
|
|
253
|
+
name: f.name,
|
|
254
|
+
...aliases.length === 0 ? {} : { aliases },
|
|
255
|
+
contextWindow: Math.min(...f.variants.map((v) => v.contextWindow)),
|
|
256
|
+
inputModalities: def.inputModalities,
|
|
257
|
+
// Only the rungs this listing really serves: a level with no id of its own must
|
|
258
|
+
// not be offered as a choice the request would ignore.
|
|
259
|
+
reasoningEfforts: LEVEL_ORDER.filter((level) => levels.has(level)),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
return out;
|
|
263
|
+
}
|
|
75
264
|
/**
|
|
76
265
|
* The ids the agent can actually call, in the order the response groups them.
|
|
77
266
|
*
|
|
@@ -154,11 +343,11 @@ const NON_AGENT = /(?:^|[-_])(?:tab|tabs|commit|transcription|audio|embedding|co
|
|
|
154
343
|
/**
|
|
155
344
|
* Read the agent-callable catalog from a `fetchAvailableModels` response.
|
|
156
345
|
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
346
|
+
* The response's own grouping decides which ids the agent envelope may call; this
|
|
347
|
+
* removes the surfaces it cannot (tabs, commit messages, transcription, standalone
|
|
348
|
+
* image generation) and keeps the response's own order. Tiers of one family then
|
|
349
|
+
* collapse into the single picker row that serves them, with a wire-id map so the
|
|
350
|
+
* request still names an id the account listed (see `wireIdFor`).
|
|
162
351
|
*
|
|
163
352
|
* @param payload - the decoded response.
|
|
164
353
|
* @returns the catalog entries, in the response's order.
|
|
@@ -167,54 +356,21 @@ export function parseAntigravityModels(payload) {
|
|
|
167
356
|
if (!isRecord(payload) || !isRecord(payload.models))
|
|
168
357
|
return [];
|
|
169
358
|
const models = payload.models;
|
|
170
|
-
|
|
171
|
-
// inspectable after a discovery run, since the node half's stdout is unreachable in
|
|
172
|
-
// the desktop shell.
|
|
173
|
-
try {
|
|
174
|
-
const all = Object.keys(models);
|
|
175
|
-
const kept = agentIds(payload);
|
|
176
|
-
const { writeFileSync } = require('node:fs');
|
|
177
|
-
const { homedir } = require('node:os');
|
|
178
|
-
const { join } = require('node:path');
|
|
179
|
-
writeFileSync(join(homedir(), '.dsh', 'antigravity-models-debug.json'), JSON.stringify({ all, kept, dropped: all.filter((id) => !kept.includes(id)) }, null, 2), 'utf8');
|
|
180
|
-
}
|
|
181
|
-
catch { /* diagnostics never break parsing */ }
|
|
182
|
-
const entries = [];
|
|
183
|
-
const seenBase = new Set();
|
|
359
|
+
const raw = [];
|
|
184
360
|
for (const wireId of agentIds(payload)) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
// fixed tiers; they fold into the base family row so the picker shows one entry with
|
|
188
|
-
// a ladder, not one row per tier. The base row is inserted the first time a tier of
|
|
189
|
-
// it is seen.
|
|
190
|
-
if (isTierMarked(wireId)) {
|
|
191
|
-
const base = baseFamilyOf(wireId);
|
|
192
|
-
if (seenBase.has(base))
|
|
193
|
-
continue;
|
|
194
|
-
seenBase.add(base);
|
|
195
|
-
const baseInfo = modelInfo(models[base]) ?? info;
|
|
196
|
-
entries.push({
|
|
197
|
-
id: base,
|
|
198
|
-
name: displayNameOf(base, models[base] ?? {}),
|
|
199
|
-
contextWindow: baseInfo.maxTokens ?? baseInfo.maxInputTokens ?? baseInfo.inputTokenLimit ?? 1_048_576,
|
|
200
|
-
inputModalities: baseInfo.supportsImages === true ? ['text', 'image'] : ['text'],
|
|
201
|
-
reasoningEfforts: antigravityEffortsFor(base),
|
|
202
|
-
});
|
|
361
|
+
// Image-generation models are never picker rows: they are agent-called, always on.
|
|
362
|
+
if (isImageModel(wireId))
|
|
203
363
|
continue;
|
|
204
|
-
|
|
205
|
-
seenBase.add(wireId);
|
|
206
|
-
const tiers = /-(?:low|medium|high)$/.test(wireId);
|
|
207
|
-
const efforts = tiers ? [] : antigravityEffortsFor(wireId);
|
|
364
|
+
const info = modelInfo(models[wireId]);
|
|
208
365
|
const contextWindow = info.maxTokens ?? info.maxInputTokens ?? info.inputTokenLimit ?? 1_048_576;
|
|
209
|
-
|
|
366
|
+
raw.push({
|
|
210
367
|
id: wireId,
|
|
211
368
|
name: displayNameOf(wireId, models[wireId] ?? {}),
|
|
212
369
|
contextWindow,
|
|
213
370
|
inputModalities: info.supportsImages === true ? ['text', 'image'] : ['text'],
|
|
214
|
-
...efforts.length === 0 ? {} : { reasoningEfforts: efforts },
|
|
215
371
|
});
|
|
216
372
|
}
|
|
217
|
-
return
|
|
373
|
+
return collapseFamilies(raw);
|
|
218
374
|
}
|
|
219
375
|
/** A quota reading normalised to a percentage spent. */
|
|
220
376
|
function usedPercentOf(raw) {
|
|
@@ -228,6 +228,8 @@ export declare class AntigravityAdapter extends LlmAdapter {
|
|
|
228
228
|
private resetAt;
|
|
229
229
|
/** Remember an account as spent (its exact reset is learned separately). */
|
|
230
230
|
private noteSpent;
|
|
231
|
+
/** Resolve the default account's token without the preempt window. */
|
|
232
|
+
private resolveNoPreempt;
|
|
231
233
|
/** Translate an auth failure into the message a user can act on. */
|
|
232
234
|
private refreshHint;
|
|
233
235
|
/** Build and send one streaming request for one model. */
|
|
@@ -20,7 +20,7 @@ import { ProviderController } from '../auth/provider-controller.js';
|
|
|
20
20
|
import { proxiedFetch } from '../http.js';
|
|
21
21
|
import { fallbackEntry, findEntry, resolveCatalog } from './catalog.js';
|
|
22
22
|
import { idleWatchdog, mapFetchFailure, httpLlmError } from './common.js';
|
|
23
|
-
import { parseAntigravityModelQuota, parseAntigravityModels, parseAntigravityQuotaSummary, } from './antigravity-catalog.js';
|
|
23
|
+
import { parseAntigravityModelQuota, parseAntigravityModels, parseAntigravityQuotaSummary, geminiTierId, wireIdFor, } from './antigravity-catalog.js';
|
|
24
24
|
import { toAntigravityRequest, streamAntigravity, antigravityReasoning } from '../translate/antigravity.js';
|
|
25
25
|
/** Route key this module registers. */
|
|
26
26
|
export const ANTIGRAVITY_PROVIDER_ID = 'antigravity';
|
|
@@ -456,7 +456,14 @@ export class AntigravityAdapter extends LlmAdapter {
|
|
|
456
456
|
// `auto` owns the fallback chain now, so this route serves exactly the
|
|
457
457
|
// model it was asked for — but it draws on every account the provider holds, so a
|
|
458
458
|
// spent allowance moves to the next account rather than failing the call.
|
|
459
|
-
|
|
459
|
+
// A picker row is the family, not the wire id: `gemini-3.8-flash` is served as
|
|
460
|
+
// `gemini-3.8-flash-tiered` and `gpt-oss-120b` only exists as
|
|
461
|
+
// `gpt-oss-120b-medium`. The live listing's own mapping is the authority; the
|
|
462
|
+
// built-in tier names are the fallback for a cold start, before discovery ran.
|
|
463
|
+
const model = wireIdFor(options.model, options.reasoningEffort)
|
|
464
|
+
?? (/^gemini-.*-flash$/.test(options.model)
|
|
465
|
+
? geminiTierId(options.model, options.reasoningEffort ?? 'medium')
|
|
466
|
+
: options.model);
|
|
460
467
|
// Accounts come from the manager when it can list them; a minimal manager (or a
|
|
461
468
|
// test double) without `accounts()` means a single account, which is just the
|
|
462
469
|
// default one.
|
|
@@ -467,7 +474,10 @@ export class AntigravityAdapter extends LlmAdapter {
|
|
|
467
474
|
// diagnosable from the browser where the node stdout is hidden.
|
|
468
475
|
const diag = [`pool=${accounts.join(',') || '(none)'}`];
|
|
469
476
|
const tried = new Set();
|
|
470
|
-
|
|
477
|
+
// Resolve without preempt: use the stored token if it is still within its life,
|
|
478
|
+
// refresh only when it has actually expired. The 5-minute preempt was causing
|
|
479
|
+
// a refresh on every stream, which made Google show the consent page.
|
|
480
|
+
let token = await this.resolveNoPreempt();
|
|
471
481
|
// Stay on the default account until it refuses. opencodex keeps account affinity —
|
|
472
482
|
// a request stays on one account and only moves after a 429 — because switching
|
|
473
483
|
// accounts on every request is what reads to Google as two accounts in play at once
|
|
@@ -571,6 +581,18 @@ export class AntigravityAdapter extends LlmAdapter {
|
|
|
571
581
|
noteSpent(account) {
|
|
572
582
|
this.options.onWarn?.(`antigravity: account ${account} allowance spent; moving to another account`);
|
|
573
583
|
}
|
|
584
|
+
/** Resolve the default account's token without the preempt window. */
|
|
585
|
+
async resolveNoPreempt() {
|
|
586
|
+
const found = await this.options.tokens.session();
|
|
587
|
+
if (found === undefined) {
|
|
588
|
+
throw new ProviderError(`${ANTIGRAVITY_PROVIDER_NAME} no account is signed in`, REAUTH_REQUIRED);
|
|
589
|
+
}
|
|
590
|
+
const { account, session } = found;
|
|
591
|
+
if (Date.now() < session.expiresAt) {
|
|
592
|
+
return { account, accessToken: session.accessToken, expiresAt: session.expiresAt };
|
|
593
|
+
}
|
|
594
|
+
return await this.options.tokens.resolve(account, true);
|
|
595
|
+
}
|
|
574
596
|
/** Translate an auth failure into the message a user can act on. */
|
|
575
597
|
refreshHint(error) {
|
|
576
598
|
if (error instanceof ProviderError && error.code === REAUTH_REQUIRED) {
|
package/lib/providers/grok.d.ts
CHANGED
|
@@ -116,8 +116,86 @@ interface GrokCliMetadata {
|
|
|
116
116
|
export declare function isImageModel(id: string): boolean;
|
|
117
117
|
/** True when a model id names a fast/lightspeed variant of a base model. */
|
|
118
118
|
export declare function isFastVariant(id: string): boolean;
|
|
119
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* Every prefix of one model id, longest first, down to its first token.
|
|
121
|
+
*
|
|
122
|
+
* `grok-4.7-build-fast` → [`grok-4.7-build`, `grok-4.7`, `grok`]. A fast variant pairs
|
|
123
|
+
* with the longest prefix that is itself a listed model id, which is how
|
|
124
|
+
* `grok-4.7-build-fast` lands on `grok-4.7` (its own prefix `grok-4.7-build` is not a
|
|
125
|
+
* model) instead of inventing a base that no catalog row serves.
|
|
126
|
+
*
|
|
127
|
+
* @param id - model id.
|
|
128
|
+
* @returns the candidate bases, longest first.
|
|
129
|
+
*/
|
|
130
|
+
export declare function baseCandidatesOf(id: string): readonly string[];
|
|
131
|
+
/**
|
|
132
|
+
* The base model id a fast variant belongs to, derived only from its own name.
|
|
133
|
+
*
|
|
134
|
+
* The fast token is removed, then any trailing generation qualifier (`build`, `beta`,
|
|
135
|
+
* `rc`, …) left behind by that removal, then any `@Nk` context suffix. What remains is
|
|
136
|
+
* the base (`grok-4.7-build-fast@500k` → `grok-4.7`, `kimi-for-coding-highspeed` →
|
|
137
|
+
* `kimi-for-coding`). Whether the base is a real catalog row is the index's job, not
|
|
138
|
+
* this function's.
|
|
139
|
+
*/
|
|
120
140
|
export declare function baseModelOf(id: string): string;
|
|
141
|
+
/**
|
|
142
|
+
* The fast variant of one picker row, from either side of the pair.
|
|
143
|
+
*
|
|
144
|
+
* Shared by every route that discovers fast ids, so the composer's quick-mode button
|
|
145
|
+
* behaves the same wherever a fast model is identifiable: it finds the pair when asked
|
|
146
|
+
* about the base row, and when asked about the fast id itself — which is what the
|
|
147
|
+
* selection becomes once the mode is on — so it can switch back off again.
|
|
148
|
+
*/
|
|
149
|
+
export declare class FastVariantIndex {
|
|
150
|
+
private readonly byBase;
|
|
151
|
+
private readonly rowOfVariant;
|
|
152
|
+
private readonly knownIds;
|
|
153
|
+
/** Forget every pair and every known id, before re-reading a catalog. */
|
|
154
|
+
clear(): void;
|
|
155
|
+
/**
|
|
156
|
+
* Record one id the catalog listed.
|
|
157
|
+
*
|
|
158
|
+
* Listed ids are what a variant may pair with: the pair is the longest listed prefix
|
|
159
|
+
* of the variant's own name, so nothing is invented that no row actually serves.
|
|
160
|
+
*
|
|
161
|
+
* @param id - a wire id from a catalog.
|
|
162
|
+
*/
|
|
163
|
+
noteListed(id: string): void;
|
|
164
|
+
/**
|
|
165
|
+
* Pair one fast id with its base, purely from its own name.
|
|
166
|
+
*
|
|
167
|
+
* Every listed prefix of the id is a candidate base, longest first; the first one
|
|
168
|
+
* that is itself a listed model wins. No curated alias or hardcoded map is needed:
|
|
169
|
+
* a provider that names its variants after the base model pairs them automatically.
|
|
170
|
+
*
|
|
171
|
+
* @param id - a wire id the catalog named.
|
|
172
|
+
* @returns the base id, or undefined when no listed prefix names one.
|
|
173
|
+
*/
|
|
174
|
+
pair(id: string): string | undefined;
|
|
175
|
+
/**
|
|
176
|
+
* Record one id, if it names a fast variant.
|
|
177
|
+
*
|
|
178
|
+
* The first variant named for a row wins: a catalog that lists two spellings of the
|
|
179
|
+
* same variant (`kimi-for-coding-highspeed` and `kimi-k2.7-code-highspeed`) offers
|
|
180
|
+
* one button, and the earlier entry is the more specific one.
|
|
181
|
+
*
|
|
182
|
+
* @param id - a wire id from a catalog.
|
|
183
|
+
* @param rowOf - maps a base id to the picker row that serves it, when the catalog
|
|
184
|
+
* spells the base differently from the variant's own prefix.
|
|
185
|
+
*/
|
|
186
|
+
note(id: string, rowOf?: (base: string) => string | undefined): void;
|
|
187
|
+
/** The fast variant's wire id for one picker row, when the catalog named one. */
|
|
188
|
+
variantOf(baseId: string): string | undefined;
|
|
189
|
+
/** The pair one picker id belongs to, from either side. */
|
|
190
|
+
pairOf(id: string): {
|
|
191
|
+
baseId: string;
|
|
192
|
+
wireId: string;
|
|
193
|
+
} | undefined;
|
|
194
|
+
}
|
|
195
|
+
/** Extract the context size from a model id's `@Nk` suffix, or undefined. */
|
|
196
|
+
export declare function contextOf(id: string): number | undefined;
|
|
197
|
+
/** Strip the `@Nk` context suffix from a model id. */
|
|
198
|
+
export declare function stripContext(id: string): string;
|
|
121
199
|
/** Constructor dependencies for {@link GrokAdapter}. */
|
|
122
200
|
export interface GrokAdapterOptions {
|
|
123
201
|
/** API base; `<base>/responses` and `<base>/models`. */
|
|
@@ -190,6 +268,11 @@ export declare class GrokAdapter extends LlmAdapter {
|
|
|
190
268
|
discover(ids: readonly string[], metadata?: ReadonlyMap<string, GrokCliMetadata>): void;
|
|
191
269
|
/** The fast/lightspeed variant wire id for a base model, when discovery reported one. */
|
|
192
270
|
fastVariant(baseId: string): string | undefined;
|
|
271
|
+
/** The fast pair one picker id belongs to, from either side. */
|
|
272
|
+
fastPairOf(id: string): {
|
|
273
|
+
baseId: string;
|
|
274
|
+
wireId: string;
|
|
275
|
+
} | undefined;
|
|
193
276
|
/** The image-generation model id, when discovery reported one. */
|
|
194
277
|
imageModelId(): string | undefined;
|
|
195
278
|
/** The curated catalog, for the Settings page's model list. */
|
|
@@ -197,6 +280,12 @@ export declare class GrokAdapter extends LlmAdapter {
|
|
|
197
280
|
/** What this route itself serves, without the ids added by hand. */
|
|
198
281
|
ownCatalogEntries(): readonly CatalogEntry[];
|
|
199
282
|
/** Build and send one request for one model. */
|
|
283
|
+
/** The default context variant per base model, set during discovery. */
|
|
284
|
+
private defaultContext;
|
|
285
|
+
/** Resolve a model id to the wire id the endpoint accepts (largest context). */
|
|
286
|
+
resolveModelId(model: string): string;
|
|
287
|
+
/** Record one default-context mapping. */
|
|
288
|
+
private noteContextVariant;
|
|
200
289
|
private request;
|
|
201
290
|
/** Resolve image blocks through the attachment service, when it is present. */
|
|
202
291
|
private imageResolver;
|
|
@@ -206,8 +295,25 @@ export declare class GrokAdapter extends LlmAdapter {
|
|
|
206
295
|
* @returns the model ids, or undefined when discovery is off or unavailable.
|
|
207
296
|
*/
|
|
208
297
|
fetchCatalog(accessToken: string, signal?: AbortSignal): Promise<readonly string[] | undefined>;
|
|
209
|
-
/**
|
|
210
|
-
|
|
298
|
+
/**
|
|
299
|
+
* Fold the fast and context variants of one id list into their base models.
|
|
300
|
+
*
|
|
301
|
+
* @param ids - wire ids to classify.
|
|
302
|
+
*/
|
|
303
|
+
private noteWireVariants;
|
|
304
|
+
/**
|
|
305
|
+
* Read per-model capabilities from the CLI catalog; failures are silent.
|
|
306
|
+
*
|
|
307
|
+
* The catalog answers only for the signed-in account's own bearer — an anonymous
|
|
308
|
+
* request is refused — and it, unlike the public `/models` list, names the account's
|
|
309
|
+
* fast variant.
|
|
310
|
+
*
|
|
311
|
+
* @param fetchFn - fetch implementation.
|
|
312
|
+
* @param signal - caller cancellation.
|
|
313
|
+
* @param accessToken - the account's bearer.
|
|
314
|
+
* @returns the capability metadata and every id the catalog named.
|
|
315
|
+
*/
|
|
316
|
+
private readCliCatalog;
|
|
211
317
|
}
|
|
212
318
|
/** Everything the `grok` route needs from the plugin configuration. */
|
|
213
319
|
export interface GrokProviderOptions {
|