@ai-sdk/google 3.0.80 → 3.0.82

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.
@@ -248,8 +248,14 @@ The following optional provider options are available for Google Generative AI m
248
248
  - **serviceTier** _'standard' | 'flex' | 'priority'_
249
249
 
250
250
  Optional. The service tier to use for the request.
251
- Set to 'flex' for 50% cheaper processing at the cost of increased latency.
252
- Set to 'priority' for ultra-low latency at a 75-100% price premium over 'standard'.
251
+ Set to `'flex'` for 50% cheaper processing at the cost of increased latency.
252
+ Set to `'priority'` for ultra-low latency at a 75-100% price premium over `'standard'`.
253
+
254
+ Because Priority can be gracefully downgraded to Standard under load, the
255
+ tier the request actually ran on is surfaced on
256
+ `result.providerMetadata.google.serviceTier`. See
257
+ [Priority inference](https://ai.google.dev/gemini-api/docs/priority-inference)
258
+ and [Flex inference](https://ai.google.dev/gemini-api/docs/flex-inference).
253
259
 
254
260
  - **threshold** _string_
255
261
 
@@ -1090,8 +1096,9 @@ The following Zod features are known to not work with Google Generative AI:
1090
1096
 
1091
1097
  The [Gemini Interactions API](https://ai.google.dev/gemini-api/docs/interactions)
1092
1098
  (`POST /v1beta/interactions`) is a separate Google endpoint with server-side
1093
- state, unified content blocks, first-class built-in tools, agent presets, and
1094
- native multimodal image output. It is reached via the `google.interactions(...)`
1099
+ state, unified content blocks, first-class built-in tools, agent presets,
1100
+ managed agents that run in a sandboxed Linux environment, and native
1101
+ multimodal image output. It is reached via the `google.interactions(...)`
1095
1102
  factory:
1096
1103
 
1097
1104
  ```ts
@@ -1104,10 +1111,12 @@ const { text } = await generateText({
1104
1111
  });
1105
1112
  ```
1106
1113
 
1107
- `google.interactions(...)` accepts either a model ID string (e.g.
1108
- `'gemini-2.5-flash'`, `'gemini-3-pro-preview'`) or `{ agent: <name> }` to use
1109
- a Gemini [agent preset](#agent-presets). The returned model can be passed to
1110
- `generateText` and `streamText` like any other AI SDK language model.
1114
+ `google.interactions(...)` accepts a model ID string (e.g.
1115
+ `'gemini-2.5-flash'`, `'gemini-3-pro-preview'`), `{ agent: <name> }` to use
1116
+ a Gemini [agent preset](#agent-presets), or `{ managedAgent: <name> }` to
1117
+ invoke a [managed agent](#managed-agents) you created on Google's side.
1118
+ The returned model can be passed to `generateText` and `streamText` like
1119
+ any other AI SDK language model.
1111
1120
 
1112
1121
  <Note>
1113
1122
  Use `google(...)` for the standard `:generateContent` /
@@ -1213,6 +1222,22 @@ The following optional provider options are available:
1213
1222
  Alternative to the AI SDK `system` message. If both are set, the AI SDK
1214
1223
  `system` message wins and a warning is emitted.
1215
1224
 
1225
+ - **background** _boolean_
1226
+
1227
+ Run the interaction in the background. Required for agents whose
1228
+ server-side workflow cannot complete within a single request/response;
1229
+ rejected by agents that only support synchronous calls. When `true`,
1230
+ the POST returns a non-terminal status and the SDK polls
1231
+ `GET /interactions/{id}` until the work completes.
1232
+
1233
+ - **environment** _string \| object_
1234
+
1235
+ Sandbox environment configuration for [managed agents](#managed-agents).
1236
+ Pass `'remote'` to provision a fresh sandbox, an `environment_id`
1237
+ string to reuse an existing one, or an object of the form
1238
+ `{ type: 'remote', sources?, network? }` to preload files and/or
1239
+ constrain outbound traffic. Only applies to agent calls.
1240
+
1216
1241
  - **pollingTimeoutMs** _number_
1217
1242
 
1218
1243
  Maximum time, in milliseconds, to poll a background interaction (agent
@@ -1466,7 +1491,10 @@ Pass `{ agent: <name> }` to target a Gemini agent preset. The factory
1466
1491
  type-checks the agent name against the supported set:
1467
1492
 
1468
1493
  ```ts
1469
- import { google } from '@ai-sdk/google';
1494
+ import {
1495
+ google,
1496
+ type GoogleLanguageModelInteractionsOptions,
1497
+ } from '@ai-sdk/google';
1470
1498
  import { generateText } from 'ai';
1471
1499
 
1472
1500
  const result = await generateText({
@@ -1475,28 +1503,143 @@ const result = await generateText({
1475
1503
  }),
1476
1504
  prompt:
1477
1505
  'Briefly summarize the most-cited papers on retrieval-augmented generation since 2024 (2-3 sentences).',
1506
+ providerOptions: {
1507
+ google: {
1508
+ background: true,
1509
+ } satisfies GoogleLanguageModelInteractionsOptions,
1510
+ },
1478
1511
  });
1479
1512
  ```
1480
1513
 
1481
- Agent calls run with `background: true` on the wire and the SDK polls the
1482
- `GET /interactions/{id}` endpoint internally until the interaction
1483
- completes. The default polling timeout is 30 minutes; raise it via
1514
+ Whether an agent runs synchronously or in the background depends on the
1515
+ agent. Long-running presets (such as the `deep-research-*` family)
1516
+ require `background: true` the POST returns a non-terminal status and
1517
+ the SDK polls `GET /interactions/{id}` internally until the interaction
1518
+ completes. Other agents accept synchronous calls only and will reject
1519
+ `background: true`. Set the flag explicitly via
1520
+ `providerOptions.google.background`.
1521
+
1522
+ The default polling timeout is 30 minutes; raise it via
1484
1523
  `pollingTimeoutMs` for slower agents:
1485
1524
 
1486
1525
  ```ts
1526
+ import {
1527
+ google,
1528
+ type GoogleLanguageModelInteractionsOptions,
1529
+ } from '@ai-sdk/google';
1530
+ import { generateText } from 'ai';
1531
+
1487
1532
  await generateText({
1488
1533
  model: google.interactions({ agent: 'deep-research-max-preview-04-2026' }),
1489
1534
  prompt: 'Produce a long-form research brief on ...',
1490
1535
  providerOptions: {
1491
1536
  google: {
1537
+ background: true,
1492
1538
  pollingTimeoutMs: 60 * 60 * 1000, // 1 hour
1493
- },
1539
+ } satisfies GoogleLanguageModelInteractionsOptions,
1494
1540
  },
1495
1541
  });
1496
1542
  ```
1497
1543
 
1498
1544
  Agents also chain through `previousInteractionId` like model-id calls.
1499
1545
 
1546
+ ### Managed Agents
1547
+
1548
+ [Managed agents](https://ai.google.dev/gemini-api/docs/agents) run inside a
1549
+ sandboxed Linux environment provisioned per interaction. Pass the `environment`
1550
+ provider option to control how the sandbox is set up; the option is only
1551
+ accepted on agent calls.
1552
+
1553
+ The simplest form provisions a fresh sandbox:
1554
+
1555
+ ```ts
1556
+ import {
1557
+ google,
1558
+ type GoogleLanguageModelInteractionsOptions,
1559
+ } from '@ai-sdk/google';
1560
+ import { generateText } from 'ai';
1561
+
1562
+ const result = await generateText({
1563
+ model: google.interactions({ agent: 'antigravity-preview-05-2026' }),
1564
+ prompt: 'What is 2 + 2?',
1565
+ providerOptions: {
1566
+ google: {
1567
+ environment: 'remote',
1568
+ } satisfies GoogleLanguageModelInteractionsOptions,
1569
+ },
1570
+ });
1571
+ ```
1572
+
1573
+ `environment` accepts three shapes:
1574
+
1575
+ - `'remote'` — provision a fresh sandbox for this call.
1576
+ - any other string — an `environment_id` to reuse, forking the previous
1577
+ sandbox so its filesystem and installed packages persist.
1578
+ - an object — provision a fresh sandbox and optionally preload `sources`
1579
+ and/or constrain outbound traffic via `network`:
1580
+
1581
+ ```ts
1582
+ import {
1583
+ google,
1584
+ type GoogleLanguageModelInteractionsOptions,
1585
+ } from '@ai-sdk/google';
1586
+ import { generateText } from 'ai';
1587
+
1588
+ await generateText({
1589
+ model: google.interactions({ agent: 'antigravity-preview-05-2026' }),
1590
+ prompt:
1591
+ 'Read the file at /data/note.txt and tell me exactly what it contains.',
1592
+ providerOptions: {
1593
+ google: {
1594
+ environment: {
1595
+ type: 'remote',
1596
+ sources: [
1597
+ {
1598
+ type: 'inline',
1599
+ content: 'hello from the AI SDK example\n',
1600
+ target: '/data/note.txt',
1601
+ },
1602
+ ],
1603
+ },
1604
+ } satisfies GoogleLanguageModelInteractionsOptions,
1605
+ },
1606
+ });
1607
+ ```
1608
+
1609
+ Three source types are supported: `inline` (write a string into the
1610
+ sandbox at `target`), `repository` (clone a git repository — pass the
1611
+ URL as `source`), and `gcs` (mount a Google Cloud Storage prefix).
1612
+
1613
+ The `network` field accepts the string `'disabled'` to block all
1614
+ outbound traffic, or an object with an `allowlist` array whose entries
1615
+ each carry a `domain` plus an optional `transform` array of header
1616
+ objects to inject into matching requests.
1617
+
1618
+ #### Custom managed agents
1619
+
1620
+ For user-defined agents that you created on Google's side via the
1621
+ Gemini API's `/v1beta/agents` endpoint, pass the agent's name through the dedicated
1622
+ `managedAgent` factory shape instead of `agent` (which only accepts
1623
+ known preset names):
1624
+
1625
+ ```ts
1626
+ import {
1627
+ google,
1628
+ type GoogleLanguageModelInteractionsOptions,
1629
+ } from '@ai-sdk/google';
1630
+ import { generateText } from 'ai';
1631
+
1632
+ const result = await generateText({
1633
+ model: google.interactions({ managedAgent: 'my-custom-agent' }),
1634
+ prompt: 'Hello!',
1635
+ providerOptions: {
1636
+ google: {
1637
+ environment: 'remote',
1638
+ } satisfies GoogleLanguageModelInteractionsOptions,
1639
+ },
1640
+ });
1641
+ ```
1642
+
1500
1643
  ### Streaming
1501
1644
 
1502
1645
  `streamText` is supported and consumes the seven Interactions SSE event
@@ -1522,22 +1665,6 @@ const googleMetadata = (await result.providerMetadata)?.google;
1522
1665
  console.log('Interaction id:', googleMetadata?.interactionId);
1523
1666
  ```
1524
1667
 
1525
- ### Runnable Examples
1526
-
1527
- Paired `generateText` + `streamText` examples live under:
1528
-
1529
- - `examples/ai-functions/src/generate-text/google/interactions-*.ts`
1530
- - `examples/ai-functions/src/stream-text/google/interactions-*.ts`
1531
-
1532
- Notable examples: `interactions-basic`, `interactions-multi-turn-stateful`,
1533
- `interactions-multi-turn-stateless`, `interactions-tool-call`,
1534
- `interactions-google-search`, `interactions-image-output`,
1535
- `interactions-image-output-modify`, `interactions-image-base64`,
1536
- `interactions-image-reference`, `interactions-image-url`,
1537
- `interactions-pdf`, `interactions-structured-output`,
1538
- `interactions-service-tier`, `interactions-agent-single-turn`, and
1539
- `interactions-agent-multi-turn`.
1540
-
1541
1668
  ## Gemma Models
1542
1669
 
1543
1670
  You can use [Gemma models](https://deepmind.google/models/gemma/) with the Google Generative AI API.
@@ -1768,6 +1895,29 @@ const { image } = await generateImage({
1768
1895
  details.
1769
1896
  </Note>
1770
1897
 
1898
+ #### Google Search Grounding
1899
+
1900
+ Gemini image models support [Google Search grounding](#google-search) through `providerOptions.google.googleSearch`. The value matches the args of `google.tools.googleSearch(...)`; pass `{}` to enable with defaults, or `{ searchTypes: { imageSearch: {} } }` to ground on reference photos.
1901
+
1902
+ ```ts
1903
+ import { google } from '@ai-sdk/google';
1904
+ import { generateImage } from 'ai';
1905
+
1906
+ const result = await generateImage({
1907
+ model: google.image('gemini-3.1-flash-image-preview'),
1908
+ prompt:
1909
+ 'Search for live footage of the 2026 Super Bowl halftime show artist, then generate a close-up in space.',
1910
+ providerOptions: {
1911
+ google: {
1912
+ googleSearch: { searchTypes: { imageSearch: {} } },
1913
+ },
1914
+ },
1915
+ });
1916
+
1917
+ // Grounding metadata is forwarded onto the image result:
1918
+ console.log(result.providerMetadata?.google?.groundingMetadata);
1919
+ ```
1920
+
1771
1921
  #### Gemini Image Model Capabilities
1772
1922
 
1773
1923
  | Model | Image Generation | Image Editing | Aspect Ratios |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "3.0.80",
3
+ "version": "3.0.82",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -36,8 +36,8 @@
36
36
  }
37
37
  },
38
38
  "dependencies": {
39
- "@ai-sdk/provider": "3.0.10",
40
- "@ai-sdk/provider-utils": "4.0.27"
39
+ "@ai-sdk/provider-utils": "4.0.29",
40
+ "@ai-sdk/provider": "3.0.10"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "20.17.24",
@@ -10,6 +10,7 @@ import {
10
10
  delay,
11
11
  type FetchFunction,
12
12
  getFromApi,
13
+ isSameOrigin,
13
14
  lazySchema,
14
15
  parseProviderOptions,
15
16
  postJsonToApi,
@@ -279,10 +280,13 @@ export class GoogleGenerativeAIVideoModel implements Experimental_VideoModelV3 {
279
280
  for (const generatedSample of response.generateVideoResponse
280
281
  .generatedSamples) {
281
282
  if (generatedSample.video?.uri) {
282
- // Append API key to URL for authentication during download
283
- const urlWithAuth = apiKey
284
- ? `${generatedSample.video.uri}${generatedSample.video.uri.includes('?') ? '&' : '?'}key=${apiKey}`
285
- : generatedSample.video.uri;
283
+ // Append the API key to the download URL for authentication, but only
284
+ // when the response-supplied URI stays on the provider's own origin —
285
+ // otherwise the key would leak to whatever host the response names.
286
+ const urlWithAuth =
287
+ apiKey && isSameOrigin(generatedSample.video.uri, this.config.baseURL)
288
+ ? `${generatedSample.video.uri}${generatedSample.video.uri.includes('?') ? '&' : '?'}key=${apiKey}`
289
+ : generatedSample.video.uri;
286
290
 
287
291
  videos.push({
288
292
  type: 'url',
@@ -276,6 +276,35 @@ function parsePath(rawPath: string): Array<string | number> {
276
276
  return segments;
277
277
  }
278
278
 
279
+ const hasOwn = Object.prototype.hasOwnProperty;
280
+
281
+ /**
282
+ * Checks only direct properties so path traversal never follows the prototype chain.
283
+ */
284
+ function hasOwnProperty(
285
+ obj: Record<string | number, unknown>,
286
+ key: string | number,
287
+ ): boolean {
288
+ return hasOwn.call(obj, key);
289
+ }
290
+
291
+ /**
292
+ * Defines path values as own data properties so special keys like `__proto__`
293
+ * cannot invoke prototype setters while accumulating streamed arguments.
294
+ */
295
+ function defineOwnProperty(
296
+ obj: Record<string | number, unknown>,
297
+ key: string | number,
298
+ value: unknown,
299
+ ): void {
300
+ Object.defineProperty(obj, key, {
301
+ value,
302
+ enumerable: true,
303
+ configurable: true,
304
+ writable: true,
305
+ });
306
+ }
307
+
279
308
  /**
280
309
  * Traverses a nested object along the given path segments and returns the leaf value.
281
310
  *
@@ -289,7 +318,9 @@ function getNestedValue(
289
318
  let current: unknown = obj;
290
319
  for (const seg of segments) {
291
320
  if (current == null || typeof current !== 'object') return undefined;
292
- current = (current as Record<string | number, unknown>)[seg];
321
+ const currentRecord = current as Record<string | number, unknown>;
322
+ if (!hasOwnProperty(currentRecord, seg)) return undefined;
323
+ current = currentRecord[seg];
293
324
  }
294
325
  return current;
295
326
  }
@@ -309,12 +340,12 @@ function setNestedValue(
309
340
  for (let i = 0; i < segments.length - 1; i++) {
310
341
  const seg = segments[i];
311
342
  const nextSeg = segments[i + 1];
312
- if (current[seg] == null) {
313
- current[seg] = typeof nextSeg === 'number' ? [] : {};
343
+ if (!hasOwnProperty(current, seg) || current[seg] == null) {
344
+ defineOwnProperty(current, seg, typeof nextSeg === 'number' ? [] : {});
314
345
  }
315
346
  current = current[seg] as Record<string | number, unknown>;
316
347
  }
317
- current[segments[segments.length - 1]] = value;
348
+ defineOwnProperty(current, segments[segments.length - 1], value);
318
349
  }
319
350
 
320
351
  /**