From e15d2ae99ee4e4020cd921e602781fed844434c8 Mon Sep 17 00:00:00 2001 From: Thor Whalen <1906276+thorwhalen@users.noreply.github.com> Date: Sat, 11 Jul 2026 11:29:46 +0200 Subject: [PATCH 1/3] feat(ai-vercel)!: require AI SDK v5+ and project tools onto `inputSchema` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `ai` peer range moves from ^4 to ^5 || ^6 || ^7; tools are built with `tool({ inputSchema })` instead of the v4-era `parameters`. Public API is otherwise unchanged. The v4 line is a dead end for Google: its final `@ai-sdk/google` (1.2.22) predates Gemini 3 and has no representation for `thoughtSignature`, which Gemini 3 requires echoed back on the first `functionCall` part of each step. Multi-step tool calling therefore fails with "Function call is missing a thought_signature in functionCall parts", with no opt-out (the requirement holds even at `thinking_level: minimal`). Support first landed in `@ai-sdk/google@2.0.3`. Also corrects the README's "Why pass Zod through (not JSON Schema)?" section, which described the opposite of what the code does — it has converted via `z.toJSONSchema()` since the Zod-4 fix. Claude-Session: https://claude.ai/code/session_01LsHz3ExTnCiZDfqQTjJnTH --- .changeset/ai-sdk-v5-plus-input-schema.md | 27 +++++++++++++++ packages/ai-vercel/README.md | 28 +++++++++++++--- packages/ai-vercel/package.json | 4 +-- packages/ai-vercel/src/index.test.ts | 41 +++++++++++++---------- packages/ai-vercel/src/index.ts | 34 +++++++++++++------ 5 files changed, 98 insertions(+), 36 deletions(-) create mode 100644 .changeset/ai-sdk-v5-plus-input-schema.md diff --git a/.changeset/ai-sdk-v5-plus-input-schema.md b/.changeset/ai-sdk-v5-plus-input-schema.md new file mode 100644 index 0000000..58cddac --- /dev/null +++ b/.changeset/ai-sdk-v5-plus-input-schema.md @@ -0,0 +1,27 @@ +--- +"acture-ai-vercel": major +--- + +Require AI SDK v5+ and project tools onto `inputSchema` (was `parameters`). + +**Breaking:** the `ai` peer range moves from `^4.0.0` to `^5.0.0 || ^6.0.0 || ^7.0.0`. +Tools are now built with `tool({ inputSchema })`, the field the AI SDK has used since +v5. Nothing else in the public API changes — `toAITools` and `toToolNameMap` keep their +signatures, tier filtering, `[DEPRECATED]` banners, wire-safe tool names, and the +errors-as-data `execute` contract. + +To upgrade, move your app to `ai@^5` (or later) and bump this package. If you must stay +on `ai@^4`, pin `acture-ai-vercel@1`. + +**Why this is worth the major.** The v4 line is a dead end for Google. Its final +`@ai-sdk/google` (1.2.22, published months before Gemini 3 shipped) has no representation +for `thoughtSignature` — it is stripped in the response schema, again when tool calls are +extracted, and again when the model turn is re-serialized. Gemini 3 *requires* that +signature to be echoed back on the first `functionCall` part of each step, and rejects the +request otherwise: + +> Function call is missing a thought_signature in functionCall parts. + +There is no fix available on v4, and no way to opt out on Gemini 3 — the requirement holds +even at `thinking_level: minimal`. Support first landed in `@ai-sdk/google@2.0.3` +(the v5-era line), which round-trips the signature via `providerOptions.google`. diff --git a/packages/ai-vercel/README.md b/packages/ai-vercel/README.md index 3f1c78b..cf6c484 100644 --- a/packages/ai-vercel/README.md +++ b/packages/ai-vercel/README.md @@ -10,19 +10,25 @@ Project an [acture](https://npm.im/acture) registry as [Vercel AI SDK](https://s pnpm add acture-ai-vercel ai acture zod ``` +Requires **AI SDK v5 or later** (`ai@^5 || ^6 || ^7`), where a tool's schema field is +`inputSchema`. For `ai@^4` — which called it `parameters` — use `acture-ai-vercel@1`. +Staying on v4 is not recommended: its final `@ai-sdk/google` predates Gemini 3 and +drops the `thoughtSignature` Gemini 3 requires you to echo back, so multi-step tool +calling fails outright. + ## Use ```ts -import { streamText } from 'ai'; +import { streamText, isStepCount } from 'ai'; import { anthropic } from '@ai-sdk/anthropic'; import { toAITools } from 'acture-ai-vercel'; import { registry } from './registry'; const result = streamText({ - model: anthropic('claude-sonnet-4-5'), + model: anthropic('claude-sonnet-5'), tools: toAITools(registry), prompt: 'Add three nodes labeled A, B, C and connect them in a triangle.', - maxSteps: 8, + stopWhen: isStepCount(8), }); for await (const part of result.fullStream) { @@ -49,9 +55,21 @@ Tool `execute` resolves to: The model sees the same shape on every surface (palette, hotkeys, MCP, AI SDK). This is the central guarantee of acture's architecture. -## Why pass Zod through (not JSON Schema)? +## Why convert to JSON Schema (not pass Zod through)? + +The AI SDK accepts a Zod schema on `inputSchema` directly, but we convert each +command's `params` up front with Zod 4's native `z.toJSONSchema()` and hand the SDK a +ready schema via `jsonSchema()`. + +This keeps the wire schema *ours*: what the model sees is decided here, not by whichever +Zod-to-JSON-Schema converter the SDK happens to bundle. That coupling has already bitten +us once — `ai` v4 shipped a Zod-**v3**-only converter that, given a Zod **v4** schema, +silently emitted `{}`. The model then saw a tool with no parameters and could not call it, +with no error anywhere. -The Vercel AI SDK accepts Zod schemas directly. Passing the original schema preserves validators (`z.refine`, `z.transform` constraints on output) that JSON Schema would silently drop. The same registry exposed via `acture-mcp-server` projects through `toJsonSchema` because MCP wants JSON Schema on the wire. +Runtime validation is unaffected. `registry.dispatch` still validates against the original +Zod schema, so refinements JSON Schema cannot express (`z.refine` predicates and friends) +are still enforced on every dispatch — the JSON Schema is only what the model is *shown*. ## See also diff --git a/packages/ai-vercel/package.json b/packages/ai-vercel/package.json index df48ac6..ab9ca45 100644 --- a/packages/ai-vercel/package.json +++ b/packages/ai-vercel/package.json @@ -37,12 +37,12 @@ }, "peerDependencies": { "acture": "^1.0.0", - "ai": "^4.0.0", + "ai": "^5.0.0 || ^6.0.0 || ^7.0.0", "zod": "^4.0.0" }, "devDependencies": { "acture": "workspace:*", - "ai": "^4.0.40", + "ai": "^7.0.22", "tsup": "^8.3.0", "typescript": "^5.7.0", "vitest": "^2.1.0", diff --git a/packages/ai-vercel/src/index.test.ts b/packages/ai-vercel/src/index.test.ts index 35530ba..c3fc7c8 100644 --- a/packages/ai-vercel/src/index.test.ts +++ b/packages/ai-vercel/src/index.test.ts @@ -105,33 +105,38 @@ describe('toAITools', () => { expect((cmd as { id: string }).id).toBe('app.search'); }); - it('projects Zod v4 params to a non-empty JSON Schema', () => { - // Regression: `ai` v4 converts a passed-through Zod schema with - // `zod-to-json-schema` (Zod v3 only) and silently yields an empty - // `{}` for a Zod v4 schema — the model then sees no parameters. The - // adapter must convert via `z.toJSONSchema()` itself. + it('projects Zod v4 params to a non-empty JSON Schema on `inputSchema`', () => { + // Two regressions guarded at once. + // + // 1. The field is `inputSchema` (AI SDK v5+). On the v4 line it was + // `parameters`; a tool object still carrying `parameters` reaches the + // model with NO schema at all, so it can never supply arguments. + // 2. We convert with `z.toJSONSchema()` ourselves rather than passing the + // Zod schema through: `ai` v4 converted it with `zod-to-json-schema` + // (Zod v3 only) and silently yielded `{}` for a Zod v4 schema. Owning + // the conversion decouples us from whatever converter the SDK bundles. const { registry } = setup(); const tools = toAITools(registry); - const params = ( - tools['app_search'] as unknown as { - parameters: { - jsonSchema?: { type?: string; properties?: Record }; - }; - } - ).parameters; - expect(params.jsonSchema?.type).toBe('object'); - expect(params.jsonSchema?.properties ?? {}).toHaveProperty('query'); + const t = tools['app_search'] as unknown as { + parameters?: unknown; + inputSchema: { + jsonSchema?: { type?: string; properties?: Record }; + }; + }; + expect(t.parameters).toBeUndefined(); + expect(t.inputSchema.jsonSchema?.type).toBe('object'); + expect(t.inputSchema.jsonSchema?.properties ?? {}).toHaveProperty('query'); }); it('projects a param-less command to an empty object schema', () => { const { registry } = setup(); const tools = toAITools(registry, { tiers: ['stable', 'experimental'] }); - const params = ( + const schema = ( tools['app_exp_thing'] as unknown as { - parameters: { jsonSchema?: { type?: string } }; + inputSchema: { jsonSchema?: { type?: string } }; } - ).parameters; - expect(params.jsonSchema?.type).toBe('object'); + ).inputSchema; + expect(schema.jsonSchema?.type).toBe('object'); }); }); diff --git a/packages/ai-vercel/src/index.ts b/packages/ai-vercel/src/index.ts index 4e418f4..c4a12b1 100644 --- a/packages/ai-vercel/src/index.ts +++ b/packages/ai-vercel/src/index.ts @@ -2,10 +2,17 @@ * `acture-ai-vercel` — project an acture registry as Vercel AI SDK * tool definitions. * + * **Requires AI SDK v5 or later** (`ai@^5`), where a tool's schema field + * is `inputSchema`. The v4 line called it `parameters` and is no longer + * supported — it is also a dead end for Google: the final v4-era + * `@ai-sdk/google` predates Gemini 3 and drops the `thoughtSignature` + * that Gemini 3 requires you to echo back on `functionCall` parts, so + * multi-step tool calling 400s. See the v1 → v2 note in CHANGELOG.md. + * * Each command's Zod `params` schema is converted to a JSON Schema (with * Zod 4's native `z.toJSONSchema()`) and handed to the AI SDK via - * `jsonSchema()` — see `toParameterSchema` for why the conversion cannot - * be left to the SDK. Runtime validation is unaffected: `registry.dispatch` + * `jsonSchema()` — see `toParameterSchema` for why we keep that + * conversion ours. Runtime validation is unaffected: `registry.dispatch` * still validates against the original Zod schema, so refinements a JSON * Schema cannot express (e.g. `z.refine` predicates) are still enforced. * @@ -116,14 +123,19 @@ function selectCommands( * Project a command's Zod `params` to a JSON Schema the AI SDK can send * to the model. * - * The AI SDK's `tool({ parameters })` *accepts* a Zod schema, but `ai` - * v4 converts it internally with `zod-to-json-schema`, which understands - * only Zod **v3**'s internals. Given a Zod **v4** schema it silently - * emits an empty `{}` — the model then sees a tool with no parameters - * and cannot supply arguments. So we convert up front with Zod 4's - * native `z.toJSONSchema()` and hand the SDK a ready JSON Schema via - * `jsonSchema()`. A command with no `params` projects to an empty object - * schema. + * `tool({ inputSchema })` accepts a Zod schema directly, but we convert + * up front with Zod 4's native `z.toJSONSchema()` and hand the SDK a + * ready JSON Schema via `jsonSchema()`. This keeps the wire schema + * ours: the exact JSON Schema the model sees is decided here, not by + * whichever Zod-to-JSON-Schema converter the SDK happens to bundle — a + * coupling that already bit us once (`ai` v4 shipped a Zod-v3-only + * converter that silently emitted `{}` for a Zod v4 schema, leaving the + * model with a parameter-less tool it could not call). + * + * Runtime validation is unaffected: `registry.dispatch` still validates + * against the original Zod schema, so refinements a JSON Schema cannot + * express (e.g. `z.refine` predicates) are still enforced. A command + * with no `params` projects to an empty object schema. */ function toParameterSchema( params: AnyCommandRecord['params'], @@ -142,7 +154,7 @@ function projectCommand( const description = applyDeprecationPrefix(cmd, cmd.description); return tool({ description: description ?? cmd.title, - parameters: toParameterSchema(cmd.params), + inputSchema: toParameterSchema(cmd.params), execute: async (args: unknown) => { // `cmd.id` (not the sanitized wire name) is what the registry // dispatches on — sanitization is a wire-format concern, not a From 1bc6b439e4dd7ef313a8ef5d3bdb163fec5a3fc8 Mon Sep 17 00:00:00 2001 From: Thor Whalen <1906276+thorwhalen@users.noreply.github.com> Date: Sat, 11 Jul 2026 20:47:30 +0200 Subject: [PATCH 2/3] chore: lockfile for ai@7 devDependency Claude-Session: https://claude.ai/code/session_01LsHz3ExTnCiZDfqQTjJnTH --- pnpm-lock.yaml | 63 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39a2625..1159131 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -296,8 +296,8 @@ importers: specifier: workspace:* version: link:../core ai: - specifier: ^4.0.40 - version: 4.3.19(react@19.2.6)(zod@4.4.3) + specifier: ^7.0.22 + version: 7.0.22(zod@4.4.3) tsup: specifier: ^8.3.0 version: 8.5.1(postcss@8.5.14)(tsx@4.21.0)(typescript@5.9.3) @@ -839,16 +839,32 @@ packages: peerDependencies: zod: ^3.0.0 + '@ai-sdk/gateway@4.0.16': + resolution: {integrity: sha512-9iYxdOLquoOFk5e+02P9qfBIA+EJU0FNJvyjGxi4iXJabt2+GlbaCg7TX0sTtxOsBVkdabkatqWM6+kvp53tBg==} + engines: {node: '>=22'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/provider-utils@2.2.8': resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} engines: {node: '>=18'} peerDependencies: zod: ^3.23.8 + '@ai-sdk/provider-utils@5.0.7': + resolution: {integrity: sha512-OSm5/5kdrHa11WIOo5LYgDKnxYWp5aB/wx5EXRHi0jpUGduMDeB6oht9U6p+UNNWIP3F/EqPpV8d7vdP/iRnqg==} + engines: {node: '>=22'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/provider@1.1.3': resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==} engines: {node: '>=18'} + '@ai-sdk/provider@4.0.3': + resolution: {integrity: sha512-e0CpNWJUY7OxAFAnCZkw+ri9QOHWwTs1tXP42782KFGCU07qt8NiXCrCVowyCB5dP2r5/Uls+g2oPd8kOJn9dw==} + engines: {node: '>=22'} + '@ai-sdk/react@1.2.12': resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} engines: {node: '>=18'} @@ -2047,6 +2063,10 @@ packages: '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@vercel/oidc@3.2.0': + resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} + engines: {node: '>= 20'} + '@vitejs/plugin-react@4.7.0': resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -2082,6 +2102,9 @@ packages: '@vitest/utils@2.1.9': resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + '@workflow/serde@4.1.0': + resolution: {integrity: sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -2110,6 +2133,12 @@ packages: react: optional: true + ai@7.0.22: + resolution: {integrity: sha512-iAXYwQtR18qN7GXwNmGVAQM4uSJOh2+nZ5RP9NtDXfH5d4tlYyYzAM133O3U+eVcyWrvNRzecN9yW58xpCdfMg==} + engines: {node: '>=22'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -3788,6 +3817,13 @@ snapshots: '@ai-sdk/provider-utils': 2.2.8(zod@4.4.3) zod: 4.4.3 + '@ai-sdk/gateway@4.0.16(zod@4.4.3)': + dependencies: + '@ai-sdk/provider': 4.0.3 + '@ai-sdk/provider-utils': 5.0.7(zod@4.4.3) + '@vercel/oidc': 3.2.0 + zod: 4.4.3 + '@ai-sdk/provider-utils@2.2.8(zod@4.4.3)': dependencies: '@ai-sdk/provider': 1.1.3 @@ -3795,10 +3831,22 @@ snapshots: secure-json-parse: 2.7.0 zod: 4.4.3 + '@ai-sdk/provider-utils@5.0.7(zod@4.4.3)': + dependencies: + '@ai-sdk/provider': 4.0.3 + '@standard-schema/spec': 1.1.0 + '@workflow/serde': 4.1.0 + eventsource-parser: 3.0.8 + zod: 4.4.3 + '@ai-sdk/provider@1.1.3': dependencies: json-schema: 0.4.0 + '@ai-sdk/provider@4.0.3': + dependencies: + json-schema: 0.4.0 + '@ai-sdk/react@1.2.12(react@19.2.6)(zod@4.4.3)': dependencies: '@ai-sdk/provider-utils': 2.2.8(zod@4.4.3) @@ -4823,6 +4871,8 @@ snapshots: dependencies: csstype: 3.2.3 + '@vercel/oidc@3.2.0': {} + '@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@22.19.19)(tsx@4.21.0))': dependencies: '@babel/core': 7.29.0 @@ -4875,6 +4925,8 @@ snapshots: loupe: 3.2.1 tinyrainbow: 1.2.0 + '@workflow/serde@4.1.0': {} + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -4900,6 +4952,13 @@ snapshots: optionalDependencies: react: 19.2.6 + ai@7.0.22(zod@4.4.3): + dependencies: + '@ai-sdk/gateway': 4.0.16(zod@4.4.3) + '@ai-sdk/provider': 4.0.3 + '@ai-sdk/provider-utils': 5.0.7(zod@4.4.3) + zod: 4.4.3 + ajv-formats@2.1.1(ajv@8.20.0): optionalDependencies: ajv: 8.20.0 From 007b1eb71da6c5357c993809a340602a21b75afd Mon Sep 17 00:00:00 2001 From: Thor Whalen <1906276+thorwhalen@users.noreply.github.com> Date: Sun, 12 Jul 2026 17:33:39 +0200 Subject: [PATCH 3/3] fix(ai-vercel): correct the npm description and purge the v4 tool shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-merge cleanup for the 2.0.0 major. Three things still described (or depended on) the v4 `parameters: ZodSchema` tool shape that this PR removes. - packages/ai-vercel/package.json: the npm description still claimed the adapter "passes Zod schemas through directly". It no longer does — it converts with z.toJSONSchema() and hands the SDK a ready schema via jsonSchema(). This string is the front door of the package on npm, so it would have shipped as the first thing a reader of 2.0.0 sees. Rewritten to state the AI SDK v5+ requirement and the JSON Schema conversion. - examples/greenfield/graph-editor: pinned ai@^4.0.40 and @ai-sdk/anthropic@^1, while depending on acture-ai-vercel via workspace:*. After this PR toAITools emits tools carrying `inputSchema`, but ai@4 reads `parameters` — so the example's tools would have reached the model with no schema at all, which is precisely the bug this PR exists to fix. It was also an unmet peer (ai-vercel peers ^5 || ^6 || ^7). Bumped to the ai@7 line that the package actually tests against, plus the matching @ai-sdk/anthropic@^4, and replaced the v4 `maxSteps: 12` idiom with `stopWhen: isStepCount(12)`. The lockfile is regenerated to match: CI installs with --frozen-lockfile, so leaving it stale would have failed the build. - Agent-facing docs described the v4 shape, wrong in both the field name and the schema type — the shape an agent would have copied: * .claude/skills/acture-schema-bridge/SKILL.md * docs/hand-written-assistant-runtime.md (code sample now uses acture's own toJsonSchema(cmd), which yields { name, description, inputSchema }) * docs/phase-2-reflection.md (a historical reflection, so the original claim is kept but banner-marked as superseded rather than silently rewritten) Gates: ai-vercel 11/11 tests, tsc --noEmit clean. Full workspace green (21 packages, 582 tests). The example — which CI does not typecheck, since ci.yml filters to ./packages/* — was verified by hand: tsc --noEmit clean and 7/7 tests against the real ai@7 types. Claude-Session: https://claude.ai/code/session_01D5qgxrtPRcERh22DZMSe3A --- .claude/skills/acture-schema-bridge/SKILL.md | 10 +- docs/hand-written-assistant-runtime.md | 10 +- docs/phase-2-reflection.md | 4 +- examples/greenfield/graph-editor/package.json | 4 +- .../graph-editor/scripts/ai-demo.ts | 4 +- packages/ai-vercel/package.json | 2 +- pnpm-lock.yaml | 177 ++---------------- 7 files changed, 41 insertions(+), 170 deletions(-) diff --git a/.claude/skills/acture-schema-bridge/SKILL.md b/.claude/skills/acture-schema-bridge/SKILL.md index 11f1aa6..abe9ad1 100644 --- a/.claude/skills/acture-schema-bridge/SKILL.md +++ b/.claude/skills/acture-schema-bridge/SKILL.md @@ -99,9 +99,15 @@ Iterates registry, filters by tier (default: `['stable']` only), projects each t registry.toAITools({ tiers: ['stable'] }) // default ``` -For Vercel AI SDK: returns `Record` keyed by command id, ready to pass to `streamText({ tools })`. Each `Tool` has `{ description, parameters: ZodSchema, execute }`. +For Vercel AI SDK: returns `Record` keyed by a wire-safe tool name (see `commandIdToToolName`), ready to pass to `streamText({ tools })`. Each `Tool` has `{ description, inputSchema, execute }`. -For Anthropic SDK: returns `AnthropicTool[]` with `{ name, description, input_schema }`. The Vercel SDK uses Zod directly; Anthropic SDK takes JSON Schema, so the Anthropic projection uses `toJsonSchema` while the Vercel projection passes the Zod schema through. +**Requires AI SDK v5+.** The schema field is `inputSchema`; the v4 line called it `parameters` and is no longer supported (`acture-ai-vercel@1` is the last v4-compatible release). Emitting `parameters` against v5+ means the tool reaches the model with *no* schema and cannot be called — silently, with no error. + +For Anthropic SDK: returns `AnthropicTool[]` with `{ name, description, input_schema }`. + +**Both projections go through JSON Schema — neither passes Zod through.** Although the Vercel SDK *accepts* a Zod schema on `inputSchema`, we convert `params` ourselves with Zod 4's native `z.toJSONSchema()` and hand the SDK a ready schema via `jsonSchema()`. This keeps the wire schema ours rather than the SDK's bundled converter's — a coupling that has already bitten us once (`ai` v4 bundled a Zod-v3-only converter that, given a Zod v4 schema, silently emitted `{}`). + +Runtime validation is unaffected: `registry.dispatch` still validates against the original Zod schema, so refinements JSON Schema cannot express (`z.refine` predicates and friends) are still enforced. The JSON Schema is only what the model is *shown*. ## Strict mode (OpenAI) diff --git a/docs/hand-written-assistant-runtime.md b/docs/hand-written-assistant-runtime.md index 46d90fb..21840cc 100644 --- a/docs/hand-written-assistant-runtime.md +++ b/docs/hand-written-assistant-runtime.md @@ -268,10 +268,14 @@ read-side context (`useAssistantContext` / `useCopilotReadable` / AG-UI // Build ONE tools config from the registry (plain data — NOT hooks in a loop, // which would violate the Rules of Hooks). Register it with your chat layer in a // single top-level call; the exact registration API is framework-specific. +// `toJsonSchema(cmd)` yields `{ name, description, inputSchema }` — JSON Schema, not +// raw Zod. Hand the SDK a schema you converted, so the wire contract is yours rather +// than whichever Zod->JSON-Schema converter the SDK bundles. (`ai` v4 bundled a +// Zod-v3-only one that silently emitted `{}` for a Zod v4 schema: the model saw a tool +// with no parameters and could not call it.) Note the field is `inputSchema` on AI SDK +// v5+; v4 called it `parameters`. const tools = registry.list({ tiers: ['stable'] }).map((cmd) => ({ - name: cmd.id, - description: cmd.description, - parameters: cmd.params, + ...toJsonSchema(cmd), handler: (args: unknown) => registry.dispatch(cmd.id, args, { channel: 'assistant' }), })); // e.g. assistant-ui / AG-UI: hand `tools` to the runtime; CopilotKit: register each diff --git a/docs/phase-2-reflection.md b/docs/phase-2-reflection.md index 99f7675..34bdbfe 100644 --- a/docs/phase-2-reflection.md +++ b/docs/phase-2-reflection.md @@ -35,7 +35,9 @@ This file answers the six questions from `docs/implementation_plan.md` §"Phase **Yes, and the dual path is cleaner than expected.** -- **Vercel AI SDK** accepts Zod schemas directly. `acture-ai-vercel` passes `record.params` through unchanged, preserving every `z.refine`, `z.transform`, and custom error message that JSON Schema would silently lose. The AI SDK's `tool({ parameters: zodSchema, execute })` handles the rest. +> **Superseded (`acture-ai-vercel@2`, AI SDK v5+).** The Zod-passthrough described in the first bullet below was reversed. Both projections now go through JSON Schema, and the AI SDK tool field is `inputSchema`, not `parameters`. The reasoning is preserved here as a record of what we believed at Phase 2; see `packages/ai-vercel/README.md` ("Why convert to JSON Schema") for current behavior. **Do not write new code against the shape below.** + +- **Vercel AI SDK** accepts Zod schemas directly, so `acture-ai-vercel` passed `record.params` through unchanged, and the AI SDK's `tool({ parameters: zodSchema, execute })` handled the rest. *(This is what broke: `ai` v4 bundled a Zod-v3-only converter that, handed a Zod v4 schema, silently emitted `{}` — the model saw a tool with no parameters and could not call it. We now convert with `z.toJSONSchema()` and pass a ready schema via `jsonSchema()`, keeping the wire schema ours. Refinements are still enforced at `registry.dispatch`, which validates against the original Zod schema.)* - **MCP** wants JSON Schema on the wire. `acture-mcp-server/tools.ts` calls `toJsonSchema(record)` and emits the envelope as a `McpToolDescriptor`. Strict mode is opt-in (the OpenAI-style `additionalProperties: false` flavor). diff --git a/examples/greenfield/graph-editor/package.json b/examples/greenfield/graph-editor/package.json index c26939c..78cd589 100644 --- a/examples/greenfield/graph-editor/package.json +++ b/examples/greenfield/graph-editor/package.json @@ -24,7 +24,7 @@ "acture-state-zustand": "workspace:*", "@modelcontextprotocol/sdk": "^1.0.4", "acture": "workspace:*", - "ai": "^4.0.40", + "ai": "^7.0.22", "cmdk": "^1.0.0", "immer": "^11.0.0", "react": "^19.0.0", @@ -34,7 +34,7 @@ "zustand": "^5.0.0" }, "devDependencies": { - "@ai-sdk/anthropic": "^1.0.0", + "@ai-sdk/anthropic": "^4.0.0", "@testing-library/react": "^16.1.0", "@types/node": "^22.10.0", "@types/react": "^19.0.0", diff --git a/examples/greenfield/graph-editor/scripts/ai-demo.ts b/examples/greenfield/graph-editor/scripts/ai-demo.ts index 547a680..fcbfbdf 100644 --- a/examples/greenfield/graph-editor/scripts/ai-demo.ts +++ b/examples/greenfield/graph-editor/scripts/ai-demo.ts @@ -16,7 +16,7 @@ * pairs to form a triangle. The final state is logged. */ -import { generateText } from 'ai'; +import { generateText, isStepCount } from 'ai'; import { createAnthropic } from '@ai-sdk/anthropic'; import { createRegistry } from 'acture'; import { createZustandAdapter } from 'acture-state-zustand'; @@ -39,7 +39,7 @@ const anthropic = createAnthropic({ apiKey }); const result = await generateText({ model: anthropic('claude-sonnet-4-5'), tools: toAITools(registry), - maxSteps: 12, + stopWhen: isStepCount(12), prompt: [ 'You have a graph editor with the listed tools.', 'Currently the graph already has nodes n1, n2, n3 and one edge.', diff --git a/packages/ai-vercel/package.json b/packages/ai-vercel/package.json index ab9ca45..bde84ff 100644 --- a/packages/ai-vercel/package.json +++ b/packages/ai-vercel/package.json @@ -1,7 +1,7 @@ { "name": "acture-ai-vercel", "version": "1.1.0", - "description": "Project an acture registry as Vercel AI SDK tool definitions. Tier-filtered; errors-as-data; passes Zod schemas through directly.", + "description": "Project an acture registry as Vercel AI SDK tool definitions. Requires AI SDK v5+ (inputSchema). Tier-filtered; errors-as-data; converts each command's Zod params to JSON Schema so the wire contract is ours, not the SDK's bundled converter's.", "license": "Apache-2.0", "author": "Thor Whalen", "repository": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1159131..5560876 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -52,7 +52,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.0 - version: 5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6) devDependencies: '@types/node': specifier: ^22.10.0 @@ -112,8 +112,8 @@ importers: specifier: workspace:* version: link:../../../packages/state-zustand ai: - specifier: ^4.0.40 - version: 4.3.19(react@19.2.6)(zod@4.4.3) + specifier: ^7.0.22 + version: 7.0.22(zod@4.4.3) cmdk: specifier: ^1.0.0 version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -134,11 +134,11 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.0 - version: 5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6) devDependencies: '@ai-sdk/anthropic': - specifier: ^1.0.0 - version: 1.2.12(zod@4.4.3) + specifier: ^4.0.0 + version: 4.0.12(zod@4.4.3) '@testing-library/react': specifier: ^16.1.0 version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -232,7 +232,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.0 - version: 5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6) devDependencies: '@types/react': specifier: ^19.0.0 @@ -266,7 +266,7 @@ importers: version: 19.2.6(react@19.2.6) zustand: specifier: ^5.0.0 - version: 5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6) devDependencies: '@types/react': specifier: ^19.0.0 @@ -754,7 +754,7 @@ importers: version: 2.1.9(@types/node@22.19.19)(jsdom@25.0.1) zustand: specifier: ^5.0.0 - version: 5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6) packages/telemetry: devDependencies: @@ -829,15 +829,15 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.0 - version: 5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6) packages: - '@ai-sdk/anthropic@1.2.12': - resolution: {integrity: sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ==} - engines: {node: '>=18'} + '@ai-sdk/anthropic@4.0.12': + resolution: {integrity: sha512-IquWNkGRSF2ulYJ6YIIUqMsNAIDeZFB3/4W92aSOtCxH1TfuVoq3qw4M4TxTxIw4As9cnkeRLe7b9LDMzPDsnA==} + engines: {node: '>=22'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4.1.8 '@ai-sdk/gateway@4.0.16': resolution: {integrity: sha512-9iYxdOLquoOFk5e+02P9qfBIA+EJU0FNJvyjGxi4iXJabt2+GlbaCg7TX0sTtxOsBVkdabkatqWM6+kvp53tBg==} @@ -845,42 +845,16 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@2.2.8': - resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.23.8 - '@ai-sdk/provider-utils@5.0.7': resolution: {integrity: sha512-OSm5/5kdrHa11WIOo5LYgDKnxYWp5aB/wx5EXRHi0jpUGduMDeB6oht9U6p+UNNWIP3F/EqPpV8d7vdP/iRnqg==} engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider@1.1.3': - resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==} - engines: {node: '>=18'} - '@ai-sdk/provider@4.0.3': resolution: {integrity: sha512-e0CpNWJUY7OxAFAnCZkw+ri9QOHWwTs1tXP42782KFGCU07qt8NiXCrCVowyCB5dP2r5/Uls+g2oPd8kOJn9dw==} engines: {node: '>=22'} - '@ai-sdk/react@1.2.12': - resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} - engines: {node: '>=18'} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - zod: ^3.23.8 - peerDependenciesMeta: - zod: - optional: true - - '@ai-sdk/ui-utils@1.2.11': - resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.23.8 - '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} @@ -1621,10 +1595,6 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@opentelemetry/api@1.9.0': - resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} - engines: {node: '>=8.0.0'} - '@playwright/test@1.60.0': resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==} engines: {node: '>=18'} @@ -2037,9 +2007,6 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} - '@types/diff-match-patch@1.0.36': - resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -2123,16 +2090,6 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} - ai@4.3.19: - resolution: {integrity: sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==} - engines: {node: '>=18'} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - zod: ^3.23.8 - peerDependenciesMeta: - react: - optional: true - ai@7.0.22: resolution: {integrity: sha512-iAXYwQtR18qN7GXwNmGVAQM4uSJOh2+nZ5RP9NtDXfH5d4tlYyYzAM133O3U+eVcyWrvNRzecN9yW58xpCdfMg==} engines: {node: '>=22'} @@ -2272,10 +2229,6 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} @@ -2403,9 +2356,6 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - diff-match-patch@1.0.5: - resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} - dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -2861,11 +2811,6 @@ packages: engines: {node: '>=6'} hasBin: true - jsondiffpatch@0.6.0: - resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -3327,9 +3272,6 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - secure-json-parse@2.7.0: - resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -3430,11 +3372,6 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - swr@2.4.1: - resolution: {integrity: sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==} - peerDependencies: - react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -3449,10 +3386,6 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - throttleit@2.1.0: - resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} - engines: {node: '>=18'} - tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -3594,11 +3527,6 @@ packages: '@types/react': optional: true - use-sync-external-store@1.6.0: - resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - validate.io-array@1.0.6: resolution: {integrity: sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==} @@ -3811,10 +3739,10 @@ packages: snapshots: - '@ai-sdk/anthropic@1.2.12(zod@4.4.3)': + '@ai-sdk/anthropic@4.0.12(zod@4.4.3)': dependencies: - '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@4.4.3) + '@ai-sdk/provider': 4.0.3 + '@ai-sdk/provider-utils': 5.0.7(zod@4.4.3) zod: 4.4.3 '@ai-sdk/gateway@4.0.16(zod@4.4.3)': @@ -3824,13 +3752,6 @@ snapshots: '@vercel/oidc': 3.2.0 zod: 4.4.3 - '@ai-sdk/provider-utils@2.2.8(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 1.1.3 - nanoid: 3.3.12 - secure-json-parse: 2.7.0 - zod: 4.4.3 - '@ai-sdk/provider-utils@5.0.7(zod@4.4.3)': dependencies: '@ai-sdk/provider': 4.0.3 @@ -3839,31 +3760,10 @@ snapshots: eventsource-parser: 3.0.8 zod: 4.4.3 - '@ai-sdk/provider@1.1.3': - dependencies: - json-schema: 0.4.0 - '@ai-sdk/provider@4.0.3': dependencies: json-schema: 0.4.0 - '@ai-sdk/react@1.2.12(react@19.2.6)(zod@4.4.3)': - dependencies: - '@ai-sdk/provider-utils': 2.2.8(zod@4.4.3) - '@ai-sdk/ui-utils': 1.2.11(zod@4.4.3) - react: 19.2.6 - swr: 2.4.1(react@19.2.6) - throttleit: 2.1.0 - optionalDependencies: - zod: 4.4.3 - - '@ai-sdk/ui-utils@1.2.11(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@4.4.3) - zod: 4.4.3 - zod-to-json-schema: 3.25.2(zod@4.4.3) - '@asamuzakjp/css-color@3.2.0': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) @@ -4516,8 +4416,6 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@opentelemetry/api@1.9.0': {} - '@playwright/test@1.60.0': dependencies: playwright: 1.60.0 @@ -4849,8 +4747,6 @@ snapshots: dependencies: '@babel/types': 7.29.0 - '@types/diff-match-patch@1.0.36': {} - '@types/estree@1.0.8': {} '@types/estree@1.0.9': {} @@ -4940,18 +4836,6 @@ snapshots: agent-base@7.1.4: {} - ai@4.3.19(react@19.2.6)(zod@4.4.3): - dependencies: - '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@4.4.3) - '@ai-sdk/react': 1.2.12(react@19.2.6)(zod@4.4.3) - '@ai-sdk/ui-utils': 1.2.11(zod@4.4.3) - '@opentelemetry/api': 1.9.0 - jsondiffpatch: 0.6.0 - zod: 4.4.3 - optionalDependencies: - react: 19.2.6 - ai@7.0.22(zod@4.4.3): dependencies: '@ai-sdk/gateway': 4.0.16(zod@4.4.3) @@ -5092,8 +4976,6 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.6.2: {} - chardet@2.1.1: {} check-error@2.1.3: {} @@ -5200,8 +5082,6 @@ snapshots: detect-node-es@1.1.0: {} - diff-match-patch@1.0.5: {} - dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -5763,12 +5643,6 @@ snapshots: json5@2.2.3: {} - jsondiffpatch@0.6.0: - dependencies: - '@types/diff-match-patch': 1.0.36 - chalk: 5.6.2 - diff-match-patch: 1.0.5 - jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 @@ -6172,8 +6046,6 @@ snapshots: scheduler@0.27.0: {} - secure-json-parse@2.7.0: {} - semver@6.3.1: {} semver@7.8.0: {} @@ -6284,12 +6156,6 @@ snapshots: dependencies: has-flag: 4.0.0 - swr@2.4.1(react@19.2.6): - dependencies: - dequal: 2.0.3 - react: 19.2.6 - use-sync-external-store: 1.6.0(react@19.2.6) - symbol-tree@3.2.4: {} term-size@2.2.1: {} @@ -6302,8 +6168,6 @@ snapshots: dependencies: any-promise: 1.3.0 - throttleit@2.1.0: {} - tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -6432,10 +6296,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - use-sync-external-store@1.6.0(react@19.2.6): - dependencies: - react: 19.2.6 - validate.io-array@1.0.6: {} validate.io-function@1.0.2: {} @@ -6575,9 +6435,8 @@ snapshots: zod@4.4.3: {} - zustand@5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)): + zustand@5.0.13(@types/react@19.2.14)(immer@11.1.8)(react@19.2.6): optionalDependencies: '@types/react': 19.2.14 immer: 11.1.8 react: 19.2.6 - use-sync-external-store: 1.6.0(react@19.2.6)