Gregor Martynus
90d · built 2026-07-24
90-day totals
- Commits
- 156
- Grow
- 5.1
- Maintenance
- 8.8
- Fixes
- 4.4
- Total ETV
- 18.3
Where this dev ranks
Percentile against the global top-100 leaderboard (all-time totals).
- By commits
- Top 56 %
- By Growth share
- Top 85 %
30-day trajectory
Last 30 days vs. the 30 days before. Up arrows on Growth and ETV mean improvement; up arrow on Fixes share means more time on fixes (worse).
↓-39.1 %
vs 64 prior
↓-7.6 pp
recent vs prior
↑+13.1 pp
recent vs prior
Daily performance
Daily ETV, stacked by Growth, Maintenance and Fixes.
Work-mix over time
Share of Growth / Maintenance / Fixes over a rolling 7-day window. Reads as 'where is effort flowing right now'.
Repository spread
Where this developer's commits land. Concentrated work (top1 > 80%) vs polymath spread (top1 < 30%).
Most impactful commits
Top 20 by ETV in the 90-day window.
- 3.5ETVfeat: Realtime API support for browser<->provider websocket connection (#13893) ## Background Part of https://github.com/vercel/ai/issues/13897 Support for Realtime API has been requested for a long time. We want to support different architectures 1. Websocket connection from browser directly to provider 2. Websocket connection from browser through user's server to provider 3. Websocket connection from browser through gateway to provider 4. Websocket connection from browser through user's server and gateway to provider This pull requests implements the first, see [Architecture](#architecture) below ## Summary Alternative implementation of the Realtime API support proposed in #13889, with a reworked developer-facing API: - **`openai.experimental_realtime('gpt-realtime')`** works in both server and browser (no separate `@ai-sdk/openai/realtime` import needed) - **`openai.experimental_realtime.getToken()`** static method for server-side ephemeral token creation - **`experimental_useRealtime`** hook returns `messages: UIMessage[]` (aligned with `useChat` format) instead of `transcript` - **`inputAudioTranscription`** session config enables rendered user messages for transcribed microphone input - **`addToolOutput(callId, result)`** for client-side tools that need manual result submission - **`onToolCall`** callback for auto-executed client-side tools - Framework-agnostic helper type: `Experimental_RealtimeSetupResponse` Provider implementations included: **OpenAI**, **Google**, and **xAI**. ElevenLabs support is split into a stacked follow-up: **#15747**. <a name="architecture"></a> ## Architecture ```mermaid sequenceDiagram participant Browser participant Server participant Provider as AI Provider<br/>(OpenAI / xAI / Gemini / ...) Browser->>Server: POST /api/setup Server->>Provider: Request token (with tool definitions) Provider-->>Server: Short-lived auth token Server-->>Browser: { token } Browser->>Provider: Open WebSocket (using token) Provider-->>Browser: Audio/text chunks (streaming) Provider-->>Browser: Tool call request Browser->>Server: App-specific tool request (optional) Server-->>Browser: App-specific tool result Browser->>Provider: Send tool result via WebSocket Provider-->>Browser: Continue streaming ``` ### Alternatives considered #### Generic RPC route for server-side tools One option was for the SDK to expose a generic server-side tool execution flow: the realtime session would receive a tool call, POST `{ name, inputs, callId }` to an `execute-tools` route, execute a matching server-side tool, and send the result back to the provider automatically. We decided against that for the initial implementation. A generic RPC route is convenient, but it creates a security-sensitive application boundary: the app must authenticate the user, bind the request to a realtime session, allowlist tool names, validate call IDs, rate limit access, and authorize each tool invocation. If the SDK provides the generic route shape, it is easy to copy into production without those controls. Instead, the SDK keeps tool execution client-driven through `onToolCall` and `addToolOutput`. Server-backed tools should call app-specific API endpoints from `onToolCall` (for example `/api/weather`), where the application can apply its normal auth, validation, authorization, and rate limiting rules. Documentation should cover secure server-backed tool calling patterns, but the SDK should not own the generic remote tool RPC abstraction yet. ## Manual verification The realtime voice example added in this PR lives at `examples/ai-e2e-next/app/realtime/page.tsx` (UI) and `examples/ai-e2e-next/app/api/realtime/[...path]/route.ts` (server-side token endpoint). 1. Add the provider API key(s) for whichever provider(s) you want to test to `examples/ai-e2e-next/.env.local`: ```bash OPENAI_API_KEY=... # OpenAI realtime (gpt-realtime) GOOGLE_GENERATIVE_AI_API_KEY=... # Google realtime (gemini-3.1-flash-live-preview) XAI_API_KEY=... # xAI realtime (grok-voice-latest) ``` 2. Start the example: ```bash cd examples/ai-e2e-next pnpm dev ``` 3. Open http://localhost:3000/realtime, pick a provider + voice, and click **Connect**. Use the microphone or type a message. Ask "what's the weather in Paris?" or "roll a dice" to exercise client-side tool calling (`onToolCall`). ## Checklist - [x] Tests have been added / updated (for bug fixes / features) - [x] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [ ] I have reviewed this pull request (self-review) - [ ] Security Audit - esp. securing remote tool calling - [ ] Update internal Architecture / how it works docs ### Future Work 1. **ElevenLabs** provider support — stacked follow-up in #15747 2. Websocket connection from browser through user's server to provider 3. Websocket connection from browser through gateway to provider 4. Websocket connection from browser through user's server and gateway to provider ## Related issues - #3176 - #3907 - #4082 - #5007 - #9559 - #12381 - #13706 - #13889 Co-authored-by: Cursor <cursoragent@cursor.com>github.com-vercel-ai · ce769dd2 · 2026-06-05
- 0.9ETVfix(providers): only send credentials to same-origin response-supplied URLs (#15989) ## Background Reported via the Anthropic CVD as **VULN-11567 (ANT-2026-FX163E39)**. Several provider clients follow a URL taken from the provider's API response — a polling/status URL or a final media URL (`polling_url`, `urls.get`, `result_url`, `result.sample`, `video.uri`) — and reuse the authenticated headers, or append `?key=<API_KEY>`, on that request. Because the host of the response-supplied URL is never validated, the long-lived API key is sent to whatever host the response names (a CDN in the benign case, or an attacker-chosen host if the provider is compromised or the response is tampered with), allowing credential exfiltration. A safe no-headers pattern already exists in `xai-image-model.ts`. ## Summary Adds an `isSameOrigin(url, baseUrl)` helper to `@ai-sdk/provider-utils` and gates every affected fetch so the provider credential is attached **only when the followed URL is same-origin with the provider's configured API origin**; a foreign origin gets the request without credentials. This single rule covers both cases correctly: - **Polling/status URLs** (need auth, normally same-origin) keep working. - **Media downloads** on a CDN (foreign origin) are fetched without the key. Google's video download legitimately needs the key, and its URI is same-origin, so it still works. ### Sites gated | Package | Site | Response field | | --- | --- | --- | | `@ai-sdk/black-forest-labs` | poll + image download | `polling_url`, `result.sample` | | `@ai-sdk/fireworks` | image download (workflows_async) | `result.sample` | | `@ai-sdk/replicate` | video poll | `urls.get` | | `@ai-sdk/gladia` | transcription poll | `result_url` | | `@ai-sdk/fal` | video status poll | `response_url` | | `@ai-sdk/google` | video download (`?key=`) | `video.uri` | (fal's transcription poll and Google's operation poll build their URL from a hardcoded/`baseURL`-anchored host, so they are not response-host-supplied and are left unchanged. `fal-image` and `replicate-image` already omit credentials on download.) ### Tests - Unit tests for `isSameOrigin` (same origin, foreign host, scheme/port mismatch, fail-closed on invalid input). - A foreign-origin regression test per provider asserting the credential header (or `?key=`) is **not** sent when the response names a different host. All suites pass in node and edge. ## Manual Verification <!-- TODO --> ## Checklist - [x] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [ ] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Future Work - Consider routing all response-supplied follow-up fetches through a small shared wrapper (origin-gated `getFromApi`) so new providers can't reintroduce this pattern, and so the rule is enforced in one place rather than per call site. ## Related Issues Linear: VULN-11567 (tracked under VULN-11626). --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>github.com-vercel-ai · aeda3739 · 2026-06-11
- 0.8ETVdocs(examples): migrate deprecated streamText result helpers to standalone helpers (#15741) ## Background Follow-up to #14652, which deprecated the five `streamText` result methods that convert a result into other forms and added stateless standalone helpers in their place. That PR intentionally **deferred** migrating the existing `examples/`, `content/docs/`, and `content/cookbook/` usages to keep the API change reviewable: > Migrate remaining usages of the deprecated methods in `examples/` and `content/docs/` + `content/cookbook/` to the new standalone helpers. Intentionally deferred to a follow-up PR so the API change here stays reviewable. This PR is that follow-up. ## Summary Migrated every remaining usage of the deprecated result methods to the standalone helpers exported from `ai`. The result object's `.stream` is passed to the helper directly: | Deprecated (on `streamText` result) | Standalone replacement | | --- | --- | | `result.toUIMessageStream(opts)` | `toUIMessageStream({ stream: result.stream, ...opts })` | | `result.toUIMessageStreamResponse(opts)` | `createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream, ...streamOpts }), ...responseInit })` | | `result.pipeUIMessageStreamToResponse(res, opts)` | `pipeUIMessageStreamToResponse({ response: res, stream: toUIMessageStream({ stream: result.stream, ...streamOpts }) })` | | `result.toTextStreamResponse(init)` | `createTextStreamResponse({ stream: toTextStream({ stream: result.stream }), ...init })` | | `result.pipeTextStreamToResponse(res, init)` | `pipeTextStreamToResponse({ response: res, stream: toTextStream({ stream: result.stream }), ...init })` | For `toUIMessageStreamResponse` / `pipeUIMessageStreamToResponse`, the options object is split: stream options (`originalMessages`, `generateMessageId`, `onFinish`, `messageMetadata`, `sendReasoning`, `sendSources`, `sendStart`, `sendFinish`, `onError`) go into `toUIMessageStream`, while response-init options (`headers`, `status`, `statusText`, `consumeSseStream`) stay on the responder helper. **Scope:** 149 files (80 examples, 37 docs, 32 cookbook). Notable cases: - **`agent.stream()` results** (`content/cookbook/01-next/77-track-agent-token-usage.mdx`) return a `StreamTextResult`, so their `toUIMessageStreamResponse` is the deprecated method and was migrated. Where the snippet used the `<AgentUIMessage>` generic to type the `messageMetadata` callback, the message generic moves to `toUIMessageStream<ToolSet, AgentUIMessage>`. - **`streamText` + `Output`** routes (e.g. `use-object`, `stream-object`) use `streamText`, so their `toTextStreamResponse` is deprecated and was migrated. `streamObject` results — whose `toTextStreamResponse`/`pipeTextStreamToResponse` are **not** deprecated — were left untouched (none were present in the migrated files). - **Chunk iteration** (`examples/ai-functions/.../anthropic-reasoning-ui-stream.ts`): the standalone `toUIMessageStream` returns a plain `ReadableStream` rather than the old `AsyncIterableStream`, so the `for await` was replaced with a reader loop. - **MDX `highlight` ranges**: expanding single-line imports / one-line returns shifted line numbers inside fenced code blocks; affected `highlight="..."` attributes were recomputed. - Minor correctness fix in `12-use-chat-an-error-occurred.mdx`: the migrated snippet's stale `getErrorMessage` option was corrected to `toUIMessageStream`'s actual `onError` option. ## Manual Verification - `oxfmt` + `oxlint` clean on all changed TS/TSX files. - `tsc --noEmit` on the affected example packages (`ai-functions`, `ai-e2e-next`, `express`, `hono`, `fastify`, `nest`, `node-http-server`, `angular`, `next`, `next-openai-pages`, `nuxt-openai`, `sveltekit-openai`) shows no migration-related type errors. (Pre-existing, unrelated errors in `ai-functions` azure/xai web-search example files are not touched by this PR.) - No production package code changed, so no changeset is required. ## Checklist - [x] All commits are signed (PRs with unsigned commits cannot be merged) - [ ] Tests have been added / updated (for bug fixes / features) - [x] Documentation has been added / updated (for bug fixes / features) - [ ] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) <!-- No changeset and no tests: this PR only updates examples/ and content/ (docs + cookbook); no published package code changed. --> ## Future Work - Deduplicate workflow's `toUIMessageChunk` against the new `ai` helper (tracked in #14652). - Remove the five deprecated instance methods in the next major release (v8). ## Related Issues Follow-up to #14652. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>github.com-vercel-ai · 42f9240c · 2026-06-01
- 0.7ETVfix(provider-utils,ai): harden download SSRF guard against hostname and redirect bypasses (#15980) ## Background The download URL validation guard (`validateDownloadUrl`) and the file download helpers (`downloadBlob`, `download`) had several bypasses when handling untrusted URLs. These were reported via the Anthropic coordinated vulnerability disclosure (Linear VULN-11554, VULN-11552, VULN-11560; tracked under VULN-11626). ## Summary Hardens the guard against three classes of bypass: - **Trailing-dot hostnames** — a fully-qualified name with a trailing dot (e.g. `localhost.`, `myhost.local.`) resolves identically to the bare name but skipped the localhost/`.local` blocklist. The hostname is now normalized (trailing dots stripped) before the checks. - **IPv6 with embedded IPv4** — the previous string-prefix logic missed addresses that carry an IPv4 target in their last 32 bits: IPv4-compatible (`::a.b.c.d`), IPv4-translated (`::ffff:0:a.b.c.d`), and NAT64 (`64:ff9b::/96` and the `64:ff9b:1::/48` local-use prefix). The address is now fully expanded into its 8 groups, the embedded IPv4 is decoded and run through the existing private-range checks, and unparseable addresses fail closed. - **Redirects validated too late** — redirects were only checked *after* `fetch` had already followed them, so the request to a redirect target had already been issued before the guard ran. **On the server**, both download helpers now follow redirects manually (`redirect: 'manual'`), re-validating each hop (resolved against the current URL) **before** requesting it, capped at 10 hops. ### Browser behavior The manual redirect handling is gated on a new `isBrowserRuntime()` helper. In a browser, `fetch(url, { redirect: 'manual' })` returns an unreadable opaque-redirect response, so per-hop validation is impossible and would break every legitimate redirected download. SSRF is also a server-side threat — browser fetch is constrained by CORS and cannot reach a server's internal network or cloud-metadata endpoints. So in the browser we fall back to `redirect: 'follow'` and let the platform follow redirects natively. The initial URL is still validated in both environments. ### Files - `packages/provider-utils/src/validate-download-url.ts` — trailing-dot normalization + IPv6 expansion/embedded-IPv4 detection - `packages/provider-utils/src/is-browser-runtime.ts` — new runtime helper (exported) - `packages/provider-utils/src/download-blob.ts`, `packages/ai/src/util/download/download.ts` — manual redirect following with per-hop validation on the server; native follow in the browser Regression tests added for all three bypasses, the browser fallback, and the runtime helper. A regression test asserts the unsafe redirect target is **never requested** on the server. ## Manual Verification <!-- TODO --> ## Checklist - [ ] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [ ] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Future Work - **DNS-resolution bypass (VULN-11509)** is intentionally not addressed here. A correct fix requires resolving the hostname and pinning the connection to the validated IP (to defeat DNS rebinding), which is Node-only and either changes the public `validateDownloadUrl` signature to async or adds an undici dispatcher dependency. Recommended as a separate, ADR-backed follow-up. ## Related Issues Linear: VULN-11554, VULN-11552, VULN-11560 (tracked under VULN-11626). VULN-11509 deferred (see Future Work). --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: dnukumamras <mukund.sarma@vercel.com>github.com-vercel-ai · 375fdd7e · 2026-06-11
- 0.7ETVfeat(ai): expose `toUIMessageChunkStream` helper (#14652) ## Background `DefaultStreamTextResult` carried five instance methods that convert a `streamText` result into other forms: - `toUIMessageStream` — inline stream-to-UI-message conversion logic, only reachable through the class method - `toUIMessageStreamResponse` and `pipeUIMessageStreamToResponse` — UI message response helpers tied to the result object - `toTextStreamResponse` and `pipeTextStreamToResponse` — text response helpers tied to the result object Two problems with this shape: 1. The `toUIMessageStream` transformation is trapped inside the class. It can't be composed over a `fullStream` captured outside of `streamText` (custom transports, tests, non-`streamText` producers that emit `TextStreamPart<TOOLS>`). 2. The five methods duplicate capability that now exists as top-level helpers, inflating the `StreamTextResult` surface and steering users toward method chaining instead of composition. ## Summary see before/after APIs in migration guide: https://github.com/vercel/ai/blob/ui-message-stream-helper/content/docs/08-migration-guides/23-migration-guide-7-0.mdx#streamtext-response-helpers-deprecated--use-stateless-helpers - Extract the per-part conversion into a standalone `toUIMessageChunk(part, options)` helper exported from `ai`. Operates on `TextStreamPart<TOOLS>`. - Add `toUIMessageChunkStream`, which maps `ReadableStream<TextStreamPart<TOOLS>>` to UI message chunks and includes response message ID injection plus `onFinish` handling, so it can replace `result.toUIMessageStream()` directly. - Add `toUIMessageChunkStreamResponse` for `fullStream -> Response` and `pipeTextStreamToUIMessageStreamResponse` for `fullStream -> Node ServerResponse` migrations. - Refactor `DefaultStreamTextResult`'s deprecated UI stream method, `createAgentUIStream`, and `DirectChatTransport` to delegate to the standalone helpers with full streams. - Add `@deprecated` JSDoc to all five instance methods on the `StreamTextResult` interface. The methods still work in v7 and will be removed in the next major. - Update the v6 -> v7 migration guide and changeset with before/after examples for each deprecated method. ## Scope notes This PR keeps each helper input shape explicit: - `toUIMessageChunkStream`, `toUIMessageChunkStreamResponse`, and `pipeTextStreamToUIMessageStreamResponse` accept `ReadableStream<TextStreamPart<TOOLS>>` from `result.fullStream`. - `createUIMessageStreamResponse` and `pipeUIMessageStreamToResponse` continue to accept already-converted UI message chunk streams. - `toUIMessageChunk` accepts `TextStreamPart<TOOLS>` only. Workflow dedup is deferred to a follow-up PR so this one stays focused. ## Manual Verification - `pnpm type-check` in `packages/ai` - `pnpm exec vitest --config vitest.node.config.js --run src/ui-message-stream/to-ui-message-chunk-stream-response.test.ts src/ui-message-stream/pipe-text-stream-to-ui-message-stream-response.test.ts` in `packages/ai` ## Checklist - [x] Tests have been added / updated (for bug fixes / features) - [x] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Future Work - Migrate remaining usages of the deprecated methods in `examples/` and `content/docs/` + `content/cookbook/` to the new standalone helpers. Intentionally deferred to a follow-up PR so the API change here stays reviewable. Regression tests in `packages/ai/src/generate-text/stream-text.test.ts` and historical migration guides under `content/docs/08-migration-guides/` intentionally stay on the deprecated API. - Deduplicate workflow's `toUIMessageChunk` against the new `ai` helper. - Remove the five deprecated instance methods in the next major release (v8). --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Aayush Kapoor <83492835+aayush-kapoor@users.noreply.github.com>github.com-vercel-ai · bcce2dd7 · 2026-05-28
- 0.6ETVfeat: `response.stream` (#15653) ## Background In preparation of https://github.com/vercel/ai/pull/14652 Follow up to https://github.com/vercel/ai/commit/b095aef28f21c44377fcbe227264c3fa99c05a80 ## Summary - introduces `response.stream` as a replacement for `response.fullStream` - deprecates `response.fullStream` Note: I didn't update `streamObject` since it's deprecated and due to be removed anyway. ## Manual Verification Ran examplesgithub.com-vercel-ai · 023550ef · 2026-05-27
- 0.6ETVfix(provider-utils): cancel response body on download rejection to prevent socket leak (#15968) ## Background When a download is rejected early, the `fetch` response body was left unconsumed and uncancelled. Under WHATWG Fetch / undici, an undisturbed body keeps the underlying TCP socket open instead of returning it to the connection pool. This happened on three paths: - `readResponseWithSizeLimit()` threw immediately when the `Content-Length` header exceeded `maxBytes`, before any reader was acquired. - The `!response.ok` early-throw paths in `download()` and `downloadBlob()`. - The redirect-validation path in `download()` and `downloadBlob()`, where a redirect resolving to a blocked URL (SSRF / open-redirect) threw before the body was consumed. An attacker-controlled origin can advertise a large `Content-Length` (or return an error status, or open-redirect to a blocked URL) **without sending a body**, accumulating open sockets on the victim until file-descriptor exhaustion (`EMFILE`) / connection-pool starvation causes a remote denial of service. This is reachable by any application that downloads user-supplied URLs (e.g. multimodal image/file inputs), even with SSRF mitigations in place. ## Summary - Added a `cancelResponseBody(response)` helper (`packages/provider-utils/src/cancel-response-body.ts`) that calls `response.body?.cancel()` and swallows cancel errors so the original rejection is preserved. Exported from `@ai-sdk/provider-utils`. Kept in its own module since it is a connection-cleanup concern reused independently of size-limiting. - Cancel the body before throwing on every early-rejection path: - Content-Length over limit in `readResponseWithSizeLimit()` - `!response.ok` in `download()` and `downloadBlob()` - blocked redirect target in `download()` and `downloadBlob()` - Added regression tests asserting the body is cancelled on the Content-Length, non-ok, and open-redirect paths, plus dedicated unit tests for `cancelResponseBody` itself. ## Manual Verification The original PoC requires a malicious HTTP server that advertises a 10 GiB `Content-Length` and never sends a body, plus OS-level `netstat` to observe leaked `ESTABLISHED` sockets — so it does not map to a runnable `examples/` script. Verified instead via unit tests that assert `ReadableStream.cancel()` is invoked on each early-rejection path (the cancel call is what releases the socket back to the pool): - `pnpm --filter @ai-sdk/provider-utils test:node` — all passing (613) - `pnpm --filter ai test:node` (download suite) — all passing (3090) - `pnpm check` — 0 warnings, 0 errors ## Checklist - [x] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [ ] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Related Issues VULN-10892 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>github.com-vercel-ai · b4507d50 · 2026-06-11
- 0.6ETVfeat(provider/openai): support GPT-5.6 reasoning and prompt caching controls (#17028) ## Summary - add GPT-5.6 `max` reasoning effort support for Chat Completions - add Responses API `reasoningMode` and `reasoningContext` options, including the effective reasoning context in provider metadata - support GPT-5.6 implicit and explicit prompt cache options, 30-minute TTL, and explicit cache breakpoints for supported message content - report cache-write tokens for Chat Completions and Responses without double-counting uncached input tokens - document GPT-5.6 reasoning, prompt caching, cache-write usage, and original image detail behavior - add `generateText` and `streamText` examples for pro mode, persisted reasoning context, and explicit prompt caching Follow-up to #17021. ## Test plan - `pnpm test:node` in `packages/openai` — 19 files, 772 tests - `pnpm test:edge` in `packages/openai` — 19 files, 772 tests - `pnpm type-check` in `packages/openai` - `pnpm type-check:full` - `pnpm check` - `node tools/validate-properties-tables.mjs` Live GPT-5.6 examples were not run locally because no `OPENAI_API_KEY` was configured.github.com-vercel-ai · b2b1bb98 · 2026-07-09
- 0.5ETVfeat(openai): report Chat Completions stream mismatch (#16423) ## Background Issue #16408 reports that after migrating to AI SDK v7, `streamText` with tools can execute tool calls but the final assistant text does not appear when using `createOpenAI({ baseURL })` against a custom OpenAI-compatible endpoint. The reproduced mismatch is that `provider('gpt-4o-mini')` uses the OpenAI Responses API in v7, while the custom endpoint returns Chat Completions SSE chunks. Those chunks were previously parsed by the Responses stream parser, producing a generic validation error instead of telling the user that the wrong OpenAI API surface was selected. Using `provider.chat('gpt-4o-mini')` against the same mocked Chat Completions chunks preserves the tool call, executes the tool, and streams the final UI text. I also checked the related #12056 Azure-style chunks (`choices: []`, empty assistant content prelude, and trailing content filter chunk); the OpenAI Chat parser already streams the text delta for that shape. Credit to @itisvincent for #16408, @mac-110 for the raw Azure-compatible stream fixture in #12056, and @PaulyBearCoding for the related no-argument tool-call investigation in #10283. #10283 is a separate empty-tool-arguments issue and is not fixed here. ## Summary - Detect Chat Completions stream chunks that reach the OpenAI Responses stream parser and return a targeted `APICallError` explaining the API mismatch. - Document that `openai('model-id')` / `createOpenAI()('model-id')` uses the Responses API, including when `baseURL` is customized. - Document that Chat Completions-only custom endpoints should use `openai.chat('model-id')` or the OpenAI-compatible provider. - Add provider tests for custom `baseURL` routing, Azure-compatible Chat Completions stream chunks, and the new mismatch error. - Add a patch changeset for `@ai-sdk/openai`. ## Manual Verification Reproduced the reported setup locally with `streamText`, `createOpenAI({ baseURL, fetch })`, a tool call step, and a follow-up Chat Completions SSE text step. With `provider('gpt-4o-mini')`, the mocked Chat Completions stream is routed through the Responses parser and now reports a helpful mismatch error. Switching only the model to `provider.chat('gpt-4o-mini')` executes the tool and yields `text: "Created test."`, `finishReason: "stop"`, and a UI stream containing the final `text-delta`. Also confirmed that the Azure/content-filter stream fixture based on #12056 is realistic and is handled by the OpenAI Chat parser. The Responses parser mismatch detection covers Azure-style `choices: []` chunks. Automated focused test run: ```sh pnpm --filter @ai-sdk/openai exec vitest --config vitest.node.config.js --run src/openai-provider.test.ts src/chat/openai-chat-language-model.test.ts src/responses/openai-responses-language-model.test.ts -t "baseURL configuration|should stream text after Azure content filter chunks|should return helpful error when Chat Completions stream is received" pnpm --filter @ai-sdk/openai type-check ``` ## Checklist - [x] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [x] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Future Work The no-argument tool call behavior from #10283 should remain a separate fix path. ## Related Issues Closes #16408. Related to #12056 and #10283.github.com-vercel-ai · 1ead90c7 · 2026-06-29
- 0.5ETVfeat (provider/quiverai): new @ai-sdk/quiverai provider (v2.0.0) (#15461) ## Background Add a 1st-party `@ai-sdk/quiverai` provider for [QuiverAI](https://quiver.ai/) SVG image generation, ported from https://github.com/quiverai/ai-sdk-provider. This PR targets `main` (canary pre-release). It must be merged **after** https://github.com/vercel/ai/pull/15463 has shipped `@ai-sdk/quiverai@1.0.0` from `release-v6.0`. With #15463's `v1.0.0` recorded as the initial version in `.changeset/pre.json`, the major changeset on this branch will publish `@ai-sdk/quiverai@2.0.0` once canary mode exits. ## Summary - New `packages/quiverai` package (image model only) supporting `generate` and `vectorize` operations against Arrow models (`arrow-1`, `arrow-1.1`, `arrow-1.1-max`). - Workflow serialization, example in `examples/ai-functions/src/generate-image/quiverai/`, docs at `content/providers/01-ai-sdk-providers/180-quiverai.mdx`, major changeset. - `package.json` version pinned to `1.0.0` and `.changeset/pre.json` initialVersions seeded with `@ai-sdk/quiverai: 1.0.0` so canary publishes start at `2.0.0-canary.x` and avoid colliding with the `v1.0.0` line shipped from `release-v6.0`. ## Manual Verification Ran both examples added in this PR against the live QuiverAI API: - `examples/ai-functions/src/generate-image/quiverai/basic.ts` — text-to-SVG generation with `arrow-1.1`. - `examples/ai-functions/src/generate-image/quiverai/vectorize.ts` — vectorization of `examples/ai-functions/data/wtf-logo.png` with `arrow-1.1`. ## Checklist - [x] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [x] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Related Issues Closes https://github.com/vercel/ai/issues/15459 Depends on https://github.com/vercel/ai/pull/15463 (must merge first). --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>github.com-vercel-ai · d56c97b0 · 2026-05-21
- 0.5ETVSupport toModelOutput in WorkflowAgent (#15917) ## Background `WorkflowAgent` did not honor tool `toModelOutput`, unlike core text generation. ## Summary - route WorkflowAgent tool results through model-output conversion - keep raw tool output for UI/results/callbacks - add next-workflow example + docs ## Manual Verification Tested examples/next-workflow end-to-end ## Checklist - [x] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [x] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>github.com-vercel-ai · 1e4b3503 · 2026-06-09
- 0.5ETVfix(google): auto-inject skip_thought_signature_validator on Gemini 3 replays (#15560) ## Background Gemini 3 rejects replayed assistant `functionCall` parts without `thoughtSignature` with HTTP 400. This happens when app/client code persists or rebuilds messages and drops `providerOptions`. ## Summary - For Gemini 3 only (`/^gemini-3[.-]/`), inject Google's documented `skip_thought_signature_validator` sentinel when a replayed tool call has no signature. - Warn once per request with affected tool names. - Adapted to `main` V4 Google provider files and existing `google` / `googleVertex` / `vertex` namespace lookup. ## Manual Verification Live replay against `google('gemini-3-flash-preview')`: missing-signature replay changed from HTTP 400 to HTTP 200 with warning. ## Checklist - [ ] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [ ] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Related Issues Closes #15550. Refs #10344, #11413, #14196, #15548, #13060. Co-authored-by: Cursor <cursoragent@cursor.com>github.com-vercel-ai · a8d70b64 · 2026-05-27
- 0.5ETVfeat(workflow): add runtimeContext and toolsContext to WorkflowAgent (#15022) ## Background Towards https://github.com/vercel/ai/issues/12164 `WorkflowAgent` lacked the runtime/tool context APIs that the AI SDK core (`generateText`, `streamText`, `ToolLoopAgent`) already exposes, so users could not pass typed shared state or per-tool context. ## Summary - Adds `runtimeContext` and `toolsContext` to `WorkflowAgent` (constructor and `stream()`), surfaced through `prepareCall`, `prepareStep`, `onFinish`, and step results. - Tools receive their own validated entry from `toolsContext` as `context` (validated against `tool.contextSchema` when defined, including missing entries). - Documents Workflow runtime serialization limits for context values and updates `examples/next-workflow` to demonstrate route-derived `runtimeContext` and per-tool `toolsContext`. - Removes `experimental_context` (and the corresponding fields on the related option/info/result/callback types). Use `runtimeContext` for shared agent state and `toolsContext` for per-tool values. ## Manual Verification Updated [examples/next-workflow](https://github.com/vercel/ai/pull/15022/changes#diff-56b85a0e575871868dced5fe09920e5d2eef3abbb25cbc1e08e9fe9f21db2c70) ## Checklist - [x] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [x] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Future Work Telemetry for `@ai-sdk/workflow` needs to be updated to the latest telemetry APIs in AI SDK. Once that is done, `includeRuntimeContext` and `includeToolsContext` should be added to `WorkflowAgent` so telemetry integrations can opt into selected context fields.github.com-vercel-ai · 1d562750 · 2026-05-07
- 0.4ETVfix: reject empty OpenAI, Anthropic, and Replicate base URLs with a helpful error (#17158) ## Background Empty base URL configuration previously allowed provider creation and later produced opaque request-time URL parsing errors, including when OPENAI_BASE_URL was empty. ## Summary Added a shared validateBaseURL helper that throws InvalidArgumentError for empty or whitespace-only values and applied it during OpenAI, Anthropic, and Replicate provider creation without changing the semantics of withoutTrailingSlash or Google Vertex default URL construction. ## Testing Added utility tests for validation and URL normalization, provider regressions for explicit OpenAI, OPENAI_BASE_URL, Anthropic, and Replicate configurations, and Google Vertex xAI and MaaS coverage using the real shared URL utility for omitted and empty defaults. ## End-to-end Validation - `issue-17156-empty-base-url.ts` — ran `pnpm -C examples/ai-functions exec tsx src/reproduction/issue-17156-empty-base-url.ts`; all invalid configurations produced helpful factory-time AI SDK errors and the live OpenAI call returned the expected text. ## Related Issues Fixes #17156 Co-authored-by: gr2m <39992+gr2m@users.noreply.github.com>github.com-vercel-ai · cd12954b · 2026-07-13
- 0.3ETVfix(provider/xai): send reasoning effort "none" for top-level `reasoning: 'none'` (#16893) ## Background The xAI API supports disabling reasoning on `grok-4.3` and newer reasoning models by sending `reasoning: { effort: "none" }` (Responses API) / `reasoning_effort: "none"` (Chat Completions API). When the parameter is omitted, xAI defaults to `effort: "low"`, so the model still reasons. The xAI provider explicitly mapped the top-level `reasoning: 'none'` call option to `undefined`, silently dropping it from the request — without even emitting a warning. Users who requested `reasoning: 'none'` still got (and paid for) reasoning tokens. The `providerOptions: { xai: { reasoningEffort: 'none' } }` escape hatch already forwarded `'none'` correctly, and the OpenAI provider passes top-level `'none'` straight through, so this was an inconsistency introduced in #13648, presumably before xAI supported `none`. Additionally, probing the live API revealed that `grok-4.20-reasoning` and `grok-4.20-non-reasoning` (including dated variants like `grok-4.20-0309-reasoning`) reject the reasoning effort parameter entirely — **any** value, not just `'none'` — with `400 invalid-argument: Model X does not support parameter reasoningEffort.` So the top-level `reasoning` option already caused request failures on those models before this PR. ## Summary - Map the top-level `reasoning: 'none'` option to effort `'none'` in both the Responses API model (`xai-responses-language-model.ts`) and the Chat Completions model (`xai-chat-language-model.ts`), instead of dropping it. - Omit the reasoning effort parameter and emit an `unsupported` warning when the top-level `reasoning` option is used with models that reject the parameter (`supports-reasoning-effort.ts`). Explicit `providerOptions.xai.reasoningEffort` is still passed through verbatim. - Updated the chat model test that locked in the old behavior, and added top-level `reasoning` coverage for the Responses API model (mapping, `'none'`, `providerOptions` precedence, and unsupported-model gating). - Added patch changesets for `@ai-sdk/xai`. ## Manual Verification Ran the following against the live xAI API (via `examples/ai-functions`), logging the request body with a custom `fetch`: ```ts import { createXai } from '@ai-sdk/xai'; import { generateText } from 'ai'; const xai = createXai({ fetch: async (url, init) => { console.dir(JSON.parse(init.body), { depth: Infinity }); return fetch(url, init); }, }); const result = await generateText({ model: xai('grok-4.3'), prompt: `How many "r"s are in the word "strawberry", and what is the square root of 144? Then, how much is the product of both of the resulting values? Think hard about it. Only respond with the resulting final number, nothing more.`, reasoning: 'none', }); console.log(result.finalStep.reasoning); console.log(result.text); ``` **Before the fix**: the request body contained no `reasoning` field, and `result.finalStep.reasoning` contained reasoning content (xAI defaults to `effort: "low"`). **After the fix**: the request body contains `reasoning: { effort: 'none' }` and the response contains no reasoning content. Also verified against the live API with `reasoning: 'none'` on models that reject the parameter: - `grok-4.3` → `reasoning: { effort: 'none' }` sent, no reasoning tokens, no warnings. - `grok-4.20-reasoning` → parameter omitted, request succeeds (would be a 400 otherwise), `unsupported` warning surfaced. - `grok-4.20-non-reasoning` → parameter omitted, request succeeds, `unsupported` warning surfaced. ## Checklist - [x] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [ ] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Related Issues Fixes #16892 --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>github.com-vercel-ai · 8e006de6 · 2026-07-07
- 0.3ETVfix(provider-utils): limit JSON response handler reads (#16374) ## Background JSON response handlers read bodies without the shared size limit. ## Summary Use `readResponseWithSizeLimit` for JSON and status-code response handlers. ## Manual Verification New tests fail without the code changes ## Checklist <!-- Do not edit this list. Leave items unchecked that don't apply. If you need to track subtasks, create a new "## Tasks" section Please check if the PR fulfills the following requirements: --> - [ ] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [ ] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Future Work None. ## Related Issues closes #15756 --------- Co-authored-by: Aayush Kapoor <aayushkapoor34@gmail.com>github.com-vercel-ai · 6a436e31 · 2026-06-27
- 0.3ETVfix(google-vertex): use embedContent for Gemini embeddings (#16238) ## Background `gemini-embedding-2` / `gemini-embedding-2-preview` are registered model ids, but the Vertex provider always calls the legacy `:predict` endpoint. Google dropped `:predict` for that family, so every call fails with `400 FAILED_PRECONDITION`. (`gemini-embedding-001` still supports `:predict`.) ## Summary - Route `gemini-embedding-2`/`-preview` to `:embedContent`. - Cap `maxEmbeddingsPerCall` at 1 for those models (`:embedContent` takes a single value). - Add a minimal `:embedContent` response schema. ## Manual Verification Added unit tests asserting the request hits `:embedContent` with the right body, parses embeddings/usage, and that `maxEmbeddingsPerCall === 1`. `pnpm vitest run src/google-vertex-embedding-model.test.ts` — 15 passed. ## Checklist - [x] All commits are signed - [x] Tests have been added / updated - [ ] Documentation has been added / updated - [x] A _patch_ changeset for relevant packages has been added - [x] I have reviewed this pull request (self-review) ## Related Issues Fixes #15853 Recreated from #15887 by @he-yufeng (co-authored), adding a changeset and signed commit so full CI runs. Co-authored-by: Yufeng He <he-yufeng@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>github.com-vercel-ai · 427a5961 · 2026-06-22
- 0.3ETVfeat(workflow): support experimental sandbox in WorkflowAgent (#16297) <!-- Welcome to contributing to AI SDK! We're excited to see your changes. We suggest you read the following contributing guide we've created before submitting: https://github.com/vercel/ai/blob/main/CONTRIBUTING.md --> ## Background Bring `WorkflowAgent` closer to `ToolLoopAgent`/`streamText` sandbox behavior. ## Summary - Pass `experimental_sandbox` from constructor/stream calls into tool execution. - Expose `experimental_sandbox` to `prepareStep` and allow per-step overrides. - Add unit, type, docs, and a dedicated next-workflow sandbox E2E harness. ## Manual Verification - `pnpm exec tsc --build examples/next-workflow/tsconfig.json` - `pnpm --filter @example/next-workflow build` - Agent browser on `/sandbox`: `PASS sandbox:stream:echo sandbox-e2e` ## Checklist - [x] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [x] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Future Work Support sandbox-aware function descriptions during WorkflowAgent tool schema serialization. ## Related Issues Related to #12164.github.com-vercel-ai · 43543dc5 · 2026-06-23
- 0.3ETVImplement WorkflowAgent telemetry support (#15078) ## Background Implements stable telemetry support for `WorkflowAgent` as tracked in #15074. ## Summary Adds WorkflowAgent telemetry integrations, focused coverage, a Next Workflow e2e harness, and a patch changeset for `@ai-sdk/workflow`. ## Manual Verification `/telemetry` route in workflow-agent example (`examples/next-workflow/app/telemetry/page.tsx`) https://github.com/user-attachments/assets/eeb5605b-9210-4511-b966-3e4279f999cc ## Checklist - [x] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [x] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Related Issues Closes #15074 --------- Co-authored-by: Cursor <cursoragent@cursor.com>github.com-vercel-ai · 39dad72d · 2026-05-08
- 0.3ETVfix(workflow): reuse core tool-approval validation in WorkflowAgent (#15987) ## Background `WorkflowAgent.stream` reconstructs approved tool calls from the client-supplied message history. Reported via the Anthropic CVD as **VULN-11497 (ANT-2026-14CDRHNM)**: the workflow package **duplicated** the core tool-approval collection/validation logic, so it could (and did) drift from the hardened `generateText`/`streamText` path. #15947 already closed the originally-reported exploit by adding a `needsApproval` guard + input-schema re-validation directly to `WorkflowAgent.stream` — but as **another inline copy**, which is the exact anti-pattern the issue flags ("patching the core implementation does not fix the workflow package"). This PR addresses the remaining recommendation: *share a single hardened implementation instead of duplicating it.* ## Summary - `WorkflowAgent.stream` now collects approvals via the shared **`collectToolApprovals`** and re-validates each one through the shared **`validateApprovedToolApprovals`** (input-schema re-validation, HMAC signature verification when configured, and approval-policy re-resolution), in addition to its existing **`needsApproval` guard** (kept because WorkflowAgent has no `toolApproval` policy/secret, and a tool that doesn't declare `needsApproval` should never have an approval). - Validation stays **graceful**: each approval is validated independently and a failure produces a per-item denial result so the agent loop continues (the shared helper throws; that is caught and converted). A new test verifies a batch with one forged + one valid approval executes only the valid one. - The duplicated `collectToolApprovalsFromMessages` (and its local type) was removed. - `collectToolApprovals` and `validateApprovedToolApprovals` are now exported from **`ai/internal`**, so the workflow path can no longer drift from core. ### Files - `packages/ai/internal/index.ts` — export `collectToolApprovals`, `validateApprovedToolApprovals` - `packages/workflow/src/workflow-agent.ts` — use the shared collector + validator; remove the duplicated collector - `packages/workflow/src/workflow-agent.test.ts` — independent-validation regression test ## Manual Verification <!-- TODO --> ## Checklist - [ ] All commits are signed (PRs with unsigned commits cannot be merged) - [x] Tests have been added / updated (for bug fixes / features) - [ ] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Future Work - **Implement approval-secret (HMAC) support for `WorkflowAgent`.** Now that WorkflowAgent routes through the shared `validateApprovedToolApprovals`, the only missing parity with `generateText`/`streamText` is signature verification: the helper already verifies an HMAC signature when a secret is passed, but WorkflowAgent currently has no `experimental_toolApprovalSecret` option and passes `toolApprovalSecret: undefined`. Adding the option (and threading the secret through to the validator + signing approval requests at issuance) would let durable workflows fail-closed on forged/tampered approvals, closing the "schema-valid forged approval, no HMAC" gap that remains for WorkflowAgent. This is also the caveat currently documented in the tool-approvals guide ("`experimental_toolApprovalSecret` is not yet supported on `WorkflowAgent`"). ## Related Issues Linear: VULN-11497 (tracked under VULN-11626). Core counterpart fixed in #15947 (VULN-11452). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>github.com-vercel-ai · 69d71283 · 2026-06-11