Skip to content

Develop#108

Merged
ssdeanx merged 2 commits intomainfrom
develop
Feb 16, 2026
Merged

Develop#108
ssdeanx merged 2 commits intomainfrom
develop

Conversation

@ssdeanx
Copy link
Owner

@ssdeanx ssdeanx commented Feb 16, 2026

Summary by Sourcery

Improve chat attachments display and token usage tracking in the chat experience.

New Features:

  • Show selected input attachments as removable badges in the chat input.
  • Display user message file attachments as simple labeled chips in the message view.
  • Extract and surface source documents/URLs from message parts for use in the UI.

Bug Fixes:

  • Ensure file attachments passed from the chat input are strongly typed as File objects when sending messages.

Enhancements:

  • Normalize and expand token usage data handling, including input/output token detail fields, across chat context and header.
  • Simplify tool invocation detection in message items and adjust reasoning panel behavior.
  • Refine styling and status mapping for workflow and network step badges.

Build:

  • Upgrade @tanstack/eslint-config to version ^0.4.0.

- Changed component alias path from "@/src/components" to "@/app/components" in components.json.
- Updated package version from "1.1.3" to "1.2.0" in package-lock.json and package.json.
- Upgraded @tanstack/eslint-config dependency from "^0.3.4" to "^0.4.0" in package.json.
- Updated eslint version from "9.39.2" to "10.0.1" in package-lock.json.
- Adjusted peer dependencies for eslint in package-lock.json.
- Refactor AgentReasoning component to remove autoClose prop.
- Update ChatHeader to include detailed output and input token information.
- Add SelectedAttachments component in ChatInput for better file management.
- Improve ChatMessages by extracting source documents from message parts.
- Extend TokenUsage interface to include inputTokenDetails for better tracking.
- Modify ChatProvider to handle detailed token usage data including cache details.
Copilot AI review requested due to automatic review settings February 16, 2026 09:44
@vercel
Copy link

vercel bot commented Feb 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-stack Error Error Feb 16, 2026 9:48am

@sourcery-ai
Copy link

sourcery-ai bot commented Feb 16, 2026

Reviewer's Guide

Refines chat UI attachment handling and source rendering, improves token usage tracking, and updates tooling, while making minor code-style and type-safety tweaks across chat components and provider.

Sequence diagram for the updated chat message submission with attachments

sequenceDiagram
    actor User
    participant ChatInput
    participant SelectedAttachments
    participant AttachmentsStore as usePromptInputAttachments
    participant ChatContext as ChatContext_sendMessage

    User->>ChatInput: Type message text
    User->>ChatInput: Add file attachments
    ChatInput->>AttachmentsStore: addAttachment(file)
    AttachmentsStore-->>SelectedAttachments: attachments[]
    SelectedAttachments->>User: Show Badge per attachment

    User->>SelectedAttachments: Click remove on attachment
    SelectedAttachments->>AttachmentsStore: removeAttachment(index)
    AttachmentsStore-->>SelectedAttachments: updated attachments[]

    User->>ChatInput: Submit message
    ChatInput->>ChatInput: handleSubmit(message{text, files: File[]})
    ChatInput->>ChatContext: sendMessage(message.text, message.files)
    ChatContext-->>ChatInput: message queued/sent
    ChatInput-->>User: Clear input, keep updated attachment state
Loading

Updated class diagram for chat message props and source documents

classDiagram
    class ChatMessagesProps {
        +UIMessage[] messages
        +string status
        +Error error
        +onSuggestionClick(suggestion string) void
        +onCopyMessage(messageId string, content string) void
        +onRegenerate(messageId string) void
    }

    class UIMessage {
        +string id
        +string role
        +UIMessagePart[] parts
    }

    class UIMessagePart {
        +string type
    }

    class SourceUrlUIPart {
        +string type
        +string title
        +string url
    }

    class SourceDocumentUIPart {
        +string type
        +string title
        +string filename
        +string mediaType
    }

    class SourceDocument {
        +string title
        +string url
        +string description
        +string sourceDocument
    }

    class SourceExtractionHelpers {
        +isSourceUrlPart(part UIMessagePart) bool
        +isSourceDocumentPart(part UIMessagePart) bool
        +getSourcesFromParts(parts UIMessagePart[]) SourceDocument[]
    }

    ChatMessagesProps --> UIMessage : uses
    UIMessage --> UIMessagePart : has many
    SourceExtractionHelpers --> UIMessagePart : inspects
    SourceExtractionHelpers --> SourceUrlUIPart : type guard
    SourceExtractionHelpers --> SourceDocumentUIPart : type guard
    SourceExtractionHelpers --> SourceDocument : builds
Loading

Updated class diagram for token usage tracking in chat provider and header

classDiagram
    class TokenUsage {
        +any inputTokenDetails
        +number inputTokens
        +number outputTokens
        +number totalTokens
    }

    class ProviderUsageData {
        +number promptTokens
        +number inputTokens
        +number completionTokens
        +number outputTokens
        +number totalTokens
        +InputTokenDetails inputTokenDetails
        +OutputTokenDetails outputTokenDetails
    }

    class InputTokenDetails {
        +number cacheCreation
        +number cacheRead
    }

    class OutputTokenDetails {
        +number reasoning
    }

    class ChatProvider {
        +mapFinishPartToTokenUsage(partAny any) TokenUsage
    }

    class HeaderUsageViewModel {
        +number inputTokens
        +number outputTokens
        +number totalTokens
        +HeaderInputTokenDetails inputTokenDetails
        +HeaderOutputTokenDetails outputTokenDetails
    }

    class HeaderInputTokenDetails {
        +number cacheReadTokens
        +number cacheWriteTokens
        +number noCacheTokens
    }

    class HeaderOutputTokenDetails {
        +number textTokens
        +number reasoningTokens
    }

    class ChatHeader {
        +buildHeaderUsage(usage TokenUsage) HeaderUsageViewModel
    }

    ChatProvider --> ProviderUsageData : reads from finish part
    ProviderUsageData --> InputTokenDetails : has
    ProviderUsageData --> OutputTokenDetails : has
    ChatProvider --> TokenUsage : creates
    ChatHeader --> TokenUsage : consumes
    ChatHeader --> HeaderUsageViewModel : creates
    HeaderUsageViewModel --> HeaderInputTokenDetails : has
    HeaderUsageViewModel --> HeaderOutputTokenDetails : has
Loading

File-Level Changes

Change Details Files
Add helpers to extract and render source documents/URLs from message parts and simplify tool part detection.
  • Introduce ChatMessagesProps and SourceDocument types for clearer props and source modeling.
  • Add type guards for source-url and source-document parts and a getSourcesFromParts helper to normalize sources.
  • Change tool detection in MessageItem to only use isToolUIPart when collecting ToolInvocationState instances.
app/chat/components/chat-messages.tsx
Revamp attachment UX in chat input and user message display to use badge-style chip rendering instead of custom attachment components.
  • Add SelectedAttachments component using usePromptInputAttachments to list/removable file badges in the input header.
  • Replace PromptInputAttachments/PromptInputAttachment usage with SelectedAttachments when files are supported.
  • Render user file parts inline as simple bordered chips showing filename/mediaType in MessageItem.
app/chat/components/chat-input.tsx
app/chat/components/chat-messages.tsx
Adjust speech-to-text trigger to be a generic prompt input button while preserving visual feedback on speaking state.
  • Replace PromptInputSpeechButton with PromptInputButton wired to toggling isSpeaking.
  • Preserve magnetic/ambient visual state classes and add a title attribute for accessibility/tooltip context.
