Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 15 additions & 16 deletions Copilot-Processing.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,22 @@ Instructions also mention Unreal Engine ThirdPerson template project and UE C++
- [x] Update `src/mastra/config/google.ts` to use `createGoogleGenerativeAI`.
- [x] Update `src/mastra/config/pg-storage.ts` to use local provider instance instead of legacy facade.
- [x] Update documentation
- [x] Improve Chat UI and Google 3 Model Support:
- [x] Add Gemini 3 Flash and update Gemini 3 Pro in `google-models.ts`.
- [x] Improve `ChatInput` with `ModelSelector`, `Context`, `SpeechButton`, and `ActionMenu`.
- [x] Create `ChatSidebar` for agent details, checkpoints, and memory settings.
- [x] Update `ChatPage` layout to include sidebar and adjust height for Navbar.
- [x] Restore all features to `ChatHeader` and add `mt-16` to lower it below the Navbar.
- [x] Add `gemini3Expert` agent to `agents.ts`.

## Summary

Enhanced the GitHub toolset in `src/mastra/tools/github.ts` by adding 5 new tools:
Enhanced the Chat UI and added support for Google Gemini 3 models:

1. `createPullRequest`: Create a new PR with title, head, base, and body.
2. `mergePullRequest`: Merge an existing PR using merge, squash, or rebase methods.
3. `addIssueComment`: Add comments to issues or PRs.
4. `getPullRequest`: Retrieve detailed information about a specific PR.
5. `getIssue`: Retrieve detailed information about a specific issue.

Fixed a TypeScript error in `src/mastra/agents/bgColorAgent.ts` where `colorChangeTool` was incompatible with the `Agent` class.

Fixed a runtime error in `app/components/navbar.tsx` caused by `NavigationMenuTrigger` receiving multiple children when `asChild` was true. The `NavigationMenuTrigger` component in `ui/navigation-menu.tsx` was updated to only render the chevron icon when `asChild` is false.

Updated `src/mastra/config/google.ts` and `src/mastra/config/pg-storage.ts` to be compatible with `@ai-sdk/google` v3. This involved:

- Using `createGoogleGenerativeAI` to create a provider instance in `google.ts`.
- Updating `pg-storage.ts` to import the `google` provider from the local config instead of the removed facade in `@ai-sdk/google`.
- Removing unnecessary type casts in `pg-storage.ts`.
1. **Google 3 Models**: Added `gemini-3-flash-preview` and updated `gemini-3-pro-preview` in the model configuration.
2. **Chat UI Improvement**:
- **Rich Input**: The `ChatInput` now features a model selector, token usage context, speech-to-text button, and an action menu for attachments.
- **Sidebar Layout**: Added a `ChatSidebar` that displays agent capabilities, conversation checkpoints, and memory configuration (Thread ID/Resource ID).
- **Lowered Header**: The `ChatHeader` now has a top margin (`mt-16`) to sit perfectly below the fixed global Navbar. All original features (checkpoints, memory settings, usage) have been restored to the header while also being available in the sidebar.
- **Layout Adjustment**: Updated the `ChatPage` height to `h-[calc(100vh-4rem)]` to account for the Navbar height and prevent unwanted scrolling.
3. **New Agent**: Added a specialized `Gemini 3 Expert` agent configuration.
6 changes: 3 additions & 3 deletions app/chat/components/agent-chain-of-thought.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ export function AgentChainOfThought({
defaultOpen = true,
className,
}: AgentChainOfThoughtProps) {
if (!steps || steps.length === 0) {return null}

const completedCount = useMemo(() => steps.filter((s) => s.status === "complete").length, [steps])
const activeStep = useMemo(() => steps.find((s) => s.status === "active"), [steps])

if (steps.length === 0) {return null}
Comment on lines 29 to +32
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

For better performance, it's a good practice to have early returns at the beginning of the function body, before any hooks like useMemo are called. This avoids unnecessary computations when the component is going to return null anyway. Consider moving the if (steps.length === 0) check to be the first line in the component.

Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

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

The early return check has been moved from before useMemo hooks (lines 29-31 in context) to after them (line 32). This violates the Rules of Hooks in React, which states that hooks must be called in the same order on every render. Moving the conditional return after hooks could cause "Rendered fewer hooks than expected" errors if steps.length becomes 0 after initially having items.

Copilot uses AI. Check for mistakes.

return (
<ChainOfThought defaultOpen={defaultOpen} className={className}>
<ChainOfThoughtHeader className="flex items-center gap-2">
Expand Down Expand Up @@ -59,7 +59,7 @@ export function AgentChainOfThought({
description={step.description}
status={step.status}
>
{step.duration && step.status === "complete" && (
{(Boolean(step.duration)) && step.status === "complete" && (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The explicit (Boolean(step.duration)) check is redundant. The original step.duration check is idiomatic in JavaScript/TypeScript for checking for truthy values (non-zero numbers, non-empty strings, etc.) and is more concise. I'd recommend simplifying this for better readability.

Suggested change
{(Boolean(step.duration)) && step.status === "complete" && (
{step.duration && step.status === "complete" && (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider removing redundant Boolean() wrapper.

The explicit Boolean() wrapper is unnecessary since JavaScript already performs implicit truthiness evaluation in conditional contexts. The code would be cleaner as:

-{(Boolean(step.duration)) && step.status === "complete" && (
+{step.duration && step.status === "complete" && (
🤖 Prompt for AI Agents
In app/chat/components/agent-chain-of-thought.tsx around line 62, the
conditional uses an unnecessary Boolean() wrapper: "(Boolean(step.duration)) &&
step.status === 'complete' && (". Remove the Boolean(...) wrapper so the
expression relies on JS truthiness (e.g., "step.duration && step.status ===
'complete' && (") to simplify the code while preserving behavior.

Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

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

Boolean(step.duration) is unnecessarily verbose. The idiomatic pattern would be step.duration && step.status === "complete". The Boolean() wrapper adds no functional benefit and reduces readability.

Suggested change
{(Boolean(step.duration)) && step.status === "complete" && (
{step.duration && step.status === "complete" && (

Copilot uses AI. Check for mistakes.
<span className="flex items-center gap-1 text-xs text-muted-foreground">
<ClockIcon className="size-3" />
{step.duration}s
Expand Down
2 changes: 1 addition & 1 deletion app/chat/components/agent-checkpoint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function AgentCheckpoint({
onRestore,
className,
}: AgentCheckpointProps) {
const date = timestamp ? (typeof timestamp === 'string' ? new Date(timestamp) : timestamp) : undefined
const date = timestamp !== undefined ? (typeof timestamp === 'string' ? new Date(timestamp) : timestamp) : undefined
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

This change might introduce a runtime error. The original check timestamp ? ... correctly handled falsy values like an empty string (''), treating it as undefined. The new check timestamp !== undefined will be true for an empty string. This leads to new Date(''), which creates an Invalid Date object. If formatTime doesn't handle invalid dates, this will likely throw a RangeError. It's safer to stick with the original truthiness check or add a specific check for empty strings.

Suggested change
const date = timestamp !== undefined ? (typeof timestamp === 'string' ? new Date(timestamp) : timestamp) : undefined
const date = timestamp ? (typeof timestamp === 'string' ? new Date(timestamp) : timestamp) : undefined

Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

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

The condition check has been changed from timestamp ? to timestamp !== undefined ?. While this is more explicit, it changes the behavior. The original code would evaluate falsy timestamps (null, 0, false, empty string) as falsy, while the new code only checks for undefined. If timestamp could be null or other falsy values besides undefined, this change could introduce bugs. Verify that timestamp is only ever undefined or a valid Date/string.

Suggested change
const date = timestamp !== undefined ? (typeof timestamp === 'string' ? new Date(timestamp) : timestamp) : undefined
const date = timestamp ? (typeof timestamp === "string" ? new Date(timestamp) : timestamp) : undefined

Copilot uses AI. Check for mistakes.
const displayLabel = label ?? (date
? `Checkpoint at ${formatTime(date)}`
: `Checkpoint ${messageIndex + 1}`)
Expand Down
3 changes: 1 addition & 2 deletions app/chat/components/agent-inline-citation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ interface AgentInlineCitationProps {
}

export function AgentInlineCitation({ citations, text }: AgentInlineCitationProps) {
const citation = citations[0]
if (!citation) {return <span>{text}</span>}
if (citations.length === 0) {return <span>{text}</span>}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Suggest formatting the conditional return for readability.

The logic is correct, but placing the return statement on the same line reduces readability.

🔎 Proposed formatting improvement
-  if (citations.length === 0) {return <span>{text}</span>}
+  if (citations.length === 0) {
+    return <span>{text}</span>
+  }
📝 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
if (citations.length === 0) {return <span>{text}</span>}
if (citations.length === 0) {
return <span>{text}</span>
}
🤖 Prompt for AI Agents
In app/chat/components/agent-inline-citation.tsx around line 27, the conditional
return is written inline ("if (citations.length === 0) {return
<span>{text}</span>}"), which harms readability; refactor it to a multi-line
conditional (use braces and place the return on its own line) so the
empty-citations early-return is clear and consistent with surrounding code
formatting.

Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

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

The original code assigned citations[0] to a variable and checked if it exists. The refactored version checks citations.length === 0 directly which is correct and more efficient. However, the original code would have handled the case where citations array exists but is empty, while this new check might behave differently if citations is undefined (though TypeScript should prevent that). The change is likely fine but verify that citations is always defined as an array.

Copilot uses AI. Check for mistakes.

return (
<InlineCitation>
Expand Down
4 changes: 2 additions & 2 deletions app/chat/components/agent-queue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ function TaskSection({
)}
</QueueItemActions>
</div>
{task.description && (
{(Boolean(task.description)) && (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The explicit (Boolean(task.description)) check is redundant. A simple truthiness check task.description && ... is more idiomatic and readable for conditional rendering.

Suggested change
{(Boolean(task.description)) && (
{task.description && (

Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

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

The condition wraps task.description in Boolean() which is unnecessary verbose. The original condition task.description && is more idiomatic JavaScript/TypeScript. The Boolean() wrapper adds no functional benefit for truthiness checks and makes the code less readable.

Copilot uses AI. Check for mistakes.
<QueueItemDescription>{task.description}</QueueItemDescription>
)}
{task.error && (
{(Boolean(task.error)) && (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Similar to the check for task.description, the explicit (Boolean(task.error)) is redundant. A simple truthiness check task.error && ... is more idiomatic and readable.

Suggested change
{(Boolean(task.error)) && (
{task.error && (

Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

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

Same issue: Boolean(task.error) is unnecessarily verbose. Use the idiomatic task.error && pattern instead.

Copilot uses AI. Check for mistakes.
<QueueItemDescription className="text-destructive">
{task.error}
</QueueItemDescription>
Expand Down
14 changes: 7 additions & 7 deletions app/chat/components/chat-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export function ChatHeader() {
const usedTokens = usage ? usage.inputTokens + usage.outputTokens : 0

return (
<header className="flex items-center justify-between border-b border-border px-4 py-3">
<header className="flex items-center justify-between border-b border-border px-4 py-3 mt-16">
<div className="flex items-center gap-3">
<MessageSquareIcon className="size-5 text-muted-foreground" />
<div className="flex flex-col">
Expand Down Expand Up @@ -201,15 +201,15 @@ export function ChatHeader() {
<Button
variant="outline"
size="sm"
className="min-w-[140px] justify-between gap-2"
className="min-w-35 justify-between gap-2"
>
<CpuIcon className="size-3.5 text-muted-foreground" />
<span className="truncate text-xs">{selectedModel.name}</span>
</Button>
</ModelSelectorTrigger>
<ModelSelectorContent className="w-[340px]">
<ModelSelectorContent className="w-85">
<ModelSelectorInput placeholder="Search models..." />
<ModelSelectorList className="max-h-[400px]">
<ModelSelectorList className="max-h-100">
Comment on lines +204 to +212
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

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

Same Tailwind CSS issues appear in this file: min-w-35 (line 204), w-85 (line 210), and max-h-100 (line 212) are not valid Tailwind classes. Use bracket notation like min-w-[140px], w-[340px], and max-h-[400px] or use predefined scale values.

Copilot uses AI. Check for mistakes.
<ModelSelectorEmpty>No models found.</ModelSelectorEmpty>
{PROVIDER_ORDER.map((provider) => {
const models = modelsByProvider[provider]
Expand Down Expand Up @@ -266,9 +266,9 @@ export function ChatHeader() {
<span className="truncate">{agentConfig?.name ?? selectedAgent}</span>
</Button>
</ModelSelectorTrigger>
<ModelSelectorContent className="w-[320px]">
<ModelSelectorContent className="w-80">
<ModelSelectorInput placeholder="Search agents..." />
<ModelSelectorList className="max-h-[400px]">
<ModelSelectorList className="max-h-100">
<ModelSelectorEmpty>No agents found.</ModelSelectorEmpty>
{CATEGORY_ORDER.map((category) => {
const agents = agentsByCategory[category]
Expand All @@ -282,7 +282,7 @@ export function ChatHeader() {
{agents.map((agent) => (
<ModelSelectorItem
key={agent.id}
value={agent.id}
value={agent.name}
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

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

The value prop has been changed from agent.id to agent.name. This could cause issues if the ModelSelectorItem component expects a unique identifier rather than a display name. If multiple agents have the same name, this could lead to key conflicts or selection bugs. Verify that agent names are unique or revert to using agent.id.

Suggested change
value={agent.name}
value={agent.id}

Copilot uses AI. Check for mistakes.
onSelect={() => handleSelectAgent(agent)}
className="flex items-center justify-between"
>
Expand Down
Loading
Loading