@bubblebrain-ai/bubble 0.0.64 → 0.0.65

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.
@@ -99,6 +99,10 @@ const GROK_45_LEVELS = ["low", "medium", "high"];
99
99
  // Gemini 4 documents only low/medium (default)/high. Verified live on
100
100
  // 2026-10-13: thinkingLevel=minimal returns 400, so there is no off/minimal.
101
101
  const GEMINI_4_LEVELS = ["low", "medium", "high"];
102
+ // Gemini 4 Hydrogen adds xhigh above the Gemini 4 ladder and defaults to high.
103
+ // Verified live on 2026-11-12: xhigh returns 200, minimal still returns 400
104
+ // (Argon rejects xhigh).
105
+ const GEMINI_4_HYDROGEN_LEVELS = ["low", "medium", "high", "xhigh"];
102
106
  const GEMINI_3_LEVELS = ["low", "medium", "high"];
103
107
  const GEMINI_3_FLASH_LEVELS = ["minimal", "low", "medium", "high"];
104
108
  // Verified against the native API on 2026-09-02: Gemini 3.7/3.8 Flash reject
@@ -163,6 +167,14 @@ export const BUILTIN_MODELS = [
163
167
  // Gemini 4 Argon: 1,048,576-token window shared by input, thinking and output.
164
168
  // https://ai.google.dev/gemini-api/docs/models/gemini-4-argon
165
169
  { id: "gemini-4-argon", name: "Gemini 4 Argon", providerId: "google", tier: "strong", reasoningLevels: GEMINI_4_LEVELS, defaultReasoningLevel: "medium", contextWindow: 1048576 },
170
+ // Gemini 4 Carbon (2026-11-10): Google's most capable reasoning model, same
171
+ // window, levels and medium default as Argon (xhigh verified 400 on 2026-11-12).
172
+ // Listed after Argon so the provider's default model does not change.
173
+ // https://ai.google.dev/gemini-api/docs/models/gemini-4-carbon
174
+ { id: "gemini-4-carbon", name: "Gemini 4 Carbon", providerId: "google", tier: "strong", reasoningLevels: GEMINI_4_LEVELS, defaultReasoningLevel: "medium", contextWindow: 1048576 },
175
+ // Gemini 4 Hydrogen (2026-11-12): same shared 1,048,576-token window, 65,536
176
+ // output by default. https://ai.google.dev/gemini-api/docs/models/gemini-4-hydrogen
177
+ { id: "gemini-4-hydrogen", name: "Gemini 4 Hydrogen", providerId: "google", tier: "balanced", reasoningLevels: GEMINI_4_HYDROGEN_LEVELS, defaultReasoningLevel: "high", contextWindow: 1048576 },
166
178
  { id: "gemini-3.8-flash", name: "Gemini 3.8 Flash", providerId: "google", tier: "fast", reasoningLevels: GEMINI_37_38_FLASH_LEVELS, contextWindow: 1048576 },
167
179
  { id: "gemini-3.7-flash", name: "Gemini 3.7 Flash", providerId: "google", tier: "fast", reasoningLevels: GEMINI_37_38_FLASH_LEVELS, contextWindow: 1048576 },
168
180
  { id: "gemini-3.5-flash", name: "Gemini 3.5 Flash", providerId: "google", tier: "fast", reasoningLevels: GEMINI_3_FLASH_LEVELS, contextWindow: 1048576 },
@@ -64,12 +64,13 @@ export const MODEL_PRICING_OVERRIDES = [
64
64
  outputPerMillion: 7.5,
65
65
  },
66
66
  })),
67
- // Gemini 4 Argon introductory pricing through 2026-11-30; standard rates from
68
- // 2026-12-01. Implicit caching only (95% off input), so no cache-write rate.
67
+ // Gemini 4 Argon and Carbon share one tariff: introductory pricing through
68
+ // 2026-11-30, standard rates from 2026-12-01. Implicit caching only (95% off
69
+ // input), so no cache-write rate.
69
70
  // https://ai.google.dev/gemini-api/docs/pricing
70
- {
71
+ ...["gemini-4-argon", "gemini-4-carbon"].map((modelId) => ({
71
72
  providerId: "google",
72
- modelId: "gemini-4-argon",
73
+ modelId,
73
74
  currency: "USD",
74
75
  inputCacheHitPerMillion: 0.1,
75
76
  inputCacheMissPerMillion: 2,
@@ -80,6 +81,17 @@ export const MODEL_PRICING_OVERRIDES = [
80
81
  inputCacheMissPerMillion: 4,
81
82
  outputPerMillion: 20,
82
83
  },
84
+ })),
85
+ // Gemini 4 Hydrogen: one flat rate for all context lengths, implicit caching
86
+ // only (95% off input). Not yet on models.dev at launch (2026-11-12).
87
+ // https://ai.google.dev/gemini-api/docs/pricing
88
+ {
89
+ providerId: "google",
90
+ modelId: "gemini-4-hydrogen",
91
+ currency: "USD",
92
+ inputCacheHitPerMillion: 0.075,
93
+ inputCacheMissPerMillion: 1.5,
94
+ outputPerMillion: 7.5,
83
95
  },
84
96
  {
85
97
  providerId: "deepseek",
@@ -50,8 +50,8 @@ export declare function fetchGeminiModels(options: {
50
50
  }): Promise<GeminiModelDescriptor[]>;
51
51
  /**
52
52
  * Ranking: one entry per (version, tier) family — GA ids beat dated previews —
53
- * then newest version first, flagship (pro/argon) before flash before
54
- * flash-lite, top N.
53
+ * then newest version first, pro/argon before carbon before hydrogen before
54
+ * flash before flash-lite, top N.
55
55
  */
56
56
  export declare function selectLatestGeminiModels(entries: GeminiModelListEntry[], limit?: number): GeminiModelDescriptor[];
57
57
  /** Mirrors the static catalog's per-family thinking support. */
@@ -16,13 +16,19 @@ import { RateLimitError } from "./network/errors.js";
16
16
  import { ProviderStreamInterruptedError, computeRetryDelayMs, getProviderMaxRetries, isRetryableHttpStatus, sleepBeforeRetry, } from "./network/retry.js";
17
17
  import { createProviderFetch, isProviderTransportError } from "./network/provider-transport.js";
18
18
  const GEMINI_DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
19
+ /**
20
+ * Per-call channel from buildCallOptions to the fetch wrapper: carries a
21
+ * thinking level the pinned @ai-sdk/google schema cannot express. Stripped
22
+ * before the request leaves the process.
23
+ */
24
+ const THINKING_LEVEL_OVERRIDE_HEADER = "x-bubble-thinking-level";
19
25
  const AI_SDK_PROVIDER_FACTORIES = {
20
26
  google: (options) => {
21
27
  const provider = createGoogleGenerativeAI({
22
28
  apiKey: options.apiKey,
23
29
  ...(options.baseURL ? { baseURL: normalizeBaseURL(options.baseURL) } : {}),
24
30
  ...(options.headers ? { headers: options.headers } : {}),
25
- fetch: (options.fetch ?? createProviderFetch({
31
+ fetch: withThinkingLevelOverride(options.fetch ?? createProviderFetch({
26
32
  providerName: "Google Gemini",
27
33
  verboseEnvVar: "BUBBLE_AI_SDK_FETCH_VERBOSE",
28
34
  })),
@@ -117,9 +123,12 @@ export function createAiSdkProvider(options) {
117
123
  // ============================================================================
118
124
  function buildCallOptions(messages, chatOptions, providerOptions) {
119
125
  const prompt = convertMessages(messages);
120
- const thinking = buildGoogleThinkingOptions(chatOptions.model, chatOptions.thinkingLevel ?? providerOptions.thinkingLevel);
126
+ const thinkingLevel = chatOptions.thinkingLevel ?? providerOptions.thinkingLevel;
127
+ const thinking = buildGoogleThinkingOptions(chatOptions.model, thinkingLevel);
128
+ const levelOverride = googleThinkingLevelOverride(chatOptions.model, thinkingLevel);
121
129
  return {
122
130
  prompt,
131
+ ...(levelOverride ? { headers: { [THINKING_LEVEL_OVERRIDE_HEADER]: levelOverride } } : {}),
123
132
  ...(chatOptions.temperature !== undefined ? { temperature: chatOptions.temperature } : {}),
124
133
  ...(chatOptions.abortSignal ? { abortSignal: chatOptions.abortSignal } : {}),
125
134
  ...(chatOptions.tools?.length
@@ -139,7 +148,8 @@ function buildCallOptions(messages, chatOptions, providerOptions) {
139
148
  /**
140
149
  * Gemini 3+ models take a graded thinking_level; 2.5-era models take a token
141
150
  * budget. "off" (budget 0) is only offered in the catalog for models that
142
- * accept it (2.5 Flash); xhigh/max/ultra clamp to high.
151
+ * accept it (2.5 Flash); xhigh/max/ultra clamp to high here, and models with
152
+ * a native xhigh get it back through googleThinkingLevelOverride.
143
153
  */
144
154
  function buildGoogleThinkingOptions(modelId, level) {
145
155
  if (!level)
@@ -159,6 +169,35 @@ function buildGoogleThinkingOptions(modelId, level) {
159
169
  const budgets = { minimal: 512, low: 2048, medium: 8192, high: 24576 };
160
170
  return { thinkingConfig: { thinkingBudget: budgets[clamped] ?? 8192, includeThoughts: true } };
161
171
  }
172
+ /**
173
+ * The @ai-sdk/google v3 schema only admits minimal/low/medium/high, so a model
174
+ * with a native xhigh (Gemini 4 Hydrogen) is sent "high" through the SDK and
175
+ * the real level is written into the request body by the fetch wrapper.
176
+ */
177
+ function googleThinkingLevelOverride(modelId, level) {
178
+ if (level !== "xhigh" && level !== "max" && level !== "ultra")
179
+ return undefined;
180
+ return geminiReasoningLevels(modelId).includes("xhigh") ? "xhigh" : undefined;
181
+ }
182
+ function withThinkingLevelOverride(fetchImpl) {
183
+ return async (input, init) => {
184
+ const headers = new Headers(init?.headers);
185
+ const level = headers.get(THINKING_LEVEL_OVERRIDE_HEADER);
186
+ if (!level)
187
+ return fetchImpl(input, init);
188
+ headers.delete(THINKING_LEVEL_OVERRIDE_HEADER);
189
+ let body = init?.body;
190
+ if (typeof body === "string") {
191
+ const parsed = JSON.parse(body);
192
+ parsed.generationConfig = {
193
+ ...parsed.generationConfig,
194
+ thinkingConfig: { ...parsed.generationConfig?.thinkingConfig, thinkingLevel: level },
195
+ };
196
+ body = JSON.stringify(parsed);
197
+ }
198
+ return fetchImpl(input, { ...init, headers: Object.fromEntries(headers), body });
199
+ };
200
+ }
162
201
  function convertMessages(messages) {
163
202
  const out = [];
164
203
  const toolNamesById = new Map();
@@ -465,13 +504,14 @@ export async function fetchGeminiModels(options) {
465
504
  }
466
505
  /**
467
506
  * Ranking: one entry per (version, tier) family — GA ids beat dated previews —
468
- * then newest version first, flagship (pro/argon) before flash before
469
- * flash-lite, top N.
507
+ * then newest version first, pro/argon before carbon before hydrogen before
508
+ * flash before flash-lite, top N.
470
509
  */
471
510
  export function selectLatestGeminiModels(entries, limit = 5) {
472
- // Gemini 4 introduced named flagship tiers (gemini-4-argon). Unknown names
473
- // stay excluded rather than guessing whether they are text models.
474
- const TIER_RANK = { argon: 0, pro: 0, flash: 1, "flash-lite": 2 };
511
+ // Gemini 4 introduced named tiers (argon, carbon, hydrogen). Unknown names
512
+ // stay excluded rather than guessing whether they are text models. Argon
513
+ // stays ahead of Carbon to match the static catalog's default model.
514
+ const TIER_RANK = { argon: 0, pro: 0, carbon: 1, hydrogen: 2, flash: 3, "flash-lite": 4 };
475
515
  const candidates = [];
476
516
  for (const entry of entries) {
477
517
  const id = (entry.name ?? "").replace(/^models\//, "");
@@ -481,7 +521,7 @@ export function selectLatestGeminiModels(entries, limit = 5) {
481
521
  continue;
482
522
  if (entry.supportedGenerationMethods && !entry.supportedGenerationMethods.includes("generateContent"))
483
523
  continue;
484
- const match = /^gemini-(\d+(?:\.\d+)?)-(argon|pro|flash)(-lite)?/.exec(id);
524
+ const match = /^gemini-(\d+(?:\.\d+)?)-(argon|carbon|hydrogen|pro|flash)(-lite)?/.exec(id);
485
525
  if (!match)
486
526
  continue;
487
527
  const tier = `${match[2]}${match[3] ?? ""}`;
@@ -490,7 +530,7 @@ export function selectLatestGeminiModels(entries, limit = 5) {
490
530
  entry,
491
531
  version: Number.parseFloat(match[1]),
492
532
  tier,
493
- tierRank: TIER_RANK[tier] ?? 3,
533
+ tierRank: TIER_RANK[tier] ?? 5,
494
534
  isPreview: id.includes("preview") || id.includes("exp"),
495
535
  });
496
536
  }
@@ -513,7 +553,7 @@ export function selectLatestGeminiModels(entries, limit = 5) {
513
553
  name: candidate.entry.displayName || candidate.id,
514
554
  contextWindow: Number.isFinite(candidate.entry.inputTokenLimit) ? candidate.entry.inputTokenLimit : undefined,
515
555
  reasoningLevels: geminiReasoningLevels(candidate.id),
516
- // Argon defaults to Google's recommended medium via the catalog.
556
+ // Argon/Carbon (medium) and Hydrogen (high) take Google's defaults from the catalog.
517
557
  ...(candidate.tier === "pro" ? { defaultReasoningLevel: "high" } : {}),
518
558
  }));
519
559
  }
@@ -524,6 +564,9 @@ function geminiVersion(modelId) {
524
564
  export function geminiReasoningLevels(modelId) {
525
565
  const version = geminiVersion(modelId);
526
566
  const isPro = modelId.includes("-pro");
567
+ // Hydrogen is the only Gemini 4 tier with xhigh; Argon and Carbon return 400 for it.
568
+ if (version >= 4 && /^gemini-[\d.]+-hydrogen/.test(modelId))
569
+ return ["low", "medium", "high", "xhigh"];
527
570
  if (version >= 4)
528
571
  return ["low", "medium", "high"];
529
572
  // Live probes show 3.7 and 3.8 Flash share this newer ladder. Keep the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bubblebrain-ai/bubble",
3
- "version": "0.0.64",
3
+ "version": "0.0.65",
4
4
  "description": "A terminal coding agent",
5
5
  "type": "module",
6
6
  "engines": {