add Nova research experience - #1391
Conversation
Adds a persisted Chat/Research mode selector, durable progress timeline, cancellation, navigation restoration, and a rendered/downloadable Markdown report experience. Research state loads in parallel with thread state, and unsupported attachments are rejected instead of silently ignored. Validation: 2 focused web tests, Biome, and diff checks.
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
How to use the Graphite Merge QueueAdd the label Main to this PR to add it to the merge queue. You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
supermemory-app | a89fdd7 | Commit Preview URL Branch Preview URL |
Aug 03 2026, 03:33 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
supermemory-mcp | a89fdd7 | Aug 03 2026, 03:32 PM |
|
@ishaanxgupta please remove test files in these as well |
|
Claude finished @ishaanxgupta's task in 3m 23s —— View job Review Complete
Summary: Reviewed ~1750 lines across 12 files. Found one error handling gap where research mode failures don't show user feedback. The rest of the implementation is solid — state management, polling, cleanup, and citation handling are well-structured. No security concerns. Score: 8/10 |
There was a problem hiding this comment.
Overview: Adds a persisted Chat/Research mode selector with durable progress timeline, cancellation, navigation restoration, and rendered Markdown report experience for Nova research.
Issues found:
- Missing
.catch()on the cancel fetch can cause unhandled promise rejections - Error thrown from
handleSendin research mode propagates as unhandled rejection - users won't see error feedback
The rest of the implementation looks solid. The state management, polling logic, citation handling, and UI components are well-structured. No security concerns.
Score: 8/10
Minor error handling gaps that should be addressed but won't cause data loss or security issues.
There was a problem hiding this comment.
Overview: Adds a persisted Chat/Research mode selector with durable progress timeline, cancellation, navigation restoration, and rendered Markdown report experience for Nova research.
Issues found:
- Error thrown from
submitChatMessagein research mode propagates as unhandled rejection — users won't see error feedback when research fails to start
The rest of the implementation is solid:
- State management and polling logic are well-structured with proper cleanup
cancelResearcherrors are properly caught inhandleStop- Citation handling and source annotation parsing have good edge-case coverage
- No security concerns with the URL construction or markdown rendering
Score: 8/10
One error handling gap that should be addressed for better UX, but won't cause data loss or security issues.
| const save = async () => { | ||
| if (saved || saving) return | ||
| setSaveState("saving") | ||
| try { | ||
| const response = await fetch( | ||
| `${apiBase}/chat/research/${runId}/save-to-memory`, | ||
| { method: "POST", credentials: "include" }, | ||
| ) | ||
| const result = (await response.json().catch(() => null)) as { | ||
| error?: string | ||
| saved?: boolean | ||
| } | null | ||
| if (!response.ok || !result?.saved) { | ||
| throw new Error(result?.error || "Could not save this report") | ||
| } | ||
| setSaveState("saved") | ||
| } catch { | ||
| setSaveState("error") | ||
| } | ||
| } |
There was a problem hiding this comment.
The save function is defined as a const arrow function expression (const save = async () => { ... }). According to the style guide rule 'Use function declarations over function expressions', this should be written as a function declaration: async function save() { ... }. Function expressions assigned to variables should be avoided in favor of named function declarations.
Spotted by Graphite (based on custom rule: TypeScript style guide (Google))
Is this helpful? React 👍 or 👎 to let us know.
| const submit = async () => { | ||
| if (!canContinue || submitting) return | ||
| setSubmitting(true) | ||
| setError(null) | ||
| try { | ||
| await onSubmit( | ||
| request.questions.map((item) => ({ | ||
| questionId: item.id, | ||
| value: answers[item.id]?.trim() ?? "", | ||
| })), | ||
| ) | ||
| } catch (submitError) { | ||
| setError( | ||
| submitError instanceof Error | ||
| ? submitError.message | ||
| : "Could not submit your answers.", | ||
| ) | ||
| setSubmitting(false) | ||
| } | ||
| } |
There was a problem hiding this comment.
The submit function is defined as a const arrow function expression (const submit = async () => { ... }). According to the style guide rule 'Use function declarations over function expressions', this should be written as a function declaration: async function submit() { ... }.
Spotted by Graphite (based on custom rule: TypeScript style guide (Google))
Is this helpful? React 👍 or 👎 to let us know.
| const invokeAction = async ( | ||
| action: NonNullable<typeof pendingAction>, | ||
| callback: (() => Promise<void> | void) | undefined, | ||
| ) => { | ||
| if (!callback || pendingAction) return | ||
| setPendingAction(action) | ||
| setActionError(null) | ||
| try { | ||
| await callback() | ||
| } catch { | ||
| setActionError( | ||
| action === "cancel" | ||
| ? "Could not stop this research. Please try again." | ||
| : action === "retry-finalization" | ||
| ? "Could not retry the report yet. Your collected sources are still preserved." | ||
| : "Could not start a new research run. Please try again.", | ||
| ) | ||
| } finally { | ||
| setPendingAction(null) | ||
| } | ||
| } |
There was a problem hiding this comment.
The invokeAction function is defined as a const arrow function expression (const invokeAction = async (...) => { ... }). According to the style guide rule 'Use function declarations over function expressions', this should be written as a function declaration: async function invokeAction(...) { ... }.
Spotted by Graphite (based on custom rule: TypeScript style guide (Google))
Is this helpful? React 👍 or 👎 to let us know.

Adds a persisted Chat/Research mode selector, durable progress timeline, cancellation, navigation restoration, and a rendered/downloadable Markdown report experience.