app/chat/components/chat-input.tsx
Enrich token usage typing and propagation to support detailed input/output token breakdowns, including cache and reasoning tokens.
  • Extend TokenUsage to include inputTokenDetails and propagate richer usage data from streamed parts in ChatProvider, including cacheCreation/cacheRead and reasoning details with sensible fallbacks.
  • Map internal usage shape into the header’s TokenUsage consumer, normalizing outputTokenDetails to textTokens/reasoningTokens and inputTokenDetails to cacheReadTokens/cacheWriteTokens/noCacheTokens.
app/chat/providers/chat-context.tsx
app/chat/components/chat-header.tsx
app/chat/providers/chat-context-types.ts
Minor UI/formatting and tooling updates for consistency and linting.
  • Normalize ternary indentation in workflow and network data sections for readability.
  • Remove unused autoClose prop from AgentReasoning usage.
  • Bump @tanstack/eslint-config from 0.3.4 to 0.4.0 and update generated lockfile/components metadata.
app/chat/components/chat-messages.tsx
app/chat/components/agent-reasoning.tsx
app/chat/providers/chat-context.tsx
app/chat/components/chat-header.tsx
package.json
package-lock.json
components.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions
Copy link

🤖 Hi @ssdeanx, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ssdeanx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces significant improvements to the chat interface's data handling and presentation. It provides users with more detailed insights into token consumption, streamlines the management and display of file attachments, and updates the underlying AI SDK integrations for enhanced message processing capabilities. The changes aim to create a more informative and user-friendly chat experience.

Highlights

  • Token Usage Reporting: Enhanced the ContextTrigger in the chat header to display a more granular breakdown of token usage, including outputTokenDetails (text and reasoning tokens) and inputTokenDetails (cache read/write, no-cache tokens).
  • File Attachment Handling Refactor: The chat input component (chat-input.tsx) was refactored to use a new usePromptInputAttachments hook and a SelectedAttachments component for displaying and managing attached files, replacing the previous PromptInputAttachments and PromptInputAttachment components. User file attachments in chat-messages.tsx are now displayed with a custom div structure instead of dedicated components.
  • AI SDK Type and Utility Integration: Numerous types and utility functions from the AI SDK were imported and integrated across chat-messages.tsx and chat-context.tsx to support more robust message processing, including new interfaces for source documents and updated type guards.
  • Dependency and Configuration Updates: The @tanstack/eslint-config dependency was upgraded in package.json, and the components alias path was updated in components.json.
Changelog
  • app/chat/components/agent-reasoning.tsx
    • Removed autoClose prop from the Reasoning component.
  • app/chat/components/chat-header.tsx
    • Added outputTokenDetails and inputTokenDetails to the ContextTrigger for detailed token usage.
  • app/chat/components/chat-input.tsx
    • Removed PromptInputAttachments and PromptInputAttachment imports.
    • Imported usePromptInputAttachments hook and XIcon.
    • Introduced SelectedAttachments component for displaying and removing attached files.
    • Updated handleSubmit to explicitly type files as File[].
    • Replaced PromptInputSpeechButton with a generic PromptInputButton for speech-to-text functionality.
  • app/chat/components/chat-messages.tsx
    • Removed MessageAttachment and MessageAttachments imports.
    • Imported various AI SDK types and utility functions for message processing.
    • Defined ChatMessagesProps, SourceDocument interfaces, and helper functions (isSourceUrlPart, isSourceDocumentPart, getSourcesFromParts) for source document handling.
    • Updated conditional rendering logic for workflow and network data sections.
    • Changed isToolOrDynamicToolUIPart to isToolUIPart for tool invocation state.
    • Refactored user file attachment display to use a custom div structure with Badge-like styling.
  • app/chat/providers/chat-context-types.ts
    • Added inputTokenDetails: any to the TokenUsage interface.
  • app/chat/providers/chat-context.tsx
    • Imported numerous AI SDK types and utility functions.
    • Added UIDataTypes from 'ai'; import.
    • Expanded the usageData type definition to include inputTokenDetails (cache creation/read) and outputTokenDetails (reasoning) for more detailed token usage tracking.
  • components.json
    • Updated the components alias path from @/src/components to @/app/components.
  • package.json
    • Upgraded @tanstack/eslint-config dependency from ^0.3.4 to ^0.4.0.
Activity
  • The pull request is titled "Develop" and lacks a detailed description, suggesting it encompasses a broad set of changes or a merge from a development branch.
  • Multiple files across UI components and data providers have been modified, indicating a focused effort on refining the chat interface and its underlying data structures.
  • Updates to package dependencies and configuration files reflect ongoing maintenance and alignment with project standards.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

Summary by CodeRabbit

  • New Features

    • Detailed token usage metrics now available, including input and output breakdowns
  • Improvements

    • File attachment interface redesigned for enhanced usability
    • Source document detection and display improved
    • Speech-to-text control refined

Walkthrough

This PR refactors chat UI components and enhances token usage tracking. Changes include removing an explicit autoClose prop, replacing file-upload UI with a new SelectedAttachments component, adding detailed token usage breakdown with cache and reasoning token information, expanding type definitions for source handling, and updating component configuration paths.

Changes

Cohort / File(s) Summary
Chat UI Components
app/chat/components/agent-reasoning.tsx, app/chat/components/chat-input.tsx, app/chat/components/chat-messages.tsx
Removed autoClose prop from Reasoning component; replaced file-upload UI blocks with new SelectedAttachments component including remove action; updated speech-to-text control to generic button; modified submit handling to accept new attachment structure; replaced MessageAttachment/MessageAttachments with div-based layout; added source-part handling and new type predicates.
Chat Context & Token Tracking
app/chat/components/chat-header.tsx, app/chat/providers/chat-context-types.ts, app/chat/providers/chat-context.tsx
Added detailed token usage breakdown with inputTokenDetails (cache read/write tokens) and outputTokenDetails (reasoning tokens); expanded TokenUsage interface; augmented imports for UI/data-part typings; widened usageData type to accommodate richer token structure with cache and reasoning metadata.
Configuration
components.json, package.json
Updated component alias resolution from "@/src/components" to "@/app/components"; bumped @tanstack/eslint-config devDependency from ^0.3.4 to ^0.4.0.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 Files refactored, tokens now bright,
Attachments removed, the UI feels light,
New SelectedComponents hop into view,
Cache reads and reasoning tracked anew,
The chat flows cleaner, hooray!

🚥 Pre-merge checks | ✅ 1 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (9 files):

