Skip to content

chore(deps): migrate to AI SDK v7 + @ai-sdk/react v4 - #686

Merged
urjitc merged 11 commits into
mainfrom
chore/ai-sdk-v7-migration
Jul 28, 2026
Merged

chore(deps): migrate to AI SDK v7 + @ai-sdk/react v4#686
urjitc merged 11 commits into
mainfrom
chore/ai-sdk-v7-migration

Conversation

@urjitc

@urjitc urjitc commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary

Upgrades ai from v6 to v7 and @ai-sdk/react from v3 to v4, working through the tool-type churn Vercel introduced and adopting the v7 quality-of-life features that pay off today. Two commits — the migration itself, plus a follow-up polish pass — so the review can stage the type-mechanical stuff separately from the intentional adoptions.

Motivation

ai@6 is now a version behind and the SDK ecosystem (agents, think, codemode, chat) is normalizing on v7 at the boundary. Staying on v6 accumulates two kinds of debt: (1) peer warnings from packages that now declare ai@^6 || ^7, and (2) missing new features like the stable structured-output pattern and the tighter runtime error class.

Changes

Commit 1 — chore(deps): migrate to AI SDK v7 and @ai-sdk/react v4

Version bump plus the type/API fallout:

  • Tool wrapper (ai-thread-tool.ts) — v7 added a required third generic CONTEXT extends Context to Tool, tool(), and ToolExecutionOptions. defineAIThreadTool now:
    • Declares the wrapped Tool as Tool<any, any, any> at the outer boundary so the resulting object is assignable to ToolSet entries (whose union types the type checker cannot narrow through our INPUT/OUTPUT intersection). Concrete INPUT/OUTPUT continue to live on the runtime metadata attached via the private symbol key.
    • Uses tool<INPUT, OUTPUT, Record<string, unknown>> and ToolExecutionOptions<unknown> internally so the AI SDK signature is satisfied without forcing every tool call site to pass a real context.
    • Test now supplies the required context: {} field on directOptions to satisfy ToolExecutionOptions<unknown>.
  • ConnectorTool.description narrows to string only in the codemode adapter (v7 opened it up to string | ((options) => string), but the connector protocol still expects a string).
  • createSandboxTools casts the spread override object back to ToolSet[string] to work around the same tool-union widening.
  • systeminstructions rename across the recorder + inspector chain (TurnConfig, TurnContext, recorders, AIInspectorRunView, and the local variable in AIThread.beforeTurn). Think 0.15 normalizes both names at the boundary, so this is cosmetic today; it silences the v7 deprecation warning and matches the new nomenclature end to end.
  • Token usage extractor — v7 moves cache and reasoning breakdowns under inputTokenDetails / outputTokenDetails. extractAiTelemetryTokenUsage now reads the nested v7 shape first and keeps v6 top-level fallbacks so historical telemetry records replayed through this normalizer still resolve.

Ran npx @ai-sdk/codemod v7 in dry-run first: it found no changes in our SDK usage because most of our AI SDK surface is normalized by Think at the boundary. The changes above are the ones the codemod couldn't do (tool typing) plus a Think-internal systeminstructions rename that a prior WIP had started but not completed.

Commit 2 — refactor(ai): adopt AI SDK v7 polish

Three quality-of-life adoptions that were low-effort/high-signal:

  • Title generation → Output.object. generateAIThreadTitle used to prompt the model with "return only the title, no quotes, no punctuation at the end" and hope for the best. Now uses Output.object({ schema: z.object({ title }), name, description }) and the shape is guaranteed. Also moves the system instruction to the new instructions field.
  • experimental_throttlethrottle in useWorkspaceAiChat. The option graduated to stable on @ai-sdk/react v4.
  • request.body fallback comment in buildAiTelemetryInputFromStep. Documents that AI SDK v7 stopped populating request.body by default (include: { requestBody } is now off), but our ctx.messages / request.messages code paths are what fire in practice through Think, so the fallback just becomes a no-op returning [] rather than crashing. Grep confirms nothing else in the code reads response.body, requestBody, or rawChunks, so no regression elsewhere.

What I explicitly did NOT do

  • NoOutputGeneratedError special-case — audited, no action needed. Our classifier only recognizes overflow; everything else bubbles as terminal via the default classifier, which is the correct behavior for "model produced nothing". Error handlers use generic instanceof Error + .message with no message pattern-matching that would break.
  • strict: true audit — all 22 tools already have it. Nothing to add.
  • Tool approvals / toolsContext / refineToolInput — genuinely useful v7 features but @cloudflare/think@0.15 doesn't forward them yet. grep -c "toolsContext|toolApproval|refineToolInput" think@0.15/dist/*.d.ts returns 0. Migration would give us the type ergonomics without the runtime plumbing, which is worse than not migrating. Deferred until Think forwards these.
  • Full AI SDK v7 codemod run — the codemod only touched files it thought had renames; none of ours matched. Reviewed manually above.

