- {item.name}
+ {item.lastDocumentTitle ?? "No saved memory yet"}
{formatToolUsageTime(item.lastUsedAt, item.hasBeenUsed)}
-
- {item.lastDocumentTitle ?? "No saved memory yet"}
-
{item.lastDocumentPreview ? (
{item.lastDocumentPreview}
From 6fa2a8c70e5ee5e00530d0f1813431908f7cca41 Mon Sep 17 00:00:00 2001
From: ved015
Date: Fri, 15 May 2026 13:13:31 +0530
Subject: [PATCH 15/19] add support for other plugins
---
apps/web/app/auth/connect/page.tsx | 11 ++++
apps/web/components/dashboard-view.tsx | 83 ++++++++++++++++++++------
apps/web/middleware.ts | 11 +++-
3 files changed, 84 insertions(+), 21 deletions(-)
diff --git a/apps/web/app/auth/connect/page.tsx b/apps/web/app/auth/connect/page.tsx
index 37f61d3d6..e8d0bac85 100644
--- a/apps/web/app/auth/connect/page.tsx
+++ b/apps/web/app/auth/connect/page.tsx
@@ -101,6 +101,17 @@ const PLUGIN_INFO: Record = {
],
icon: "/images/plugins/codex.svg",
},
+ codex: {
+ name: "OpenAI Codex",
+ description:
+ "Memory layer for Codex. Keeps coding context, preferences, and decisions available across sessions.",
+ features: [
+ "Auto-recalls relevant context before prompts",
+ "Captures useful coding decisions from sessions",
+ "Supports project and custom memory containers",
+ ],
+ icon: "/images/plugins/opencode.svg",
+ },
}
function getPluginName(client: string): string {
diff --git a/apps/web/components/dashboard-view.tsx b/apps/web/components/dashboard-view.tsx
index a6216e3df..ab8b037d5 100644
--- a/apps/web/components/dashboard-view.tsx
+++ b/apps/web/components/dashboard-view.tsx
@@ -364,22 +364,66 @@ function getDocumentText(document: DocumentWithMemories): string {
return typeof document.content === "string" ? document.content : ""
}
+function toMetadataRecord(value: unknown): Record | null {
+ if (!value) return null
+ if (typeof value === "object" && !Array.isArray(value)) {
+ return value as Record
+ }
+ if (typeof value !== "string") return null
+
+ try {
+ const parsed = JSON.parse(value)
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
+ ? (parsed as Record)
+ : null
+ } catch {
+ return null
+ }
+}
+
+function getDocumentMetadataRecords(
+ document: DocumentWithMemories,
+): Record[] {
+ const records = [toMetadataRecord(document.metadata)]
+
+ for (const entry of document.memoryEntries ?? []) {
+ records.push(
+ toMetadataRecord(entry.metadata),
+ toMetadataRecord(entry.sourceMetadata),
+ )
+ }
+
+ return records.filter((record): record is Record => !!record)
+}
+
+function hasClaudeCodeContainer(document: DocumentWithMemories): boolean {
+ const containerTags =
+ (document as { containerTags?: string[] }).containerTags ?? []
+ if (containerTags.some((tag) => tag.startsWith("claudecode_"))) return true
+
+ return (document.memoryEntries ?? []).some((entry) =>
+ entry.spaceContainerTag?.startsWith("claudecode_"),
+ )
+}
+
function getPluginClientFromDocument(
document: DocumentWithMemories,
): string | null {
- const metadata =
- document.metadata && typeof document.metadata === "object"
- ? document.metadata
- : {}
- const metadataClient =
- typeof metadata.sm_client === "string"
- ? metadata.sm_client
- : typeof metadata.sm_internal_plugin_client === "string"
- ? metadata.sm_internal_plugin_client
- : typeof metadata.sm_internal_mcp_client_name === "string"
- ? metadata.sm_internal_mcp_client_name
- : null
- if (metadataClient) return metadataClient.toLowerCase()
+ for (const metadata of getDocumentMetadataRecords(document)) {
+ const metadataClient =
+ typeof metadata.sm_client === "string"
+ ? metadata.sm_client
+ : typeof metadata.sm_internal_plugin_client === "string"
+ ? metadata.sm_internal_plugin_client
+ : typeof metadata.sm_internal_mcp_client_name === "string"
+ ? metadata.sm_internal_mcp_client_name
+ : null
+ if (metadataClient) return metadataClient.toLowerCase()
+
+ if (metadata.sm_source === "claude-code-plugin") return "claude_code"
+ }
+
+ if (hasClaudeCodeContainer(document)) return "claude_code"
const content = getDocumentText(document)
const title = document.title ?? ""
@@ -526,16 +570,17 @@ function parseToolUsage(
}
for (const doc of recentMcpDocuments) {
- const metadata =
- doc.metadata && typeof doc.metadata === "object" ? doc.metadata : {}
+ const metadataRecords = getDocumentMetadataRecords(doc)
const clientName =
- typeof metadata.sm_internal_mcp_client_name === "string"
- ? metadata.sm_internal_mcp_client_name
- : null
+ metadataRecords
+ .map((record) => record.sm_internal_mcp_client_name)
+ .find((value): value is string => typeof value === "string") ?? null
const pluginClient = getPluginClientFromDocument(doc)
const isMcpDocument =
doc.source === "mcp" ||
- metadata.sm_internal_event_from === "mcp" ||
+ metadataRecords.some(
+ (record) => record.sm_internal_event_from === "mcp",
+ ) ||
!!clientName
const isPluginDocument = !!pluginClient && pluginClient !== "mcp"
diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts
index 885144acd..9d42e1bb8 100644
--- a/apps/web/middleware.ts
+++ b/apps/web/middleware.ts
@@ -8,7 +8,14 @@ export default async function proxy(request: Request) {
console.debug("[PROXY] Path:", url.pathname)
console.debug("[PROXY] Method:", request.method)
- const sessionCookie = getSessionCookie(request)
+ const isDevHost =
+ url.hostname === "localhost" ||
+ url.hostname.includes(".localhost") ||
+ url.hostname.includes(".dev.supermemory.ai")
+
+ const sessionCookie = isDevHost
+ ? getSessionCookie(request, { cookiePrefix: "better-auth-dev" })
+ : getSessionCookie(request)
console.debug("[PROXY] Session cookie exists:", !!sessionCookie)
// Always allow access to login and waitlist pages
@@ -71,6 +78,6 @@ export default async function proxy(request: Request) {
export const config = {
matcher: [
- "/((?!_next/static|_next/image|images|icon.png|monitoring|opengraph-image.png|bg-rectangle.png|onboarding|ingest|login|api/emails|mcp-supported-tools|mcp-icon.svg).*)",
+ "/((?!_next/static|_next/image|images|icon.png|manifest.webmanifest|monitoring|opengraph-image.png|bg-rectangle.png|onboarding|ingest|login|api/emails|mcp-supported-tools|mcp-icon.svg).*)",
],
}
From 91b96c679f1abe326099f510df802d3cb6ac40fd Mon Sep 17 00:00:00 2001
From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com>
Date: Fri, 15 May 2026 08:08:04 +0000
Subject: [PATCH 16/19] fix: remove duplicate codex key in PLUGIN_INFO
Co-Authored-By: Claude Opus 4.5
---
apps/web/app/auth/connect/page.tsx | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/apps/web/app/auth/connect/page.tsx b/apps/web/app/auth/connect/page.tsx
index e8d0bac85..37f61d3d6 100644
--- a/apps/web/app/auth/connect/page.tsx
+++ b/apps/web/app/auth/connect/page.tsx
@@ -101,17 +101,6 @@ const PLUGIN_INFO: Record = {
],
icon: "/images/plugins/codex.svg",
},
- codex: {
- name: "OpenAI Codex",
- description:
- "Memory layer for Codex. Keeps coding context, preferences, and decisions available across sessions.",
- features: [
- "Auto-recalls relevant context before prompts",
- "Captures useful coding decisions from sessions",
- "Supports project and custom memory containers",
- ],
- icon: "/images/plugins/opencode.svg",
- },
}
function getPluginName(client: string): string {
From 504490f2a889273fde03cb83ac2759625011aff7 Mon Sep 17 00:00:00 2001
From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com>
Date: Thu, 21 May 2026 10:45:16 +0000
Subject: [PATCH 17/19] fix(web): restore UTF-8 encoding in dashboard-view.tsx
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fix corrupted Unicode characters (⌘, —, ✓, →, ·) that were displaying
as mojibake (⌘, â€", âœ", â†', ·) in the TIPS array and other strings.
Co-authored-by: Ishaan Gupta
---
apps/web/components/dashboard-view.tsx | 110 ++++++++++++-------------
1 file changed, 55 insertions(+), 55 deletions(-)
diff --git a/apps/web/components/dashboard-view.tsx b/apps/web/components/dashboard-view.tsx
index 123f2aa6e..5b0af8469 100644
--- a/apps/web/components/dashboard-view.tsx
+++ b/apps/web/components/dashboard-view.tsx
@@ -141,108 +141,108 @@ export type MemoryOfDay = {
const TIPS: Record = {
developer: [
- "Use ⌘K to search code snippets and docs by intent, not just keywords",
+ "Use ⌘K to search code snippets and docs by intent, not just keywords",
"Connect Claude MCP to query your saved knowledge from any IDE",
- "Save GitHub repos and READMEs — ask questions across all of them",
+ "Save GitHub repos and READMEs — ask questions across all of them",
"Use 'Related' on highlights to find connected technical concepts",
- "Save a Stack Overflow answer once — find it again by what it does",
+ "Save a Stack Overflow answer once — find it again by what it does",
"Drop in your last 3 PRs and ask Supermemory for the review patterns",
"Save your team's RFCs and surface the ones touching your work",
- "Save error messages with their fixes — search by symptom next time",
- "Save framework docs once — semantic search beats Cmd+F across pages",
+ "Save error messages with their fixes — search by symptom next time",
+ "Save framework docs once — semantic search beats Cmd+F across pages",
"Connect Notion to make your engineering specs instantly findable",
"Save the docs for libraries you keep forgetting and grep them by intent",
"Use Daily Brief to resurface the design doc you skimmed last week",
- "Save changelogs as you skim — pull breaking changes back later",
- "Save a debugging session as a note — find it again by the symptom",
+ "Save changelogs as you skim — pull breaking changes back later",
+ "Save a debugging session as a note — find it again by the symptom",
],
research: [
"Save papers and ask questions across your entire reading list",
"Use 'Related' on highlights to surface connected research",
"Connect Notion to index your notes alongside your papers",
"Semantic search means you can ask questions, not just search titles",
- "Save a paper once — Supermemory finds it later by what it argued",
+ "Save a paper once — Supermemory finds it later by what it argued",
"Drop in 5 papers on a topic and ask for the consensus and disagreements",
- "Save citations as you read — pull them back out by claim",
+ "Save citations as you read — pull them back out by claim",
"Connect Google Drive to make your dataset notes searchable",
"Use Daily Brief to resurface a finding you almost forgot",
- "Save a methodology note once — find it next time you need that protocol",
- "Save preprints alongside your reading list — ask what's new since last week",
- "Save quotes with their source — find them later by the idea",
+ "Save a methodology note once — find it next time you need that protocol",
+ "Save preprints alongside your reading list — ask what's new since last week",
+ "Save quotes with their source — find them later by the idea",
],
finance: [
"Save articles and ask follow-up questions across your research",
"Connect Notion to keep your investment thesis searchable",
- "Use ⌘K to find specific data points across all your saves",
+ "Use ⌘K to find specific data points across all your saves",
"Daily Brief surfaces connections you may have missed",
- "Save earnings call transcripts once — pull guidance back by ticker or theme",
- "Save a thesis once — find it months later by the conviction, not the filename",
+ "Save earnings call transcripts once — pull guidance back by ticker or theme",
+ "Save a thesis once — find it months later by the conviction, not the filename",
"Drop in three sell-side reports and ask for the disagreements",
- "Save market commentary daily — surface the calls that aged well",
+ "Save market commentary daily — surface the calls that aged well",
"Connect Google Drive to query your models without opening them",
- "Save a chart with a note — find it again by what it showed",
- "Save analyst takes — pull them back when the thesis matters again",
+ "Save a chart with a note — find it again by what it showed",
+ "Save analyst takes — pull them back when the thesis matters again",
],
design: [
- "Save inspiration and search by concept — 'minimalist UI' finds the right ones",
- "Use ⌘K to rediscover references by meaning, not filename",
+ "Save inspiration and search by concept — 'minimalist UI' finds the right ones",
+ "Use ⌘K to rediscover references by meaning, not filename",
"Connect Notion to make your briefs and moodboards searchable",
"Chrome extension saves any page in one click while you browse",
- "Save a screenshot with a note — find it later by what it taught you",
+ "Save a screenshot with a note — find it later by what it taught you",
"Drop in 10 onboarding flows and ask Supermemory for the common patterns",
- "Save your design crits — find the feedback on a specific decision later",
- "Save a brand guideline once — search it by intent, not page number",
+ "Save your design crits — find the feedback on a specific decision later",
+ "Save a brand guideline once — search it by intent, not page number",
"Connect Google Drive to index your Figma exports and briefs",
"Use Daily Brief to resurface a reference that fits today's work",
- "Save references by mood — pull them back when the brief calls for it",
+ "Save references by mood — pull them back when the brief calls for it",
],
legal: [
"Save documents and search across them semantically in seconds",
"Connect Notion to index your memos and case notes together",
"Use Daily Brief to resurface relevant precedents automatically",
"Google Drive sync keeps your contracts indexed and queryable",
- "Save a clause once — find it next time by what it does, not where it lives",
- "Save case law as you read — pull precedents back by argument",
+ "Save a clause once — find it next time by what it does, not where it lives",
+ "Save case law as you read — pull precedents back by argument",
"Drop in three contracts and ask for the diffs in indemnity language",
- "Save regulator updates — surface the ones touching your matter",
- "Save a memo once — search by issue, not by file name",
- "Save deposition notes — find specific testimony by claim later",
+ "Save regulator updates — surface the ones touching your matter",
+ "Save a memo once — search by issue, not by file name",
+ "Save deposition notes — find specific testimony by claim later",
],
marketing: [
- "Save campaigns and resources — ask what worked across all of them",
+ "Save campaigns and resources — ask what worked across all of them",
"Chrome extension captures competitor pages in one click",
"Use 'Related' to find similar campaigns in your archive",
"Connect Notion to make your campaign briefs instantly searchable",
- "Save a competitor's landing page — surface their positioning later by claim",
+ "Save a competitor's landing page — surface their positioning later by claim",
"Drop in five launch retros and ask for the patterns that drove growth",
"Save ad references and find them by mood, not by URL",
- "Save your weekly metrics notes — pull trends back by quarter",
+ "Save your weekly metrics notes — pull trends back by quarter",
"Use Daily Brief to resurface a positioning note from last campaign",
- "Save creative briefs — find similar ones when starting a new one",
+ "Save creative briefs — find similar ones when starting a new one",
],
medical: [
"Save studies and query across your entire reading list",
"Connect Notion to keep clinical notes alongside research",
- "Use ⌘K to find specific findings across hundreds of papers",
+ "Use ⌘K to find specific findings across hundreds of papers",
"Daily Brief surfaces relevant research from your saves automatically",
- "Save a guideline once — pull it back by clinical scenario",
+ "Save a guideline once — pull it back by clinical scenario",
"Drop in three trials and ask Supermemory for the methodological diffs",
- "Save case reports — surface them later by symptom or finding",
+ "Save case reports — surface them later by symptom or finding",
"Connect Google Drive to index protocols across your team",
- "Save teaching points from rounds — find them by topic next month",
- "Save differentials as notes — pull them back when the presentation repeats",
+ "Save teaching points from rounds — find them by topic next month",
+ "Save differentials as notes — pull them back when the presentation repeats",
],
default: [
- "Use ⌘K to search by meaning — ask questions, not just keywords",
+ "Use ⌘K to search by meaning — ask questions, not just keywords",
"Daily Brief surfaces insights from your saves each morning",
"Chrome extension saves any page in one click while you browse",
"Connect integrations to make all your knowledge searchable here",
- "Save a page once — find it later by what it said, not its title",
- "Save the thing you'd normally bookmark — find it again by intent",
+ "Save a page once — find it later by what it said, not its title",
+ "Save the thing you'd normally bookmark — find it again by intent",
"Drop in 10 articles on a topic and ask for the through-line",
- "Save an idea — Supermemory connects it to your earlier ones",
+ "Save an idea — Supermemory connects it to your earlier ones",
"Use Daily Brief to resurface something useful you forgot you saved",
- "Save a thread you liked — pull it back later by what it was about",
+ "Save a thread you liked — pull it back later by what it was about",
],
}
@@ -270,7 +270,7 @@ const PROFESSION_LABELS: {
{ value: "medical", label: "Medical" },
]
-// Static plugin metadata — shared between PluginPromoCard and RecommendedPluginsCard
+// Static plugin metadata — shared between PluginPromoCard and RecommendedPluginsCard
const PLUGIN_STATIC = [
{
id: "mcp",
@@ -285,7 +285,7 @@ const PLUGIN_STATIC = [
name: "Chrome Extension",
Icon: ChromeIcon,
accentColor: "#4BA0FA",
- tagline: "Save any page in one click — findable by meaning, forever",
+ tagline: "Save any page in one click — findable by meaning, forever",
cta: "Install",
},
{
@@ -310,7 +310,7 @@ const PLUGIN_STATIC = [
Icon: GoogleDrive,
accentColor: "#4BA0FA",
tagline:
- "Index your Drive files — ask questions across docs, slides, sheets",
+ "Index your Drive files — ask questions across docs, slides, sheets",
cta: "Connect",
},
] as const
@@ -479,7 +479,7 @@ function getDocumentPreview(document: DocumentWithMemories): string | null {
})
.filter(Boolean)
- if (transcriptTurns.length > 0) return transcriptTurns.join(" · ")
+ if (transcriptTurns.length > 0) return transcriptTurns.join(" · ")
const cleaned = compactText(
content
@@ -870,7 +870,7 @@ function RecommendedPluginsCard({
) : suggestions.length === 0 ? (
- You're all set ✓
+ You're all set ✓
) : (
@@ -893,7 +893,7 @@ function RecommendedPluginsCard({