loom-agent 1.3.3 → 1.5.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.
@@ -52,7 +52,7 @@ function formatTools(tools) {
52
52
  }));
53
53
  }
54
54
 
55
- function buildRequest(messages, options) {
55
+ function buildRequest(messages, options, providerId) {
56
56
  const body = {
57
57
  model: options.model,
58
58
  messages: formatMessages(messages, options),
@@ -60,6 +60,12 @@ function buildRequest(messages, options) {
60
60
  max_tokens: options.maxTokens || 8192,
61
61
  temperature: options.temperature ?? 0.7,
62
62
  };
63
+ // OpenRouter/TokenRouter prompt caching: top-level "cache_control" true
64
+ // (auto-detects prefix-cache-capable models, ignores it otherwise). Cuts
65
+ // input cost on cache hits (~10% of full price) after the first turn.
66
+ if (providerId === 'openrouter' || providerId === 'tokenrouter') {
67
+ body.cache_control = true;
68
+ }
63
69
  // Reasoning models (o1/o3/o4/gpt-5, tagged 'reasoning' by the session):
64
70
  // push the effort to "high" so the model reasons deeply on every step —
65
71
  // opencode-style multi-pass thinking. Other reasoning families (DeepSeek
@@ -110,27 +116,43 @@ function sanitizeErrBody(msg) {
110
116
  function wrapErr(err, modelId, envKeyHint, baseURL) {
111
117
  const raw = err.message || 'Unknown error';
112
118
  const msg = sanitizeErrBody(raw);
113
- if (!err.status) return new Error(`${envKeyHint || 'API'} error: ${msg}`);
114
- const status = Number(err.status);
115
- const hint = String(envKeyHint || 'api').toLowerCase();
116
- if (status === 401) {
117
- return new Error(`${envKeyHint || 'API'} 401 Unauthorized: the API key is invalid or expired. Run /connect ${hint} and paste a new key.`);
118
- }
119
- if (status === 403) {
120
- return new Error(`${envKeyHint || 'API'} 403 Forbidden: the API key is not authorized for ${modelId || 'this model'}. You may need to accept the model's terms on the provider site or use a key with access. Run /connect ${hint} to change the key.`);
121
- }
122
- if (status === 402) return new Error(`${envKeyHint || 'API'} quota exceeded (${err.status}). The model may not be available on your billing tier for model ${(modelId || '?')}.`);
123
- if (status === 404) {
124
- const modelHint = modelId ? ` ${modelId}` : '';
125
- return new Error(`${envKeyHint || 'API'} 404: model${modelHint} does not exist (or was removed) on this provider. Try /models and pick a listed model, or /model ${modelId ? 'to switch' : ''}.`);
126
- }
127
- if (err.error && err.error.message) return new Error(`${envKeyHint || 'API'} error ${status}: ${sanitizeErrBody(err.error.message)}`);
128
- if (err.body && err.body.message) return new Error(`${envKeyHint || 'API'} error ${status}: ${sanitizeErrBody(err.body.message)}`);
129
- if (!msg || msg.indexOf('no body') >= 0) {
130
- const modelHint = modelId ? ` (model: ${modelId})` : '';
131
- return new Error(`${envKeyHint || 'API'} error ${status}${modelHint}. Check model name or URL:\n${baseURL || '(default)'}`);
119
+ // Carry the HTTP status + Retry-After onto the wrapped error so the
120
+ // session loop can tell a rate-limit (wait+retry) from a quota switch.
121
+ const out = /** @type {any} */ (!err.status ? new Error(`${envKeyHint || 'API'} error: ${msg}`) : (() => {
122
+ const status = Number(err.status);
123
+ const hint = String(envKeyHint || 'api').toLowerCase();
124
+ if (status === 401) {
125
+ return new Error(`${envKeyHint || 'API'} 401 Unauthorized: the API key is invalid or expired. Run /connect ${hint} and paste a new key.`);
126
+ }
127
+ if (status === 403) {
128
+ return new Error(`${envKeyHint || 'API'} 403 Forbidden: the API key is not authorized for ${modelId || 'this model'}. You may need to accept the model's terms on the provider site or use a key with access. Run /connect ${hint} to change the key.`);
129
+ }
130
+ if (status === 402) return new Error(`${envKeyHint || 'API'} quota exceeded (${err.status}). The model may not be available on your billing tier for model ${(modelId || '?')}.`);
131
+ if (status === 404) {
132
+ const modelHint = modelId ? ` ${modelId}` : '';
133
+ return new Error(`${envKeyHint || 'API'} 404: model${modelHint} does not exist (or was removed) on this provider. Try /models and pick a listed model, or /model ${modelId ? 'to switch' : ''}.`);
134
+ }
135
+ if (err.error && err.error.message) return new Error(`${envKeyHint || 'API'} error ${status}: ${sanitizeErrBody(err.error.message)}`);
136
+ if (err.body && err.body.message) return new Error(`${envKeyHint || 'API'} error ${status}: ${sanitizeErrBody(err.body.message)}`);
137
+ if (!msg || msg.indexOf('no body') >= 0) {
138
+ const modelHint = modelId ? ` (model: ${modelId})` : '';
139
+ return new Error(`${envKeyHint || 'API'} error ${status}${modelHint}. Check model name or URL:\n${baseURL || '(default)'}`);
140
+ }
141
+ return new Error(`${envKeyHint || 'API'} error ${status}: ${msg}`);
142
+ })());
143
+ out.status = err.status ? Number(err.status) : undefined;
144
+ // Retry-After from the openai SDK error headers (seconds or HTTP-date).
145
+ const hdrs = err.headers;
146
+ const ra = hdrs && (typeof hdrs.get === 'function' ? hdrs.get('retry-after') : hdrs['retry-after']);
147
+ if (ra) {
148
+ const secs = Number(ra);
149
+ if (Number.isFinite(secs) && secs >= 0) out.retryAfterMs = Math.min(secs * 1000, 60000);
150
+ else {
151
+ const t = Date.parse(ra);
152
+ if (Number.isFinite(t)) out.retryAfterMs = Math.min(Math.max(0, t - Date.now()), 60000);
153
+ }
132
154
  }
133
- return new Error(`${envKeyHint || 'API'} error ${status}: ${msg}`);
155
+ return out;
134
156
  }
135
157
 
136
158
  async function retryWithBackoff(fn, maxTries, envKeyHint) {
@@ -142,10 +164,20 @@ async function retryWithBackoff(fn, maxTries, envKeyHint) {
142
164
  lastErr = err;
143
165
  if (isAbortError(err)) throw err;
144
166
  const status = err.status || err.code;
145
- if (status !== 429 && status !== 503 && status !== 502) throw lastErr;
146
- if (attempt > 0) {
147
- const delay = Math.min(1000 * Math.pow(3, attempt - 1), 30000);
148
- await new Promise((resolve) => setTimeout(resolve, delay));
167
+ if (status !== 429 && status !== 503 && status !== 502 && status !== 529) throw lastErr;
168
+ {
169
+ // Honor Retry-After when the provider sends one (even on the first
170
+ // retry); otherwise the first retry is immediate (transient blip)
171
+ // and later ones back off exponentially. Jitter avoids a thundering
172
+ // herd of parallel retries.
173
+ let delay = attempt > 0 ? Math.min(1000 * Math.pow(3, attempt - 1), 30000) : 0;
174
+ const hdrs = err.headers;
175
+ const ra = hdrs && (typeof hdrs.get === 'function' ? hdrs.get('retry-after') : hdrs['retry-after']);
176
+ if (ra) {
177
+ const secs = Number(ra);
178
+ if (Number.isFinite(secs) && secs >= 0) delay = Math.min(secs * 1000, 60000);
179
+ }
180
+ await new Promise((resolve) => setTimeout(resolve, delay + (delay > 0 ? Math.floor(Math.random() * 250) : 0)));
149
181
  }
150
182
  }
151
183
  }
@@ -194,7 +226,7 @@ function createOpenAICompatProvider({ getKey, providerId, envKeyHint, clientFact
194
226
 
195
227
  async function chat(messages, options = {}) {
196
228
  const client = getClient();
197
- const req = buildRequest(messages, options);
229
+ const req = buildRequest(messages, options, providerId);
198
230
  const requestOpts = { signal: options.signal };
199
231
  try {
200
232
  const resp = await retryWithBackoff(() => client.chat.completions.create(req, requestOpts), 4, envKeyHint);
@@ -207,7 +239,7 @@ function createOpenAICompatProvider({ getKey, providerId, envKeyHint, clientFact
207
239
 
208
240
  async function stream(messages, options = {}, onDelta, onReasoning) {
209
241
  const client = getClient();
210
- const req = { ...buildRequest(messages, options), stream: true, stream_options: { include_usage: true } };
242
+ const req = { ...buildRequest(messages, options, providerId), stream: true, stream_options: { include_usage: true } };
211
243
  const requestOpts = { signal: options.signal };
212
244
  try {
213
245
  const s = await retryWithBackoff(() => client.chat.completions.create(req, requestOpts), 4, envKeyHint);
@@ -11,24 +11,48 @@ const provider = {
11
11
  envKeyHint: 'OPENAI',
12
12
  }),
13
13
  models: [
14
- // Flagship / GPT-5 Series
15
- { id: 'gpt-5', name: 'GPT-5', provider: 'openai', tags: ['frontier'], context: 400000, priceIn: 1.25, priceOut: 10 },
16
- { id: 'gpt-5-mini', name: 'GPT-5 Mini', provider: 'openai', tags: ['fast'], context: 400000, priceIn: 0.25, priceOut: 2.00 },
17
- { id: 'gpt-5-nano', name: 'GPT-5 Nano', provider: 'openai', tags: ['cheap', 'fast'], context: 400000, priceIn: 0.10, priceOut: 0.40 },
18
- { id: 'gpt-5-chat-latest', name: 'GPT-5 Chat (Latest)', provider: 'openai', tags: ['frontier'], context: 400000, priceIn: 5.00, priceOut: 30.00 },
19
-
20
- // GPT-4 Series & Omni Models
21
- { id: 'gpt-4o', name: 'GPT-4o', provider: 'openai', tags: ['legacy', 'multimodal'], context: 128000, priceIn: 2.50, priceOut: 10.00 },
22
- { id: 'gpt-4o-mini', name: 'GPT-4o Mini', provider: 'openai', tags: ['fast', 'multimodal'], context: 128000, priceIn: 0.15, priceOut: 0.60 },
23
- { id: 'gpt-4.1', name: 'GPT-4.1', provider: 'openai', tags: ['frontier'], context: 1048576, priceIn: 2.00, priceOut: 8.00 },
24
-
25
- // O-Series (Reasoning) Models
26
- { id: 'o3', name: 'o3', provider: 'openai', tags: ['reasoning', 'frontier'], context: 200000, priceIn: 2.00, priceOut: 8.00 },
27
- { id: 'o3-mini', name: 'o3 Mini', provider: 'openai', tags: ['reasoning', 'fast'], context: 200000, priceIn: 1.10, priceOut: 4.40 },
28
- { id: 'o4-mini', name: 'o4 Mini', provider: 'openai', tags: ['reasoning', 'fast'], context: 200000, priceIn: 1.10, priceOut: 4.40 },
29
- { id: 'o1', name: 'o1', provider: 'openai', tags: ['reasoning'], context: 200000, priceIn: 15.00, priceOut: 60.00 },
30
- { id: 'o1-pro', name: 'o1 Pro', provider: 'openai', tags: ['reasoning', 'pro'], context: 200000, priceIn: 150.00, priceOut: 600.00 },
31
- { id: 'o1-mini', name: 'o1 Mini', provider: 'openai', tags: ['reasoning', 'cheap'], context: 128000, priceIn: 1.10, priceOut: 4.40 },
14
+ // ── GPT-6 Series ──
15
+ { id: 'gpt-6-astra', name: 'GPT-6 Astra', provider: 'openai', tags: ['frontier', 'reasoning'], context: 1050000, priceIn: 10, priceOut: 50 },
16
+ { id: 'gpt-6-sol', name: 'GPT-6 Sol', provider: 'openai', tags: ['reasoning'], context: 1050000, priceIn: 2, priceOut: 10 },
17
+ { id: 'gpt-6.1-sol', name: 'GPT-6.1 Sol', provider: 'openai', tags: ['reasoning'], context: 1050000, priceIn: 2, priceOut: 10 },
18
+ { id: 'gpt-6-luna', name: 'GPT-6 Luna', provider: 'openai', tags: ['fast', 'cheap', 'reasoning'], context: 1050000, priceIn: 0.1, priceOut: 0.5 },
19
+
20
+ // ── GPT-5.6 / 5.5 / 5.4 ──
21
+ { id: 'gpt-5.6', name: 'GPT-5.6', provider: 'openai', tags: ['frontier', 'reasoning'], context: 1050000, priceIn: 4, priceOut: 20 },
22
+ { id: 'gpt-5.6-sol', name: 'GPT-5.6 Sol', provider: 'openai', tags: ['reasoning'], context: 1050000, priceIn: 4, priceOut: 20 },
23
+ { id: 'gpt-5.6-terra', name: 'GPT-5.6 Terra', provider: 'openai', tags: ['reasoning'], context: 1050000, priceIn: 2, priceOut: 12 },
24
+ { id: 'gpt-5.6-luna', name: 'GPT-5.6 Luna', provider: 'openai', tags: ['fast', 'cheap', 'reasoning'], context: 1050000, priceIn: 0.2, priceOut: 1.2 },
25
+ { id: 'gpt-5.5', name: 'GPT-5.5', provider: 'openai', tags: ['frontier', 'reasoning'], context: 1050000, priceIn: 5, priceOut: 30 },
26
+ { id: 'gpt-5.5-pro', name: 'GPT-5.5 Pro', provider: 'openai', tags: ['reasoning', 'pro'], context: 1050000, priceIn: 30, priceOut: 180 },
27
+ { id: 'gpt-5.4', name: 'GPT-5.4', provider: 'openai', tags: ['reasoning'], context: 1050000, priceIn: 2.5, priceOut: 15 },
28
+ { id: 'gpt-5.4-mini', name: 'GPT-5.4 Mini', provider: 'openai', tags: ['fast', 'reasoning'], context: 400000, priceIn: 0.75, priceOut: 4.5 },
29
+ { id: 'gpt-5.4-nano', name: 'GPT-5.4 Nano', provider: 'openai', tags: ['cheap', 'fast', 'reasoning'], context: 400000, priceIn: 0.2, priceOut: 1.25 },
30
+ { id: 'gpt-5.4-pro', name: 'GPT-5.4 Pro', provider: 'openai', tags: ['reasoning', 'pro'], context: 1050000, priceIn: 30, priceOut: 180 },
31
+
32
+ // ── GPT-5 Codex (coding) ──
33
+ { id: 'gpt-5.3-codex', name: 'GPT-5.3 Codex', provider: 'openai', tags: ['coding', 'reasoning'], context: 400000, priceIn: 1.75, priceOut: 14 },
34
+ { id: 'gpt-5.3-codex-spark', name: 'GPT-5.3 Codex Spark', provider: 'openai', tags: ['coding', 'fast', 'reasoning'], context: 128000, priceIn: 1.75, priceOut: 14 },
35
+
36
+ // ── GPT-5 Series ──
37
+ { id: 'gpt-5.2', name: 'GPT-5.2', provider: 'openai', tags: ['reasoning'], context: 400000, priceIn: 1.75, priceOut: 14 },
38
+ { id: 'gpt-5.1', name: 'GPT-5.1', provider: 'openai', tags: ['reasoning'], context: 400000, priceIn: 1.25, priceOut: 10 },
39
+ { id: 'gpt-5', name: 'GPT-5', provider: 'openai', tags: ['frontier', 'reasoning'], context: 400000, priceIn: 1.25, priceOut: 10 },
40
+ { id: 'gpt-5-mini', name: 'GPT-5 Mini', provider: 'openai', tags: ['fast', 'reasoning'], context: 400000, priceIn: 0.25, priceOut: 2 },
41
+ { id: 'gpt-5-nano', name: 'GPT-5 Nano', provider: 'openai', tags: ['cheap', 'fast', 'reasoning'], context: 400000, priceIn: 0.05, priceOut: 0.4 },
42
+ { id: 'gpt-5-pro', name: 'GPT-5 Pro', provider: 'openai', tags: ['reasoning', 'pro'], context: 400000, priceIn: 15, priceOut: 120 },
43
+
44
+ // ── O-Series (Reasoning, legacy) ──
45
+ { id: 'o3', name: 'o3', provider: 'openai', tags: ['reasoning', 'legacy'], context: 200000, priceIn: 2, priceOut: 8 },
46
+ { id: 'o3-pro', name: 'o3 Pro', provider: 'openai', tags: ['reasoning', 'pro', 'legacy'], context: 200000, priceIn: 20, priceOut: 80 },
47
+ { id: 'o3-mini', name: 'o3 Mini', provider: 'openai', tags: ['reasoning', 'fast', 'legacy'], context: 200000, priceIn: 1.1, priceOut: 4.4 },
48
+ { id: 'o4-mini', name: 'o4 Mini', provider: 'openai', tags: ['reasoning', 'fast', 'legacy'], context: 200000, priceIn: 1.1, priceOut: 4.4 },
49
+
50
+ // ── GPT-4.1 / 4o (legacy) ──
51
+ { id: 'gpt-4.1', name: 'GPT-4.1', provider: 'openai', tags: ['legacy'], context: 1047576, priceIn: 2, priceOut: 8 },
52
+ { id: 'gpt-4.1-mini', name: 'GPT-4.1 Mini', provider: 'openai', tags: ['fast', 'legacy'], context: 1047576, priceIn: 0.4, priceOut: 1.6 },
53
+ { id: 'gpt-4.1-nano', name: 'GPT-4.1 Nano', provider: 'openai', tags: ['cheap', 'fast', 'legacy'], context: 1047576, priceIn: 0.1, priceOut: 0.4 },
54
+ { id: 'gpt-4o', name: 'GPT-4o', provider: 'openai', tags: ['legacy', 'multimodal'], context: 128000, priceIn: 2.5, priceOut: 10 },
55
+ { id: 'gpt-4o-mini', name: 'GPT-4o Mini', provider: 'openai', tags: ['fast', 'multimodal', 'legacy'], context: 128000, priceIn: 0.15, priceOut: 0.6 },
32
56
  ],
33
57
  };
34
58
 
@@ -12,32 +12,62 @@ const provider = {
12
12
  envKeyHint: 'OPENROUTER',
13
13
  }),
14
14
  models: [
15
- // Anthropic Models
16
- { id: 'anthropic/claude-3-opus', name: 'Claude 3 Opus (via OpenRouter)', provider: 'openrouter', context: 200000, priceIn: 15, priceOut: 75 },
17
-
18
- // OpenAI Models
19
- { id: 'openai/gpt-4o-mini', name: 'GPT-4o Mini (via OpenRouter)', provider: 'openrouter', context: 128000, priceIn: 0.15, priceOut: 0.60 },
20
- { id: 'openai/gpt-4o', name: 'GPT-4o (via OpenRouter)', provider: 'openrouter', context: 128000, priceIn: 2.50, priceOut: 10.00 },
21
- { id: 'openai/o3-mini-high', name: 'O3 Mini High (via OpenRouter)', provider: 'openrouter', context: 200000, priceIn: 1.10, priceOut: 4.40 },
22
- { id: 'openai/o1', name: 'O1 (via OpenRouter)', provider: 'openrouter', context: 200000, priceIn: 15, priceOut: 60 },
23
-
24
- // Google Models
25
- { id: 'google/gemini-2.5-pro', name: 'Gemini 2.5 Pro (via OpenRouter)', provider: 'openrouter', context: 1048576, priceIn: 1.25, priceOut: 10 },
26
- { id: 'google/gemini-2.5-flash', name: 'Gemini 2.5 Flash (via OpenRouter)', provider: 'openrouter', context: 1048576, priceIn: 0.15, priceOut: 1.25 },
27
-
28
- // DeepSeek
29
- { id: 'deepseek/deepseek-v4-flash', name: 'DeepSeek V4 Flash (via OpenRouter)', provider: 'openrouter', context: 1048576, priceIn: 0.07, priceOut: 0.18 },
30
-
31
- // NVIDIA free-tier variants (the :free slugs are genuinely free on
32
- // OpenRouter; the un-suffixed Nemotron IDs are paid and must not be tagged free)
33
- { id: 'nvidia/nemotron-3-super-120b-a12b:free', name: 'Nemotron 3 Super (free, via OpenRouter)', provider: 'openrouter', tags: ['free'], context: 262144, priceIn: 0, priceOut: 0 },
34
- { id: 'nvidia/nemotron-3-ultra-550b-a55b:free', name: 'Nemotron 3 Ultra (free, via OpenRouter)', provider: 'openrouter', tags: ['free'], context: 1048576, priceIn: 0, priceOut: 0 },
35
-
36
- // Other Top-Tier Alternatives
37
- { id: 'zhipuai/glm-4-plus', name: 'GLM-4 Plus (via OpenRouter)', provider: 'openrouter', context: 128000, priceIn: 1.5, priceOut: 1.5 },
38
- { id: 'minimax/minimax-01', name: 'MiniMax-01 (via OpenRouter)', provider: 'openrouter', context: 1000000, priceIn: 0.14, priceOut: 1.4 },
39
- { id: 'mistralai/mistral-large-2411', name: 'Mistral Large 2411 (via OpenRouter)', provider: 'openrouter', context: 128000, priceIn: 2, priceOut: 6 }
40
- ]
15
+ // ── Anthropic ──
16
+ { id: 'anthropic/claude-opus-5.5', name: 'Claude Opus 5.5 (via OpenRouter)', provider: 'openrouter', tags: ['frontier', 'reasoning'], context: 1000000, priceIn: 4, priceOut: 20 },
17
+ { id: 'anthropic/claude-opus-4.8', name: 'Claude Opus 4.8 (via OpenRouter)', provider: 'openrouter', tags: ['frontier', 'reasoning'], context: 1000000, priceIn: 5, priceOut: 25 },
18
+ { id: 'anthropic/claude-sonnet-5.5', name: 'Claude Sonnet 5.5 (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 1000000, priceIn: 2, priceOut: 10 },
19
+ { id: 'anthropic/claude-sonnet-4.6', name: 'Claude Sonnet 4.6 (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 1000000, priceIn: 3, priceOut: 15 },
20
+ { id: 'anthropic/claude-haiku-5.5', name: 'Claude Haiku 5.5 (via OpenRouter)', provider: 'openrouter', tags: ['fast', 'reasoning'], context: 1000000, priceIn: 0.1, priceOut: 0.5 },
21
+
22
+ // ── OpenAI ──
23
+ { id: 'openai/gpt-6-astra', name: 'GPT-6 Astra (via OpenRouter)', provider: 'openrouter', tags: ['frontier', 'reasoning'], context: 1050000, priceIn: 10, priceOut: 50 },
24
+ { id: 'openai/gpt-6-sol', name: 'GPT-6 Sol (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 1050000, priceIn: 2, priceOut: 10 },
25
+ { id: 'openai/gpt-6-luna', name: 'GPT-6 Luna (via OpenRouter)', provider: 'openrouter', tags: ['fast', 'cheap', 'reasoning'], context: 1050000, priceIn: 0.1, priceOut: 0.5 },
26
+ { id: 'openai/gpt-5.6-sol', name: 'GPT-5.6 Sol (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 1050000, priceIn: 4, priceOut: 20 },
27
+ { id: 'openai/gpt-5.6-terra', name: 'GPT-5.6 Terra (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 1050000, priceIn: 2, priceOut: 12 },
28
+ { id: 'openai/gpt-5.6-luna', name: 'GPT-5.6 Luna (via OpenRouter)', provider: 'openrouter', tags: ['fast', 'cheap', 'reasoning'], context: 1050000, priceIn: 0.2, priceOut: 1.2 },
29
+ { id: 'openai/gpt-5.5', name: 'GPT-5.5 (via OpenRouter)', provider: 'openrouter', tags: ['frontier', 'reasoning'], context: 1050000, priceIn: 5, priceOut: 30 },
30
+ { id: 'openai/gpt-5.4', name: 'GPT-5.4 (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 1050000, priceIn: 2.5, priceOut: 15 },
31
+ { id: 'openai/gpt-5.3-codex', name: 'GPT-5.3 Codex (via OpenRouter)', provider: 'openrouter', tags: ['coding', 'reasoning'], context: 400000, priceIn: 1.75, priceOut: 14 },
32
+ { id: 'openai/o3', name: 'o3 (via OpenRouter)', provider: 'openrouter', tags: ['reasoning', 'legacy'], context: 200000, priceIn: 2, priceOut: 8 },
33
+
34
+ // ── Google ──
35
+ { id: 'google/gemini-4-argon', name: 'Gemini 4 Argon (via OpenRouter)', provider: 'openrouter', tags: ['frontier', 'reasoning'], context: 1048576, priceIn: 2, priceOut: 10 },
36
+ { id: 'google/gemini-3.1-pro-preview', name: 'Gemini 3.1 Pro Preview (via OpenRouter)', provider: 'openrouter', tags: ['frontier', 'reasoning'], context: 1048576, priceIn: 2, priceOut: 12 },
37
+ { id: 'google/gemini-3.8-flash', name: 'Gemini 3.8 Flash (via OpenRouter)', provider: 'openrouter', tags: ['fast', 'reasoning'], context: 1048576, priceIn: 0.82, priceOut: 4.12 },
38
+ { id: 'google/gemini-3.5-flash', name: 'Gemini 3.5 Flash (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 1048576, priceIn: 1.5, priceOut: 9 },
39
+
40
+ // ── DeepSeek ──
41
+ { id: 'deepseek/deepseek-v4.1-flash', name: 'DeepSeek V4.1 Flash (via OpenRouter)', provider: 'openrouter', tags: ['fast', 'reasoning'], context: 1048576, priceIn: 0.04, priceOut: 0.64 },
42
+ { id: 'deepseek/deepseek-v4-pro', name: 'DeepSeek V4 Pro (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 1048576, priceIn: 0.3, priceOut: 0.59 },
43
+
44
+ // ── Moonshot / Z.ai / Qwen / MiniMax ──
45
+ { id: 'moonshotai/kimi-k3', name: 'Kimi K3 (via OpenRouter)', provider: 'openrouter', tags: ['frontier', 'reasoning'], context: 1048576, priceIn: 0.3, priceOut: 11.94 },
46
+ { id: 'moonshotai/kimi-k2.6', name: 'Kimi K2.6 (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 262144, priceIn: 0.65, priceOut: 3.41 },
47
+ { id: 'z-ai/glm-5.3', name: 'GLM-5.3 (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 1048576, priceIn: 0.08, priceOut: 4.8 },
48
+ { id: 'z-ai/glm-5.3-flash', name: 'GLM-5.3 Flash (via OpenRouter)', provider: 'openrouter', tags: ['fast', 'reasoning'], context: 1048576, priceIn: 0.03, priceOut: 0.58 },
49
+ { id: 'qwen/qwen3.5-397b-a17b', name: 'Qwen3.5 397B A17B (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 262144, priceIn: 0.5, priceOut: 3.6 },
50
+ { id: 'qwen/qwen3-coder', name: 'Qwen3 Coder 480B (via OpenRouter)', provider: 'openrouter', tags: ['coding'], context: 262144, priceIn: 0.3, priceOut: 1 },
51
+ { id: 'qwen/qwen3-coder-flash', name: 'Qwen3 Coder Flash (via OpenRouter)', provider: 'openrouter', tags: ['coding', 'fast'], context: 1000000, priceIn: 0.2, priceOut: 0.98 },
52
+ { id: 'minimax/minimax-m3', name: 'MiniMax M3 (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 1048576, priceIn: 0.3, priceOut: 1.2 },
53
+
54
+ // ── xAI / Meta / Mistral / Cohere / Writer ──
55
+ { id: 'x-ai/grok-4.7', name: 'Grok 4.7 (via OpenRouter)', provider: 'openrouter', tags: ['frontier', 'reasoning'], context: 500000, priceIn: 2, priceOut: 6 },
56
+ { id: 'x-ai/grok-4.3', name: 'Grok 4.3 (via OpenRouter)', provider: 'openrouter', tags: ['reasoning'], context: 1000000, priceIn: 1.25, priceOut: 2.5 },
57
+ { id: 'meta-llama/llama-4-maverick', name: 'Llama 4 Maverick (via OpenRouter)', provider: 'openrouter', tags: [], context: 1048576, priceIn: 0.19, priceOut: 0.65 },
58
+ { id: 'meta-llama/llama-4-scout', name: 'Llama 4 Scout (via OpenRouter)', provider: 'openrouter', tags: ['fast'], context: 1310720, priceIn: 0.1, priceOut: 0.3 },
59
+ { id: 'mistralai/mistral-large-4-0', name: 'Mistral Large 4 (via OpenRouter)', provider: 'openrouter', tags: ['frontier'], context: 1048576, priceIn: 0.68, priceOut: 2.09 },
60
+ { id: 'cohere/command-a', name: 'Command A (via OpenRouter)', provider: 'openrouter', tags: [], context: 256000, priceIn: 2.5, priceOut: 10 },
61
+ { id: 'writer/palmyra-x5', name: 'Palmyra X5 (via OpenRouter)', provider: 'openrouter', tags: [], context: 1040000, priceIn: 0.6, priceOut: 6 },
62
+
63
+ // ── Free tiers (genuinely free slugs) ──
64
+ { id: 'nvidia/nemotron-3-super-120b-a12b:free', name: 'Nemotron 3 Super (free, via OpenRouter)', provider: 'openrouter', tags: ['free', 'reasoning'], context: 262144, priceIn: 0, priceOut: 0 },
65
+ { id: 'nvidia/nemotron-3-ultra-550b-a55b:free', name: 'Nemotron 3 Ultra (free, via OpenRouter)', provider: 'openrouter', tags: ['free', 'reasoning'], context: 1000000, priceIn: 0, priceOut: 0 },
66
+ { id: 'nvidia/nemotron-3.5-lightning:free', name: 'Nemotron 3.5 Lightning (free, via OpenRouter)', provider: 'openrouter', tags: ['free', 'fast', 'reasoning'], context: 1000000, priceIn: 0, priceOut: 0 },
67
+ { id: 'nvidia/nemotron-3.5-super-vl:free', name: 'Nemotron 3.5 Super VL (free, via OpenRouter)', provider: 'openrouter', tags: ['free', 'vision', 'reasoning'], context: 1000000, priceIn: 0, priceOut: 0 },
68
+ { id: 'google/gemma-4-31b-it:free', name: 'Gemma 4 31B (free, via OpenRouter)', provider: 'openrouter', tags: ['free', 'reasoning'], context: 262144, priceIn: 0, priceOut: 0 },
69
+ { id: 'google/gemma-4-26b-a4b-it:free', name: 'Gemma 4 26B A4B (free, via OpenRouter)', provider: 'openrouter', tags: ['free', 'fast', 'reasoning'], context: 262144, priceIn: 0, priceOut: 0 },
70
+ ],
41
71
  };
42
72
 
43
73
  module.exports = provider;