Test plan

  • pnpm exec tsc --noEmit — clean
  • pnpm test — 158/158 pass
  • Run pnpm dev locally and exercise a chat turn against Claude, OpenAI, and Gemini models to confirm title generation still produces titles and the chat round-trip works end-to-end
  • Send a message that triggers a tool call (e.g. workspace read) to confirm tool execution + telemetry recording still works
  • Trigger a compaction (long conversation) and confirm the compaction pipeline still runs

Risks

  • Widened tool typingAIThreadTool<INPUT, OUTPUT> now presents as Tool<any, any, any> to the SDK boundary. Concrete input/output types are still enforced at the execute site via the runtime metadata, so this is a boundary-cast, not a runtime loosening. If a downstream consumer relied on reading INPUT/OUTPUT off the raw tool type, they'd now see any — none of ours do.
  • Title generation shape change — the returned .title string comes from a schema-validated object instead of raw result.text. Consumers that depended on the exact wording the model produced (including trailing whitespace or quotes) will now get a cleaner string. Reviewed downstream: the value is only used as a title label in the UI.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Chat recovery and streaming can now append to the existing assistant message for smoother continuity.
  • Improvements
    • AI inspection views and conversation context panels now show “instructions” instead of “system”.
    • Tool receipt summaries in AI chat are now more structured and truncation-friendly (including approval-required and highlighted segments).
    • AI thread title generation and token-usage reporting are updated for more consistent, SDK-aligned results.
  • Chores
    • Updated application and development tooling dependencies and test utilities.

urjitc and others added 7 commits July 28, 2026 11:53
nanoid 6.0.0 is a 4x-perf release that drops Node 18/20 support; we run
Node 24+ everywhere so this is transparent. Zero API changes.