⚔️ app/chat/components/agent-reasoning.tsx (content)
⚔️ app/chat/components/chat-header.tsx (content)
⚔️ app/chat/components/chat-input.tsx (content)
⚔️ app/chat/components/chat-messages.tsx (content)
⚔️ app/chat/providers/chat-context-types.ts (content)
⚔️ app/chat/providers/chat-context.tsx (content)
⚔️ components.json (content)
⚔️ package-lock.json (content)
⚔️ package.json (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
Title check ❓ Inconclusive The title 'Develop' is vague and does not clearly convey the specific changes in the pull request, which include chat attachments display improvements and token usage tracking enhancements. Use a more descriptive title that summarizes the main changes, such as 'Improve chat attachments display and token usage tracking' or 'Add removable attachment badges and enhance token details'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is well-organized and directly relates to the changeset, detailing new features for attachment display, bug fixes for file typing, and enhancements to token usage tracking across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch develop
  • Create stacked PR with resolved conflicts
  • Post resolved changes as copyable diffs in a comment

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 and usage tips.

@github-actions
Copy link

🤖 I'm sorry @ssdeanx, but I was unable to process your request. Please see the logs for more details.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • In chat-context.tsx you import UIDataTypes from ai but never use it; consider removing this import to avoid dead code.
  • The change from isToolOrDynamicToolUIPart to isToolUIPart in MessageItem narrows which parts are treated as tools; please double-check that dynamic tool parts are still handled where needed or explicitly document/handle their exclusion.
  • The token usage types and shapes are inconsistent: TokenUsage.outputTokens is typed as a number but chat-header.tsx treats it as possibly an object, and inputTokenDetails is typed as any with several unsafe casts—consider tightening these types (e.g., with a dedicated interface) so the header and provider agree on a single, strongly-typed structure.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `chat-context.tsx` you import `UIDataTypes` from `ai` but never use it; consider removing this import to avoid dead code.
- The change from `isToolOrDynamicToolUIPart` to `isToolUIPart` in `MessageItem` narrows which parts are treated as tools; please double-check that dynamic tool parts are still handled where needed or explicitly document/handle their exclusion.
- The token usage types and shapes are inconsistent: `TokenUsage.outputTokens` is typed as a `number` but `chat-header.tsx` treats it as possibly an object, and `inputTokenDetails` is typed as `any` with several unsafe casts—consider tightening these types (e.g., with a dedicated interface) so the header and provider agree on a single, strongly-typed structure.

## Individual Comments

### Comment 1
<location> `app/chat/components/chat-input.tsx:201-210` </location>
<code_context>
-                            <PromptInputSpeechButton
-                                onTranscriptionChange={setInput}
-                                textareaRef={textareaRef}
+                            <PromptInputButton
                                 className={cn(
                                     'magnetic transition-all duration-300',
                                     isSpeaking &&
                                         'text-primary glow-primary animate-ambient-pulse scale-110'
                                 )}
                                 onClick={() => setIsSpeaking(!isSpeaking)}
+                                title="Speech to text"
                             >
                                 <MicIcon className="size-4" />
-                            </PromptInputSpeechButton>
+                            </PromptInputButton>

                             <PromptInputButton
</code_context>

<issue_to_address>
**issue (bug_risk):** Replacing `PromptInputSpeechButton` with a generic button drops the transcription behavior.

The previous `PromptInputSpeechButton` handled speech-to-text via `onTranscriptionChange={setInput}` and `textareaRef={textareaRef}`. The new `PromptInputButton` only toggles `isSpeaking` and doesn’t connect transcription to the input, so recording may appear to work but never update the text area. If speech is still required, you’ll need to either keep `PromptInputSpeechButton` or reintroduce the transcription wiring here.
</issue_to_address>

### Comment 2
<location> `app/chat/providers/chat-context.tsx:657-661` </location>
<code_context>
+                                        textTokens: (usage.outputTokens as unknown as { textTokens?: number }).textTokens ?? 0,
+                                        reasoningTokens: (usage.outputTokens as unknown as { reasoningTokens?: number }).reasoningTokens ?? 0,
+                                    },
+                            inputTokenDetails: {
+                                cacheReadTokens: (usage.inputTokenDetails as unknown as { cacheReadTokens?: number }).cacheReadTokens ?? 0,
+                                cacheWriteTokens: (usage.inputTokenDetails as unknown as { cacheWriteTokens?: number }).cacheWriteTokens ?? 0,
</code_context>

<issue_to_address>
**issue (bug_risk):** Token detail field names differ from what `ChatHeader` expects, so cache-related metrics will always be zero.

Here you normalize `inputTokenDetails` as `{ cacheReadTokens, cacheWriteTokens }`, but `chat-header.tsx` expects `{ cacheReadTokens, cacheWriteTokens, noCacheTokens }` on `usage.inputTokenDetails`. Because the shapes differ, the header will always read `0` for these metrics. Please align the normalized structure with what the UI reads (or update the UI) so cache/RC metrics are correctly surfaced.
</issue_to_address>

### Comment 3
<location> `app/chat/components/chat-header.tsx:202-208` </location>
<code_context>
                             inputTokens: usage.inputTokens,
                             outputTokens: usage.outputTokens,
                             totalTokens: usage.totalTokens,
+                            outputTokenDetails:
+                                typeof usage.outputTokens === 'number'
+                                    ? { textTokens: usage.outputTokens, reasoningTokens: 0 }
+                                    : {
+                                        textTokens: (usage.outputTokens as unknown as { textTokens?: number }).textTokens ?? 0,
+                                        reasoningTokens: (usage.outputTokens as unknown as { reasoningTokens?: number }).reasoningTokens ?? 0,
+                                    },
+                            inputTokenDetails: {
</code_context>

<issue_to_address>
**suggestion:** The `outputTokenDetails` logic assumes `usage.outputTokens` can be a structured object, which conflicts with the current `TokenUsage` shape.

Given `TokenUsage` defines `outputTokens` as a number (and providers normalize to that), only the numeric branch will ever run and the `else` branch with the `unknown` casts is dead code. If you need to support structured token details, consider making `TokenUsage` a discriminated union and normalizing it in the provider instead of relying on `unknown` casts here.

```suggestion
                            outputTokenDetails: {
                                textTokens: usage.outputTokens,
                                reasoningTokens: 0,
                            },
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +201 to 210
<PromptInputButton
className={cn(
'magnetic transition-all duration-300',
isSpeaking &&
'text-primary glow-primary animate-ambient-pulse scale-110'
)}
onClick={() => setIsSpeaking(!isSpeaking)}
title="Speech to text"
>
<MicIcon className="size-4" />
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): Replacing PromptInputSpeechButton with a generic button drops the transcription behavior.

The previous PromptInputSpeechButton handled speech-to-text via onTranscriptionChange={setInput} and textareaRef={textareaRef}. The new PromptInputButton only toggles isSpeaking and doesn’t connect transcription to the input, so recording may appear to work but never update the text area. If speech is still required, you’ll need to either keep PromptInputSpeechButton or reintroduce the transcription wiring here.

Comment on lines +657 to +661
inputTokenDetails: {
cacheCreation:
usageData.inputTokenDetails
?.cacheCreation ?? 0,
cacheRead:
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): Token detail field names differ from what ChatHeader expects, so cache-related metrics will always be zero.

Here you normalize inputTokenDetails as { cacheReadTokens, cacheWriteTokens }, but chat-header.tsx expects { cacheReadTokens, cacheWriteTokens, noCacheTokens } on usage.inputTokenDetails. Because the shapes differ, the header will always read 0 for these metrics. Please align the normalized structure with what the UI reads (or update the UI) so cache/RC metrics are correctly surfaced.

Comment on lines +202 to +208
outputTokenDetails:
typeof usage.outputTokens === 'number'
? { textTokens: usage.outputTokens, reasoningTokens: 0 }
: {
textTokens: (usage.outputTokens as unknown as { textTokens?: number }).textTokens ?? 0,
reasoningTokens: (usage.outputTokens as unknown as { reasoningTokens?: number }).reasoningTokens ?? 0,
},
Copy link

Choose a reason for hiding this comment

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

suggestion: The outputTokenDetails logic assumes usage.outputTokens can be a structured object, which conflicts with the current TokenUsage shape.

Given TokenUsage defines outputTokens as a number (and providers normalize to that), only the numeric branch will ever run and the else branch with the unknown casts is dead code. If you need to support structured token details, consider making TokenUsage a discriminated union and normalizing it in the provider instead of relying on unknown casts here.

Suggested change
outputTokenDetails:
typeof usage.outputTokens === 'number'
? { textTokens: usage.outputTokens, reasoningTokens: 0 }
: {
textTokens: (usage.outputTokens as unknown as { textTokens?: number }).textTokens ?? 0,
reasoningTokens: (usage.outputTokens as unknown as { reasoningTokens?: number }).reasoningTokens ?? 0,
},
outputTokenDetails: {
textTokens: usage.outputTokens,
reasoningTokens: 0,
},

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces several valuable UI enhancements for chat attachments and expands token usage tracking. The code is generally moving in a good direction. However, I've identified a few critical issues that need attention. There are significant type-safety problems related to token usage data, which lead to bugs in token calculation and display. Specifically, the use of any and inconsistent property names between data producers and consumers is problematic. Additionally, there appears to be a feature regression where the speech-to-text functionality has been inadvertently removed. I've also noted a minor issue with an unused import. Addressing these points will greatly improve the robustness and correctness of the implementation.

Comment on lines +201 to +211
<PromptInputButton
className={cn(
'magnetic transition-all duration-300',
isSpeaking &&
'text-primary glow-primary animate-ambient-pulse scale-110'
)}
onClick={() => setIsSpeaking(!isSpeaking)}
title="Speech to text"
>
<MicIcon className="size-4" />
</PromptInputSpeechButton>
</PromptInputButton>
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

The replacement of PromptInputSpeechButton with a standard PromptInputButton seems to have caused a feature regression. The new button's onClick handler only toggles the isSpeaking state and no longer appears to handle the actual speech-to-text transcription, as props like onTranscriptionChange and textareaRef have been removed. If speech-to-text is an intended feature, this change has likely broken it. If the feature is being deprecated, it would be clearer to remove the microphone button entirely.

}

export interface TokenUsage {
inputTokenDetails: any
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

Using any for inputTokenDetails compromises type safety and has led to a bug due to inconsistent property names between where the data is produced and consumed.

  • Producer (chat-context.tsx) provides: { cacheCreation: number; cacheRead: number; }
  • Consumer (chat-header.tsx) expects: { cacheReadTokens: number; cacheWriteTokens: number; noCacheTokens: number; }

This discrepancy will cause incorrect token calculations in the UI. Please define a specific type for inputTokenDetails to enforce consistency and catch such errors at compile time. For example:

interface InputTokenDetails {
  cacheReadTokens: number;
  cacheWriteTokens: number;
  noCacheTokens: number;
}

You would then need to update chat-context.tsx to provide properties with these names. Additionally, outputTokenDetails should be added to the TokenUsage interface to improve type safety elsewhere.

Comment on lines +202 to +208
outputTokenDetails:
typeof usage.outputTokens === 'number'
? { textTokens: usage.outputTokens, reasoningTokens: 0 }
: {
textTokens: (usage.outputTokens as unknown as { textTokens?: number }).textTokens ?? 0,
reasoningTokens: (usage.outputTokens as unknown as { reasoningTokens?: number }).reasoningTokens ?? 0,
},
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The logic here checks if usage.outputTokens is a number, implying it could be an object. However, the TokenUsage type in app/chat/providers/chat-context-types.ts defines outputTokens as strictly a number. This makes the else branch of this ternary operator unreachable and the type casting with as unknown as unsafe.

If usage.outputTokens can indeed be an object, please update the TokenUsage interface to reflect this (e.g., outputTokens: number | { textTokens?: number; reasoningTokens?: number }) to ensure type safety and clarity.

ChatContextValue,
} from './chat-context-types'
import { ChatContext } from './chat-context-hooks'
import UIDataTypes from 'ai';
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This import is unused and can be removed to keep the code clean.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request represents a minor version bump from 1.1.3 to 1.2.0, primarily focused on dependency updates and enhancements to token usage tracking in the chat interface. The PR updates the ESLint configuration and related tooling dependencies while improving the granularity of token usage reporting to track cache hits and reasoning tokens separately.

Changes:

  • Updated @tanstack/eslint-config from 0.3.4 to 0.4.0 with transitive dependency updates (ESLint 10.x, globals 17.x, TypeScript-ESLint 8.55)
  • Enhanced token usage tracking to include detailed breakdowns for input/output token details (cache creation/read, reasoning tokens)
  • Replaced attachment display components with custom inline implementations in chat UI
  • Changed components path alias from @/src/components to @/app/components

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
package.json Version bump to 1.2.0 and ESLint config upgrade to 0.4.0
package-lock.json Lockfile updates for dependency upgrades including ESLint 10.x, TypeScript-ESLint 8.55, and related tooling
components.json Updated components alias to point to app/components directory
app/chat/providers/chat-context.tsx Added extensive imports from 'ai' package and enhanced token usage tracking with detailed cache/reasoning breakdown
app/chat/providers/chat-context-types.ts Extended TokenUsage interface with inputTokenDetails field
app/chat/components/chat-messages.tsx Added numerous type imports, replaced MessageAttachment components with inline implementation, added type guards for source handling
app/chat/components/chat-input.tsx Replaced PromptInputAttachments/PromptInputSpeechButton with custom SelectedAttachments component and standard button
app/chat/components/chat-header.tsx Enhanced token usage display with detailed cache and reasoning token information
app/chat/components/agent-reasoning.tsx Removed autoClose prop from Reasoning component
Comments suppressed due to low confidence (1)

app/chat/providers/chat-context-types.ts:36

  • The TokenUsage interface now includes inputTokenDetails but is missing outputTokenDetails which is being used in chat-context.tsx (lines 665-669) and chat-header.tsx (lines 202-208). The interface should include both token detail fields for type consistency.
export interface TokenUsage {
    inputTokenDetails: any
    inputTokens: number
    outputTokens: number
    totalTokens: number
}

ChatContextValue,
} from './chat-context-types'
import { ChatContext } from './chat-context-hooks'
import UIDataTypes from 'ai';
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The import statement 'import UIDataTypes from 'ai';' appears to be unused. This import should be removed to keep the codebase clean and avoid confusion.

Suggested change
import UIDataTypes from 'ai';

Copilot uses AI. Check for mistakes.
Comment on lines 31 to +32
export interface TokenUsage {
inputTokenDetails: any
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The inputTokenDetails field is typed as 'any', which bypasses TypeScript's type safety. Based on the usage in chat-context.tsx (lines 657-664), this should be properly typed as an interface with cacheCreation and cacheRead properties. Consider defining a proper interface for token details.

Suggested change
export interface TokenUsage {
inputTokenDetails: any
export interface TokenDetails {
cacheCreation?: number
cacheRead?: number
}
export interface TokenUsage {
inputTokenDetails: TokenDetails

Copilot uses AI. Check for mistakes.
} from 'lucide-react'
import { useState, useCallback, useMemo, Fragment } from 'react'
import type { UIMessage, FileUIPart } from 'ai'
import { useState, useCallback, useMemo, Fragment, memo } from 'react'
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

There's a trailing space after 'memo' in the import statement. This should be removed for code cleanliness and consistency with the project's Prettier configuration.

Suggested change
import { useState, useCallback, useMemo, Fragment, memo } from 'react'
import { useState, useCallback, useMemo, Fragment, memo } from 'react'

Copilot uses AI. Check for mistakes.
} from 'lucide-react'
import { useState, useCallback, useMemo, Fragment } from 'react'
import type { UIMessage, FileUIPart } from 'ai'
import { useState, useCallback, useMemo, Fragment, memo } from 'react'
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The 'memo' import from React is not used anywhere in this file. It should be removed to keep the imports clean and reduce bundle size.

Suggested change
import { useState, useCallback, useMemo, Fragment, memo } from 'react'
import { useState, useCallback, useMemo, Fragment } from 'react'

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +64
TextStreamPart,
TextPart,
ToolResultPart,
ReasoningOutput,
UIDataPartSchemas,
UIMessageChunk,
UIMessagePart,
DataContent,
FinishReason,
FileUIPart,
Tool,
DataUIPart,
SourceDocumentUIPart,
SourceUrlUIPart,
StepResult,
PrepareStepResult,
StepStartUIPart,
InferSchema,
InferUIDataParts,
InferAgentUIMessage,
InferToolInput,
InferUIMessageChunk,
InferToolOutput,
InferUITool,
InferUITools,
InferGenerateOutput,
InferStreamOutput,
} from 'ai'
import {
getToolName,
isDataUIPart,
isFileUIPart,
isReasoningUIPart,
isTextUIPart,
isToolUIPart,
isStaticToolUIPart,
isDeepEqualData,
InvalidResponseDataError,
InvalidMessageRoleError,
InvalidArgumentError,
getStaticToolName,
getTextFromDataUrl,
safeValidateUIMessages,
generateId
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

Many of the newly added type imports from 'ai' appear to be unused in this file (TextStreamPart, TextPart, ToolResultPart, ReasoningOutput, UIDataPartSchemas, UIMessageChunk, UIMessagePart, DataContent, FinishReason, Tool, StepResult, PrepareStepResult, StepStartUIPart, InferSchema, InferUIDataParts, InferAgentUIMessage, InferToolInput, InferUIMessageChunk, InferToolOutput, InferUITool, InferUITools, InferGenerateOutput, InferStreamOutput). Additionally, many of the utility functions imported seem unused (getToolName, isStaticToolUIPart, isDeepEqualData, InvalidResponseDataError, InvalidMessageRoleError, InvalidArgumentError, getStaticToolName, getTextFromDataUrl, safeValidateUIMessages, generateId). These should be removed to keep the imports clean and maintainable.

Suggested change
TextStreamPart,
TextPart,
ToolResultPart,
ReasoningOutput,
UIDataPartSchemas,
UIMessageChunk,
UIMessagePart,
DataContent,
FinishReason,
FileUIPart,
Tool,
DataUIPart,
SourceDocumentUIPart,
SourceUrlUIPart,
StepResult,
PrepareStepResult,
StepStartUIPart,
InferSchema,
InferUIDataParts,
InferAgentUIMessage,
InferToolInput,
InferUIMessageChunk,
InferToolOutput,
InferUITool,
InferUITools,
InferGenerateOutput,
InferStreamOutput,
} from 'ai'
import {
getToolName,
isDataUIPart,
isFileUIPart,
isReasoningUIPart,
isTextUIPart,
isToolUIPart,
isStaticToolUIPart,
isDeepEqualData,
InvalidResponseDataError,
InvalidMessageRoleError,
InvalidArgumentError,
getStaticToolName,
getTextFromDataUrl,
safeValidateUIMessages,
generateId
FileUIPart,
DataUIPart,
SourceDocumentUIPart,
SourceUrlUIPart,
} from 'ai'
import {
isDataUIPart,
isFileUIPart,
isReasoningUIPart,
isTextUIPart,
isToolUIPart,

Copilot uses AI. Check for mistakes.
"registries": {
"@ai-elements": "https://registry.ai-sdk.dev/{name}.json"
}
"@ai-elements": "https://registry.ai-sdk.dev/{name}.json"}
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The registries field formatting has been changed to a single line, which is inconsistent with the formatting style used in the rest of the file. For consistency and readability, this should match the multi-line formatting pattern used elsewhere in this configuration file.

Suggested change
"@ai-elements": "https://registry.ai-sdk.dev/{name}.json"}
"@ai-elements": "https://registry.ai-sdk.dev/{name}.json"
}

Copilot uses AI. Check for mistakes.
Comment on lines +206 to +213
textTokens: (usage.outputTokens as unknown as { textTokens?: number }).textTokens ?? 0,
reasoningTokens: (usage.outputTokens as unknown as { reasoningTokens?: number }).reasoningTokens ?? 0,
},
inputTokenDetails: {
cacheReadTokens: (usage.inputTokenDetails as unknown as { cacheReadTokens?: number }).cacheReadTokens ?? 0,
cacheWriteTokens: (usage.inputTokenDetails as unknown as { cacheWriteTokens?: number }).cacheWriteTokens ?? 0,
noCacheTokens: (usage.inputTokenDetails as unknown as { noCacheTokens?: number }).noCacheTokens ?? 0,
}
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The complex type assertions using 'as unknown as' pattern to access nested properties from usage.outputTokens and usage.inputTokenDetails indicate a type mismatch. This is happening because the TokenUsage interface in chat-context-types.ts doesn't properly define these structures. Instead of using unsafe type assertions, the TokenUsage interface should be updated to properly type outputTokenDetails and inputTokenDetails fields.

Suggested change
textTokens: (usage.outputTokens as unknown as { textTokens?: number }).textTokens ?? 0,
reasoningTokens: (usage.outputTokens as unknown as { reasoningTokens?: number }).reasoningTokens ?? 0,
},
inputTokenDetails: {
cacheReadTokens: (usage.inputTokenDetails as unknown as { cacheReadTokens?: number }).cacheReadTokens ?? 0,
cacheWriteTokens: (usage.inputTokenDetails as unknown as { cacheWriteTokens?: number }).cacheWriteTokens ?? 0,
noCacheTokens: (usage.inputTokenDetails as unknown as { noCacheTokens?: number }).noCacheTokens ?? 0,
}
textTokens: (usage.outputTokens as any)?.textTokens ?? 0,
reasoningTokens: (usage.outputTokens as any)?.reasoningTokens ?? 0,
},
inputTokenDetails: {
cacheReadTokens: (usage.inputTokenDetails as any)?.cacheReadTokens ?? 0,
cacheWriteTokens: (usage.inputTokenDetails as any)?.cacheWriteTokens ?? 0,
noCacheTokens: (usage.inputTokenDetails as any)?.noCacheTokens ?? 0,
},

Copilot uses AI. Check for mistakes.
Comment on lines +62 to +88
TextStreamPart,
TextPart,
ToolResultPart,
ReasoningOutput,
UIDataPartSchemas,
UIMessageChunk,
UIMessagePart,
DataContent,
FinishReason,
FileUIPart,
Tool,
DataUIPart,
SourceDocumentUIPart,
SourceUrlUIPart,
StepResult,
PrepareStepResult,
StepStartUIPart,
InferSchema,
InferUIDataParts,
InferAgentUIMessage,
InferToolInput,
InferUIMessageChunk,
InferToolOutput,
InferUITool,
InferUITools,
InferGenerateOutput,
InferStreamOutput,
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

Many of the newly added type imports from 'ai' appear to be unused in this file (InferSchema, InferUIDataParts, InferAgentUIMessage, InferToolInput, InferUIMessageChunk, InferToolOutput, InferUITool, InferUITools, InferGenerateOutput, InferStreamOutput, TextStreamPart, TextPart, ToolResultPart, ReasoningOutput, UIDataPartSchemas, UIMessageChunk, UIMessagePart, DataContent, FinishReason, Tool, StepResult, PrepareStepResult, StepStartUIPart). These should be removed to keep the imports clean and maintainable.

Suggested change
TextStreamPart,
TextPart,
ToolResultPart,
ReasoningOutput,
UIDataPartSchemas,
UIMessageChunk,
UIMessagePart,
DataContent,
FinishReason,
FileUIPart,
Tool,
DataUIPart,
SourceDocumentUIPart,
SourceUrlUIPart,
StepResult,
PrepareStepResult,
StepStartUIPart,
InferSchema,
InferUIDataParts,
InferAgentUIMessage,
InferToolInput,
InferUIMessageChunk,
InferToolOutput,
InferUITool,
InferUITools,
InferGenerateOutput,
InferStreamOutput,
FileUIPart,
DataUIPart,
SourceDocumentUIPart,
SourceUrlUIPart,

Copilot uses AI. Check for mistakes.
function SelectedAttachments() {
const { attachments, removeAttachment } = usePromptInputAttachments()

if (attachments.length === 0) {return null}
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The formatting here is inconsistent with standard JavaScript/TypeScript practices. The return statement should have a space between 'return' and the value, and the closing brace should be on its own line. This line should be formatted as 'if (attachments.length === 0) return null' or split across multiple lines with proper indentation.

Suggested change
if (attachments.length === 0) {return null}
if (attachments.length === 0) return null

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

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

⚠️ Outside diff range comments (4)
app/chat/providers/chat-context-types.ts (1)

31-36: ⚠️ Potential issue | 🟠 Major

inputTokenDetails: any defeats type safety, and outputTokenDetails is missing from the interface.

The TokenUsage interface is out of sync with the actual data shape constructed in chat-context.tsx (lines 657-669) and consumed in chat-header.tsx (lines 202-213). The provider builds objects with inputTokenDetails: { cacheCreation, cacheRead } and outputTokenDetails: { reasoning }, but this interface only declares inputTokenDetails: any and omits outputTokenDetails entirely.

This causes:

  1. No type checking on inputTokenDetails consumers — they resort to as unknown as ... casts (see chat-header.tsx lines 210-212).
  2. outputTokenDetails access is untyped and invisible to the compiler.
🔧 Proposed fix — add proper types for both fields
 export interface TokenUsage {
-    inputTokenDetails: any
+    inputTokenDetails: {
+        cacheCreation: number
+        cacheRead: number
+    }
+    outputTokenDetails: {
+        reasoning: number
+    }
     inputTokens: number
     outputTokens: number
     totalTokens: number
 }

As per coding guidelines, "Avoid explicit any types (@typescript-eslint/no-explicit-any: 'warn') — use proper types or unknown instead".

app/chat/providers/chat-context.tsx (1)

631-670: ⚠️ Potential issue | 🟠 Major

Usage data expansion looks correct, but the returned object includes outputTokenDetails which is not declared in the TokenUsage interface.

The fallback logic (defaulting to 0) is sound. However, the outputTokenDetails field at lines 665-669 is not part of the TokenUsage interface in chat-context-types.ts, which will cause a TypeScript excess-property error in strict mode, or silently drop the field at the type level. This is the downstream effect of the incomplete interface definition flagged in chat-context-types.ts.

app/chat/components/chat-header.tsx (1)

198-214: ⚠️ Potential issue | 🔴 Critical

Critical data shape mismatch — token detail property names don't match the producer.

The usage object from ChatContext (produced in chat-context.tsx) provides:

  • inputTokenDetails: { cacheCreation, cacheRead }
  • outputTokenDetails: { reasoning }

But this code attempts to access entirely different property names:

  • cacheReadTokens, cacheWriteTokens, noCacheTokens (lines 210–212)
  • textTokens, reasoningTokens (lines 204–208)

These properties don't exist, so every ?? 0 fallback triggers, reporting all token details as 0 to the Context component.

Additionally, the typeof usage.outputTokens === 'number' check on line 203 is always true (the type is number), making the else branch dead code.

🐛 Proposed fix — align property names with the producer
                         usage={{
                             inputTokens: usage.inputTokens,
                             outputTokens: usage.outputTokens,
                             totalTokens: usage.totalTokens,
-                            outputTokenDetails:
-                                typeof usage.outputTokens === 'number'
-                                    ? { textTokens: usage.outputTokens, reasoningTokens: 0 }
-                                    : {
-                                        textTokens: (usage.outputTokens as unknown as { textTokens?: number }).textTokens ?? 0,
-                                        reasoningTokens: (usage.outputTokens as unknown as { reasoningTokens?: number }).reasoningTokens ?? 0,
-                                    },
-                            inputTokenDetails: {
-                                cacheReadTokens: (usage.inputTokenDetails as unknown as { cacheReadTokens?: number }).cacheReadTokens ?? 0,
-                                cacheWriteTokens: (usage.inputTokenDetails as unknown as { cacheWriteTokens?: number }).cacheWriteTokens ?? 0,
-                                noCacheTokens: (usage.inputTokenDetails as unknown as { noCacheTokens?: number }).noCacheTokens ?? 0,
-                            }
+                            reasoningTokens: usage.outputTokenDetails?.reasoning ?? 0,
+                            cachedInputTokens: usage.inputTokenDetails?.cacheRead ?? 0,
                         }}
app/chat/components/chat-messages.tsx (1)

818-829: ⚠️ Potential issue | 🟠 Major

messageTools only checks isToolUIPart, missing dynamic-tool parts.

At line 823, the code filters message parts to collect tools but only checks isToolUIPart(p). This misses DynamicToolUIPart parts (which have type === 'dynamic-tool'). The type ToolInvocationState is defined as ToolUIPart | DynamicToolUIPart, so both should be collected.

Use the same pattern as chat-context.tsx (lines 286–295):

for (const p of parts) {
    if (p.type === 'dynamic-tool') {
        tools.push(p as DynamicToolUIPart)
    } else if (typeof p.type === 'string' && p.type.startsWith('tool-')) {
        tools.push(p as ToolUIPart)
    }
}

Dynamic tool invocations will be silently dropped from the per-message tools display otherwise.

🤖 Fix all issues with AI agents
In `@app/chat/components/chat-input.tsx`:
- Around line 60-87: In SelectedAttachments, fix the early-return formatting to
satisfy the project's curly rule by placing the opening brace on its own line
for the if (attachments.length === 0) return null; statement, and make the
remove button accessible by adding an aria-label (e.g., aria-label={`Remove
${file.name}`} ) to the button that calls removeAttachment(index); reference the
SelectedAttachments function, the attachments array, removeAttachment callback,
and the XIcon button when applying these changes.

In `@app/chat/components/chat-messages.tsx`:
- Around line 129-178: Remove the unused ChatMessagesProps interface and the
dead helper functions getSourcesFromParts, isSourceUrlPart, and
isSourceDocumentPart from the file; confirm ChatMessages reads state via
useChatContext() (not props) and that no other code in this file or imported
modules references those symbols before deletion, then delete the four
declarations to clean up dead code.

In `@app/chat/providers/chat-context.tsx`:
- Around line 49-65: The import list from 'ai' at the top of chat-context.tsx
includes many runtime values that are not used (getToolName, isDataUIPart,
isFileUIPart, isReasoningUIPart, isTextUIPart, isToolUIPart, isStaticToolUIPart,
isDeepEqualData, InvalidResponseDataError, InvalidMessageRoleError,
InvalidArgumentError, getStaticToolName, getTextFromDataUrl,
safeValidateUIMessages, generateId); remove these unused runtime imports from
the import declaration (keeping only the actual used exports, if any) so they
are not bundled into the client; double-check usages of each named symbol (e.g.,
getToolName, isTextUIPart, generateId) before removing and retain any that are
referenced elsewhere in this file.
- Line 83: Remove the invalid default import "UIDataTypes from 'ai'": delete the
line importing UIDataTypes as the ai package v6 has no default export and
UIDataTypes is not exported; if you actually need types from the AI SDK, replace
it with the appropriate named import(s) (e.g., import { SomeNamedType } from
'ai') or declare a local type instead, and ensure no unused import remains in
chat-context.tsx.

In `@components.json`:
- Around line 23-25: The JSON "registries" block has its closing brace placed on
the same line as the last entry (the "@ai-elements" entry) which is a formatting
mistake; open the components.json, locate the "registries" object and move the
closing '}' to its own line (or simply run your project's formatter/Prettier on
the file) so the object and subsequent braces are properly line-broken and the
file is validly formatted.
- Line 15: The JSON file has inconsistent formatting: move the closing brace of
the "registries" object onto its own line and ensure proper indentation so the
object is formatted as a multi-line JSON object (e.g., "registries": { ... } ),
and keep the components alias value "@/app/components" as-is; update
components.json to match Prettier-style multi-line object formatting for
"registries" while preserving the existing "components": "@/app/components"
entry.

Comment on lines +60 to +87
function SelectedAttachments() {
const { attachments, removeAttachment } = usePromptInputAttachments()

if (attachments.length === 0) {return null}

return (
<div className="flex flex-wrap gap-2 p-2 border-b border-border/50">
{attachments.map((file, index) => (
<Badge
key={`${file.name}-${index}`}
variant="secondary"
className="h-6 gap-1 pr-1"
>
<span className="max-w-30 truncate text-[10px]">
{file.name}
</span>
<button
type="button"
onClick={() => removeAttachment(index)}
className="rounded-full hover:bg-foreground/10 transition-colors"
>
<XIcon className="size-3" />
</button>
</Badge>
))}
</div>
)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Minor formatting and accessibility issues in SelectedAttachments.

  1. Line 63: The early return should have the opening brace on a separate line per project ESLint config (curly: ['error', 'all']).
  2. The remove button (lines 76-82) is missing an aria-label — screen readers will not convey the button's purpose.
🔧 Proposed fix
-    if (attachments.length === 0) {return null}
+    if (attachments.length === 0) {
+        return null
+    }
                     <button
                         type="button"
                         onClick={() => removeAttachment(index)}
                         className="rounded-full hover:bg-foreground/10 transition-colors"
+                        aria-label={`Remove ${file.name}`}
                     >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function SelectedAttachments() {
const { attachments, removeAttachment } = usePromptInputAttachments()
if (attachments.length === 0) {return null}
return (
<div className="flex flex-wrap gap-2 p-2 border-b border-border/50">
{attachments.map((file, index) => (
<Badge
key={`${file.name}-${index}`}
variant="secondary"
className="h-6 gap-1 pr-1"
>
<span className="max-w-30 truncate text-[10px]">
{file.name}
</span>
<button
type="button"
onClick={() => removeAttachment(index)}
className="rounded-full hover:bg-foreground/10 transition-colors"
>
<XIcon className="size-3" />
</button>
</Badge>
))}
</div>
)
}
function SelectedAttachments() {
const { attachments, removeAttachment } = usePromptInputAttachments()
if (attachments.length === 0) {
return null
}
return (
<div className="flex flex-wrap gap-2 p-2 border-b border-border/50">
{attachments.map((file, index) => (
<Badge
key={`${file.name}-${index}`}
variant="secondary"
className="h-6 gap-1 pr-1"
>
<span className="max-w-30 truncate text-[10px]">
{file.name}
</span>
<button
type="button"
onClick={() => removeAttachment(index)}
className="rounded-full hover:bg-foreground/10 transition-colors"
aria-label={`Remove ${file.name}`}
>
<XIcon className="size-3" />
</button>
</Badge>
))}
</div>
)
}
🤖 Prompt for AI Agents
In `@app/chat/components/chat-input.tsx` around lines 60 - 87, In
SelectedAttachments, fix the early-return formatting to satisfy the project's
curly rule by placing the opening brace on its own line for the if
(attachments.length === 0) return null; statement, and make the remove button
accessible by adding an aria-label (e.g., aria-label={`Remove ${file.name}`} )
to the button that calls removeAttachment(index); reference the
SelectedAttachments function, the attachments array, removeAttachment callback,
and the XIcon button when applying these changes.

Comment on lines +129 to +178
interface ChatMessagesProps {
messages: UIMessage[]
status: string
error: Error | undefined
onSuggestionClick: (suggestion: string) => void
onCopyMessage?: (messageId: string, content: string) => void
onRegenerate?: (messageId: string) => void
}

interface SourceDocument {
title?: string
url?: string
description?: string
sourceDocument?: string
}


function isSourceUrlPart(part: UIMessage['parts'][number]): part is SourceUrlUIPart {
return part.type === 'source-url'
}

function isSourceDocumentPart(
part: UIMessage['parts'][number]
): part is SourceDocumentUIPart {
return part.type === 'source-document'
}


// Extract sources from message parts
const getSourcesFromParts = (parts: UIMessage['parts']): SourceDocument[] => {
const sources: SourceDocument[] = []
for (const part of parts) {
if (isSourceUrlPart(part)) {
sources.push({
title: part.title,
url: part.url,
description: part.url,
})
continue
}

if (isSourceDocumentPart(part)) {
sources.push({
title: part.title,
description: part.filename ?? part.mediaType,
})
}
}
return sources
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's read the entire chat-messages.tsx file to understand the context
cat -n app/chat/components/chat-messages.tsx

Repository: ssdeanx/AgentStack

Length of output: 50376


🏁 Script executed:

#!/bin/bash
# Search for each symbol and count occurrences
echo "=== ChatMessagesProps usage ==="
grep -n "ChatMessagesProps" app/chat/components/chat-messages.tsx | head -20

echo -e "\n=== getSourcesFromParts usage ==="
grep -n "getSourcesFromParts" app/chat/components/chat-messages.tsx | head -20

echo -e "\n=== isSourceUrlPart usage ==="
grep -n "isSourceUrlPart" app/chat/components/chat-messages.tsx | head -20

echo -e "\n=== isSourceDocumentPart usage ==="
grep -n "isSourceDocumentPart" app/chat/components/chat-messages.tsx | head -20

echo -e "\n=== ChatMessages component definition ==="
grep -n "function ChatMessages" app/chat/components/chat-messages.tsx | head -5

echo -e "\n=== Search for ChatMessages export or definition ==="
grep -n "export.*ChatMessages" app/chat/components/chat-messages.tsx | head -5

Repository: ssdeanx/AgentStack

Length of output: 697


🏁 Script executed:

# Let me check if getSourcesFromParts is actually called anywhere in the file
grep -n "getSourcesFromParts(" app/chat/components/chat-messages.tsx

# Also check the ChatMessages function signature to confirm it takes no props
sed -n '1493,1510p' app/chat/components/chat-messages.tsx

Repository: ssdeanx/AgentStack

Length of output: 475


Remove unused ChatMessagesProps interface and dead getSourcesFromParts function with helpers.

ChatMessagesProps (line 129) is never used—ChatMessages component takes no parameters and reads everything via useChatContext(). getSourcesFromParts (line 158) and its helpers isSourceUrlPart and isSourceDocumentPart (lines 146, 150) are dead code—getSourcesFromParts is never called anywhere in the file. Remove all four unless they're needed for future functionality.

🤖 Prompt for AI Agents
In `@app/chat/components/chat-messages.tsx` around lines 129 - 178, Remove the
unused ChatMessagesProps interface and the dead helper functions
getSourcesFromParts, isSourceUrlPart, and isSourceDocumentPart from the file;
confirm ChatMessages reads state via useChatContext() (not props) and that no
other code in this file or imported modules references those symbols before
deletion, then delete the four declarations to clean up dead code.

Comment on lines +49 to 65
import {
getToolName,
isDataUIPart,
isFileUIPart,
isReasoningUIPart,
isTextUIPart,
isToolUIPart,
isStaticToolUIPart,
isDeepEqualData,
InvalidResponseDataError,
InvalidMessageRoleError,
InvalidArgumentError,
getStaticToolName,
getTextFromDataUrl,
safeValidateUIMessages,
generateId
} from 'ai'
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's read the chat-context.tsx file to see all imports and the code
cat -n app/chat/providers/chat-context.tsx

Repository: ssdeanx/AgentStack

Length of output: 29788


Remove all unused runtime imports from 'ai' to reduce bundle size.

The runtime imports on lines 49-65 (getToolName, isDataUIPart, isFileUIPart, isReasoningUIPart, isTextUIPart, isToolUIPart, isStaticToolUIPart, isDeepEqualData, InvalidResponseDataError, InvalidMessageRoleError, InvalidArgumentError, getStaticToolName, getTextFromDataUrl, safeValidateUIMessages, generateId) are not referenced anywhere in this file. The code uses inline type guards instead (e.g., (p): p is TextUIPart => p.type === 'text' on lines 190 and 252) rather than calling these helper functions. These unused runtime values will be bundled with the client code; removing them aligns with the guideline to "remove unused code using tree shaking to reduce bundle size."

🤖 Prompt for AI Agents
In `@app/chat/providers/chat-context.tsx` around lines 49 - 65, The import list
from 'ai' at the top of chat-context.tsx includes many runtime values that are
not used (getToolName, isDataUIPart, isFileUIPart, isReasoningUIPart,
isTextUIPart, isToolUIPart, isStaticToolUIPart, isDeepEqualData,
InvalidResponseDataError, InvalidMessageRoleError, InvalidArgumentError,
getStaticToolName, getTextFromDataUrl, safeValidateUIMessages, generateId);
remove these unused runtime imports from the import declaration (keeping only
the actual used exports, if any) so they are not bundled into the client;
double-check usages of each named symbol (e.g., getToolName, isTextUIPart,
generateId) before removing and retain any that are referenced elsewhere in this
file.

ChatContextValue,
} from './chat-context-types'
import { ChatContext } from './chat-context-hooks'
import UIDataTypes from 'ai';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check if UIDataTypes is used anywhere in the file
grep -n "UIDataTypes" app/chat/providers/chat-context.tsx

Repository: ssdeanx/AgentStack

Length of output: 94


🏁 Script executed:

# Check the package.json to confirm the ai package version
cat package.json | grep -A2 '"ai"'

Repository: ssdeanx/AgentStack

Length of output: 272


🌐 Web query:

Vercel AI SDK v6 default exports and UIDataTypes export

💡 Result:

Default exports (AI SDK v6)

  • ai has no default export (only named exports). Import like:

    import { generateText, streamText, UIMessage } from 'ai';

    Source: ai type declarations show no export default. [1]

  • Provider packages also expose their “default provider instance” as a named export, e.g.:

    import { vercel } from '@ai-sdk/vercel';

    (This is not a JS/TS default export.) [2]

UIDataTypes export (AI SDK v6)

  • UIDataTypes exists as an internal type (type UIDataTypes = Record<string, unknown>) used by UIMessage, but it is not exported from ai, so import type { UIDataTypes } from 'ai' won’t work. [1][3]

  • Workarounds:

    1. Use the structural type directly:
      type UIDataTypes = Record<string, unknown>;
    2. Or just pass your data-parts type to UIMessage without referencing UIDataTypes:
      type MyDataParts = { product: { id: string } };
      
      type MyMsg = UIMessage<unknown, MyDataParts>;

Sources: [1] [2] [3]


Remove the invalid import UIDataTypes from 'ai' — AI SDK v6 has no default export and UIDataTypes is not exported from the package.

The ai package v6 exposes only named exports (no default export), and UIDataTypes is an internal type that is not exported. This import is unused in the file and will cause a build error.

-import UIDataTypes from 'ai';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import UIDataTypes from 'ai';
🤖 Prompt for AI Agents
In `@app/chat/providers/chat-context.tsx` at line 83, Remove the invalid default
import "UIDataTypes from 'ai'": delete the line importing UIDataTypes as the ai
package v6 has no default export and UIDataTypes is not exported; if you
actually need types from the AI SDK, replace it with the appropriate named
import(s) (e.g., import { SomeNamedType } from 'ai') or declare a local type
instead, and ensure no unused import remains in chat-context.tsx.

"iconLibrary": "lucide",
"aliases": {
"components": "@/src/components",
"components": "@/app/components",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check where existing components live and what imports reference them
echo "=== Directories containing ai-elements ==="
fd -t d 'ai-elements'

echo ""
echo "=== Imports referencing `@/src/components` ==="
rg -n '@/src/components' --type=ts --type=tsx | head -40

echo ""
echo "=== Imports referencing `@/app/components` ==="
rg -n '@/app/components' --type=ts --type=tsx | head -40

echo ""
echo "=== Current components.json content ==="
cat components.json 2>/dev/null || echo "File not found"

echo ""
echo "=== tsconfig.json paths section ==="
cat tsconfig.json 2>/dev/null | jq '.compilerOptions.paths' 2>/dev/null || echo "Could not extract paths"

Repository: ssdeanx/AgentStack

Length of output: 1055


🏁 Script executed:

#!/bin/bash
echo "=== Searching for imports in all TypeScript files ==="
rg 'import.*from.*@/.*components' --glob='*.ts' --glob='*.tsx' -n | head -50

echo ""
echo "=== Files mentioned in review ==="
find . -name 'chat-header.tsx' -o -name 'chat-input.tsx' -o -name 'chat-messages.tsx' | xargs ls -la 2>/dev/null

echo ""
echo "=== Content of chat-header.tsx ==="
cat chat-header.tsx 2>/dev/null | head -20

echo ""
echo "=== Content of chat-input.tsx ==="
cat chat-input.tsx 2>/dev/null | head -20

echo ""
echo "=== Full tsconfig.json ==="
cat tsconfig.json 2>/dev/null

echo ""
echo "=== All files in src/components/ai-elements ==="
find src/components/ai-elements -type f 2>/dev/null | head -20

echo ""
echo "=== Check if app/components directory exists ==="
ls -la app/components 2>/dev/null || echo "Directory does not exist"

Repository: ssdeanx/AgentStack

Length of output: 11768


Fix JSON formatting in components.json to align with Prettier standards.

The registries closing brace is improperly formatted on the same line as the content (line 24). Format it consistently:

Formatting fix
    "registries": {
        "@ai-elements": "https://registry.ai-sdk.dev/{name}.json"
    }

The components alias change from @/src/components to @/app/components is intentional and correct. The codebase uses a sound architecture: specialized ai-elements components reside in src/components/ai-elements/ and are imported throughout the app (workflows, networks), while general UI components live in app/components/. Shadcn should generate new components into app/components/, which is exactly what the alias directs. No migration of existing ai-elements is necessary.

🤖 Prompt for AI Agents
In `@components.json` at line 15, The JSON file has inconsistent formatting: move
the closing brace of the "registries" object onto its own line and ensure proper
indentation so the object is formatted as a multi-line JSON object (e.g.,
"registries": { ... } ), and keep the components alias value "@/app/components"
as-is; update components.json to match Prettier-style multi-line object
formatting for "registries" while preserving the existing "components":
"@/app/components" entry.

Comment on lines 23 to 25
"registries": {
"@ai-elements": "https://registry.ai-sdk.dev/{name}.json"
}
"@ai-elements": "https://registry.ai-sdk.dev/{name}.json"}
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Formatting: closing brace on same line as content.

The registries block has the closing } jammed onto the same line as the last entry. This looks like a formatting accident.

     "registries": {
-    "@ai-elements": "https://registry.ai-sdk.dev/{name}.json"}
+        "@ai-elements": "https://registry.ai-sdk.dev/{name}.json"
+    }
 }

As per coding guidelines, "Use a code formatter like Prettier to automatically format code on save".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"registries": {
"@ai-elements": "https://registry.ai-sdk.dev/{name}.json"
}
"@ai-elements": "https://registry.ai-sdk.dev/{name}.json"}
}
"registries": {
"@ai-elements": "https://registry.ai-sdk.dev/{name}.json"
}
}
🤖 Prompt for AI Agents
In `@components.json` around lines 23 - 25, The JSON "registries" block has its
closing brace placed on the same line as the last entry (the "@ai-elements"
entry) which is a formatting mistake; open the components.json, locate the
"registries" object and move the closing '}' to its own line (or simply run your
project's formatter/Prettier on the file) so the object and subsequent braces
are properly line-broken and the file is validly formatted.

@ssdeanx ssdeanx merged commit ac43ef0 into main Feb 16, 2026
132 of 137 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant