-
Notifications
You must be signed in to change notification settings - Fork 51
Feat/memories #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/memories #22
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds end-to-end “memory” capability: schema/table, Convex queries/mutations, DB action wrappers, a new update_memory tool wired into tool factory/context, async systemPrompt that optionally embeds user memories, chat route plumbing for memory gating, and UI for enabling/disabling and managing memories, including a Switch, Settings dialog, and in-chat MemoryToolHandler rendering. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant C as Chat Route
participant P as systemPrompt
participant T as createTools(update_memory?)
participant L as LLM Stream
participant DB as Convex (memories)
U->>C: Send chat message
C->>P: await systemPrompt(userId, mode, execMode, customization)
alt include_memory_entries === true
P->>DB: getMemoriesForBackend(userId, serviceKey)
DB-->>P: Memories[]
P-->>C: Prompt with <memories> section
else
P-->>C: Prompt without memories
end
C->>T: createTools(..., memoryEnabled)
C->>L: Stream with tools + system prompt
par Tool call (optional)
L-->>C: tool-call: update_memory(action,...)
C->>DB: create/update/delete/getMemoryById (serviceKey)
DB-->>C: Result/ID/content
C-->>L: tool-result
end
L-->>U: Assistant response (with tool parts)
sequenceDiagram
autonumber
actor U as User
participant S as SettingsDialog
participant M as ManageMemoriesDialog
participant V as Convex (userCustomization, memories)
U->>S: Open Settings
S->>V: getUserCustomization
V-->>S: { include_memory_entries }
U->>S: Toggle "Enable memory"
S->>V: saveUserCustomization({ include_memory_entries })
U->>M: Open Manage Memories
M->>V: getUserMemories
V-->>M: List<Memory>
U->>M: Delete one / Delete all
M->>V: deleteUserMemory / deleteAllUserMemories
V-->>M: OK
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
✨ Finishing Touches
🧪 Generate unit tests
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 18
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
convex/userCustomization.ts (1)
31-43: Bug: unintentionally re-enables memories when updating other fields.Defaulting include_memory_entries to true when arg is undefined overwrites an existing false, violating user intent/privacy.
- const customizationData = { + const customizationData = { user_id: identity.subject, nickname: args.nickname?.trim() || undefined, occupation: args.occupation?.trim() || undefined, personality: args.personality?.trim() || undefined, traits: args.traits?.trim() || undefined, additional_info: args.additional_info?.trim() || undefined, - include_memory_entries: - args.include_memory_entries !== undefined - ? args.include_memory_entries - : true, // Default to enabled + include_memory_entries: + args.include_memory_entries ?? + (existing?.include_memory_entries ?? true), // preserve existing, default true only when first creating updated_at: Date.now(), };lib/ai/tools/index.ts (2)
13-16: Type userID with Convex Id helper per guidelines.Prevents passing plain strings and catches cross-table mixups at compile time.
import type { UIMessageStreamWriter } from "ai"; import type { ChatMode, ExecutionMode, ToolContext, Todo } from "@/types"; import type { Geo } from "@vercel/functions"; +import type { Id } from "@/convex/_generated/dataModel"; // Factory function to create tools with context export const createTools = ( - userID: string, + userID: Id<"users">, writer: UIMessageStreamWriter, mode: ChatMode = "agent", executionMode: ExecutionMode = "local", userLocation: Geo, initialTodos?: Todo[], memoryEnabled: boolean = true, ) => {Also applies to: 18-26
39-46: Use Id<'users'> instead of string for all userID fields and parameters
- In types/agent.ts (interface ToolContext, SandboxContext): change userID: string to userID: Id<'users'> and add
import { Id } from './_generated/dataModel'.- In lib/ai/tools/index.ts ( createTools signature): update userID: string → userID: Id<'users'>.
- In lib/ai/tools/utils/sandbox.ts (createOrConnectPersistentTerminal) and lib/ai/tools/utils/sandbox-manager.ts (constructor): update all userID: string → Id<'users'>.
- In lib/chat/chat-processor.ts (line 81) and app/api/chat/route.ts (line 94): change userID: string parameter to Id<'users'>.
- Import Id from
./_generated/dataModelwherever applied.lib/system-prompt.ts (1)
18-20: Do not cache the date at module load; compute per-call to avoid staleness.
currentDateTime will be wrong after midnight (long-lived process). Compute inside systemPrompt or via a getter.Apply this diff:
-// Cache the current date to avoid repeated Date creation -export const currentDateTime = `${new Date().toLocaleDateString("en-US", DATE_FORMAT_OPTIONS)}`; +const getCurrentDate = () => + new Date().toLocaleDateString("en-US", DATE_FORMAT_OPTIONS); ... - const basePrompt = `You are an AI penetration testing assistant developed by HackerAI. + const basePrompt = `You are an AI penetration testing assistant developed by HackerAI. ... -The current date is ${currentDateTime}. +The current date is ${getCurrentDate()}. ${getCommunicationSection()}`;Also applies to: 160-161
🧹 Nitpick comments (21)
types/agent.ts (2)
11-14: Mirror the same naming in SandboxContext if this field is required.SandboxContext also includes userID; align with userId for consistency.
export interface SandboxContext { - userID: string; + userId: string; setSandbox: (sandbox: Sandbox) => void; }
22-22: Standardize naming: renameuserID→userIdacross the codebase
- In
types/agent.ts(lines 12 & 22), change- userID: string; + userId: string;- Update all usages of
userIDin consuming modules (e.g.lib/chat/chat-processor.ts,lib/ai/tools/index.ts,lib/ai/tools/update-memory.ts,lib/ai/tools/utils/sandbox-utils.ts, etc.) touserIdto prevent type errors.app/components/SidebarUserNav.tsx (1)
237-241: Defer-load SettingsDialog to reduce initial bundle.Importing a large dialog eagerly from the sidebar can bloat the menu bundle. Use dynamic import.
-import { SettingsDialog } from "./SettingsDialog"; +import dynamic from "next/dynamic"; +const SettingsDialog = dynamic(() => import("./SettingsDialog").then(m => m.SettingsDialog), { ssr: false });Also applies to: 284-289
components/ui/switch.tsx (1)
8-12: Forward ref the Switch for form libs and accessibility tooling.Radix components generally forward refs; this improves integration with react-hook-form and focus management.
-import * as React from "react" +import * as React from "react" +import type { ComponentPropsWithoutRef, ElementRef } from "react" @@ -function Switch({ - className, - ...props -}: React.ComponentProps<typeof SwitchPrimitive.Root>) { - return ( +const Switch = React.forwardRef<ElementRef<typeof SwitchPrimitive.Root>, ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>>( + ({ className, ...props }, ref) => { + return ( <SwitchPrimitive.Root + ref={ref} data-slot="switch" className={cn( "peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", className )} {...props} > <SwitchPrimitive.Thumb data-slot="switch-thumb" className={cn( "bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0" )} /> </SwitchPrimitive.Root> - ) -} + ) + } +) +Switch.displayName = "Switch" export { Switch }Also applies to: 13-29, 31-31
convex/userCustomization.ts (1)
113-129: Use internalQuery for getUserCustomizationForBackend
Switch this endpoint in convex/userCustomization.ts from query to internalQuery — it’s only called from lib/db/actions.ts on the server and never by client code, so you can avoid exposing a service-keyed endpoint.lib/system-prompt/memory.ts (1)
25-31: Sort by recency, bound list size, and guard date parsingUnsorted, unbounded lists can bloat tokens and bury recent items. Also guard seconds-vs-ms timestamps to avoid 1970 dates.
Apply:
- // Show all memories without sorting - const memoryContent = memories + // Sort by recency and bound list size + const MAX_MEMORIES_TO_SHOW = 20; + const toMs = (t: number) => (t < 1e12 ? t * 1000 : t); + const memoryContent = [...memories] + .sort((a, b) => toMs(b.update_time) - toMs(a.update_time)) + .slice(0, MAX_MEMORIES_TO_SHOW) .map((memory) => { - const date = new Date(memory.update_time).toISOString().split("T")[0]; - return `- [${date}] ${memory.content} (ID: ${memory.memory_id})`; + const date = new Date(toMs(memory.update_time)) + .toISOString() + .split("T")[0]; + return `- [${date}] ${escapeInline(memory.content)} (ID: ${memory.memory_id})`; }) .join("\n");Outside the selected range, add a tiny sanitizer:
const escapeInline = (s: string) => s.replaceAll("`", "ʼ").replaceAll("</memories>", "</ memories>");lib/system-prompt/bio.ts (2)
30-37: Escape fence delimiters to avoid breaking the code blockIf
traits(or other fields) contain ``` they can prematurely close the fenced block. Lightly escape backticks.Apply:
- const userInstructionsSection = traits - ? `\nUser's Instructions\nThe user provided the additional info about how they would like you to respond:\n\`${traits}\`` + const sanitize = (s: string) => s.replaceAll("```", "``\\`"); + const userInstructionsSection = traits + ? `\nUser's Instructions\nThe user provided the additional info about how they would like you to respond:\n\`${sanitize(traits)}\`` : "";
45-55: Minor: trim leading blank line in returned blockThe template starts with a blank line which is unnecessary in tight token budgets.
Apply:
- return ` - -<user_bio> + return `<user_bio> The user provided the following information about themselves. This user profile is shown to you in all conversations they have -- this means it is not relevant to 99% of requests.lib/system-prompt/personality.ts (1)
13-22: Prefer satisfies for exhaustive key checking (drop redundant annotation).This enforces key coverage without losing literal types.
-const PERSONALITY_INSTRUCTIONS: Record<PersonalityType, string> = { +const PERSONALITY_INSTRUCTIONS = { cynic: "Adopt a critical and sarcastic tone. Be skeptical of claims and point out potential flaws or weaknesses in approaches.", robot: "Be efficient and blunt in your responses. Focus on facts, be direct, and avoid unnecessary pleasantries.", listener: "Be thoughtful and supportive. Ask clarifying questions and show empathy while providing guidance.", nerd: "Be exploratory and enthusiastic about technical details. Dive deep into explanations and share interesting technical insights.", -} as const; +} as const satisfies Record<PersonalityType, string>;app/components/ManageMemoriesDialog.tsx (1)
113-121: Confirm destructive “Delete all” and provide basic feedback.Add a confirm step to avoid accidental wipes; optionally disable while processing.
- <Button - onClick={handleDeleteAllMemories} + <Button + onClick={() => { + if (confirm("Delete all saved memories? This cannot be undone.")) { + void handleDeleteAllMemories(); + } + }} variant="outline" className="border-destructive text-destructive hover:bg-destructive/10" > Delete all </Button>app/components/tools/MemoryToolHandler.tsx (1)
137-149: Expose popover state to assistive tech.Add aria-expanded for better accessibility.
<Button variant="ghost" size="sm" className="h-auto text-muted-foreground hover:text-foreground justify-start w-auto" onClick={handleClick} onKeyDown={handleKeyDown} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} + aria-expanded={isOpen} >lib/ai/tools/update-memory.ts (2)
148-150: Inconsistent return type when memory is not foundWhen
getMemoryByIdreturnsnullorundefined, the function returns the string "Memory not found" asmemoryContent. This inconsistency with other actions (which return the actual content string) could cause issues downstream.Consider returning
nullorundefinedconsistently:- const memoryContentToDelete = await getMemoryById({ - memoryId: existing_knowledge_id, - }); + let memoryContentToDelete: string | null = null; + try { + const memory = await getMemoryById({ + memoryId: existing_knowledge_id, + }); + memoryContentToDelete = memory?.content || null; + } catch (error) { + // Memory might not exist, proceed with deletion attempt + console.warn(`Memory ${existing_knowledge_id} not found before deletion`); + }Then update the return:
return { result: `Memory deleted successfully.`, - memoryContent: memoryContentToDelete || "Memory not found", + memoryContent: memoryContentToDelete, };
13-69: Extensive tool description could be modularizedThe 57-line description string makes the code harder to maintain. Consider moving it to a separate constant or file for better maintainability.
Extract the description to a constant:
const UPDATE_MEMORY_TOOL_DESCRIPTION = `The update_memory tool creates, updates, or deletes memories...`; export const createUpdateMemory = (context: ToolContext) => { return tool({ description: UPDATE_MEMORY_TOOL_DESCRIPTION, // ... }); };app/components/SettingsDialog.tsx (2)
24-24: Type the component properly for better type safetyThe component should be typed using React.FC or explicit return type annotation for better type safety and IDE support.
-const SettingsDialog = ({ open, onOpenChange }: SettingsDialogProps) => { +const SettingsDialog: React.FC<SettingsDialogProps> = ({ open, onOpenChange }) => {
168-168: Inconsistent dark mode stylingThe Switch component has a hardcoded dark mode class
dark:bg-blue-400which may not follow the app's theming system consistently.Consider using theme-aware classes or CSS variables:
- className="dark:bg-blue-400" + className="data-[state=checked]:bg-primary"lib/system-prompt.ts (2)
142-147: Type memories for safety and clarity.
getMemories returns Memory[]; annotate to prevent any/loose typing.Apply this diff:
-import { generateMemorySection } from "./system-prompt/memory"; +import { generateMemorySection, type Memory } from "./system-prompt/memory"; ... - const memories = - userId && shouldIncludeMemories ? await getMemories({ userId }) : null; + const memories: Memory[] | null = + userId && shouldIncludeMemories ? await getMemories({ userId }) : null;
184-185: Join with a newline for predictable formatting.
Minimizes accidental tag adjacency if a section forgets leading/trailing newlines.Apply this diff:
- return sections.filter(Boolean).join(""); + return sections.filter(Boolean).join("\n");convex/memories.ts (4)
88-97: Use .unique() when fetching by unique indexed field to detect duplicates.
Ensures invariant on memory_id and catches accidental duplicates.Apply representative diffs:
- const existing = await ctx.db - .query("memories") - .withIndex("by_memory_id", (q) => q.eq("memory_id", args.memoryId)) - .first(); + const existing = await ctx.db + .query("memories") + .withIndex("by_memory_id", (q) => q.eq("memory_id", args.memoryId)) + .unique();Repeat in update/delete/getById/user-delete call sites.
Also applies to: 135-139, 184-187, 221-224, 293-296
82-86: Ambiguous return when memories are disabled.
Returning memoryId as-if created when include_memory_entries is false can mislead callers.Consider returning a structured result:
- returns: v.string(), + returns: v.object({ memoryId: v.string(), created: v.boolean() }), ... - if (!memoryEnabled) { - return args.memoryId; // Return the ID as if created, but don't actually create - } + if (!memoryEnabled) { + return { memoryId: args.memoryId, created: false }; + } ... - return args.memoryId; + return { memoryId: args.memoryId, created: true };If changing the return type is too disruptive, at least log and document the no-op behavior clearly.
340-342: Delete-all should stream and optionally yield progress for large datasets.
Current approach is fine for small N; with many docs, prefer streaming (already suggested) and consider batching.No code changes beyond the streaming iteration already proposed are strictly necessary.
34-46: Comment vs behavior mismatch.
Docstring says “removing old memories,” but function only limits the returned set; it does not delete anything.Update the comment to “Enforces a 10,000-token return limit by truncating older entries (no deletion).”
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
convex/_generated/api.d.tsis excluded by!**/_generated/**pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (19)
app/api/chat/route.ts(3 hunks)app/components/ManageMemoriesDialog.tsx(1 hunks)app/components/MessagePartHandler.tsx(2 hunks)app/components/SettingsDialog.tsx(1 hunks)app/components/SidebarUserNav.tsx(5 hunks)app/components/tools/MemoryToolHandler.tsx(1 hunks)components/ui/switch.tsx(1 hunks)convex/memories.ts(1 hunks)convex/schema.ts(1 hunks)convex/userCustomization.ts(6 hunks)lib/ai/tools/index.ts(5 hunks)lib/ai/tools/update-memory.ts(1 hunks)lib/db/actions.ts(1 hunks)lib/system-prompt.ts(3 hunks)lib/system-prompt/bio.ts(1 hunks)lib/system-prompt/memory.ts(1 hunks)lib/system-prompt/personality.ts(1 hunks)package.json(1 hunks)types/agent.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)
**/*.{ts,tsx}: Use Id helper type from ./_generated/dataModel to type document IDs (e.g., Id<'users'>) instead of string
When defining Record types, specify key and value types matching validators (e.g., Record<Id<'users'>, string>)
Be strict with types for document IDs; prefer Id<'table'> over string in function args and variables
Useas constfor string literals in discriminated unions
Declare arrays with explicit generic type: const arr: Array = [...]
Declare records with explicit generic types: const record: Record<KeyType, ValueType> = {...}
Files:
lib/system-prompt/bio.tstypes/agent.tsapp/components/tools/MemoryToolHandler.tsxconvex/schema.tslib/db/actions.tsapp/components/MessagePartHandler.tsxapp/components/SidebarUserNav.tsxapp/components/ManageMemoriesDialog.tsxlib/system-prompt/memory.tslib/system-prompt/personality.tsconvex/memories.tslib/ai/tools/update-memory.tsapp/api/chat/route.tscomponents/ui/switch.tsxlib/ai/tools/index.tsconvex/userCustomization.tsapp/components/SettingsDialog.tsxlib/system-prompt.ts
package.json
📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)
Add @types/node to devDependencies when using Node.js built-in modules
Files:
package.json
convex/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)
convex/**/*.ts: Always use the new Convex function syntax (query/mutation/action objects with args/returns/handler) when defining Convex functions
When a function returns null, include returns: v.null() and return null explicitly
Use internalQuery/internalMutation/internalAction for private functions callable only by other Convex functions; do not expose sensitive logic via public query/mutation/action
Use query/mutation/action only for public API functions
Do not try to register functions via the api or internal objects
Always include argument and return validators for all Convex functions (query/internalQuery/mutation/internalMutation/action/internalAction)
In JS implementations, functions without an explicit return value implicitly return null
Use ctx.runQuery from queries/mutations/actions to call a query
Use ctx.runMutation from mutations/actions to call a mutation
Use ctx.runAction from actions to call an action
Only call an action from another action when crossing runtimes (e.g., V8 to Node); otherwise extract shared helper code
Minimize calls from actions to queries/mutations to avoid race conditions from splitting transactions
Pass FunctionReference values (from api/internal) to ctx.runQuery/ctx.runMutation/ctx.runAction; do not pass function implementations
When calling a function in the same file via ctx.run*, add an explicit return type annotation at the call site to avoid TS circularity
Use the generated api object for public functions and internal object for internal functions from convex/_generated/api.ts
Respect file-based routing for function references: e.g., convex/example.ts export f -> api.example.f; nested paths map to dot-separated namespaces
For paginated queries use paginationOptsValidator in args and .paginate(args.paginationOpts) on a query
v.bigint() is deprecated; use v.int64() for signed 64-bit integers
Use v.record(keys, values) for record-like data; v.map() and v.set() are not supported
For full-text search, use withSearchIndex("ind...
Files:
convex/schema.tsconvex/memories.tsconvex/userCustomization.ts
convex/schema.ts
📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)
convex/schema.ts: Define the Convex schema in convex/schema.ts
Import schema definition functions (defineSchema, defineTable) from convex/server
Understand system fields: _id uses v.id(tableName) and _creationTime uses v.number(); they’re auto-added to all documents
Include all indexed fields in the index name (e.g., by_field1_and_field2 for ["field1","field2"])
Query indexes in the same column order as defined; create separate indexes for alternate orders
Files:
convex/schema.ts
🧠 Learnings (3)
📚 Learning: 2025-08-29T13:33:09.937Z
Learnt from: CR
PR: hackerai-tech/hackerai#0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-08-29T13:33:09.937Z
Learning: Applies to convex/schema.ts : Define the Convex schema in convex/schema.ts
Applied to files:
convex/schema.ts
📚 Learning: 2025-08-29T13:33:09.937Z
Learnt from: CR
PR: hackerai-tech/hackerai#0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-08-29T13:33:09.937Z
Learning: Applies to convex/schema.ts : Import schema definition functions (defineSchema, defineTable) from convex/server
Applied to files:
convex/schema.ts
📚 Learning: 2025-08-29T13:33:09.937Z
Learnt from: CR
PR: hackerai-tech/hackerai#0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-08-29T13:33:09.937Z
Learning: Applies to convex/schema.ts : Understand system fields: _id uses v.id(tableName) and _creationTime uses v.number(); they’re auto-added to all documents
Applied to files:
convex/schema.ts
🧬 Code graph analysis (12)
app/components/tools/MemoryToolHandler.tsx (1)
types/chat.ts (1)
ChatStatus(57-57)
lib/db/actions.ts (2)
convex/_generated/api.js (2)
api(21-21)api(21-21)lib/errors.ts (1)
ChatSDKError(36-74)
app/components/MessagePartHandler.tsx (1)
app/components/tools/MemoryToolHandler.tsx (1)
MemoryToolHandler(17-166)
app/components/SidebarUserNav.tsx (2)
components/ui/dropdown-menu.tsx (1)
DropdownMenuItem(248-248)app/components/SettingsDialog.tsx (1)
SettingsDialog(209-209)
app/components/ManageMemoriesDialog.tsx (1)
lib/db/actions.ts (1)
deleteMemory(323-343)
convex/memories.ts (1)
convex/chats.ts (1)
validateServiceKey(5-9)
lib/ai/tools/update-memory.ts (2)
types/agent.ts (1)
ToolContext(16-23)lib/db/actions.ts (4)
createMemory(269-296)updateMemory(298-321)getMemoryById(255-267)deleteMemory(323-343)
app/api/chat/route.ts (3)
convex/userCustomization.ts (1)
getUserCustomization(64-108)lib/db/actions.ts (1)
getUserCustomization(221-235)lib/system-prompt.ts (1)
systemPrompt(136-185)
components/ui/switch.tsx (1)
lib/utils.ts (1)
cn(16-18)
lib/ai/tools/index.ts (1)
lib/ai/tools/update-memory.ts (1)
createUpdateMemory(11-174)
app/components/SettingsDialog.tsx (2)
hooks/use-mobile.ts (1)
useIsMobile(5-21)app/components/CustomizeHackerAIDialog.tsx (1)
CustomizeHackerAIDialog(51-258)
lib/system-prompt.ts (4)
types/chat.ts (1)
ChatMode(4-4)types/agent.ts (1)
ExecutionMode(25-25)lib/db/actions.ts (1)
getMemories(237-248)lib/system-prompt/memory.ts (1)
generateMemorySection(8-41)
🔇 Additional comments (13)
package.json (1)
30-31: LGTM: dependency addition matches new Switch component.@radix-ui/react-switch aligns with components/ui/switch.tsx. @types/node is already in devDependencies per guideline.
app/components/MessagePartHandler.tsx (2)
7-7: LGTM: MemoryToolHandler import fits existing tool-handler pattern.
72-74: LGTM: new tool-update_memory branch renders correctly.Return-based switch matches other handlers; no side effects introduced.
convex/schema.ts (1)
79-80: LGTM: include_memory_entries added to user_customization.Optional boolean is appropriate; matches downstream defaulting in queries.
convex/userCustomization.ts (1)
64-77: Return type: OK with v.union(null, object); defaulting to true is consistent.Both getters surface include_memory_entries as boolean with sane default; matches schema.
Also applies to: 118-129
app/api/chat/route.ts (2)
98-101: Good: fetch customization once, outside the streamFetching userCustomization before streaming avoids duplicate calls and flakiness. Defaulting to enabled matches backend semantics.
111-111: Default memoryEnabled is explicit and call site covers it
Call site in app/api/chat/route.ts passes memoryEnabled, and createTools in lib/ai/tools/index.ts defines memoryEnabled: boolean = true.lib/ai/tools/index.ts (1)
57-71: Conditional exposure looks good.Memory tool is cleanly gated by memoryEnabled and by mode. Nice.
app/components/tools/MemoryToolHandler.tsx (1)
110-165: Overall handler flow reads clean and resilient.Nice fallbacks for memory content and streaming states.
app/components/SettingsDialog.tsx (2)
57-57: Good accessibility practice with sr-only DialogTitleThe implementation correctly includes a screen-reader only DialogTitle for accessibility compliance, ensuring the dialog is properly announced to screen reader users.
161-161:?? truecorrectly provides the intended default without overridingfalse
Theinclude_memory_entriesproperty is optional and defaults totrueon creation; using the nullish coalescing operator preserves an explicitfalsewhile only falling back totruewhen the value isnullorundefined.Likely an incorrect or invalid review comment.
lib/system-prompt.ts (2)
1-8: Imports look good; type-only imports used correctly.
No issues spotted with these import changes.
136-141: No missing awaits – all calls tosystemPromptare already awaited as required.
| interface MemoryToolHandlerProps { | ||
| part: any; | ||
| status: ChatStatus; | ||
| } | ||
|
|
||
| export const MemoryToolHandler = ({ part, status }: MemoryToolHandlerProps) => { | ||
| const [isOpen, setIsOpen] = useState(false); | ||
| const { toolCallId, state, input, output } = part; | ||
|
|
||
| const memoryInput = (input || {}) as { | ||
| action?: "create" | "update" | "delete"; | ||
| knowledge_to_store?: string; | ||
| title?: string; | ||
| existing_knowledge_id?: string; | ||
| }; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Type the tool input and action union; avoid loose strings/any.
Improves safety and IDE help.
interface MemoryToolHandlerProps {
part: any;
status: ChatStatus;
}
export const MemoryToolHandler = ({ part, status }: MemoryToolHandlerProps) => {
const [isOpen, setIsOpen] = useState(false);
const { toolCallId, state, input, output } = part;
- const memoryInput = (input || {}) as {
- action?: "create" | "update" | "delete";
- knowledge_to_store?: string;
- title?: string;
- existing_knowledge_id?: string;
- };
+ type MemoryAction = "create" | "update" | "delete";
+ interface MemoryToolInput {
+ action?: MemoryAction;
+ knowledge_to_store?: string;
+ title?: string;
+ existing_knowledge_id?: string;
+ }
+ const memoryInput = (input ?? {}) as MemoryToolInput;
@@
- const getActionText = (action?: string) => {
+ const getActionText = (action?: MemoryAction) => {
@@
- const getStreamingActionText = (action?: string) => {
+ const getStreamingActionText = (action?: MemoryAction) => {Also applies to: 28-53
🤖 Prompt for AI Agents
In app/components/tools/MemoryToolHandler.tsx around lines 12-27 (and similarly
apply to 28-53), replace the loose any and string literals with a typed
discriminated union for the tool input/action: define a MemoryToolInput type
with action: "create" | "update" | "delete" and the optional fields title,
knowledge_to_store, existing_knowledge_id, then type the part prop as a
MemoryToolPart { toolCallId: string; state: string; input?: MemoryToolInput;
output?: any } (or a more precise output type) and update the destructuring to
use those types instead of casting from any; ensure you export or colocate the
types and use them throughout the component so IDEs and the compiler get proper
type-checking.
| interface Memory { | ||
| readonly memory_id: string; | ||
| readonly content: string; | ||
| readonly update_time: number; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Use Convex Id type for document IDs instead of string
Per repo guidelines, type document IDs strictly. memory_id should be Id<'memories'> (with a type-only import) to prevent accidental string mixing.
Apply this diff in-place:
+import type { Id } from "@/convex/_generated/dataModel";
interface Memory {
- readonly memory_id: string;
+ readonly memory_id: Id<"memories">;
readonly content: string;
readonly update_time: number;
}If the import path differs, run this to find the correct dataModel.ts and adjust the import:
🏁 Script executed:
#!/bin/bash
fd -a dataModel.ts
rg -nP 'export\s+type\s+Id<'Length of output: 101
🏁 Script executed:
#!/bin/bash
# Locate TypeScript config to inspect module alias mappings
fd -t f tsconfig.json
grep -R '"paths"' -n tsconfig.json -A10
grep -R '"baseUrl"' -n tsconfig.json -A5
# Search for any existing imports referencing dataModel or convex in TS/TSX files
rg -n "dataModel" -t ts -t tsx
rg -n "convex/" -t ts -t tsxLength of output: 247
Add Convex Id type for memory_id
Replace the string-typed ID with the Convex-generated Id<"memories"> and import the helper type:
+import type { Id } from "@/convex/_generated/dataModel";
interface Memory {
- readonly memory_id: string;
+ readonly memory_id: Id<"memories">;
readonly content: string;
readonly update_time: number;
}📝 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.
| interface Memory { | |
| readonly memory_id: string; | |
| readonly content: string; | |
| readonly update_time: number; | |
| } | |
| import type { Id } from "@/convex/_generated/dataModel"; | |
| interface Memory { | |
| readonly memory_id: Id<"memories">; | |
| readonly content: string; | |
| readonly update_time: number; | |
| } |
🤖 Prompt for AI Agents
In lib/system-prompt/memory.ts lines 1-5, replace the string-typed memory_id
with the Convex Id type and add the Id import: change memory_id's type from
string to Id<"memories"> and add an import line at the top like `import type {
Id } from "convex/values";` so the interface uses the Convex-generated ID type
for memories.
| // Memory section generation with combined logic | ||
| export const generateMemorySection = (memories: Memory[] | null): string => { | ||
| const baseMemoryInstructions = ` | ||
|
|
||
| <memories> | ||
| You may be provided a list of memories. These memories are generated from past conversations with the agent. | ||
| They may or may not be correct, so follow them if deemed relevant, but the moment you notice the user correct something you've done based on a memory, or you come across some information that contradicts or augments an existing memory, IT IS CRITICAL that you MUST update/delete the memory immediately using the update_memory tool. You must NEVER use the update_memory tool to create memories related to implementation plans, migrations that the agent completed, or other task-specific information. | ||
| If the user EVER contradicts your memory, then it's better to delete that memory rather than updating the memory. | ||
| You may create, update, or delete memories based on the criteria from the tool description.`; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid mentioning update_memory tool when memories are disabled
This base instruction always references update_memory. When the feature is off (include_memory_entries = false), tools are not exposed, but the prompt still instructs the model to use them, creating inconsistency.
Minimal fix: let this helper accept a memoryEnabled boolean and return an empty string (or omit tool mentions) when false. Example:
-export const generateMemorySection = (memories: Memory[] | null): string => {
+export const generateMemorySection = (
+ memories: Memory[] | null,
+ memoryEnabled = true,
+): string => {
+ if (!memoryEnabled) return "";Follow-up (outside this file): pass the flag from systemPrompt and only push the section when enabled.
📝 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.
| // Memory section generation with combined logic | |
| export const generateMemorySection = (memories: Memory[] | null): string => { | |
| const baseMemoryInstructions = ` | |
| <memories> | |
| You may be provided a list of memories. These memories are generated from past conversations with the agent. | |
| They may or may not be correct, so follow them if deemed relevant, but the moment you notice the user correct something you've done based on a memory, or you come across some information that contradicts or augments an existing memory, IT IS CRITICAL that you MUST update/delete the memory immediately using the update_memory tool. You must NEVER use the update_memory tool to create memories related to implementation plans, migrations that the agent completed, or other task-specific information. | |
| If the user EVER contradicts your memory, then it's better to delete that memory rather than updating the memory. | |
| You may create, update, or delete memories based on the criteria from the tool description.`; | |
| // Memory section generation with combined logic | |
| export const generateMemorySection = ( | |
| memories: Memory[] | null, | |
| memoryEnabled = true, | |
| ): string => { | |
| if (!memoryEnabled) return ""; | |
| const baseMemoryInstructions = ` | |
| <memories> | |
| You may be provided a list of memories. These memories are generated from past conversations with the agent. | |
| They may or may not be correct, so follow them if deemed relevant, but the moment you notice the user correct something you've done based on a memory, or you come across some information that contradicts or augments an existing memory, IT IS CRITICAL that you MUST update/delete the memory immediately using the update_memory tool. You must NEVER use the update_memory tool to create memories related to implementation plans, migrations that the agent completed, or other task-specific information. | |
| If the user EVER contradicts your memory, then it's better to delete that memory rather than updating the memory. | |
| You may create, update, or delete memories based on the criteria from the tool description.`; | |
| // ...rest of function... | |
| } |
| return `${baseMemoryInstructions} | ||
|
|
||
| The following memories were generated by the agent based on the user's interactions with the agent. | ||
| If relevant to the user query, you should follow them as you generate code, answer questions, and search the codebase. | ||
| If you notice that any memory is incorrect, you should update it using the update_memory tool. | ||
| "> | ||
| ${memoryContent} | ||
| </memories>`; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove stray characters breaking the section
There’s a dangling "> on Line 38 that will leak into the prompt.
Apply:
return `${baseMemoryInstructions}
The following memories were generated by the agent based on the user's interactions with the agent.
If relevant to the user query, you should follow them as you generate code, answer questions, and search the codebase.
If you notice that any memory is incorrect, you should update it using the update_memory tool.
- ">
+
${memoryContent}
</memories>`;📝 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.
| return `${baseMemoryInstructions} | |
| The following memories were generated by the agent based on the user's interactions with the agent. | |
| If relevant to the user query, you should follow them as you generate code, answer questions, and search the codebase. | |
| If you notice that any memory is incorrect, you should update it using the update_memory tool. | |
| "> | |
| ${memoryContent} | |
| </memories>`; | |
| }; | |
| return `${baseMemoryInstructions} | |
| The following memories were generated by the agent based on the user's interactions with the agent. | |
| If relevant to the user query, you should follow them as you generate code, answer questions, and search the codebase. | |
| If you notice that any memory is incorrect, you should update it using the update_memory tool. | |
| ${memoryContent} | |
| </memories>`; | |
| }; |
🤖 Prompt for AI Agents
In lib/system-prompt/memory.ts around lines 33 to 41, there's a stray '">` at
line 38 that will leak into the generated prompt; remove that dangling
characters so the template literal ends cleanly and the closing tag is correct
(ensure the template returns `${baseMemoryInstructions}\n\nThe following
memories... \n${memoryContent}\n</memories>` without the extra
quote/greater-than characters), and verify no other stray characters remain in
the surrounding template string.
| interface UserCustomization { | ||
| readonly nickname?: string; | ||
| readonly occupation?: string; | ||
| readonly personality?: string; | ||
| readonly traits?: string; | ||
| readonly additional_info?: string; | ||
| readonly include_memory_entries?: boolean; | ||
| readonly updated_at: number; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Tighten types: use PersonalityType instead of string
Aligns with stricter typing and avoids casts/guards at call sites.
-interface UserCustomization {
+interface UserCustomization {
readonly nickname?: string;
readonly occupation?: string;
- readonly personality?: string;
+ readonly personality?: PersonalityType;
readonly traits?: string;
readonly additional_info?: string;
readonly include_memory_entries?: boolean;
readonly updated_at: number;
}
-export const getPersonalityInstructions = (personality?: string): string => {
- if (!personality || !(personality in PERSONALITY_INSTRUCTIONS)) {
- return "";
- }
- return PERSONALITY_INSTRUCTIONS[personality as PersonalityType];
-};
+export const getPersonalityInstructions = (
+ personality?: PersonalityType,
+): string => (personality ? PERSONALITY_INSTRUCTIONS[personality] : "");Also applies to: 24-29
🤖 Prompt for AI Agents
In lib/system-prompt/personality.ts around lines 1-9 (and also lines 24-29), the
UserCustomization interface uses string for the personality field; replace
personality?: string with personality?: PersonalityType to tighten types, and
update any other occurrences in the file where a personality property or
parameter is typed as string (lines 24-29) to use PersonalityType; ensure
PersonalityType is imported or declared in this module and run typechecks to fix
any resulting mismatches at call sites.
Summary by CodeRabbit
New Features
Chores