chore(deps): migrate to AI SDK v7 + @ai-sdk/react v4 - #686
Conversation
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>
|
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 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. |
📝 WalkthroughWalkthroughUpdates AI SDK integration, renames turn metadata to ChangesAI runtime and instruction metadata
Continuation streaming
Structured tool receipts
Tooling and deployment configuration
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
React Doctor found no new issues. 🎉 Reviewed by React Doctor for commit |
There was a problem hiding this comment.
💡 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".
| "wrangler": "^4.107.0" | ||
| "vite-bundle-analyzer": "^1.3.9", | ||
| "vite-plus": "^0.2.6", | ||
| "vitest": "4.1.10", |
There was a problem hiding this comment.
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 👍 / 👎.
| "wrangler": "^4.107.0" | ||
| "vite-bundle-analyzer": "^1.3.9", | ||
| "vite-plus": "^0.2.6", | ||
| "vitest": "4.1.10", |
Greptile SummaryThis PR migrates the workspace AI stack to AI SDK v7 and React bindings v4.
Confidence Score: 4/5The 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
What T-Rex did
|
| 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. |
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>
There was a problem hiding this comment.
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 winOnly reuse the terminal assistant message.
findLastlets recovery continue append to any prior assistant message when the chat tail is a user/tool turn. SetappendToExistingAssistant/continuationAssistantonly whenthis.messages.at(-1)?.role === "assistant"so it matches the UI’s last-message contract inai-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 winKeep continuation state separate from append mode.
This patch still passes
continuation: trueto_streamResult, but the newStreamAccumulatorinitial state setscontinuationtooptions?.appendToExistingAssistant. For non-append continuations, that drops the marker on the completed response, so_shouldSettleRunAfterResponsesettles the run immediately. Passcontinuationfrom_streamResult/ctx.continuation, and useappendToExistingAssistantonly 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 winDead fallback branch:
record.inputTokens/record.outputTokensare never objects here.The first two fallback tiers correctly map to v7's
inputTokenDetails.cacheReadTokens/cacheWriteTokensandoutputTokenDetails.reasoningTokens, matching the SDK migration guide. The third tier, however, callsgetNestedTokenValue(record.inputTokens, "cacheRead")/getNestedTokenValue(record.outputTokens, "reasoning"), butrecord.inputTokens/record.outputTokensare the same plain-number fields already consumed a few lines above viagetTokenValue.getNestedTokenValuereturnsundefinedfor 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 winUndocumented double-cast for the
tool()config.The
as unknown as Tool<INPUT, OUTPUT, Record<string, unknown>>cast bypasses structural checking on the object passed totool(). The analogous cast inai-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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (18)
package.jsonpatches/@cloudflare__think@0.15.0.patchpnpm-workspace.yamlsrc/features/workspaces/ai/ai-inspector-view-model.tssrc/features/workspaces/ai/ai-inspector-view-types.tssrc/features/workspaces/ai/ai-thread-inspector-recorder.tssrc/features/workspaces/ai/ai-thread-orchestration.tssrc/features/workspaces/ai/ai-thread-runtime.tssrc/features/workspaces/ai/ai-thread-tcc-recorder.tssrc/features/workspaces/ai/ai-thread-telemetry-format.tssrc/features/workspaces/ai/ai-thread-telemetry-recorder.tssrc/features/workspaces/ai/ai-thread-tool.test.tssrc/features/workspaces/ai/ai-thread-tool.tssrc/features/workspaces/ai/ai-thread.tssrc/features/workspaces/components/ai-chat/AiChatInspectorConversation.tsxsrc/features/workspaces/components/ai-chat/AiChatInspectorViews.tsxsrc/features/workspaces/components/ai-chat/useWorkspaceAiChat.tswrangler.jsonc
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
| "wrangler": "^4.107.0" | ||
| "vite-bundle-analyzer": "^1.3.9", | ||
| "vite-plus": "^0.2.6", | ||
| "vitest": "4.1.10", |
There was a problem hiding this comment.
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>
Dismissed because Cubic found issues in a newer review.
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>
There was a problem hiding this comment.
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
| - "@cloudflare/shell@0.4.3" | ||
| - "@cloudflare/think@0.15.0" | ||
| - agents@0.19.0 | ||
| - "@voidzero-dev/vite-plus-core@0.2.6" |
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
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
📒 Files selected for processing (6)
src/features/workspaces/ai/ai-thread-runtime.tssrc/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsxsrc/features/workspaces/components/ai-chat/ai-chat-codemode-activity.tssrc/features/workspaces/components/ai-chat/ai-chat-display-state.test.tssrc/features/workspaces/components/ai-chat/ai-chat-display-state.tssrc/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[]; |
There was a problem hiding this comment.
🩺 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.
Summary
Upgrades
aifrom v6 to v7 and@ai-sdk/reactfrom 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@6is 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 declareai@^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 v4Version bump plus the type/API fallout:
ai-thread-tool.ts) — v7 added a required third genericCONTEXT extends ContexttoTool,tool(), andToolExecutionOptions.defineAIThreadToolnow:ToolasTool<any, any, any>at the outer boundary so the resulting object is assignable toToolSetentries (whose union types the type checker cannot narrow through ourINPUT/OUTPUTintersection). ConcreteINPUT/OUTPUTcontinue to live on the runtime metadata attached via the private symbol key.tool<INPUT, OUTPUT, Record<string, unknown>>andToolExecutionOptions<unknown>internally so the AI SDK signature is satisfied without forcing every tool call site to pass a real context.context: {}field ondirectOptionsto satisfyToolExecutionOptions<unknown>.ConnectorTool.descriptionnarrows to string only in the codemode adapter (v7 opened it up tostring | ((options) => string), but the connector protocol still expects a string).createSandboxToolscasts the spread override object back toToolSet[string]to work around the same tool-union widening.system→instructionsrename across the recorder + inspector chain (TurnConfig,TurnContext, recorders,AIInspectorRunView, and the local variable inAIThread.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.inputTokenDetails/outputTokenDetails.extractAiTelemetryTokenUsagenow 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 v7in 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-internalsystem→instructionsrename that a prior WIP had started but not completed.Commit 2 —
refactor(ai): adopt AI SDK v7 polishThree quality-of-life adoptions that were low-effort/high-signal:
Output.object.generateAIThreadTitleused to prompt the model with "return only the title, no quotes, no punctuation at the end" and hope for the best. Now usesOutput.object({ schema: z.object({ title }), name, description })and the shape is guaranteed. Also moves the system instruction to the newinstructionsfield.experimental_throttle→throttleinuseWorkspaceAiChat. The option graduated to stable on@ai-sdk/reactv4.request.bodyfallback comment inbuildAiTelemetryInputFromStep. Documents that AI SDK v7 stopped populatingrequest.bodyby default (include: { requestBody }is now off), but ourctx.messages/request.messagescode 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 readsresponse.body,requestBody, orrawChunks, so no regression elsewhere.What I explicitly did NOT do
NoOutputGeneratedErrorspecial-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 genericinstanceof Error+.messagewith no message pattern-matching that would break.strict: trueaudit — all 22 tools already have it. Nothing to add.toolsContext/refineToolInput— genuinely useful v7 features but@cloudflare/think@0.15doesn't forward them yet.grep -c "toolsContext|toolApproval|refineToolInput" think@0.15/dist/*.d.tsreturns 0. Migration would give us the type ergonomics without the runtime plumbing, which is worse than not migrating. Deferred until Think forwards these.Test plan
pnpm exec tsc --noEmit— cleanpnpm test— 158/158 passpnpm devlocally 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-endRisks
AIThreadTool<INPUT, OUTPUT>now presents asTool<any, any, any>to the SDK boundary. Concrete input/output types are still enforced at theexecutesite via the runtime metadata, so this is a boundary-cast, not a runtime loosening. If a downstream consumer relied on readingINPUT/OUTPUToff the raw tool type, they'd now seeany— none of ours do..titlestring comes from a schema-validated object instead of rawresult.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
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by CodeRabbit