@cloudflare/workers-types 5.20260727 removes the dated entrypoints
(2022-11-30, 2023-03-01, …) in favor of the single main entrypoint that
mirrors the current compatibility date. We already use wrangler types
via pnpm cf-typegen so there is no consumer-side change; grep confirms
zero dated imports across the codebase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rolls all 15 @tiptap/* packages plus @tiptap/y-tiptap forward two minors.
Notable fixes for us: getPos() now returns undefined instead of throwing
during a React 19 render race, React portal batching + details-cursor
fix in 3.28, caret placement fix after splitting a React NodeView block
in 3.29.1, and phantom highlight after AllSelection-delete. No breaking
changes across the range.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
wrangler 4.107 to 4.114 pulls seven minors of local-dev polish
(declarative DO exports, workflow rollback on terminate, local
observability opt-in) with one behavior change to opt out of:
wrangler 4.110 starts sending the project npm dependency list to
Cloudflare on every deploy for build analytics. Turn it off in
wrangler.jsonc via dependencies_instrumentation.enabled = false.

@cloudflare/vite-plugin 1.43 to 1.47 and vitest-pool-workers 0.18.0 to
0.18.8 are all local-dev bug fix rollups (DO log ordering during
blockConcurrencyWhile rejection, Windows non-ASCII paths, rate-limiter
binding reset). No API changes.

Our wrangler.jsonc has no legacy_env field, so the 4.111 removal is a
no-op for us.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Moves agents 0.18 to 0.19, @cloudflare/think 0.14 to 0.15, @cloudflare/
codemode 0.4.4 to 0.5.0, and @cloudflare/shell 0.4.2 to 0.4.3. All four
released in the same upstream PR that widens their AI SDK peer range to
^6 || ^7 without any consumer API breaks — think.AIChatAgent, agents'
Agent<>/useAgent/useAgentChat/callable/getAgentByName, codemode's
DynamicWorkerExecutor and CodemodeConnector, and shell's WorkspaceLike
all keep the same shape.

Rebases patches/@cloudflare__think@0.14.0.patch onto 0.15's
_streamResult call site, StreamAccumulator constructor, and
_chatRecoveryContinue continueLastTurn call. The three hunks apply
against the same call-site shape (only line numbers moved), so the
appendToExistingAssistant recovery-continue behavior is preserved.

Left on AI SDK v6 for now; the v7 migration is separate work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rolls up all remaining low-risk version bumps across runtime and dev
dependencies. No behavior changes expected. Grouped intentionally into
a single commit because every entry is a same-major bump with no
migration work.

Runtime:
  - better-auth + @better-auth/oauth-provider 1.6.23 to 1.6.25
  - @tanstack/react-query 5.101.2 to 5.101.4
  - @tanstack/react-router 1.170.17 to 1.170.18
  - @tanstack/react-start 1.168.27 to 1.168.32
  - react + react-dom 19.2.7 to 19.2.8
  - react-resizable-panels 4.12.1 to 4.12.2
  - @contextcompany/custom 1.2.1 to 1.2.2
  - autumn-js 1.2.35 to 1.2.44
  - @fontsource-variable/geist 5.2.9 to 5.3.0
  - @posthog/ai 8.2.2 to 8.4.0, @posthog/mcp 0.9.1 to 0.10.1
  - posthog-js 1.396.6 to 1.407.3, posthog-node 5.39.4 to 5.46.1
  - lucide-react 1.23.0 to 1.27.0
  - mermaid 11.15.0 to 11.16.0
  - shadcn 4.13.0 to 4.15.0

Dev:
  - @commitlint/cli 21.2.0 to 21.2.1
  - @tailwindcss/vite + tailwindcss 4.3.2 to 4.3.3
  - @tanstack/react-query-devtools 5.101.2 to 5.101.4
  - @tanstack/router-plugin 1.168.19 to 1.168.23
  - @tanstack/react-devtools 0.10.8 to 0.10.9
  - @tanstack/devtools-vite 0.8.1 to 0.8.3
  - @tiptap/y-tiptap 3.0.6 to 3.0.7
  - @types/node 26.1.0 to 26.1.1
  - @vitejs/plugin-react 6.0.3 to 6.0.4
  - tsx 4.23.0 to 4.23.1
  - vite-bundle-analyzer 1.3.8 to 1.3.9
  - vitest 4.1.9 to 4.1.10
  - @voidzero-dev/vite-plus-core + vite-plus 0.2.2 to 0.2.6

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vercel AI SDK v7 introduces a required third generic (CONTEXT) on Tool,
tool(), and ToolExecutionOptions plus a matching required context field
on the runtime options. Our tools do not use tool context, so:

  - defineAIThreadTool now declares its wrapped Tool as
    Tool<any, any, any> at the outer boundary so the resulting object is
    assignable to AI SDK v7 ToolSet entries (whose union types the type
    checker cannot narrow through our INPUT/OUTPUT intersection). Concrete
    INPUT/OUTPUT continue to live on the runtime metadata attached via the
    private symbol key.
  - Internal tool<INPUT, OUTPUT, Record<string, unknown>> and matching
    ToolExecutionOptions<unknown> keep the AI SDK signature happy while
    letting us pass any context shape at the boundary.
  - ai-thread-tool.test.ts now provides the required context field on
    directOptions so it satisfies ToolExecutionOptions<unknown>.
  - ConnectorTool.description narrowly requires string, and the v7 field
    is string | ((options) => string) — the codemode adapter now coerces
    functional descriptions to undefined.
  - createSandboxTools casts its override object back to ToolSet[string]
    to work around the same tool-union widening.

Aligns the recorder + inspector chain on the v7 preferred field name:

  - TurnConfig / TurnContext / recorders / AIInspectorRunView all use
    instructions instead of the deprecated system, and the local
    variable in AIThread.beforeTurn is renamed accordingly. Think 0.15
    normalizes both names at the boundary so this is cosmetic today; it
    stops the deprecation warning and matches AI SDK v7 nomenclature.

Token usage extractor keeps v6 fallbacks while adding v7 nested shapes:

  - AI SDK v7 moves cache + reasoning breakdowns under inputTokenDetails
    / outputTokenDetails. extractAiTelemetryTokenUsage now reads the
    nested v7 shape first and falls back to the v6 top-level fields so
    historical telemetry records replayed through this normalizer still
    resolve.

Vitest suite: 158/158 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three focused v7 quality-of-life adoptions.

Title generation now uses Output.object with a small Zod schema instead
of free-form generateText. Removes the "return only the title, no
quotes, no punctuation at the end" prompt gymnastics and guarantees the
result shape. Falls back to an empty string if the model still returns
nothing so downstream chain-of-fallbacks stays intact. The system
instruction moves to the new instructions field (v7 preferred over
system) and the prompt becomes just the raw first user message.

experimental_throttle renamed to throttle in useWorkspaceAiChat now that
the option is stable on @ai-sdk/react v4. Zero behavior change.

Documents in a comment that request.body is a legacy fallback: AI SDK v7
stopped populating it by default (include: { requestBody } is now off),
but the earlier ctx.messages and request.messages code paths are what
fire in practice through Think, so the fallback just becomes a no-op
returning an empty array rather than crashing. Nothing else in the code
touches response.body, requestBody, or rawChunks, so no regression
elsewhere from the v7 include default flip.

Verified: NoOutputGeneratedError needs no special handling — our
classifier only recognizes overflow and everything else bubbles as
terminal via the existing default classifier, which is the correct
behavior for "model produced nothing". All 22 tools already have
strict: true so no per-tool audit was needed.

Vitest suite: 158/158 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 28, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@capy-ai

capy-ai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Capy auto-review is paused for this organization because the usage-cycle auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Updates AI SDK integration, renames turn metadata to instructions, adds continuation message appending, introduces structured tool receipt segments, refreshes dependencies, and disables Wrangler dependency instrumentation.

Changes

AI runtime and instruction metadata

Layer / File(s) Summary
AI SDK runtime contracts
src/features/workspaces/ai/ai-thread-tool.ts, src/features/workspaces/ai/ai-thread-runtime.ts, src/features/workspaces/ai/ai-thread-telemetry-format.ts, src/features/workspaces/components/ai-chat/useWorkspaceAiChat.ts
Updates tool typings, structured title generation, telemetry token extraction, tool descriptions, and chat throttling for newer AI SDK interfaces.
Instruction metadata propagation
src/features/workspaces/ai/ai-thread.ts, src/features/workspaces/ai/*recorder.ts, src/features/workspaces/ai/ai-inspector-*, src/features/workspaces/components/ai-chat/AiChatInspector*.tsx
Renames turn-start metadata from system to instructions across runtime, telemetry, inspector state, and rendering.

Continuation streaming

Layer / File(s) Summary
Continuation streaming behavior
patches/@cloudflare__think@0.15.0.patch, pnpm-workspace.yaml
Continuation recovery can append streamed output to the latest assistant message while reusing its message data and metadata.

Structured tool receipts

Layer / File(s) Summary
Receipt construction and summarization
src/features/workspaces/components/ai-chat/ai-chat-tool-receipts.ts
Adds segmented receipt types and builders, and rewrites running, completed, failed, workspace, web, research, compute, and orchestration summaries to compose receipt parts.
Activity propagation and rendering
src/features/workspaces/components/ai-chat/ai-chat-display-state.ts, src/features/workspaces/components/ai-chat/ai-chat-codemode-activity.ts, src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx
Carries receipt segments through tool activity state and renders named segments with existing truncation and running-state behavior.
Segmented activity assertions
src/features/workspaces/components/ai-chat/ai-chat-display-state.test.ts
Updates durable call-log and sibling-tool fallback expectations to include structured query segments.

Tooling and deployment configuration

Layer / File(s) Summary
Dependency and deployment updates
package.json, pnpm-workspace.yaml, wrangler.jsonc
Updates runtime and development dependencies, Vitest and package pins, the Think patch mapping, and Wrangler’s dependency instrumentation setting.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: migrating to AI SDK v7 and @ai-sdk/react v4.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/ai-sdk-v7-migration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit f037fd6.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b771fd31a8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread package.json
"wrangler": "^4.107.0"
"vite-bundle-analyzer": "^1.3.9",
"vite-plus": "^0.2.6",
"vitest": "4.1.10",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Align the Vitest override with the declared version

The workspace-level vitest: 4.1.9 override still wins over this declaration on every pnpm install; the committed lockfile confirms that the root importer resolves to 4.1.9, not 4.1.10. Consequently the intended Vitest upgrade never takes effect and the manifest misrepresents the test version being executed. Update the override to 4.1.10 as well, or keep this declaration at 4.1.9 if the pin remains necessary.

Useful? React with 👍 / 👎.

Comment thread package.json
"wrangler": "^4.107.0"
"vite-bundle-analyzer": "^1.3.9",
"vite-plus": "^0.2.6",
"vitest": "4.1.10",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Vitest version remains overridden

The manifest declares Vitest 4.1.10, but the workspace override still forces 4.1.9, so installs never select the declared version and the dependency metadata no longer reflects the resolved toolchain.

Fix in Cursor

@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR migrates the workspace AI stack to AI SDK v7 and React bindings v4.

  • Updates AI, Cloudflare Think, Agents, Code Mode, Shell, and related dependency versions.
  • Adapts tool wrappers, structured title generation, chat throttling, prompt naming, telemetry token fields, and inspector presentation to the new APIs.
  • Carries the Think chat-recovery patch forward to version 0.15 and disables Wrangler dependency instrumentation.

Confidence Score: 4/5

The PR appears safe to merge, with a non-blocking dependency-version inconsistency that should be reconciled.

The AI migration paths preserve the existing prompt, tool, title, and telemetry contracts, but the declared Vitest upgrade is silently defeated by the workspace override.

Files Needing Attention: package.json, pnpm-workspace.yaml, pnpm-lock.yaml

T-Rex T-Rex Logs

What T-Rex did

  • The test run completed and the full console output along with execution metadata were captured for review.
  • Final results show 35 test files passed and 158 tests passed.
  • The command ran from 2026-07-28T17:25:52Z through 2026-07-28T17:26:03Z.
  • All run data and results are archived in a log artifact suitable for validation.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
package.json Upgrades the AI SDK ecosystem and numerous unrelated packages; the Vitest declaration conflicts with the workspace override.
pnpm-lock.yaml Resolves the migrated AI dependency graph and confirms that the Vitest override still selects 4.1.9.
src/features/workspaces/ai/ai-thread-runtime.ts Adopts structured title output and updates tool typing while retaining downstream title normalization.
src/features/workspaces/ai/ai-thread.ts Renames the final turn prompt configuration from the deprecated system field to instructions.
src/features/workspaces/ai/ai-thread-tool.ts Adapts the validated tool wrapper to AI SDK v7’s context generic.
src/features/workspaces/ai/ai-thread-telemetry-format.ts Reads v7 nested token-detail fields while retaining legacy telemetry fallbacks.
patches/@cloudflare__think@0.15.0.patch Reapplies assistant-stream continuation behavior against Think 0.15.
src/features/workspaces/components/ai-chat/useWorkspaceAiChat.ts Migrates the graduated React chat throttle option to its stable name.

Fix All in Cursor

Reviews (1): Last reviewed commit: "refactor(ai): adopt AI SDK v7 polish" | Re-trigger Greptile

Both Codex and Greptile flagged that the workspace override still pinned
vitest to 4.1.9 even after the package.json declaration moved to ^4.1.10
in the earlier general-patch bump, so installs resolved to 4.1.9 and the
declared version misrepresented the resolved toolchain. Bump the
override to 4.1.10 so the whole tree lines up.

158/158 tests still pass on 4.1.10.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
patches/@cloudflare__think@0.15.0.patch (2)

21-21: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Only reuse the terminal assistant message.

findLast lets recovery continue append to any prior assistant message when the chat tail is a user/tool turn. Set appendToExistingAssistant/continuationAssistant only when this.messages.at(-1)?.role === "assistant" so it matches the UI’s last-message contract in ai-chat-display-state.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@patches/`@cloudflare__think@0.15.0.patch at line 21, Update the
continuationAssistant logic to reuse an assistant message only when
this.messages.at(-1)?.role is "assistant"; otherwise leave it undefined, even if
an earlier assistant message exists. Ensure appendToExistingAssistant follows
this same terminal-message condition to preserve the UI’s last-message contract.

24-24: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep continuation state separate from append mode.

This patch still passes continuation: true to _streamResult, but the new StreamAccumulator initial state sets continuation to options?.appendToExistingAssistant. For non-append continuations, that drops the marker on the completed response, so _shouldSettleRunAfterResponse settles the run immediately. Pass continuation from _streamResult/ctx.continuation, and use appendToExistingAssistant only for message reuse.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@patches/`@cloudflare__think@0.15.0.patch at line 24, Update the
StreamAccumulator initialization so its continuation state comes from
_streamResult or ctx.continuation, not options?.appendToExistingAssistant. Keep
appendToExistingAssistant limited to message reuse behavior, preserving the
continuation marker for non-append continuations and correct
_shouldSettleRunAfterResponse handling.
🧹 Nitpick comments (2)
src/features/workspaces/ai/ai-thread-telemetry-format.ts (1)

134-148: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Dead fallback branch: record.inputTokens/record.outputTokens are never objects here.

The first two fallback tiers correctly map to v7's inputTokenDetails.cacheReadTokens/cacheWriteTokens and outputTokenDetails.reasoningTokens, matching the SDK migration guide. The third tier, however, calls getNestedTokenValue(record.inputTokens, "cacheRead") / getNestedTokenValue(record.outputTokens, "reasoning"), but record.inputTokens/record.outputTokens are the same plain-number fields already consumed a few lines above via getTokenValue. getNestedTokenValue returns undefined for any non-object input, so this branch can never resolve to a value — it reads as a real v5/v4 compatibility path but is actually unreachable.

🧹 Suggested cleanup
 	const cacheReadInputTokens =
 		getNestedTokenValue(record.inputTokenDetails, "cacheReadTokens") ??
-		getTokenValue(record.cachedInputTokens) ??
-		getNestedTokenValue(record.inputTokens, "cacheRead");
+		getTokenValue(record.cachedInputTokens);
 	const cacheCreationInputTokens =
 		getNestedTokenValue(record.inputTokenDetails, "cacheWriteTokens") ??
-		getTokenValue(record.cacheCreationInputTokens) ??
-		getNestedTokenValue(record.inputTokens, "cacheWrite");
+		getTokenValue(record.cacheCreationInputTokens);
 	const reasoningTokens =
 		getNestedTokenValue(record.outputTokenDetails, "reasoningTokens") ??
-		getTokenValue(record.reasoningTokens) ??
-		getNestedTokenValue(record.outputTokens, "reasoning");
+		getTokenValue(record.reasoningTokens);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/workspaces/ai/ai-thread-telemetry-format.ts` around lines 134 -
148, Remove the unreachable getNestedTokenValue fallback branches using
record.inputTokens and record.outputTokens in the cacheReadInputTokens,
cacheCreationInputTokens, and reasoningTokens normalization logic. Keep the v7
detail lookups and existing top-level getTokenValue fallbacks unchanged.
src/features/workspaces/ai/ai-thread-tool.ts (1)

83-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Undocumented double-cast for the tool() config.

The as unknown as Tool<INPUT, OUTPUT, Record<string, unknown>> cast bypasses structural checking on the object passed to tool(). The analogous cast in ai-thread-runtime.ts (sandbox_bash override) has an explanatory comment about the v7 widened union; this one doesn't. A short comment here would help future maintainers understand why the literal needs bypassing instead of accidentally "tightening" it in a way that hides a real mismatch.

♻️ Suggested comment
+	// v7's tool() config type does not structurally accept the picked
+	// AIThreadToolDefinition fields combined with the narrowed execute
+	// signature above; bypass via unknown to keep INPUT/OUTPUT inference.
 	const aiTool = tool<INPUT, OUTPUT, Record<string, unknown>>({
 		...definition,
 		execute: (input: INPUT, options: ToolExecutionOptions<unknown>) =>
 			runtime.execute(input, directExecutionContext(options)),
 	} as unknown as Tool<INPUT, OUTPUT, Record<string, unknown>>);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/workspaces/ai/ai-thread-tool.ts` around lines 83 - 92, Add a
brief explanatory comment immediately before the `tool()` configuration cast in
the `aiTool` construction, documenting that the `as unknown as Tool<INPUT,
OUTPUT, Record<string, unknown>>` bypasses the v7 widened-union typing
limitation. Keep the existing cast and execution behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pnpm-workspace.yaml`:
- Line 68: Update the minimumReleaseAgeExclude configuration in
pnpm-workspace.yaml to include `@cloudflare/think`@0.15.0 alongside the existing
0.14.0 entry, matching the patched dependency version.

---

Outside diff comments:
In `@patches/`@cloudflare__think@0.15.0.patch:
- Line 21: Update the continuationAssistant logic to reuse an assistant message
only when this.messages.at(-1)?.role is "assistant"; otherwise leave it
undefined, even if an earlier assistant message exists. Ensure
appendToExistingAssistant follows this same terminal-message condition to
preserve the UI’s last-message contract.
- Line 24: Update the StreamAccumulator initialization so its continuation state
comes from _streamResult or ctx.continuation, not
options?.appendToExistingAssistant. Keep appendToExistingAssistant limited to
message reuse behavior, preserving the continuation marker for non-append
continuations and correct _shouldSettleRunAfterResponse handling.

---

Nitpick comments:
In `@src/features/workspaces/ai/ai-thread-telemetry-format.ts`:
- Around line 134-148: Remove the unreachable getNestedTokenValue fallback
branches using record.inputTokens and record.outputTokens in the
cacheReadInputTokens, cacheCreationInputTokens, and reasoningTokens
normalization logic. Keep the v7 detail lookups and existing top-level
getTokenValue fallbacks unchanged.

In `@src/features/workspaces/ai/ai-thread-tool.ts`:
- Around line 83-92: Add a brief explanatory comment immediately before the
`tool()` configuration cast in the `aiTool` construction, documenting that the
`as unknown as Tool<INPUT, OUTPUT, Record<string, unknown>>` bypasses the v7
widened-union typing limitation. Keep the existing cast and execution behavior
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cc9e7f7d-a0fd-40d6-b725-d9bb501b53bc

📥 Commits

Reviewing files that changed from the base of the PR and between 80b1a35 and b771fd3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • package.json
  • patches/@cloudflare__think@0.15.0.patch
  • pnpm-workspace.yaml
  • src/features/workspaces/ai/ai-inspector-view-model.ts
  • src/features/workspaces/ai/ai-inspector-view-types.ts
  • src/features/workspaces/ai/ai-thread-inspector-recorder.ts
  • src/features/workspaces/ai/ai-thread-orchestration.ts
  • src/features/workspaces/ai/ai-thread-runtime.ts
  • src/features/workspaces/ai/ai-thread-tcc-recorder.ts
  • src/features/workspaces/ai/ai-thread-telemetry-format.ts
  • src/features/workspaces/ai/ai-thread-telemetry-recorder.ts
  • src/features/workspaces/ai/ai-thread-tool.test.ts
  • src/features/workspaces/ai/ai-thread-tool.ts
  • src/features/workspaces/ai/ai-thread.ts
  • src/features/workspaces/components/ai-chat/AiChatInspectorConversation.tsx
  • src/features/workspaces/components/ai-chat/AiChatInspectorViews.tsx
  • src/features/workspaces/components/ai-chat/useWorkspaceAiChat.ts
  • wrangler.jsonc

Comment thread pnpm-workspace.yaml
cubic-dev-ai[bot]
cubic-dev-ai Bot previously approved these changes Jul 28, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Auto-approved: Dependency upgrade with mechanical code adjustments (type changes, renames, patch update). Changes are bounded, tests pass, and no new behavioral risks beyond ordinary implementation correctness.

Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="package.json">

<violation number="1" location="package.json:179">
P2: The vitest devDependency here was bumped to 4.1.10, but a workspace-level override still pins vitest to 4.1.9, per the committed lockfile. This means the declared version never actually gets installed, and the manifest misrepresents the toolchain version being used in tests. Align the override with 4.1.10, or revert this declaration to 4.1.9 if the pin is still required.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread package.json
"wrangler": "^4.107.0"
"vite-bundle-analyzer": "^1.3.9",
"vite-plus": "^0.2.6",
"vitest": "4.1.10",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The vitest devDependency here was bumped to 4.1.10, but a workspace-level override still pins vitest to 4.1.9, per the committed lockfile. This means the declared version never actually gets installed, and the manifest misrepresents the toolchain version being used in tests. Align the override with 4.1.10, or revert this declaration to 4.1.9 if the pin is still required.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At package.json, line 179:

<comment>The vitest devDependency here was bumped to 4.1.10, but a workspace-level override still pins vitest to 4.1.9, per the committed lockfile. This means the declared version never actually gets installed, and the manifest misrepresents the toolchain version being used in tests. Align the override with 4.1.10, or revert this declaration to 4.1.9 if the pin is still required.</comment>

<file context>
@@ -153,31 +146,38 @@
-		"wrangler": "^4.107.0"
+		"vite-bundle-analyzer": "^1.3.9",
+		"vite-plus": "^0.2.6",
+		"vitest": "4.1.10",
+		"wrangler": "^4.114.0"
 	},
</file context>

Comment thread pnpm-workspace.yaml
Comment thread src/features/workspaces/ai/ai-thread-runtime.ts
@cubic-dev-ai
cubic-dev-ai Bot dismissed their stale review July 28, 2026 17:37

Dismissed because Cubic found issues in a newer review.

urjitc and others added 2 commits July 28, 2026 14:26
Two follow-ups from cubic's PR review.

cubic flagged that minimumReleaseAgeExclude in pnpm-workspace still
listed @cloudflare/think@0.14.0 after the patched version moved to
0.15.0. Extending the same audit to the whole exclusion list surfaced
that codemode, shell, agents, vite-plus, and every vite-plus platform
binary were still pinned at their pre-bump versions. Refresh all of
them so the exclusion list actually applies to the versions the
lockfile now resolves.

cubic also noted that generated titles can violate the advertised 2 to
6 word contract because the schema only enforces character length
(min 1, max 80). Add a Zod refinement so the structured output
validates the same requirement the instructions communicate to the
model.

158/158 tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The word-count refine would fail structured-output validation whenever
the model returned 7 or more words, which meant the whole title
generation errored out and the thread ended up with no title at all.
That is a worse UX than a slightly wordy title. The model already gets
the 2-to-6-word nudge through the instructions field and the schema
field's describe(), and the max(80) character cap catches the truly
absurd cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="pnpm-workspace.yaml">

<violation number="1" location="pnpm-workspace.yaml:56">
P2: minimumReleaseAgeExclude bumps vite-plus packages to 0.2.6 but the vite override still pins to @voidzero-dev/vite-plus-core@0.2.2. The actually installed 0.2.2 packages are no longer excluded from the 1440-minute minimum release age check, which can fail CI installs if those versions are recent.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread pnpm-workspace.yaml
- "@cloudflare/shell@0.4.3"
- "@cloudflare/think@0.15.0"
- agents@0.19.0
- "@voidzero-dev/vite-plus-core@0.2.6"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: minimumReleaseAgeExclude bumps vite-plus packages to 0.2.6 but the vite override still pins to @voidzero-dev/vite-plus-core@0.2.2. The actually installed 0.2.2 packages are no longer excluded from the 1440-minute minimum release age check, which can fail CI installs if those versions are recent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At pnpm-workspace.yaml, line 56:

<comment>minimumReleaseAgeExclude bumps vite-plus packages to 0.2.6 but the vite override still pins to @voidzero-dev/vite-plus-core@0.2.2. The actually installed 0.2.2 packages are no longer excluded from the 1440-minute minimum release age check, which can fail CI installs if those versions are recent.</comment>

<file context>
@@ -49,20 +49,20 @@ allowBuilds:
+  - "@cloudflare/shell@0.4.3"
+  - "@cloudflare/think@0.15.0"
+  - agents@0.19.0
+  - "@voidzero-dev/vite-plus-core@0.2.6"
+  - "@voidzero-dev/vite-plus-darwin-arm64@0.2.6"
+  - "@voidzero-dev/vite-plus-darwin-x64@0.2.6"
</file context>

Rewrites the tool activity row summary path so long workspace item
names truncate dynamically at the CSS layer instead of being clipped to
a hard 72-character budget that would eat trailing status like "with 5
edits" from the display. The row already lays out its icon, status
badge, favicons, and chevron as flex siblings; the fix is to give the
summary the same treatment.

AiChatToolReceipt now optionally carries a segments array whose entries
are either { kind: "text" } (rendered shrink-0 whitespace-pre so
prefixes and suffixes stay visible) or { kind: "name" } (rendered
min-w-0 truncate so the variable-width name absorbs all remaining
horizontal space and ellipsizes first). ActivitySummaryText picks the
segment path when present and falls back to the legacy single-truncate
span for string-only receipts.

Receipts that embed a workspace item name, URL host, or search query
now emit segments via new receipt.{running,completed,failed}(...parts)
and name(value) helpers. The workspace edit/read/rename/move/create
plus web_search/web_markdown/web_links/research_discover/research_
deepen paths all migrated. Static receipts ("Reading workspace",
"Compaction complete") keep their string form because they have
nothing variable to truncate.

Retires the character-count boilerplate: quoteName, truncateReceipt
Value, TOOL_RECEIPT_VALUE_MAX_LENGTH, and formatDestinationName are
gone; adjacent text segments are consolidated in the builder so the
segment array stays clean for tests. The AiChatToolChildActivity type
now also threads segments through the code-mode child summary so
"Approval required · " gets prepended as a text segment while the tool
name underneath keeps its dynamic truncation.

Vitest suite: 158/158 pass (two ai-chat-display-state assertions
updated to include the new segments field).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@urjitc
urjitc merged commit 7ddf6cf into main Jul 28, 2026
10 of 11 checks passed
@urjitc
urjitc deleted the chore/ai-sdk-v7-migration branch July 28, 2026 19:31
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Dev Board Jul 28, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/workspaces/components/ai-chat/ai-chat-codemode-activity.ts`:
- Line 19: Update isCompactToolActivity to validate that segments is an array
before narrowing persisted records, or remove invalid segments from the returned
value. Ensure getCodemodeCallActivities cannot pass malformed segments through
to ActivitySummaryText, while preserving valid AiChatToolReceiptSegment[]
values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 27430314-24dc-4887-b436-7fb9c5414d2c

📥 Commits

Reviewing files that changed from the base of the PR and between da1343a and f037fd6.

📒 Files selected for processing (6)
  • src/features/workspaces/ai/ai-thread-runtime.ts
  • src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx
  • src/features/workspaces/components/ai-chat/ai-chat-codemode-activity.ts
  • src/features/workspaces/components/ai-chat/ai-chat-display-state.test.ts
  • src/features/workspaces/components/ai-chat/ai-chat-display-state.ts
  • src/features/workspaces/components/ai-chat/ai-chat-tool-receipts.ts
💤 Files with no reviewable changes (1)
  • src/features/workspaces/ai/ai-thread-runtime.ts

presentation: AiToolPresentation;
status: AiChatToolReceiptStatus;
summary: string;
segments?: AiChatToolReceiptSegment[];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

segments is now read from persisted activities but never validated.

isCompactToolActivity narrows persisted log records to Omit<AiChatToolChildActivity, "presentation"> without checking segments, and getCodemodeCallActivities spreads ...call straight through. Previously an unexpected segments value on a stored record was inert; now ActivitySummaryText calls segments.map(...) on it, so a non-array value from an older/malformed durable log throws during render. Consider validating the field in the guard (or dropping it there).

🛡️ Proposed validation in the type guard
 	return (
 		typeof record.id === "string" &&
 		isReceiptStatus(record.status) &&
 		typeof record.summary === "string" &&
+		(record.segments === undefined ||
+			(Array.isArray(record.segments) &&
+				record.segments.every((segment) => {
+					const item = segment as Record<string, unknown> | null;
+					return (
+						!!item &&
+						typeof item === "object" &&
+						(item.kind === "text" || item.kind === "name") &&
+						typeof item.value === "string"
+					);
+				}))) &&
 		typeof record.toolName === "string"
 	);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/workspaces/components/ai-chat/ai-chat-codemode-activity.ts` at
line 19, Update isCompactToolActivity to validate that segments is an array
before narrowing persisted records, or remove invalid segments from the returned
value. Ensure getCodemodeCallActivities cannot pass malformed segments through
to ActivitySummaryText, while preserving valid AiChatToolReceiptSegment[]
values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant