aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorDhravya Shah <[email protected]>2025-09-18 20:34:18 -0700
committerDhravya Shah <[email protected]>2025-09-18 21:03:49 -0700
commit1fcb56908920da386900abb4ce2383374a625c72 (patch)
tree0f9d7f695d4c9b1b85be3950fc869e0061dff3ed /packages
parentrefetching logic change (diff)
downloadsupermemory-09-18-formatting.tar.xz
supermemory-09-18-formatting.zip
Diffstat (limited to 'packages')
-rw-r--r--packages/ai-sdk/src/tools.test.ts124
-rw-r--r--packages/ai-sdk/src/tools.ts48
-rw-r--r--packages/ai-sdk/tsdown.config.ts4
-rw-r--r--packages/hooks/use-keypress.ts16
-rw-r--r--packages/hooks/use-mobile.ts24
-rw-r--r--packages/lib/api.ts18
-rw-r--r--packages/lib/auth-context.tsx77
-rw-r--r--packages/lib/auth.middleware.ts6
-rw-r--r--packages/lib/auth.ts14
-rw-r--r--packages/lib/constants.ts10
-rw-r--r--packages/lib/error-tracking.tsx103
-rw-r--r--packages/lib/generate-id.ts4
-rw-r--r--packages/lib/glass-effect-manager.ts196
-rw-r--r--packages/lib/posthog.tsx46
-rw-r--r--packages/lib/queries.ts94
-rw-r--r--packages/lib/query-client.tsx14
-rw-r--r--packages/lib/similarity.ts50
-rw-r--r--packages/lib/utils.ts8
-rw-r--r--packages/tools/src/ai-sdk.ts44
-rw-r--r--packages/tools/src/index.ts2
-rw-r--r--packages/tools/src/openai.ts104
-rw-r--r--packages/tools/src/shared.ts16
-rw-r--r--packages/tools/src/tools.test.ts232
-rw-r--r--packages/tools/src/types.ts6
-rw-r--r--packages/tools/tsdown.config.ts4
-rw-r--r--packages/ui/memory-graph/hooks/use-graph-data.ts18
-rw-r--r--packages/ui/memory-graph/memory-graph.tsx64
-rw-r--r--packages/ui/memory-graph/navigation-controls.tsx97
-rw-r--r--packages/ui/pages/login.tsx4
-rw-r--r--packages/validation/api.ts104
-rw-r--r--packages/validation/connection.ts68
31 files changed, 831 insertions, 788 deletions
diff --git a/packages/ai-sdk/src/tools.test.ts b/packages/ai-sdk/src/tools.test.ts
index ebbe2235..aab4d451 100644
--- a/packages/ai-sdk/src/tools.test.ts
+++ b/packages/ai-sdk/src/tools.test.ts
@@ -1,27 +1,29 @@
-import { createOpenAI } from "@ai-sdk/openai"
-import { generateText } from "ai"
-import { describe, expect, it } from "vitest"
-import { type SupermemoryToolsConfig, supermemoryTools } from "./tools"
+import { createOpenAI } from "@ai-sdk/openai";
+import { generateText } from "ai";
+import { describe, expect, it } from "vitest";
+import { type SupermemoryToolsConfig, supermemoryTools } from "./tools";
-import "dotenv/config"
+import "dotenv/config";
describe("supermemoryTools", () => {
// Required API keys - tests will fail if not provided
- const testApiKey = process.env.SUPERMEMORY_API_KEY
- const testOpenAIKey = process.env.OPENAI_API_KEY
+ const testApiKey = process.env.SUPERMEMORY_API_KEY;
+ const testOpenAIKey = process.env.OPENAI_API_KEY;
if (!testApiKey) {
throw new Error(
"SUPERMEMORY_API_KEY environment variable is required for tests",
- )
+ );
}
if (!testOpenAIKey) {
- throw new Error("OPENAI_API_KEY environment variable is required for tests")
+ throw new Error(
+ "OPENAI_API_KEY environment variable is required for tests",
+ );
}
// Optional configuration with defaults
- const testBaseUrl = process.env.SUPERMEMORY_BASE_URL ?? undefined
- const testModelName = process.env.MODEL_NAME || "gpt-5-mini"
+ const testBaseUrl = process.env.SUPERMEMORY_BASE_URL ?? undefined;
+ const testModelName = process.env.MODEL_NAME || "gpt-5-mini";
const testPrompts = [
"What do you remember about my preferences?",
@@ -29,57 +31,57 @@ describe("supermemoryTools", () => {
"What are my current projects?",
"Remind me of my interests and hobbies",
"What should I focus on today?",
- ]
+ ];
describe("client initialization", () => {
it("should create tools with default configuration", () => {
- const config: SupermemoryToolsConfig = {}
- const tools = supermemoryTools(testApiKey, config)
+ const config: SupermemoryToolsConfig = {};
+ const tools = supermemoryTools(testApiKey, config);
- expect(tools).toBeDefined()
- expect(tools.searchMemories).toBeDefined()
- expect(tools.addMemory).toBeDefined()
- })
+ expect(tools).toBeDefined();
+ expect(tools.searchMemories).toBeDefined();
+ expect(tools.addMemory).toBeDefined();
+ });
it("should create tools with custom baseUrl", () => {
const config: SupermemoryToolsConfig = {
baseUrl: testBaseUrl,
- }
- const tools = supermemoryTools(testApiKey, config)
+ };
+ const tools = supermemoryTools(testApiKey, config);
- expect(tools).toBeDefined()
- expect(tools.searchMemories).toBeDefined()
- expect(tools.addMemory).toBeDefined()
- })
+ expect(tools).toBeDefined();
+ expect(tools.searchMemories).toBeDefined();
+ expect(tools.addMemory).toBeDefined();
+ });
it("should create tools with projectId configuration", () => {
const config: SupermemoryToolsConfig = {
projectId: "test-project-123",
- }
- const tools = supermemoryTools(testApiKey, config)
+ };
+ const tools = supermemoryTools(testApiKey, config);
- expect(tools).toBeDefined()
- expect(tools.searchMemories).toBeDefined()
- expect(tools.addMemory).toBeDefined()
- })
+ expect(tools).toBeDefined();
+ expect(tools.searchMemories).toBeDefined();
+ expect(tools.addMemory).toBeDefined();
+ });
it("should create tools with custom container tags", () => {
const config: SupermemoryToolsConfig = {
containerTags: ["custom-tag-1", "custom-tag-2"],
- }
- const tools = supermemoryTools(testApiKey, config)
+ };
+ const tools = supermemoryTools(testApiKey, config);
- expect(tools).toBeDefined()
- expect(tools.searchMemories).toBeDefined()
- expect(tools.addMemory).toBeDefined()
- })
- })
+ expect(tools).toBeDefined();
+ expect(tools.searchMemories).toBeDefined();
+ expect(tools.addMemory).toBeDefined();
+ });
+ });
describe("AI SDK integration", () => {
it("should work with AI SDK generateText", async () => {
const openai = createOpenAI({
apiKey: testOpenAIKey,
- })
+ });
const result = await generateText({
model: openai(testModelName),
@@ -100,22 +102,22 @@ describe("supermemoryTools", () => {
baseUrl: testBaseUrl,
}),
},
- })
+ });
- expect(result).toBeDefined()
- expect(result.text).toBeDefined()
- expect(typeof result.text).toBe("string")
- })
+ expect(result).toBeDefined();
+ expect(result.text).toBeDefined();
+ expect(typeof result.text).toBe("string");
+ });
it("should use tools when prompted", async () => {
const openai = createOpenAI({
apiKey: testOpenAIKey,
- })
+ });
const tools = supermemoryTools(testApiKey, {
projectId: "test-tool-usage",
baseUrl: testBaseUrl,
- })
+ });
const result = await generateText({
model: openai(testModelName),
@@ -133,24 +135,24 @@ describe("supermemoryTools", () => {
tools: {
addMemory: tools.addMemory,
},
- })
+ });
- expect(result).toBeDefined()
- expect(result.text).toBeDefined()
- expect(result.toolCalls).toBeDefined()
+ expect(result).toBeDefined();
+ expect(result.text).toBeDefined();
+ expect(result.toolCalls).toBeDefined();
if (result.toolCalls && result.toolCalls.length > 0) {
const addMemoryCall = result.toolCalls.find(
(call) => call.toolName === "addMemory",
- )
- expect(addMemoryCall).toBeDefined()
+ );
+ expect(addMemoryCall).toBeDefined();
}
- })
+ });
it("should handle multiple tool types", async () => {
const openai = createOpenAI({
apiKey: testOpenAIKey,
- })
+ });
const result = await generateText({
model: openai(testModelName),
@@ -171,11 +173,11 @@ describe("supermemoryTools", () => {
containerTags: ["test-multi-tools"],
}),
},
- })
-
- expect(result).toBeDefined()
- expect(result.text).toBeDefined()
- expect(typeof result.text).toBe("string")
- })
- })
-})
+ });
+
+ expect(result).toBeDefined();
+ expect(result.text).toBeDefined();
+ expect(typeof result.text).toBe("string");
+ });
+ });
+});
diff --git a/packages/ai-sdk/src/tools.ts b/packages/ai-sdk/src/tools.ts
index d0461924..97f54791 100644
--- a/packages/ai-sdk/src/tools.ts
+++ b/packages/ai-sdk/src/tools.ts
@@ -1,15 +1,15 @@
-import { tool } from "ai"
-import Supermemory from "supermemory"
-import { z } from "zod"
+import { tool } from "ai";
+import Supermemory from "supermemory";
+import { z } from "zod";
/**
* Supermemory configuration
* Only one of `projectId` or `containerTags` can be provided.
*/
export interface SupermemoryToolsConfig {
- baseUrl?: string
- containerTags?: string[]
- projectId?: string
+ baseUrl?: string;
+ containerTags?: string[];
+ projectId?: string;
}
/**
@@ -22,11 +22,11 @@ export function supermemoryTools(
const client = new Supermemory({
apiKey,
...(config?.baseUrl ? { baseURL: config.baseUrl } : {}),
- })
+ });
const containerTags = config?.projectId
? [`sm_project_${config?.projectId}`]
- : (config?.containerTags ?? ["sm_project_default"])
+ : (config?.containerTags ?? ["sm_project_default"]);
const searchMemories = tool({
description:
@@ -60,21 +60,21 @@ export function supermemoryTools(
limit,
chunkThreshold: 0.6,
includeFullDocs,
- })
+ });
return {
success: true,
results: response.results,
count: response.results?.length || 0,
- }
+ };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
- }
+ };
}
},
- })
+ });
const addMemory = tool({
description:
@@ -88,31 +88,31 @@ export function supermemoryTools(
}),
execute: async ({ memory }) => {
try {
- const metadata: Record<string, string | number | boolean> = {}
+ const metadata: Record<string, string | number | boolean> = {};
const response = await client.memories.add({
content: memory,
containerTags,
...(Object.keys(metadata).length > 0 && { metadata }),
- })
+ });
return {
success: true,
memory: response,
- }
+ };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
- }
+ };
}
},
- })
+ });
return {
searchMemories,
addMemory,
- }
+ };
}
// Export individual tool creators for more flexibility
@@ -120,14 +120,14 @@ export const searchMemoriesTool = (
apiKey: string,
config?: SupermemoryToolsConfig,
) => {
- const { searchMemories } = supermemoryTools(apiKey, config)
- return searchMemories
-}
+ const { searchMemories } = supermemoryTools(apiKey, config);
+ return searchMemories;
+};
export const addMemoryTool = (
apiKey: string,
config?: SupermemoryToolsConfig,
) => {
- const { addMemory } = supermemoryTools(apiKey, config)
- return addMemory
-}
+ const { addMemory } = supermemoryTools(apiKey, config);
+ return addMemory;
+};
diff --git a/packages/ai-sdk/tsdown.config.ts b/packages/ai-sdk/tsdown.config.ts
index f587b211..53839a00 100644
--- a/packages/ai-sdk/tsdown.config.ts
+++ b/packages/ai-sdk/tsdown.config.ts
@@ -1,4 +1,4 @@
-import { defineConfig } from "tsdown"
+import { defineConfig } from "tsdown";
export default defineConfig({
entry: ["src/index.ts"],
@@ -12,4 +12,4 @@ export default defineConfig({
sourcemap: true,
},
exports: true,
-})
+});
diff --git a/packages/hooks/use-keypress.ts b/packages/hooks/use-keypress.ts
index 42906660..eee23acb 100644
--- a/packages/hooks/use-keypress.ts
+++ b/packages/hooks/use-keypress.ts
@@ -1,15 +1,15 @@
-import { useEffect } from "react"
+import { useEffect } from "react";
export const useKeyPress = (key: string, callback: () => void) => {
useEffect(() => {
const handler = (e: KeyboardEvent) => {
if (e.key === key && e.altKey) {
- callback()
+ callback();
}
- }
- window.addEventListener("keydown", handler)
+ };
+ window.addEventListener("keydown", handler);
return () => {
- window.removeEventListener("keydown", handler)
- }
- }, [key, callback])
-}
+ window.removeEventListener("keydown", handler);
+ };
+ }, [key, callback]);
+};
diff --git a/packages/hooks/use-mobile.ts b/packages/hooks/use-mobile.ts
index 283bbb4c..0a892310 100644
--- a/packages/hooks/use-mobile.ts
+++ b/packages/hooks/use-mobile.ts
@@ -1,19 +1,21 @@
-import * as React from "react"
+import * as React from "react";
-const MOBILE_BREAKPOINT = 768
+const MOBILE_BREAKPOINT = 768;
export function useIsMobile() {
- const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
+ undefined,
+ );
React.useEffect(() => {
- const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
const onChange = () => {
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
- }
- mql.addEventListener("change", onChange)
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
- return () => mql.removeEventListener("change", onChange)
- }, [])
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
+ };
+ mql.addEventListener("change", onChange);
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
+ return () => mql.removeEventListener("change", onChange);
+ }, []);
- return !!isMobile
+ return !!isMobile;
}
diff --git a/packages/lib/api.ts b/packages/lib/api.ts
index ad343050..82c0b8ef 100644
--- a/packages/lib/api.ts
+++ b/packages/lib/api.ts
@@ -10,6 +10,7 @@ import {
DeleteProjectSchema,
DocumentsWithMemoriesQuerySchema,
DocumentsWithMemoriesResponseSchema,
+ GetMemoryResponseSchema,
ListMemoriesResponseSchema,
ListProjectsResponseSchema,
MemoryAddSchema,
@@ -126,7 +127,24 @@ export const apiSchema = createSchema({
input: SettingsRequestSchema,
output: SettingsResponseSchema,
},
+
+ "@get/documents/:id": {
+ output: GetMemoryResponseSchema,
+ params: z.object({ id: z.string() }),
+ },
+
// Memory operations
+ "@get/documents": {
+ output: ListMemoriesResponseSchema,
+ query: z
+ .object({
+ limit: z.number().optional(),
+ page: z.number().optional(),
+ status: z.string().optional(),
+ containerTags: z.array(z.string()).optional(),
+ })
+ .optional(),
+ },
"@post/documents": {
input: MemoryAddSchema,
output: MemoryResponseSchema,
diff --git a/packages/lib/auth-context.tsx b/packages/lib/auth-context.tsx
index 66ff84bc..a88c73c8 100644
--- a/packages/lib/auth-context.tsx
+++ b/packages/lib/auth-context.tsx
@@ -1,4 +1,4 @@
-"use client"
+"use client";
import {
createContext,
@@ -6,75 +6,74 @@ import {
useContext,
useEffect,
useState,
-} from "react"
-import { authClient, useSession } from "./auth"
+} from "react";
+import { authClient, useSession } from "./auth";
-type Organization = typeof authClient.$Infer.ActiveOrganization
-type SessionData = NonNullable<ReturnType<typeof useSession>["data"]>
+type Organization = typeof authClient.$Infer.ActiveOrganization;
+type SessionData = NonNullable<ReturnType<typeof useSession>["data"]>;
interface AuthContextType {
- session: SessionData["session"] | null
- user: SessionData["user"] | null
- org: Organization | null
- setActiveOrg: (orgSlug: string) => Promise<void>
+ session: SessionData["session"] | null;
+ user: SessionData["user"] | null;
+ org: Organization | null;
+ setActiveOrg: (orgSlug: string) => Promise<void>;
}
-const AuthContext = createContext<AuthContextType | undefined>(undefined)
+const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: ReactNode }) {
- const { data: session } = useSession()
- const [org, setOrg] = useState<Organization | null>(null)
+ const { data: session } = useSession();
+ const [org, setOrg] = useState<Organization | null>(null);
useEffect(() => {
if (session?.session.activeOrganizationId) {
authClient.organization.getFullOrganization().then((org) => {
- setOrg(org)
- })
+ setOrg(org);
+ });
}
- }, [session?.session.activeOrganizationId])
+ }, [session?.session.activeOrganizationId]);
// When a session exists and there is a pending login method recorded,
// promote it to the last-used method (successful login) and clear pending.
useEffect(() => {
- if (typeof window === "undefined") return
- if (!session?.session) return
+ if (typeof window === "undefined") return;
+ if (!session?.session) return;
try {
const pendingMethod = localStorage.getItem(
"supermemory-pending-login-method",
- )
+ );
const pendingTsRaw = localStorage.getItem(
"supermemory-pending-login-timestamp",
- )
+ );
if (pendingMethod) {
- const now = Date.now()
- const ts = pendingTsRaw ? Number.parseInt(pendingTsRaw, 10) : NaN
- const isFresh = Number.isFinite(ts) && now - ts < 10 * 60 * 1000 // 10 minutes TTL
+ const now = Date.now();
+ const ts = pendingTsRaw
+ ? Number.parseInt(pendingTsRaw, 10)
+ : Number.NaN;
+ const isFresh = Number.isFinite(ts) && now - ts < 10 * 60 * 1000; // 10 minutes TTL
if (isFresh) {
- localStorage.setItem(
- "supermemory-last-login-method",
- pendingMethod,
- )
+ localStorage.setItem("supermemory-last-login-method", pendingMethod);
}
}
- } catch { }
+ } catch {}
// Always clear pending markers once a session is present
try {
- localStorage.removeItem("supermemory-pending-login-method")
- localStorage.removeItem("supermemory-pending-login-timestamp")
- } catch { }
- }, [session?.session])
+ localStorage.removeItem("supermemory-pending-login-method");
+ localStorage.removeItem("supermemory-pending-login-timestamp");
+ } catch {}
+ }, [session?.session]);
const setActiveOrg = async (slug: string) => {
- if (!slug) return
+ if (!slug) return;
const activeOrg = await authClient.organization.setActive({
organizationSlug: slug,
- })
- setOrg(activeOrg)
- }
+ });
+ setOrg(activeOrg);
+ };
return (
<AuthContext.Provider
@@ -87,13 +86,13 @@ export function AuthProvider({ children }: { children: ReactNode }) {
>
{children}
</AuthContext.Provider>
- )
+ );
}
export function useAuth() {
- const context = useContext(AuthContext)
+ const context = useContext(AuthContext);
if (context === undefined) {
- throw new Error("useAuth must be used within an AuthProvider")
+ throw new Error("useAuth must be used within an AuthProvider");
}
- return context
+ return context;
}
diff --git a/packages/lib/auth.middleware.ts b/packages/lib/auth.middleware.ts
index 3b1f1f40..884f14b8 100644
--- a/packages/lib/auth.middleware.ts
+++ b/packages/lib/auth.middleware.ts
@@ -1,4 +1,4 @@
-import { createAuthClient } from "better-auth/client"
+import { createAuthClient } from "better-auth/client";
import {
adminClient,
anonymousClient,
@@ -7,7 +7,7 @@ import {
magicLinkClient,
organizationClient,
usernameClient,
-} from "better-auth/client/plugins"
+} from "better-auth/client/plugins";
export const middlewareAuthClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_BACKEND_URL ?? "https://api.supermemory.ai",
@@ -23,4 +23,4 @@ export const middlewareAuthClient = createAuthClient({
organizationClient(),
anonymousClient(),
],
-})
+});
diff --git a/packages/lib/auth.ts b/packages/lib/auth.ts
index 4369bef1..02709e2c 100644
--- a/packages/lib/auth.ts
+++ b/packages/lib/auth.ts
@@ -6,8 +6,8 @@ import {
magicLinkClient,
organizationClient,
usernameClient,
-} from "better-auth/client/plugins"
-import { createAuthClient } from "better-auth/react"
+} from "better-auth/client/plugins";
+import { createAuthClient } from "better-auth/react";
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_BACKEND_URL ?? "https://api.supermemory.ai",
@@ -24,9 +24,9 @@ export const authClient = createAuthClient({
organizationClient(),
anonymousClient(),
],
-})
+});
-export const signIn = authClient.signIn
-export const signOut = authClient.signOut
-export const useSession = authClient.useSession
-export const getSession = authClient.getSession
+export const signIn = authClient.signIn;
+export const signOut = authClient.signOut;
+export const useSession = authClient.useSession;
+export const getSession = authClient.getSession;
diff --git a/packages/lib/constants.ts b/packages/lib/constants.ts
index fde5bce1..74eb27ec 100644
--- a/packages/lib/constants.ts
+++ b/packages/lib/constants.ts
@@ -1,13 +1,13 @@
-const BIG_DIMENSIONS_NEW = 1536
-const DEFAULT_PROJECT_ID = "sm_project_default"
+const BIG_DIMENSIONS_NEW = 1536;
+const DEFAULT_PROJECT_ID = "sm_project_default";
const SEARCH_MEMORY_SHORTCUT_URL =
- "https://www.icloud.com/shortcuts/f2b5c544372844a38ab4c6900e2a88de"
+ "https://www.icloud.com/shortcuts/f2b5c544372844a38ab4c6900e2a88de";
const ADD_MEMORY_SHORTCUT_URL =
- "https://www.icloud.com/shortcuts/ec33b029b2c7481d89eda7640dbb7688"
+ "https://www.icloud.com/shortcuts/ec33b029b2c7481d89eda7640dbb7688";
export {
BIG_DIMENSIONS_NEW,
DEFAULT_PROJECT_ID,
SEARCH_MEMORY_SHORTCUT_URL,
ADD_MEMORY_SHORTCUT_URL,
-}
+};
diff --git a/packages/lib/error-tracking.tsx b/packages/lib/error-tracking.tsx
index bf320271..cc3a9b3c 100644
--- a/packages/lib/error-tracking.tsx
+++ b/packages/lib/error-tracking.tsx
@@ -1,14 +1,14 @@
-"use client"
+"use client";
-import { usePathname } from "next/navigation"
-import { useEffect } from "react"
-import { useSession } from "./auth"
-import { usePostHog } from "./posthog"
+import { usePathname } from "next/navigation";
+import { useEffect } from "react";
+import { useSession } from "./auth";
+import { usePostHog } from "./posthog";
export function useErrorTracking() {
- const posthog = usePostHog()
- const { data: session } = useSession()
- const pathname = usePathname()
+ const posthog = usePostHog();
+ const { data: session } = useSession();
+ const pathname = usePathname();
const trackError = (
error: Error | unknown,
@@ -23,10 +23,10 @@ export function useErrorTracking() {
user_email: session?.user?.email,
timestamp: new Date().toISOString(),
...context,
- }
+ };
- posthog.capture("error_occurred", errorDetails)
- }
+ posthog.capture("error_occurred", errorDetails);
+ };
const trackApiError = (
error: Error | unknown,
@@ -37,8 +37,8 @@ export function useErrorTracking() {
error_type: "api_error",
api_endpoint: endpoint,
api_method: method,
- })
- }
+ });
+ };
const trackComponentError = (
error: Error | unknown,
@@ -47,8 +47,8 @@ export function useErrorTracking() {
trackError(error, {
error_type: "component_error",
component_name: componentName,
- })
- }
+ });
+ };
const trackValidationError = (
error: Error | unknown,
@@ -59,24 +59,24 @@ export function useErrorTracking() {
error_type: "validation_error",
form_name: formName,
field_name: field,
- })
- }
+ });
+ };
return {
trackError,
trackApiError,
trackComponentError,
trackValidationError,
- }
+ };
}
// Global error boundary component
export function ErrorTrackingProvider({
children,
}: {
- children: React.ReactNode
+ children: React.ReactNode;
}) {
- const { trackError } = useErrorTracking()
+ const { trackError } = useErrorTracking();
useEffect(() => {
// Global error handler for unhandled errors
@@ -87,34 +87,37 @@ export function ErrorTrackingProvider({
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
- })
- }
+ });
+ };
// Global handler for unhandled promise rejections
const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
trackError(event.reason, {
error_type: "unhandled_promise_rejection",
source: "promise_rejection",
- })
- }
+ });
+ };
- window.addEventListener("error", handleError)
- window.addEventListener("unhandledrejection", handleUnhandledRejection)
+ window.addEventListener("error", handleError);
+ window.addEventListener("unhandledrejection", handleUnhandledRejection);
return () => {
- window.removeEventListener("error", handleError)
- window.removeEventListener("unhandledrejection", handleUnhandledRejection)
- }
- }, [trackError])
-
- return <>{children}</>
+ window.removeEventListener("error", handleError);
+ window.removeEventListener(
+ "unhandledrejection",
+ handleUnhandledRejection,
+ );
+ };
+ }, [trackError]);
+
+ return <>{children}</>;
}
// Hook for tracking user interactions
export function useInteractionTracking() {
- const posthog = usePostHog()
- const { data: session } = useSession()
- const pathname = usePathname()
+ const posthog = usePostHog();
+ const { data: session } = useSession();
+ const pathname = usePathname();
const trackInteraction = (action: string, details?: Record<string, any>) => {
posthog.capture("user_interaction", {
@@ -123,8 +126,8 @@ export function useInteractionTracking() {
user_id: session?.user?.id,
timestamp: new Date().toISOString(),
...details,
- })
- }
+ });
+ };
const trackFormSubmission = (
formName: string,
@@ -138,15 +141,15 @@ export function useInteractionTracking() {
user_id: session?.user?.id,
timestamp: new Date().toISOString(),
...details,
- })
- }
+ });
+ };
const trackButtonClick = (buttonName: string, context?: string) => {
trackInteraction("button_click", {
button_name: buttonName,
context,
- })
- }
+ });
+ };
const trackLinkClick = (
url: string,
@@ -157,27 +160,27 @@ export function useInteractionTracking() {
url,
link_text: linkText,
external,
- })
- }
+ });
+ };
const trackModalOpen = (modalName: string) => {
trackInteraction("modal_open", {
modal_name: modalName,
- })
- }
+ });
+ };
const trackModalClose = (modalName: string) => {
trackInteraction("modal_close", {
modal_name: modalName,
- })
- }
+ });
+ };
const trackTabChange = (fromTab: string, toTab: string) => {
trackInteraction("tab_change", {
from_tab: fromTab,
to_tab: toTab,
- })
- }
+ });
+ };
return {
trackInteraction,
@@ -187,5 +190,5 @@ export function useInteractionTracking() {
trackModalOpen,
trackModalClose,
trackTabChange,
- }
+ };
}
diff --git a/packages/lib/generate-id.ts b/packages/lib/generate-id.ts
index bbe201fd..6007027e 100644
--- a/packages/lib/generate-id.ts
+++ b/packages/lib/generate-id.ts
@@ -1,6 +1,6 @@
-import { customAlphabet } from "nanoid"
+import { customAlphabet } from "nanoid";
export const generateId = () =>
customAlphabet("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")(
22,
- )
+ );
diff --git a/packages/lib/glass-effect-manager.ts b/packages/lib/glass-effect-manager.ts
index 291a30aa..03a79734 100644
--- a/packages/lib/glass-effect-manager.ts
+++ b/packages/lib/glass-effect-manager.ts
@@ -1,56 +1,56 @@
// Singleton WebGL context manager for glass effects
class GlassEffectManager {
- private static instance: GlassEffectManager | null = null
- private canvas: HTMLCanvasElement | null = null
- private gl: WebGLRenderingContext | null = null
- private program: WebGLProgram | null = null
- private uniforms: Record<string, WebGLUniformLocation | null> = {}
- private effects: Map<string, EffectInstance> = new Map()
- private animationFrame: number | null = null
- private startTime: number = performance.now()
- private mousePositions: Map<string, { x: number; y: number }> = new Map()
+ private static instance: GlassEffectManager | null = null;
+ private canvas: HTMLCanvasElement | null = null;
+ private gl: WebGLRenderingContext | null = null;
+ private program: WebGLProgram | null = null;
+ private uniforms: Record<string, WebGLUniformLocation | null> = {};
+ private effects: Map<string, EffectInstance> = new Map();
+ private animationFrame: number | null = null;
+ private startTime: number = performance.now();
+ private mousePositions: Map<string, { x: number; y: number }> = new Map();
static getInstance(): GlassEffectManager {
if (!GlassEffectManager.instance) {
- GlassEffectManager.instance = new GlassEffectManager()
+ GlassEffectManager.instance = new GlassEffectManager();
}
- return GlassEffectManager.instance
+ return GlassEffectManager.instance;
}
private constructor() {
- this.initializeContext()
+ this.initializeContext();
}
private initializeContext() {
// Create offscreen canvas
- this.canvas = document.createElement("canvas")
- this.canvas.width = 1024 // Default size, will be adjusted
- this.canvas.height = 1024
+ this.canvas = document.createElement("canvas");
+ this.canvas.width = 1024; // Default size, will be adjusted
+ this.canvas.height = 1024;
this.gl = this.canvas.getContext("webgl", {
alpha: true,
premultipliedAlpha: false,
preserveDrawingBuffer: true,
- })
+ });
if (!this.gl) {
- console.error("WebGL not supported")
- return
+ console.error("WebGL not supported");
+ return;
}
- this.setupShaders()
- this.startRenderLoop()
+ this.setupShaders();
+ this.startRenderLoop();
}
private setupShaders() {
- if (!this.gl) return
+ if (!this.gl) return;
const vsSource = `
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
- `
+ `;
const fsSource = `
precision mediump float;
@@ -106,48 +106,48 @@ class GlassEffectManager {
gl_FragColor = vec4(glassColor, alpha);
}
- `
+ `;
const createShader = (type: number, source: string) => {
- const shader = this.gl!.createShader(type)
- if (!shader) return null
+ const shader = this.gl!.createShader(type);
+ if (!shader) return null;
- this.gl!.shaderSource(shader, source)
- this.gl!.compileShader(shader)
+ this.gl!.shaderSource(shader, source);
+ this.gl!.compileShader(shader);
if (!this.gl!.getShaderParameter(shader, this.gl!.COMPILE_STATUS)) {
- console.error("Shader error:", this.gl!.getShaderInfoLog(shader))
- this.gl!.deleteShader(shader)
- return null
+ console.error("Shader error:", this.gl!.getShaderInfoLog(shader));
+ this.gl!.deleteShader(shader);
+ return null;
}
- return shader
- }
+ return shader;
+ };
- const vs = createShader(this.gl.VERTEX_SHADER, vsSource)
- const fs = createShader(this.gl.FRAGMENT_SHADER, fsSource)
- if (!vs || !fs) return
+ const vs = createShader(this.gl.VERTEX_SHADER, vsSource);
+ const fs = createShader(this.gl.FRAGMENT_SHADER, fsSource);
+ if (!vs || !fs) return;
- this.program = this.gl.createProgram()
- if (!this.program) return
+ this.program = this.gl.createProgram();
+ if (!this.program) return;
- this.gl.attachShader(this.program, vs)
- this.gl.attachShader(this.program, fs)
- this.gl.linkProgram(this.program)
+ this.gl.attachShader(this.program, vs);
+ this.gl.attachShader(this.program, fs);
+ this.gl.linkProgram(this.program);
// biome-ignore lint/correctness/useHookAtTopLevel: Well, not a hook
- this.gl.useProgram(this.program)
+ this.gl.useProgram(this.program);
// Buffer setup
- const buffer = this.gl.createBuffer()
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer)
+ const buffer = this.gl.createBuffer();
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.bufferData(
this.gl.ARRAY_BUFFER,
new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
this.gl.STATIC_DRAW,
- )
+ );
- const position = this.gl.getAttribLocation(this.program, "position")
- this.gl.enableVertexAttribArray(position)
- this.gl.vertexAttribPointer(position, 2, this.gl.FLOAT, false, 0, 0)
+ const position = this.gl.getAttribLocation(this.program, "position");
+ this.gl.enableVertexAttribArray(position);
+ this.gl.vertexAttribPointer(position, 2, this.gl.FLOAT, false, 0, 0);
// Store uniform locations
this.uniforms = {
@@ -155,11 +155,11 @@ class GlassEffectManager {
time: this.gl.getUniformLocation(this.program, "iTime"),
mouse: this.gl.getUniformLocation(this.program, "iMouse"),
expanded: this.gl.getUniformLocation(this.program, "iExpanded"),
- }
+ };
// Enable blending
- this.gl.enable(this.gl.BLEND)
- this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA)
+ this.gl.enable(this.gl.BLEND);
+ this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
}
registerEffect(
@@ -168,8 +168,8 @@ class GlassEffectManager {
isExpanded: boolean,
): () => void {
// Ensure minimum dimensions
- const width = Math.max(1, targetCanvas.width)
- const height = Math.max(1, targetCanvas.height)
+ const width = Math.max(1, targetCanvas.width);
+ const height = Math.max(1, targetCanvas.height);
const effect: EffectInstance = {
id,
@@ -177,58 +177,58 @@ class GlassEffectManager {
isExpanded,
width,
height,
- }
+ };
- this.effects.set(id, effect)
- this.mousePositions.set(id, { x: 0, y: 0 })
+ this.effects.set(id, effect);
+ this.mousePositions.set(id, { x: 0, y: 0 });
// Return cleanup function
return () => {
- this.effects.delete(id)
- this.mousePositions.delete(id)
+ this.effects.delete(id);
+ this.mousePositions.delete(id);
if (this.effects.size === 0 && this.animationFrame) {
- cancelAnimationFrame(this.animationFrame)
- this.animationFrame = null
+ cancelAnimationFrame(this.animationFrame);
+ this.animationFrame = null;
}
- }
+ };
}
updateMousePosition(id: string, x: number, y: number) {
- this.mousePositions.set(id, { x, y })
+ this.mousePositions.set(id, { x, y });
}
updateExpanded(id: string, isExpanded: boolean) {
- const effect = this.effects.get(id)
+ const effect = this.effects.get(id);
if (effect) {
- effect.isExpanded = isExpanded
+ effect.isExpanded = isExpanded;
}
}
updateSize(id: string, width: number, height: number) {
- const effect = this.effects.get(id)
+ const effect = this.effects.get(id);
if (effect) {
// Ensure minimum dimensions
- effect.width = Math.max(1, width)
- effect.height = Math.max(1, height)
+ effect.width = Math.max(1, width);
+ effect.height = Math.max(1, height);
}
}
private startRenderLoop() {
const render = () => {
if (!this.gl || !this.program || this.effects.size === 0) {
- this.animationFrame = requestAnimationFrame(render)
- return
+ this.animationFrame = requestAnimationFrame(render);
+ return;
}
- const currentTime = (performance.now() - this.startTime) / 1000
+ const currentTime = (performance.now() - this.startTime) / 1000;
// Render each effect
for (const [id, effect] of Array.from(this.effects)) {
- const mousePos = this.mousePositions.get(id) || { x: 0, y: 0 }
+ const mousePos = this.mousePositions.get(id) || { x: 0, y: 0 };
// Skip rendering if dimensions are invalid
if (effect.width <= 0 || effect.height <= 0) {
- continue
+ continue;
}
// Set canvas size if needed
@@ -236,14 +236,14 @@ class GlassEffectManager {
this.canvas!.width !== effect.width ||
this.canvas!.height !== effect.height
) {
- this.canvas!.width = effect.width
- this.canvas!.height = effect.height
- this.gl.viewport(0, 0, effect.width, effect.height)
+ this.canvas!.width = effect.width;
+ this.canvas!.height = effect.height;
+ this.gl.viewport(0, 0, effect.width, effect.height);
}
// Clear and render
- this.gl.clearColor(0, 0, 0, 0)
- this.gl.clear(this.gl.COLOR_BUFFER_BIT)
+ this.gl.clearColor(0, 0, 0, 0);
+ this.gl.clear(this.gl.COLOR_BUFFER_BIT);
// Set uniforms
if (this.uniforms.resolution) {
@@ -251,58 +251,58 @@ class GlassEffectManager {
this.uniforms.resolution,
effect.width,
effect.height,
- )
+ );
}
if (this.uniforms.time) {
- this.gl.uniform1f(this.uniforms.time, currentTime)
+ this.gl.uniform1f(this.uniforms.time, currentTime);
}
if (this.uniforms.mouse) {
- this.gl.uniform2f(this.uniforms.mouse, mousePos.x, mousePos.y)
+ this.gl.uniform2f(this.uniforms.mouse, mousePos.x, mousePos.y);
}
if (this.uniforms.expanded) {
this.gl.uniform1f(
this.uniforms.expanded,
effect.isExpanded ? 1.0 : 0.0,
- )
+ );
}
// Draw
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4)
+ this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
// Copy to target canvas
- const targetCtx = effect.targetCanvas.getContext("2d")
+ const targetCtx = effect.targetCanvas.getContext("2d");
if (targetCtx) {
- targetCtx.clearRect(0, 0, effect.width, effect.height)
- targetCtx.drawImage(this.canvas!, 0, 0)
+ targetCtx.clearRect(0, 0, effect.width, effect.height);
+ targetCtx.drawImage(this.canvas!, 0, 0);
}
}
- this.animationFrame = requestAnimationFrame(render)
- }
+ this.animationFrame = requestAnimationFrame(render);
+ };
- render()
+ render();
}
// Clean up method (optional, for when the app unmounts)
destroy() {
if (this.animationFrame) {
- cancelAnimationFrame(this.animationFrame)
+ cancelAnimationFrame(this.animationFrame);
}
if (this.gl && this.program) {
- this.gl.deleteProgram(this.program)
+ this.gl.deleteProgram(this.program);
}
- this.effects.clear()
- this.mousePositions.clear()
- GlassEffectManager.instance = null
+ this.effects.clear();
+ this.mousePositions.clear();
+ GlassEffectManager.instance = null;
}
}
interface EffectInstance {
- id: string
- targetCanvas: HTMLCanvasElement
- isExpanded: boolean
- width: number
- height: number
+ id: string;
+ targetCanvas: HTMLCanvasElement;
+ isExpanded: boolean;
+ width: number;
+ height: number;
}
-export default GlassEffectManager
+export default GlassEffectManager;
diff --git a/packages/lib/posthog.tsx b/packages/lib/posthog.tsx
index ac563aae..d1105cbc 100644
--- a/packages/lib/posthog.tsx
+++ b/packages/lib/posthog.tsx
@@ -1,20 +1,20 @@
-"use client"
+"use client";
-import { usePathname, useSearchParams } from "next/navigation"
-import posthog from "posthog-js"
-import { Suspense, useEffect } from "react"
-import { useSession } from "./auth"
+import { usePathname, useSearchParams } from "next/navigation";
+import posthog from "posthog-js";
+import { Suspense, useEffect } from "react";
+import { useSession } from "./auth";
function PostHogPageTracking() {
- const pathname = usePathname()
- const searchParams = useSearchParams()
+ const pathname = usePathname();
+ const searchParams = useSearchParams();
// Page tracking
useEffect(() => {
if (pathname) {
- let url = window.origin + pathname
+ let url = window.origin + pathname;
if (searchParams.toString()) {
- url = `${url}?${searchParams.toString()}`
+ url = `${url}?${searchParams.toString()}`;
}
// Extract page context for better tracking
@@ -24,17 +24,17 @@ function PostHogPageTracking() {
search_params: searchParams.toString(),
page_type: getPageType(pathname),
org_slug: getOrgSlug(pathname),
- }
+ };
- posthog.capture("$pageview", pageContext)
+ posthog.capture("$pageview", pageContext);
}
- }, [pathname, searchParams])
+ }, [pathname, searchParams]);
- return null
+ return null;
}
export function PostHogProvider({ children }: { children: React.ReactNode }) {
- const { data: session } = useSession()
+ const { data: session } = useSession();
useEffect(() => {
if (typeof window !== "undefined") {
@@ -44,9 +44,9 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) {
person_profiles: "identified_only",
capture_pageview: false,
capture_pageleave: true,
- })
+ });
}
- }, [])
+ }, []);
// User identification
useEffect(() => {
@@ -56,9 +56,9 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) {
name: session.user.name,
userId: session.user.id,
createdAt: session.user.createdAt,
- })
+ });
}
- }, [session?.user])
+ }, [session?.user]);
return (
<>
@@ -67,18 +67,18 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) {
</Suspense>
{children}
</>
- )
+ );
}
function getPageType(pathname: string): string {
- return "other"
+ return "other";
}
function getOrgSlug(pathname: string): string | null {
- const match = pathname.match(/^\/([^/]+)\//)
- return match ? (match[1] ?? null) : null
+ const match = pathname.match(/^\/([^/]+)\//);
+ return match ? (match[1] ?? null) : null;
}
export function usePostHog() {
- return posthog
+ return posthog;
}
diff --git a/packages/lib/queries.ts b/packages/lib/queries.ts
index 3e9e1ab9..d0f93915 100644
--- a/packages/lib/queries.ts
+++ b/packages/lib/queries.ts
@@ -1,12 +1,12 @@
-import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
-import type { useCustomer } from "autumn-js/react"
-import { toast } from "sonner"
-import type { z } from "zod"
-import type { DocumentsWithMemoriesResponseSchema } from "../validation/api"
-import { $fetch } from "./api"
+import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
+import type { useCustomer } from "autumn-js/react";
+import { toast } from "sonner";
+import type { z } from "zod";
+import type { DocumentsWithMemoriesResponseSchema } from "../validation/api";
+import { $fetch } from "./api";
-type DocumentsResponse = z.infer<typeof DocumentsWithMemoriesResponseSchema>
-type DocumentWithMemories = DocumentsResponse["documents"][0]
+type DocumentsResponse = z.infer<typeof DocumentsWithMemoriesResponseSchema>;
+type DocumentWithMemories = DocumentsResponse["documents"][0];
export const fetchSubscriptionStatus = (
autumn: ReturnType<typeof useCustomer>,
@@ -18,54 +18,54 @@ export const fetchSubscriptionStatus = (
"memory_starter",
"memory_growth",
"consumer_pro",
- ]
- const statusMap: Record<string, boolean | null> = {}
+ ];
+ const statusMap: Record<string, boolean | null> = {};
await Promise.all(
allPlans.map(async (plan) => {
try {
const res = await autumn.check({
productId: plan,
- })
- statusMap[plan] = res.data?.allowed ?? false
+ });
+ statusMap[plan] = res.data?.allowed ?? false;
} catch (error) {
- console.error(`Error checking status for ${plan}:`, error)
- statusMap[plan] = false
+ console.error(`Error checking status for ${plan}:`, error);
+ statusMap[plan] = false;
}
}),
- )
+ );
- return statusMap
+ return statusMap;
},
queryKey: ["subscription-status"],
refetchInterval: 5000, // Refetch every 5 seconds
staleTime: 4000, // Consider data stale after 4 seconds
- })
+ });
// Feature checks
export const fetchMemoriesFeature = (autumn: ReturnType<typeof useCustomer>) =>
useQuery({
queryFn: async () => {
- const res = await autumn.check({ featureId: "memories" })
- return res.data
+ const res = await autumn.check({ featureId: "memories" });
+ return res.data;
},
queryKey: ["autumn-feature", "memories"],
staleTime: 30 * 1000, // 30 seconds
gcTime: 5 * 60 * 1000, // 5 minutes
- })
+ });
export const fetchConnectionsFeature = (
autumn: ReturnType<typeof useCustomer>,
) =>
useQuery({
queryFn: async () => {
- const res = await autumn.check({ featureId: "connections" })
- return res.data
+ const res = await autumn.check({ featureId: "connections" });
+ return res.data;
},
queryKey: ["autumn-feature", "connections"],
staleTime: 30 * 1000, // 30 seconds
gcTime: 5 * 60 * 1000, // 5 minutes
- })
+ });
// Product checks
export const fetchConsumerProProduct = (
@@ -73,54 +73,54 @@ export const fetchConsumerProProduct = (
) =>
useQuery({
queryFn: async () => {
- const res = await autumn.check({ productId: "consumer_pro" })
- return res.data
+ const res = await autumn.check({ productId: "consumer_pro" });
+ return res.data;
},
queryKey: ["autumn-product", "consumer_pro"],
staleTime: 30 * 1000, // 30 seconds
gcTime: 5 * 60 * 1000, // 5 minutes
- })
+ });
export const fetchProProduct = (autumn: ReturnType<typeof useCustomer>) =>
useQuery({
queryFn: async () => {
- const res = await autumn.check({ productId: "pro" })
- return res.data
+ const res = await autumn.check({ productId: "pro" });
+ return res.data;
},
queryKey: ["autumn-product", "pro"],
staleTime: 30 * 1000, // 30 seconds
gcTime: 5 * 60 * 1000, // 5 minutes
- })
+ });
export const useDeleteDocument = (selectedProject: string) => {
- const queryClient = useQueryClient()
+ const queryClient = useQueryClient();
return useMutation({
mutationFn: async (documentId: string) => {
// context for LLM: delete/memories/:documentId is documents delete endpoint not memories delete endpoint
- const response = await $fetch(`@delete/documents/${documentId}`)
+ const response = await $fetch(`@delete/documents/${documentId}`);
if (response.error) {
- throw new Error(response.error?.message || "Failed to delete document")
+ throw new Error(response.error?.message || "Failed to delete document");
}
- return response.data
+ return response.data;
},
onMutate: async (documentId: string) => {
await queryClient.cancelQueries({
queryKey: ["documents-with-memories", selectedProject],
- })
+ });
const previousData = queryClient.getQueryData([
"documents-with-memories",
selectedProject,
- ])
+ ]);
queryClient.setQueryData(
["documents-with-memories", selectedProject],
(old: unknown) => {
- if (!old || typeof old !== "object") return old
+ if (!old || typeof old !== "object") return old;
const typedOld = old as {
- pages?: Array<{ documents?: DocumentWithMemories[] }>
- }
+ pages?: Array<{ documents?: DocumentWithMemories[] }>;
+ };
return {
...typedOld,
pages: typedOld.pages?.map((page) => ({
@@ -129,30 +129,30 @@ export const useDeleteDocument = (selectedProject: string) => {
(doc: DocumentWithMemories) => doc.id !== documentId,
),
})),
- }
+ };
},
- )
+ );
- return { previousData }
+ return { previousData };
},
onSuccess: () => {
- toast.success("Memory deleted successfully")
+ toast.success("Memory deleted successfully");
},
onError: (error, _documentId, context) => {
if (context?.previousData) {
queryClient.setQueryData(
["documents-with-memories", selectedProject],
context.previousData,
- )
+ );
}
toast.error("Failed to delete memory", {
description: error instanceof Error ? error.message : "Unknown error",
- })
+ });
},
onSettled: () => {
queryClient.invalidateQueries({
queryKey: ["documents-with-memories", selectedProject],
- })
+ });
},
- })
-}
+ });
+};
diff --git a/packages/lib/query-client.tsx b/packages/lib/query-client.tsx
index eb9c5a21..322e9f89 100644
--- a/packages/lib/query-client.tsx
+++ b/packages/lib/query-client.tsx
@@ -1,8 +1,8 @@
-"use client"
+"use client";
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
-import { ReactQueryDevtools } from "@tanstack/react-query-devtools"
-import { useState } from "react"
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
+import { useState } from "react";
export const QueryProvider = ({ children }: { children: React.ReactNode }) => {
const [queryClient] = useState(
@@ -16,12 +16,12 @@ export const QueryProvider = ({ children }: { children: React.ReactNode }) => {
},
},
}),
- )
+ );
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
- )
-}
+ );
+};
diff --git a/packages/lib/similarity.ts b/packages/lib/similarity.ts
index 09d3a2cc..0d43710d 100644
--- a/packages/lib/similarity.ts
+++ b/packages/lib/similarity.ts
@@ -10,27 +10,27 @@ export const cosineSimilarity = (
vectorB: number[],
): number => {
if (vectorA.length !== vectorB.length) {
- throw new Error("Vectors must have the same length")
+ throw new Error("Vectors must have the same length");
}
- let dotProduct = 0
+ let dotProduct = 0;
for (let i = 0; i < vectorA.length; i++) {
- const vectorAi = vectorA[i]
- const vectorBi = vectorB[i]
+ const vectorAi = vectorA[i];
+ const vectorBi = vectorB[i];
if (
typeof vectorAi !== "number" ||
typeof vectorBi !== "number" ||
isNaN(vectorAi) ||
isNaN(vectorBi)
) {
- throw new Error("Vectors must contain only numbers")
+ throw new Error("Vectors must contain only numbers");
}
- dotProduct += vectorAi * vectorBi
+ dotProduct += vectorAi * vectorBi;
}
- return dotProduct
-}
+ return dotProduct;
+};
/**
* Calculate semantic similarity between two documents
@@ -47,13 +47,13 @@ export const calculateSemanticSimilarity = (
document1Embedding.length > 0 &&
document2Embedding.length > 0
) {
- const similarity = cosineSimilarity(document1Embedding, document2Embedding)
+ const similarity = cosineSimilarity(document1Embedding, document2Embedding);
// Convert from [-1, 1] to [0, 1] range
- return similarity >= 0 ? similarity : 0
+ return similarity >= 0 ? similarity : 0;
}
- return 0
-}
+ return 0;
+};
/**
* Calculate semantic similarity between a document and memory entry
@@ -71,34 +71,34 @@ export const calculateDocumentMemorySimilarity = (
documentEmbedding.length > 0 &&
memoryEmbedding.length > 0
) {
- const similarity = cosineSimilarity(documentEmbedding, memoryEmbedding)
+ const similarity = cosineSimilarity(documentEmbedding, memoryEmbedding);
// Convert from [-1, 1] to [0, 1] range
- return similarity >= 0 ? similarity : 0
+ return similarity >= 0 ? similarity : 0;
}
// Fall back to relevance score from database (0-100 scale)
if (relevanceScore !== null && relevanceScore !== undefined) {
- return Math.max(0, Math.min(1, relevanceScore / 100))
+ return Math.max(0, Math.min(1, relevanceScore / 100));
}
// Default similarity for connections without embeddings or relevance scores
- return 0.5
-}
+ return 0.5;
+};
/**
* Get visual properties for connection based on similarity
*/
export const getConnectionVisualProps = (similarity: number) => {
// Ensure similarity is between 0 and 1
- const normalizedSimilarity = Math.max(0, Math.min(1, similarity))
+ const normalizedSimilarity = Math.max(0, Math.min(1, similarity));
return {
opacity: Math.max(0, normalizedSimilarity), // 0 to 1 range
thickness: Math.max(1, normalizedSimilarity * 4), // 1 to 4 pixels
glow: normalizedSimilarity * 0.6, // Glow intensity
pulseDuration: 2000 + (1 - normalizedSimilarity) * 3000, // Faster pulse for higher similarity
- }
-}
+ };
+};
/**
* Generate magical color based on similarity and connection type
@@ -107,9 +107,9 @@ export const getMagicalConnectionColor = (
similarity: number,
hue = 220,
): string => {
- const normalizedSimilarity = Math.max(0, Math.min(1, similarity))
- const saturation = 60 + normalizedSimilarity * 40 // 60% to 100%
- const lightness = 40 + normalizedSimilarity * 30 // 40% to 70%
+ const normalizedSimilarity = Math.max(0, Math.min(1, similarity));
+ const saturation = 60 + normalizedSimilarity * 40; // 60% to 100%
+ const lightness = 40 + normalizedSimilarity * 30; // 40% to 70%
- return `hsl(${hue}, ${saturation}%, ${lightness}%)`
-}
+ return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
+};
diff --git a/packages/lib/utils.ts b/packages/lib/utils.ts
index 02a2ddaf..59e86ae1 100644
--- a/packages/lib/utils.ts
+++ b/packages/lib/utils.ts
@@ -1,8 +1,8 @@
-import { type ClassValue, clsx } from "clsx"
-import { twMerge } from "tailwind-merge"
+import { type ClassValue, clsx } from "clsx";
+import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs))
+ return twMerge(clsx(inputs));
}
-export const isSelfHosted = process.env.NEXT_PUBLIC_HOST_ID !== "supermemory"
+export const isSelfHosted = process.env.NEXT_PUBLIC_HOST_ID !== "supermemory";
diff --git a/packages/tools/src/ai-sdk.ts b/packages/tools/src/ai-sdk.ts
index 703175e7..37f346c2 100644
--- a/packages/tools/src/ai-sdk.ts
+++ b/packages/tools/src/ai-sdk.ts
@@ -1,13 +1,13 @@
-import Supermemory from "supermemory"
-import { tool } from "ai"
-import { z } from "zod"
+import { tool } from "ai";
+import Supermemory from "supermemory";
+import { z } from "zod";
import {
DEFAULT_VALUES,
+ getContainerTags,
PARAMETER_DESCRIPTIONS,
TOOL_DESCRIPTIONS,
- getContainerTags,
-} from "./shared"
-import type { SupermemoryToolsConfig } from "./types"
+} from "./shared";
+import type { SupermemoryToolsConfig } from "./types";
// Export individual tool creators
export const searchMemoriesTool = (
@@ -17,9 +17,9 @@ export const searchMemoriesTool = (
const client = new Supermemory({
apiKey,
...(config?.baseUrl ? { baseURL: config.baseUrl } : {}),
- })
+ });
- const containerTags = getContainerTags(config)
+ const containerTags = getContainerTags(config);
return tool({
description: TOOL_DESCRIPTIONS.searchMemories,
@@ -50,22 +50,22 @@ export const searchMemoriesTool = (
limit,
chunkThreshold: DEFAULT_VALUES.chunkThreshold,
includeFullDocs,
- })
+ });
return {
success: true,
results: response.results,
count: response.results?.length || 0,
- }
+ };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
- }
+ };
}
},
- })
-}
+ });
+};
export const addMemoryTool = (
apiKey: string,
@@ -74,9 +74,9 @@ export const addMemoryTool = (
const client = new Supermemory({
apiKey,
...(config?.baseUrl ? { baseURL: config.baseUrl } : {}),
- })
+ });
- const containerTags = getContainerTags(config)
+ const containerTags = getContainerTags(config);
return tool({
description: TOOL_DESCRIPTIONS.addMemory,
@@ -85,27 +85,27 @@ export const addMemoryTool = (
}),
execute: async ({ memory }) => {
try {
- const metadata: Record<string, string | number | boolean> = {}
+ const metadata: Record<string, string | number | boolean> = {};
const response = await client.memories.add({
content: memory,
containerTags,
...(Object.keys(metadata).length > 0 && { metadata }),
- })
+ });
return {
success: true,
memory: response,
- }
+ };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
- }
+ };
}
},
- })
-}
+ });
+};
/**
* Create Supermemory tools for AI SDK
@@ -117,5 +117,5 @@ export function supermemoryTools(
return {
searchMemories: searchMemoriesTool(apiKey, config),
addMemory: addMemoryTool(apiKey, config),
- }
+ };
}
diff --git a/packages/tools/src/index.ts b/packages/tools/src/index.ts
index 4f21246e..588a4fb3 100644
--- a/packages/tools/src/index.ts
+++ b/packages/tools/src/index.ts
@@ -1,2 +1,2 @@
// Export shared types and utilities
-export type { SupermemoryToolsConfig } from "./types"
+export type { SupermemoryToolsConfig } from "./types";
diff --git a/packages/tools/src/openai.ts b/packages/tools/src/openai.ts
index 5c79a9c1..197a5829 100644
--- a/packages/tools/src/openai.ts
+++ b/packages/tools/src/openai.ts
@@ -1,27 +1,27 @@
-import type OpenAI from "openai"
-import Supermemory from "supermemory"
+import type OpenAI from "openai";
+import Supermemory from "supermemory";
import {
DEFAULT_VALUES,
+ getContainerTags,
PARAMETER_DESCRIPTIONS,
TOOL_DESCRIPTIONS,
- getContainerTags,
-} from "./shared"
-import type { SupermemoryToolsConfig } from "./types"
+} from "./shared";
+import type { SupermemoryToolsConfig } from "./types";
/**
* Result types for memory operations
*/
export interface MemorySearchResult {
- success: boolean
- results?: Awaited<ReturnType<Supermemory["search"]["execute"]>>["results"]
- count?: number
- error?: string
+ success: boolean;
+ results?: Awaited<ReturnType<Supermemory["search"]["execute"]>>["results"];
+ count?: number;
+ error?: string;
}
export interface MemoryAddResult {
- success: boolean
- memory?: Awaited<ReturnType<Supermemory["memories"]["add"]>>
- error?: string
+ success: boolean;
+ memory?: Awaited<ReturnType<Supermemory["memories"]["add"]>>;
+ error?: string;
}
/**
@@ -67,7 +67,7 @@ export const memoryToolSchemas = {
required: ["memory"],
},
} satisfies OpenAI.FunctionDefinition,
-} as const
+} as const;
/**
* Create a Supermemory client with configuration
@@ -76,11 +76,11 @@ function createClient(apiKey: string, config?: SupermemoryToolsConfig) {
const client = new Supermemory({
apiKey,
...(config?.baseUrl && { baseURL: config.baseUrl }),
- })
+ });
- const containerTags = getContainerTags(config)
+ const containerTags = getContainerTags(config);
- return { client, containerTags }
+ return { client, containerTags };
}
/**
@@ -90,16 +90,16 @@ export function createSearchMemoriesFunction(
apiKey: string,
config?: SupermemoryToolsConfig,
) {
- const { client, containerTags } = createClient(apiKey, config)
+ const { client, containerTags } = createClient(apiKey, config);
return async function searchMemories({
informationToGet,
includeFullDocs = DEFAULT_VALUES.includeFullDocs,
limit = DEFAULT_VALUES.limit,
}: {
- informationToGet: string
- includeFullDocs?: boolean
- limit?: number
+ informationToGet: string;
+ includeFullDocs?: boolean;
+ limit?: number;
}): Promise<MemorySearchResult> {
try {
const response = await client.search.execute({
@@ -108,20 +108,20 @@ export function createSearchMemoriesFunction(
limit,
chunkThreshold: DEFAULT_VALUES.chunkThreshold,
includeFullDocs,
- })
+ });
return {
success: true,
results: response.results,
count: response.results?.length || 0,
- }
+ };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
- }
+ };
}
- }
+ };
}
/**
@@ -131,33 +131,33 @@ export function createAddMemoryFunction(
apiKey: string,
config?: SupermemoryToolsConfig,
) {
- const { client, containerTags } = createClient(apiKey, config)
+ const { client, containerTags } = createClient(apiKey, config);
return async function addMemory({
memory,
}: {
- memory: string
+ memory: string;
}): Promise<MemoryAddResult> {
try {
- const metadata: Record<string, string | number | boolean> = {}
+ const metadata: Record<string, string | number | boolean> = {};
const response = await client.memories.add({
content: memory,
containerTags,
...(Object.keys(metadata).length > 0 && { metadata }),
- })
+ });
return {
success: true,
memory: response,
- }
+ };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
- }
+ };
}
- }
+ };
}
/**
@@ -167,13 +167,13 @@ export function supermemoryTools(
apiKey: string,
config?: SupermemoryToolsConfig,
) {
- const searchMemories = createSearchMemoriesFunction(apiKey, config)
- const addMemory = createAddMemoryFunction(apiKey, config)
+ const searchMemories = createSearchMemoriesFunction(apiKey, config);
+ const addMemory = createAddMemoryFunction(apiKey, config);
return {
searchMemories,
addMemory,
- }
+ };
}
/**
@@ -183,7 +183,7 @@ export function getToolDefinitions(): OpenAI.Chat.Completions.ChatCompletionTool
return [
{ type: "function", function: memoryToolSchemas.searchMemories },
{ type: "function", function: memoryToolSchemas.addMemory },
- ]
+ ];
}
/**
@@ -193,26 +193,26 @@ export function createToolCallExecutor(
apiKey: string,
config?: SupermemoryToolsConfig,
) {
- const tools = supermemoryTools(apiKey, config)
+ const tools = supermemoryTools(apiKey, config);
return async function executeToolCall(
toolCall: OpenAI.Chat.Completions.ChatCompletionMessageToolCall,
): Promise<string> {
- const functionName = toolCall.function.name
- const args = JSON.parse(toolCall.function.arguments)
+ const functionName = toolCall.function.name;
+ const args = JSON.parse(toolCall.function.arguments);
switch (functionName) {
case "searchMemories":
- return JSON.stringify(await tools.searchMemories(args))
+ return JSON.stringify(await tools.searchMemories(args));
case "addMemory":
- return JSON.stringify(await tools.addMemory(args))
+ return JSON.stringify(await tools.addMemory(args));
default:
return JSON.stringify({
success: false,
error: `Unknown function: ${functionName}`,
- })
+ });
}
- }
+ };
}
/**
@@ -222,24 +222,24 @@ export function createToolCallsExecutor(
apiKey: string,
config?: SupermemoryToolsConfig,
) {
- const executeToolCall = createToolCallExecutor(apiKey, config)
+ const executeToolCall = createToolCallExecutor(apiKey, config);
return async function executeToolCalls(
toolCalls: OpenAI.Chat.Completions.ChatCompletionMessageToolCall[],
): Promise<OpenAI.Chat.Completions.ChatCompletionToolMessageParam[]> {
const results = await Promise.all(
toolCalls.map(async (toolCall) => {
- const result = await executeToolCall(toolCall)
+ const result = await executeToolCall(toolCall);
return {
tool_call_id: toolCall.id,
role: "tool" as const,
content: result,
- }
+ };
}),
- )
+ );
- return results
- }
+ return results;
+ };
}
/**
@@ -249,7 +249,7 @@ export function createSearchMemoriesTool(
apiKey: string,
config?: SupermemoryToolsConfig,
) {
- const searchMemories = createSearchMemoriesFunction(apiKey, config)
+ const searchMemories = createSearchMemoriesFunction(apiKey, config);
return {
definition: {
@@ -257,14 +257,14 @@ export function createSearchMemoriesTool(
function: memoryToolSchemas.searchMemories,
},
execute: searchMemories,
- }
+ };
}
export function createAddMemoryTool(
apiKey: string,
config?: SupermemoryToolsConfig,
) {
- const addMemory = createAddMemoryFunction(apiKey, config)
+ const addMemory = createAddMemoryFunction(apiKey, config);
return {
definition: {
@@ -272,5 +272,5 @@ export function createAddMemoryTool(
function: memoryToolSchemas.addMemory,
},
execute: addMemory,
- }
+ };
}
diff --git a/packages/tools/src/shared.ts b/packages/tools/src/shared.ts
index 0ff14e86..ffe07a11 100644
--- a/packages/tools/src/shared.ts
+++ b/packages/tools/src/shared.ts
@@ -8,7 +8,7 @@ export const TOOL_DESCRIPTIONS = {
"Search (recall) memories/details/information about the user or other facts or entities. Run when explicitly asked or when context about user's past choices would be helpful.",
addMemory:
"Add (remember) memories/details/information about the user or other facts or entities. Run when explicitly asked or when the user mentions any information generalizable beyond the context of the current conversation.",
-} as const
+} as const;
// Parameter descriptions
export const PARAMETER_DESCRIPTIONS = {
@@ -18,30 +18,30 @@ export const PARAMETER_DESCRIPTIONS = {
limit: "Maximum number of results to return",
memory:
"The text content of the memory to add. This should be a single sentence or a short paragraph.",
-} as const
+} as const;
// Default values
export const DEFAULT_VALUES = {
includeFullDocs: true,
limit: 10,
chunkThreshold: 0.6,
-} as const
+} as const;
// Container tag constants
export const CONTAINER_TAG_CONSTANTS = {
projectPrefix: "sm_project_",
defaultTags: ["sm_project_default"] as string[],
-} as const
+} as const;
/**
* Helper function to generate container tags based on config
*/
export function getContainerTags(config?: {
- projectId?: string
- containerTags?: string[]
+ projectId?: string;
+ containerTags?: string[];
}): string[] {
if (config?.projectId) {
- return [`${CONTAINER_TAG_CONSTANTS.projectPrefix}${config.projectId}`]
+ return [`${CONTAINER_TAG_CONSTANTS.projectPrefix}${config.projectId}`];
}
- return config?.containerTags ?? CONTAINER_TAG_CONSTANTS.defaultTags
+ return config?.containerTags ?? CONTAINER_TAG_CONSTANTS.defaultTags;
}
diff --git a/packages/tools/src/tools.test.ts b/packages/tools/src/tools.test.ts
index 5cde729d..bcff0f35 100644
--- a/packages/tools/src/tools.test.ts
+++ b/packages/tools/src/tools.test.ts
@@ -1,70 +1,72 @@
-import { createOpenAI } from "@ai-sdk/openai"
-import { generateText } from "ai"
-import { describe, expect, it } from "vitest"
-import * as aiSdk from "./ai-sdk"
-import * as openAi from "./openai"
-import type { SupermemoryToolsConfig } from "./types"
+import { createOpenAI } from "@ai-sdk/openai";
+import { generateText } from "ai";
+import { describe, expect, it } from "vitest";
+import * as aiSdk from "./ai-sdk";
+import * as openAi from "./openai";
+import type { SupermemoryToolsConfig } from "./types";
-import "dotenv/config"
+import "dotenv/config";
describe("@supermemory/tools", () => {
// Required API keys - tests will fail if not provided
- const testApiKey = process.env.SUPERMEMORY_API_KEY
- const testOpenAIKey = process.env.OPENAI_API_KEY
+ const testApiKey = process.env.SUPERMEMORY_API_KEY;
+ const testOpenAIKey = process.env.OPENAI_API_KEY;
if (!testApiKey) {
throw new Error(
"SUPERMEMORY_API_KEY environment variable is required for tests",
- )
+ );
}
if (!testOpenAIKey) {
- throw new Error("OPENAI_API_KEY environment variable is required for tests")
+ throw new Error(
+ "OPENAI_API_KEY environment variable is required for tests",
+ );
}
// Optional configuration with defaults
- const testBaseUrl = process.env.SUPERMEMORY_BASE_URL ?? undefined
- const testModelName = process.env.MODEL_NAME || "gpt-4o-mini"
+ const testBaseUrl = process.env.SUPERMEMORY_BASE_URL ?? undefined;
+ const testModelName = process.env.MODEL_NAME || "gpt-4o-mini";
describe("aiSdk module", () => {
describe("client initialization", () => {
it("should create tools with default configuration", () => {
- const config: SupermemoryToolsConfig = {}
- const tools = aiSdk.supermemoryTools(testApiKey, config)
+ const config: SupermemoryToolsConfig = {};
+ const tools = aiSdk.supermemoryTools(testApiKey, config);
- expect(tools).toBeDefined()
- expect(tools.searchMemories).toBeDefined()
- expect(tools.addMemory).toBeDefined()
- })
+ expect(tools).toBeDefined();
+ expect(tools.searchMemories).toBeDefined();
+ expect(tools.addMemory).toBeDefined();
+ });
it("should create tools with custom baseUrl", () => {
const config: SupermemoryToolsConfig = {
baseUrl: testBaseUrl,
- }
- const tools = aiSdk.supermemoryTools(testApiKey, config)
+ };
+ const tools = aiSdk.supermemoryTools(testApiKey, config);
- expect(tools).toBeDefined()
- expect(tools.searchMemories).toBeDefined()
- expect(tools.addMemory).toBeDefined()
- })
+ expect(tools).toBeDefined();
+ expect(tools.searchMemories).toBeDefined();
+ expect(tools.addMemory).toBeDefined();
+ });
it("should create individual tools", () => {
const searchTool = aiSdk.searchMemoriesTool(testApiKey, {
projectId: "test-project-123",
- })
+ });
const addTool = aiSdk.addMemoryTool(testApiKey, {
projectId: "test-project-123",
- })
+ });
- expect(searchTool).toBeDefined()
- expect(addTool).toBeDefined()
- })
- })
+ expect(searchTool).toBeDefined();
+ expect(addTool).toBeDefined();
+ });
+ });
describe("AI SDK integration", () => {
it("should work with AI SDK generateText", async () => {
const openai = createOpenAI({
apiKey: testOpenAIKey,
- })
+ });
const result = await generateText({
model: openai(testModelName),
@@ -85,22 +87,22 @@ describe("@supermemory/tools", () => {
baseUrl: testBaseUrl,
}),
},
- })
+ });
- expect(result).toBeDefined()
- expect(result.text).toBeDefined()
- expect(typeof result.text).toBe("string")
- })
+ expect(result).toBeDefined();
+ expect(result.text).toBeDefined();
+ expect(typeof result.text).toBe("string");
+ });
it("should use tools when prompted", async () => {
const openai = createOpenAI({
apiKey: testOpenAIKey,
- })
+ });
const tools = aiSdk.supermemoryTools(testApiKey, {
projectId: "test-tool-usage",
baseUrl: testBaseUrl,
- })
+ });
const result = await generateText({
model: openai(testModelName),
@@ -118,157 +120,159 @@ describe("@supermemory/tools", () => {
tools: {
addMemory: tools.addMemory,
},
- })
+ });
- expect(result).toBeDefined()
- expect(result.text).toBeDefined()
- })
- })
- })
+ expect(result).toBeDefined();
+ expect(result.text).toBeDefined();
+ });
+ });
+ });
describe("openAi module", () => {
describe("function-based tools", () => {
it("should create function-based tools", () => {
const tools = openAi.supermemoryTools(testApiKey, {
projectId: "test-openai-functions",
- })
+ });
- expect(tools).toBeDefined()
- expect(tools.searchMemories).toBeDefined()
- expect(tools.addMemory).toBeDefined()
- })
+ expect(tools).toBeDefined();
+ expect(tools.searchMemories).toBeDefined();
+ expect(tools.addMemory).toBeDefined();
+ });
it("should create individual tool functions", () => {
const searchFunction = openAi.createSearchMemoriesFunction(testApiKey, {
projectId: "test-individual",
- })
+ });
const addFunction = openAi.createAddMemoryFunction(testApiKey, {
projectId: "test-individual",
- })
+ });
- expect(searchFunction).toBeDefined()
- expect(addFunction).toBeDefined()
- expect(typeof searchFunction).toBe("function")
- expect(typeof addFunction).toBe("function")
- })
- })
+ expect(searchFunction).toBeDefined();
+ expect(addFunction).toBeDefined();
+ expect(typeof searchFunction).toBe("function");
+ expect(typeof addFunction).toBe("function");
+ });
+ });
describe("tool definitions", () => {
it("should return proper OpenAI function definitions", () => {
- const definitions = openAi.getToolDefinitions()
+ const definitions = openAi.getToolDefinitions();
- expect(definitions).toBeDefined()
- expect(definitions.length).toBe(2)
+ expect(definitions).toBeDefined();
+ expect(definitions.length).toBe(2);
// Check searchMemories
const searchTool = definitions.find(
(d) => d.function.name === "searchMemories",
- )
- expect(searchTool).toBeDefined()
- expect(searchTool!.type).toBe("function")
+ );
+ expect(searchTool).toBeDefined();
+ expect(searchTool!.type).toBe("function");
expect(searchTool!.function.parameters?.required).toContain(
"informationToGet",
- )
+ );
// Check addMemory
- const addTool = definitions.find((d) => d.function.name === "addMemory")
- expect(addTool).toBeDefined()
- expect(addTool!.type).toBe("function")
- expect(addTool!.function.parameters?.required).toContain("memory")
- })
- })
+ const addTool = definitions.find(
+ (d) => d.function.name === "addMemory",
+ );
+ expect(addTool).toBeDefined();
+ expect(addTool!.type).toBe("function");
+ expect(addTool!.function.parameters?.required).toContain("memory");
+ });
+ });
describe("tool execution", () => {
it("should create tool call executor", () => {
const executor = openAi.createToolCallExecutor(testApiKey, {
containerTags: ["test-executor"],
baseUrl: testBaseUrl,
- })
+ });
- expect(executor).toBeDefined()
- expect(typeof executor).toBe("function")
- })
+ expect(executor).toBeDefined();
+ expect(typeof executor).toBe("function");
+ });
it("should create tool calls executor", () => {
const executor = openAi.createToolCallsExecutor(testApiKey, {
containerTags: ["test-executors"],
baseUrl: testBaseUrl,
- })
+ });
- expect(executor).toBeDefined()
- expect(typeof executor).toBe("function")
- })
- })
+ expect(executor).toBeDefined();
+ expect(typeof executor).toBe("function");
+ });
+ });
describe("individual tool creators", () => {
it("should create individual search tool", () => {
const searchTool = openAi.createSearchMemoriesTool(testApiKey, {
projectId: "test-individual",
- })
+ });
- expect(searchTool).toBeDefined()
- expect(searchTool.definition).toBeDefined()
- expect(searchTool.execute).toBeDefined()
- expect(searchTool.definition.function.name).toBe("searchMemories")
- })
+ expect(searchTool).toBeDefined();
+ expect(searchTool.definition).toBeDefined();
+ expect(searchTool.execute).toBeDefined();
+ expect(searchTool.definition.function.name).toBe("searchMemories");
+ });
it("should create individual add tool", () => {
const addTool = openAi.createAddMemoryTool(testApiKey, {
projectId: "test-individual",
- })
+ });
- expect(addTool).toBeDefined()
- expect(addTool.definition).toBeDefined()
- expect(addTool.execute).toBeDefined()
- expect(addTool.definition.function.name).toBe("addMemory")
- })
- })
+ expect(addTool).toBeDefined();
+ expect(addTool.definition).toBeDefined();
+ expect(addTool.execute).toBeDefined();
+ expect(addTool.definition.function.name).toBe("addMemory");
+ });
+ });
describe("memory operations", () => {
it("should search memories", async () => {
const searchFunction = openAi.createSearchMemoriesFunction(testApiKey, {
projectId: "test-search",
baseUrl: testBaseUrl,
- })
+ });
const result = await searchFunction({
informationToGet: "test preferences",
limit: 5,
- })
+ });
- expect(result).toBeDefined()
- expect(result.success).toBeDefined()
- expect(typeof result.success).toBe("boolean")
+ expect(result).toBeDefined();
+ expect(result.success).toBeDefined();
+ expect(typeof result.success).toBe("boolean");
if (result.success) {
- expect(result.results).toBeDefined()
- expect(result.count).toBeDefined()
- expect(typeof result.count).toBe("number")
+ expect(result.results).toBeDefined();
+ expect(result.count).toBeDefined();
+ expect(typeof result.count).toBe("number");
} else {
- expect(result.error).toBeDefined()
+ expect(result.error).toBeDefined();
}
- })
+ });
it("should add memory", async () => {
const addFunction = openAi.createAddMemoryFunction(testApiKey, {
containerTags: ["test-add-memory"],
baseUrl: testBaseUrl,
- })
+ });
const result = await addFunction({
memory: "User prefers dark roast coffee in the morning - test memory",
- })
+ });
- expect(result).toBeDefined()
- expect(result.success).toBeDefined()
- expect(typeof result.success).toBe("boolean")
+ expect(result).toBeDefined();
+ expect(result.success).toBeDefined();
+ expect(typeof result.success).toBe("boolean");
if (result.success) {
- expect(result.memory).toBeDefined()
+ expect(result.memory).toBeDefined();
} else {
- expect(result.error).toBeDefined()
+ expect(result.error).toBeDefined();
}
- })
- })
- })
-})
+ });
+ });
+ });
+});
diff --git a/packages/tools/src/types.ts b/packages/tools/src/types.ts
index dfff0f00..86c7fc16 100644
--- a/packages/tools/src/types.ts
+++ b/packages/tools/src/types.ts
@@ -3,7 +3,7 @@
* Only one of `projectId` or `containerTags` can be provided.
*/
export interface SupermemoryToolsConfig {
- baseUrl?: string
- containerTags?: string[]
- projectId?: string
+ baseUrl?: string;
+ containerTags?: string[];
+ projectId?: string;
}
diff --git a/packages/tools/tsdown.config.ts b/packages/tools/tsdown.config.ts
index 59be1b93..60005c69 100644
--- a/packages/tools/tsdown.config.ts
+++ b/packages/tools/tsdown.config.ts
@@ -1,4 +1,4 @@
-import { defineConfig } from "tsdown"
+import { defineConfig } from "tsdown";
export default defineConfig({
entry: ["src/index.ts", "src/ai-sdk.ts", "src/openai.ts"],
@@ -12,4 +12,4 @@ export default defineConfig({
sourcemap: false,
},
exports: true,
-})
+});
diff --git a/packages/ui/memory-graph/hooks/use-graph-data.ts b/packages/ui/memory-graph/hooks/use-graph-data.ts
index 3e9fa5cc..ec17a756 100644
--- a/packages/ui/memory-graph/hooks/use-graph-data.ts
+++ b/packages/ui/memory-graph/hooks/use-graph-data.ts
@@ -29,26 +29,26 @@ export function useGraphData(
const allEdges: GraphEdge[] = [];
// Filter documents that have memories in selected space
- const filteredDocuments = data.documents
+ const filteredDocuments = (data.documents || [])
.map((doc) => ({
...doc,
memoryEntries:
selectedSpace === "all"
- ? doc.memoryEntries
- : doc.memoryEntries.filter(
+ ? doc.memoryEntries || []
+ : (doc.memoryEntries || []).filter(
(memory) =>
(memory.spaceContainerTag ?? memory.spaceId ?? "default") ===
selectedSpace,
),
}))
- .filter((doc) => doc.memoryEntries.length > 0);
+ .filter((doc) => (doc.memoryEntries || []).length > 0);
// Group documents by space for better clustering
const documentsBySpace = new Map<string, typeof filteredDocuments>();
filteredDocuments.forEach((doc) => {
const docSpace =
- doc.memoryEntries[0]?.spaceContainerTag ??
- doc.memoryEntries[0]?.spaceId ??
+ (doc.memoryEntries || [])[0]?.spaceContainerTag ??
+ (doc.memoryEntries || [])[0]?.spaceId ??
"default";
if (!documentsBySpace.has(docSpace)) {
documentsBySpace.set(docSpace, []);
@@ -171,7 +171,7 @@ export function useGraphData(
const memoryNodeMap = new Map<string, GraphNode>();
const doc = docNode.data as DocumentWithMemories;
- doc.memoryEntries.forEach((memory, memIndex) => {
+ (doc.memoryEntries || []).forEach((memory, memIndex) => {
const memoryId = `${memory.id}`;
const customMemPos = nodePositions.get(memoryId);
@@ -231,8 +231,8 @@ export function useGraphData(
});
// Add version-chain edges (old -> new)
- data.documents.forEach((doc) => {
- doc.memoryEntries.forEach((mem: MemoryEntry) => {
+ (data.documents || []).forEach((doc) => {
+ (doc.memoryEntries || []).forEach((mem: MemoryEntry) => {
// Support both new object structure and legacy array/single parent fields
let parentRelations: Record<string, MemoryRelation> = {};
diff --git a/packages/ui/memory-graph/memory-graph.tsx b/packages/ui/memory-graph/memory-graph.tsx
index 912a741a..72ddf090 100644
--- a/packages/ui/memory-graph/memory-graph.tsx
+++ b/packages/ui/memory-graph/memory-graph.tsx
@@ -157,8 +157,8 @@ export const MemoryGraph = ({
const spaceSet = new Set<string>();
const counts: Record<string, number> = {};
- data.documents.forEach((doc) => {
- doc.memoryEntries.forEach((memory) => {
+ (data.documents || []).forEach((doc) => {
+ (doc.memoryEntries || []).forEach((memory) => {
const spaceId = memory.spaceContainerTag || memory.spaceId || "default";
spaceSet.add(spaceId);
counts[spaceId] = (counts[spaceId] || 0) + 1;
@@ -199,32 +199,47 @@ export const MemoryGraph = ({
const handleCenter = useCallback(() => {
if (nodes.length > 0) {
// Calculate center of all nodes
- let sumX = 0
- let sumY = 0
- let count = 0
-
+ let sumX = 0;
+ let sumY = 0;
+ let count = 0;
+
nodes.forEach((node) => {
- sumX += node.x
- sumY += node.y
- count++
- })
-
+ sumX += node.x;
+ sumY += node.y;
+ count++;
+ });
+
if (count > 0) {
- const centerX = sumX / count
- const centerY = sumY / count
- centerViewportOn(centerX, centerY, containerSize.width, containerSize.height)
+ const centerX = sumX / count;
+ const centerY = sumY / count;
+ centerViewportOn(
+ centerX,
+ centerY,
+ containerSize.width,
+ containerSize.height,
+ );
}
}
- }, [nodes, centerViewportOn, containerSize.width, containerSize.height])
+ }, [nodes, centerViewportOn, containerSize.width, containerSize.height]);
const handleAutoFit = useCallback(() => {
- if (nodes.length > 0 && containerSize.width > 0 && containerSize.height > 0) {
+ if (
+ nodes.length > 0 &&
+ containerSize.width > 0 &&
+ containerSize.height > 0
+ ) {
autoFitToViewport(nodes, containerSize.width, containerSize.height, {
occludedRightPx,
animate: true,
- })
+ });
}
- }, [nodes, containerSize.width, containerSize.height, occludedRightPx, autoFitToViewport])
+ }, [
+ nodes,
+ containerSize.width,
+ containerSize.height,
+ occludedRightPx,
+ autoFitToViewport,
+ ]);
// Get selected node data
const selectedNodeData = useMemo(() => {
@@ -251,7 +266,7 @@ export const MemoryGraph = ({
};
// Count visible documents
- const visibleDocuments = data.documents.filter((doc) => {
+ const visibleDocuments = (data.documents || []).filter((doc) => {
const docNodes = nodes.filter(
(node) => node.type === "document" && node.data.id === doc.id,
);
@@ -265,7 +280,8 @@ export const MemoryGraph = ({
});
// If 80% or more of documents are visible, load more
- const visibilityRatio = visibleDocuments.length / data.documents.length;
+ const visibilityRatio =
+ visibleDocuments.length / (data.documents || []).length;
if (visibilityRatio >= 0.8) {
loadMoreDocuments();
}
@@ -421,8 +437,12 @@ export const MemoryGraph = ({
{containerSize.width > 0 && (
<NavigationControls
onCenter={handleCenter}
- onZoomIn={() => zoomIn(containerSize.width / 2, containerSize.height / 2)}
- onZoomOut={() => zoomOut(containerSize.width / 2, containerSize.height / 2)}
+ onZoomIn={() =>
+ zoomIn(containerSize.width / 2, containerSize.height / 2)
+ }
+ onZoomOut={() =>
+ zoomOut(containerSize.width / 2, containerSize.height / 2)
+ }
onAutoFit={handleAutoFit}
nodes={nodes}
className="absolute bottom-4 left-4"
diff --git a/packages/ui/memory-graph/navigation-controls.tsx b/packages/ui/memory-graph/navigation-controls.tsx
index b2abd67f..afd98a0f 100644
--- a/packages/ui/memory-graph/navigation-controls.tsx
+++ b/packages/ui/memory-graph/navigation-controls.tsx
@@ -1,67 +1,62 @@
-"use client"
+"use client";
-import { memo } from "react"
-import type { GraphNode } from "./types"
+import { memo } from "react";
+import type { GraphNode } from "./types";
interface NavigationControlsProps {
- onCenter: () => void
- onZoomIn: () => void
- onZoomOut: () => void
- onAutoFit: () => void
- nodes: GraphNode[]
- className?: string
+ onCenter: () => void;
+ onZoomIn: () => void;
+ onZoomOut: () => void;
+ onAutoFit: () => void;
+ nodes: GraphNode[];
+ className?: string;
}
-export const NavigationControls = memo<NavigationControlsProps>(({
- onCenter,
- onZoomIn,
- onZoomOut,
- onAutoFit,
- nodes,
- className = "",
-}) => {
- if (nodes.length === 0) {
- return null
- }
+export const NavigationControls = memo<NavigationControlsProps>(
+ ({ onCenter, onZoomIn, onZoomOut, onAutoFit, nodes, className = "" }) => {
+ if (nodes.length === 0) {
+ return null;
+ }
- return (
- <div className={`flex flex-col gap-1 ${className}`}>
- <button
- type="button"
- onClick={onAutoFit}
- className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16"
- title="Auto-fit graph to viewport"
- >
- Fit
- </button>
- <button
- type="button"
- onClick={onCenter}
- className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16"
- title="Center view on graph"
- >
- Center
- </button>
- <div className="flex flex-col">
+ return (
+ <div className={`flex flex-col gap-1 ${className}`}>
<button
type="button"
- onClick={onZoomIn}
- className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-t-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16 border-b-0"
- title="Zoom in"
+ onClick={onAutoFit}
+ className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16"
+ title="Auto-fit graph to viewport"
>
- +
+ Fit
</button>
<button
type="button"
- onClick={onZoomOut}
- className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-b-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16"
- title="Zoom out"
+ onClick={onCenter}
+ className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16"
+ title="Center view on graph"
>
- −
+ Center
</button>
+ <div className="flex flex-col">
+ <button
+ type="button"
+ onClick={onZoomIn}
+ className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-t-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16 border-b-0"
+ title="Zoom in"
+ >
+ +
+ </button>
+ <button
+ type="button"
+ onClick={onZoomOut}
+ className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-b-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16"
+ title="Zoom out"
+ >
+ −
+ </button>
+ </div>
</div>
- </div>
- )
-})
+ );
+ },
+);
-NavigationControls.displayName = "NavigationControls" \ No newline at end of file
+NavigationControls.displayName = "NavigationControls";
diff --git a/packages/ui/pages/login.tsx b/packages/ui/pages/login.tsx
index 74765da1..387309cb 100644
--- a/packages/ui/pages/login.tsx
+++ b/packages/ui/pages/login.tsx
@@ -5,8 +5,8 @@ import { usePostHog } from "@lib/posthog";
import { LogoFull } from "@repo/ui/assets/Logo";
import { TextSeparator } from "@repo/ui/components/text-separator";
import { ExternalAuthButton } from "@ui/button/external-auth";
-import { Button } from "@ui/components/button";
import { Badge } from "@ui/components/badge";
+import { Button } from "@ui/components/button";
import {
Carousel,
CarouselContent,
@@ -20,7 +20,7 @@ import { Title1Bold } from "@ui/text/title/title-1-bold";
import Autoplay from "embla-carousel-autoplay";
import Image from "next/image";
import { useRouter, useSearchParams } from "next/navigation";
-import { useState, useEffect } from "react";
+import { useEffect, useState } from "react";
export function LoginPage({
heroText = "The unified memory API for the AI era.",
diff --git a/packages/validation/api.ts b/packages/validation/api.ts
index 91ac5e09..0363cf2c 100644
--- a/packages/validation/api.ts
+++ b/packages/validation/api.ts
@@ -1,21 +1,21 @@
-import { z } from "zod"
-import "zod-openapi/extend"
+import { z } from "zod";
+import "zod-openapi/extend";
import {
MetadataSchema as BaseMetadataSchema,
DocumentSchema,
MemoryEntrySchema,
OrganizationSettingsSchema,
RequestTypeEnum,
-} from "./schemas"
+} from "./schemas";
-export const MetadataSchema = BaseMetadataSchema
+export const MetadataSchema = BaseMetadataSchema;
export const SearchFiltersSchema = z
.object({
AND: z.array(z.unknown()).optional(),
OR: z.array(z.unknown()).optional(),
})
- .or(z.record(z.unknown()))
+ .or(z.record(z.unknown()));
const exampleMetadata: Record<string, string | number | boolean> = {
category: "technology",
@@ -24,7 +24,7 @@ const exampleMetadata: Record<string, string | number | boolean> = {
source: "web",
tag_1: "ai",
tag_2: "machine-learning",
-} as const
+} as const;
const exampleMemory = {
connectionId: "conn_123",
@@ -45,7 +45,7 @@ const exampleMemory = {
type: "text",
updatedAt: new Date().toISOString(),
url: "https://example.com/article",
-} as const
+} as const;
export const MemorySchema = z
.object({
@@ -136,7 +136,7 @@ export const MemorySchema = z
.openapi({
description: "Memory object",
example: exampleMemory,
- })
+ });
export const MemoryUpdateSchema = z.object({
containerTags: z
@@ -162,9 +162,9 @@ export const MemoryUpdateSchema = z.object({
"Optional metadata for the memory. This is used to store additional information about the memory. You can use this to store any additional information you need about the memory. Metadata can be filtered through. Keys must be strings and are case sensitive. Values can be strings, numbers, or booleans. You cannot nest objects.",
example: exampleMetadata,
}),
-})
+});
-export const MemoryAddSchema = MemoryUpdateSchema
+export const MemoryAddSchema = MemoryUpdateSchema;
export const PaginationSchema = z
.object({
@@ -181,9 +181,9 @@ export const PaginationSchema = z
totalItems: 100,
totalPages: 10,
},
- })
+ });
-export const GetMemoryResponseSchema = MemorySchema
+export const GetMemoryResponseSchema = MemorySchema;
export const ListMemoriesResponseSchema = z
.object({
@@ -229,7 +229,7 @@ export const ListMemoriesResponseSchema = z
totalPages: 10,
},
},
- })
+ });
export const ListMemoriesQuerySchema = z
.object({
@@ -317,12 +317,12 @@ export const ListMemoriesQuerySchema = z
page: 1,
sort: "createdAt",
},
- })
+ });
export const MemoryResponseSchema = z.object({
id: z.string(),
status: z.string(),
-})
+});
export const SearchRequestSchema = z.object({
categoriesFilter: z
@@ -455,7 +455,7 @@ export const SearchRequestSchema = z.object({
"If true, rewrites the query to make it easier to find documents. This increases the latency by about 400ms",
example: false,
}),
-})
+});
export const Searchv4RequestSchema = z.object({
containerTag: z.string().optional().openapi({
@@ -546,7 +546,7 @@ export const Searchv4RequestSchema = z.object({
"If true, rewrites the query to make it easier to find documents. This increases the latency by about 400ms",
example: false,
}),
-})
+});
export const SearchResultSchema = z.object({
chunks: z
@@ -632,13 +632,13 @@ export const SearchResultSchema = z.object({
description: "Document type",
example: "web",
}),
-})
+});
export const SearchResponseSchema = z.object({
results: z.array(SearchResultSchema),
timing: z.number(),
total: z.number(),
-})
+});
// V4 Memory Search Schemas
export const MemorySearchDocumentSchema = z.object({
@@ -666,7 +666,7 @@ export const MemorySearchDocumentSchema = z.object({
description: "Document last update date",
format: "date-time",
}),
-})
+});
export const MemorySearchResult = z.object({
id: z.string().openapi({
@@ -765,7 +765,7 @@ export const MemorySearchResult = z.object({
documents: z.array(MemorySearchDocumentSchema).optional().openapi({
description: "Associated documents for this memory entry",
}),
-})
+});
export const MemorySearchResponseSchema = z.object({
results: z.array(MemorySearchResult).openapi({
@@ -779,7 +779,7 @@ export const MemorySearchResponseSchema = z.object({
description: "Total number of results returned",
example: 5,
}),
-})
+});
export const ErrorResponseSchema = z.object({
details: z.string().optional().openapi({
@@ -790,15 +790,15 @@ export const ErrorResponseSchema = z.object({
description: "Error message",
example: "Invalid request parameters",
}),
-})
+});
-export type SearchResult = z.infer<typeof SearchResultSchema>
+export type SearchResult = z.infer<typeof SearchResultSchema>;
export const SettingsRequestSchema = OrganizationSettingsSchema.omit({
id: true,
orgId: true,
updatedAt: true,
-})
+});
export const ConnectionResponseSchema = z.object({
createdAt: z.string().datetime(),
@@ -808,21 +808,21 @@ export const ConnectionResponseSchema = z.object({
id: z.string(),
metadata: z.record(z.any()).optional(),
provider: z.string(),
-})
+});
-export const RequestTypeSchema = RequestTypeEnum
+export const RequestTypeSchema = RequestTypeEnum;
export const HourlyAnalyticsSchema = z.object({
count: z.number(),
hour: z.union([z.date(), z.string()]),
-})
+});
export const ApiKeyAnalyticsBaseSchema = z.object({
count: z.number(),
keyId: z.string(),
keyName: z.string().nullable(),
lastUsed: z.union([z.date(), z.string()]).nullable(),
-})
+});
export const AnalyticsUsageResponseSchema = z.object({
byKey: z.array(
@@ -845,7 +845,7 @@ export const AnalyticsUsageResponseSchema = z.object({
type: RequestTypeSchema,
}),
),
-})
+});
export const AnalyticsErrorResponseSchema = z.object({
byKey: z.array(
@@ -878,7 +878,7 @@ export const AnalyticsErrorResponseSchema = z.object({
type: RequestTypeSchema,
}),
),
-})
+});
export const AnalyticsLogSchema = z.object({
createdAt: z.date(),
@@ -917,12 +917,12 @@ export const AnalyticsLogSchema = z.object({
]),
statusCode: z.number(),
type: RequestTypeSchema,
-})
+});
export const AnalyticsLogsResponseSchema = z.object({
logs: z.array(z.unknown()),
pagination: PaginationSchema,
-})
+});
export const AnalyticsChatResponseSchema = z.object({
analytics: z.object({
@@ -1022,7 +1022,7 @@ export const AnalyticsChatResponseSchema = z.object({
}),
}),
}),
-})
+});
export const AnalyticsMemoryResponseSchema = z.object({
connectionsGrowth: z.number(),
@@ -1033,7 +1033,7 @@ export const AnalyticsMemoryResponseSchema = z.object({
tokensProcessed: z.number(),
totalConnections: z.number(),
totalMemories: z.number(),
-})
+});
export const MemoryEntryAPISchema = MemoryEntrySchema.extend({
sourceAddedAt: z.date().nullable(), // From join relationship
@@ -1042,7 +1042,7 @@ export const MemoryEntryAPISchema = MemoryEntrySchema.extend({
spaceContainerTag: z.string().nullable(), // From join relationship
}).openapi({
description: "Memory entry with source relationship data",
-})
+});
// Extended document schema with memory entries
export const DocumentWithMemoriesSchema = z
@@ -1075,7 +1075,7 @@ export const DocumentWithMemoriesSchema = z
})
.openapi({
description: "Document with associated memory entries",
- })
+ });
export const DocumentsWithMemoriesResponseSchema = z
.object({
@@ -1084,7 +1084,7 @@ export const DocumentsWithMemoriesResponseSchema = z
})
.openapi({
description: "List of documents with their memory entries",
- })
+ });
export const DocumentsWithMemoriesQuerySchema = z
.object({
@@ -1114,7 +1114,7 @@ export const DocumentsWithMemoriesQuerySchema = z
})
.openapi({
description: "Query parameters for listing documents with memory entries",
- })
+ });
export const MigrateMCPRequestSchema = z
.object({
@@ -1129,7 +1129,7 @@ export const MigrateMCPRequestSchema = z
})
.openapi({
description: "Request body for migrating MCP documents",
- })
+ });
export const MigrateMCPResponseSchema = z
.object({
@@ -1155,7 +1155,7 @@ export const MigrateMCPResponseSchema = z
})
.openapi({
description: "Response for MCP document migration",
- })
+ });
// Processing documents schema
export const ProcessingDocumentsResponseSchema = z
@@ -1196,7 +1196,7 @@ export const ProcessingDocumentsResponseSchema = z
],
totalCount: 5,
},
- })
+ });
// Project schemas
export const ProjectSchema = z
@@ -1235,7 +1235,7 @@ export const ProjectSchema = z
})
.openapi({
description: "Project object for organizing memories",
- })
+ });
export const CreateProjectSchema = z
.object({
@@ -1248,7 +1248,7 @@ export const CreateProjectSchema = z
})
.openapi({
description: "Request body for creating a new project",
- })
+ });
export const ListProjectsResponseSchema = z
.object({
@@ -1258,7 +1258,7 @@ export const ListProjectsResponseSchema = z
})
.openapi({
description: "Response containing list of projects",
- })
+ });
export const DeleteProjectSchema = z
.object({
@@ -1275,9 +1275,9 @@ export const DeleteProjectSchema = z
(data) => {
// If action is "move", targetProjectId is required
if (data.action === "move") {
- return !!data.targetProjectId
+ return !!data.targetProjectId;
}
- return true
+ return true;
},
{
message: "targetProjectId is required when action is 'move'",
@@ -1286,7 +1286,7 @@ export const DeleteProjectSchema = z
)
.openapi({
description: "Request body for deleting a project",
- })
+ });
export const DeleteProjectResponseSchema = z
.object({
@@ -1309,7 +1309,7 @@ export const DeleteProjectResponseSchema = z
})
.openapi({
description: "Response for project deletion",
- })
+ });
// Bulk delete schema - supports both IDs and container tags
export const BulkDeleteMemoriesSchema = z
@@ -1336,7 +1336,7 @@ export const BulkDeleteMemoriesSchema = z
.refine(
(data) => {
// At least one of ids or containerTags must be provided
- return !!data.ids?.length || !!data.containerTags?.length
+ return !!data.ids?.length || !!data.containerTags?.length;
},
{
message: "Either 'ids' or 'containerTags' must be provided",
@@ -1348,7 +1348,7 @@ export const BulkDeleteMemoriesSchema = z
example: {
ids: ["acxV5LHMEsG2hMSNb4umbn", "bxcV5LHMEsG2hMSNb4umbn"],
},
- })
+ });
export const BulkDeleteMemoriesResponseSchema = z
.object({
@@ -1383,4 +1383,4 @@ export const BulkDeleteMemoriesResponseSchema = z
})
.openapi({
description: "Response for bulk memory deletion",
- })
+ });
diff --git a/packages/validation/connection.ts b/packages/validation/connection.ts
index e7bc8352..e4baade6 100644
--- a/packages/validation/connection.ts
+++ b/packages/validation/connection.ts
@@ -1,13 +1,13 @@
-import { z } from "zod"
-import { ConnectionProviderEnum } from "./schemas"
+import { z } from "zod";
+import { ConnectionProviderEnum } from "./schemas";
-export const providers = ConnectionProviderEnum
-export type Provider = z.infer<typeof providers>
+export const providers = ConnectionProviderEnum;
+export type Provider = z.infer<typeof providers>;
const BaseMetadataSchema = <T extends z.ZodTypeAny>(provider: T) =>
z.object({
provider,
- })
+ });
export const NotionMetadataSchema = BaseMetadataSchema(
z.literal("notion"),
@@ -17,8 +17,8 @@ export const NotionMetadataSchema = BaseMetadataSchema(
workspaceIcon: z.string().optional(),
workspaceId: z.string(),
workspaceName: z.string(),
-})
-export type NotionMetadata = z.infer<typeof NotionMetadataSchema>
+});
+export type NotionMetadata = z.infer<typeof NotionMetadataSchema>;
export const GoogleDriveMetadataSchema = BaseMetadataSchema(
z.literal("google-drive"),
@@ -27,8 +27,8 @@ export const GoogleDriveMetadataSchema = BaseMetadataSchema(
webhookChannelId: z.string().optional(),
webhookExpiration: z.number().optional(),
webhookResourceId: z.string().optional(),
-})
-export type GoogleDriveMetadata = z.infer<typeof GoogleDriveMetadataSchema>
+});
+export type GoogleDriveMetadata = z.infer<typeof GoogleDriveMetadataSchema>;
export const OneDriveMetadataSchema = BaseMetadataSchema(
z.literal("onedrive"),
@@ -38,38 +38,38 @@ export const OneDriveMetadataSchema = BaseMetadataSchema(
webhookClientState: z.string().optional(),
webhookExpiration: z.number().optional(),
webhookSubscriptionId: z.string().optional(),
-})
-export type OneDriveMetadata = z.infer<typeof OneDriveMetadataSchema>
+});
+export type OneDriveMetadata = z.infer<typeof OneDriveMetadataSchema>;
export const ConnectionMetadataSchema = z.discriminatedUnion("provider", [
NotionMetadataSchema,
GoogleDriveMetadataSchema,
OneDriveMetadataSchema,
-])
+]);
export type ConnectionMetadata<T extends Provider> = T extends "notion"
? NotionMetadata
: T extends "google-drive"
? GoogleDriveMetadata
: T extends "onedrive"
? OneDriveMetadata
- : never
+ : never;
export function isNotionMetadata(
metadata: unknown,
): metadata is NotionMetadata {
- return NotionMetadataSchema.safeParse(metadata).success
+ return NotionMetadataSchema.safeParse(metadata).success;
}
export function isGoogleDriveMetadata(
metadata: unknown,
): metadata is GoogleDriveMetadata {
- return GoogleDriveMetadataSchema.safeParse(metadata).success
+ return GoogleDriveMetadataSchema.safeParse(metadata).success;
}
export function isOneDriveMetadata(
metadata: unknown,
): metadata is OneDriveMetadata {
- return OneDriveMetadataSchema.safeParse(metadata).success
+ return OneDriveMetadataSchema.safeParse(metadata).success;
}
export const ConnectionStateSchema = z.object({
@@ -77,8 +77,8 @@ export const ConnectionStateSchema = z.object({
org: z.string(),
provider: providers,
userId: z.string(),
-})
-export type ConnectionState = z.infer<typeof ConnectionStateSchema>
+});
+export type ConnectionState = z.infer<typeof ConnectionStateSchema>;
export const TokenDataSchema = z.object({
// Only used for Notion connections since they don't support refresh tokens
@@ -90,8 +90,8 @@ export const TokenDataSchema = z.object({
metadata: ConnectionMetadataSchema.optional(),
refreshToken: z.string().optional(),
userId: z.string().optional(),
-})
-export type TokenData = z.infer<typeof TokenDataSchema>
+});
+export type TokenData = z.infer<typeof TokenDataSchema>;
export const NotionTokenResponseSchema = z.object({
access_token: z.string(),
@@ -123,8 +123,8 @@ export const NotionTokenResponseSchema = z.object({
workspace_icon: z.string().optional(),
workspace_id: z.string(),
workspace_name: z.string(),
-})
-export type NotionTokenResponse = z.infer<typeof NotionTokenResponseSchema>
+});
+export type NotionTokenResponse = z.infer<typeof NotionTokenResponseSchema>;
export const GoogleDriveTokenResponseSchema = z.object({
access_token: z.string(),
@@ -132,10 +132,10 @@ export const GoogleDriveTokenResponseSchema = z.object({
refresh_token: z.string().optional(),
scope: z.string(),
token_type: z.literal("Bearer"),
-})
+});
export type GoogleDriveTokenResponse = z.infer<
typeof GoogleDriveTokenResponseSchema
->
+>;
export const OneDriveTokenResponseSchema = z.object({
access_token: z.string(),
@@ -143,8 +143,8 @@ export const OneDriveTokenResponseSchema = z.object({
refresh_token: z.string().optional(),
scope: z.string(),
token_type: z.literal("Bearer"),
-})
-export type OneDriveTokenResponse = z.infer<typeof OneDriveTokenResponseSchema>
+});
+export type OneDriveTokenResponse = z.infer<typeof OneDriveTokenResponseSchema>;
export const NotionConfigSchema = z.object({
clientId: z.string(),
@@ -154,23 +154,23 @@ export const NotionConfigSchema = z.object({
token: z.string().url(),
}),
scopes: z.array(z.string()),
-})
-export type NotionConfig = z.infer<typeof NotionConfigSchema>
+});
+export type NotionConfig = z.infer<typeof NotionConfigSchema>;
export const ConnectionQuerySchema = z.object({
id: z.string(),
redirectUrl: z.string().optional(),
-})
+});
export const GoogleDrivePageTokenResponseSchema = z.object({
startPageToken: z.union([z.string(), z.number()]),
-})
+});
export const GoogleDriveWatchResponseSchema = z.object({
expiration: z.string(),
id: z.string(),
resourceId: z.string(),
-})
+});
export const OneDriveSubscriptionResponseSchema = z.object({
changeType: z.string(),
@@ -179,13 +179,13 @@ export const OneDriveSubscriptionResponseSchema = z.object({
id: z.string(),
notificationUrl: z.string(),
resource: z.string(),
-})
+});
export const GoogleUserInfoResponseSchema = z.object({
email: z.string().email(),
-})
+});
export const MicrosoftUserInfoResponseSchema = z.object({
mail: z.string().optional(),
userPrincipalName: z.string().optional(),
-})
+});