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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 98 additions & 21 deletions src/chatgpt/content.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,49 @@
// Extract and store runId (from ChatGPT conversation id), then run callback
function extractAndStoreRunIdFromConversation(chatgptCurrentUrl?: string, callback?: () => void): void {
let runId: string | null = null;
const url = chatgptCurrentUrl || window.location.href;
if (url.includes('chatgpt.com/c/') || url.includes('chatgpt.com/g/')) {
const extracted = url.split('/c/')[1]?.split('?')[0];
runId = extracted ? extracted : null;
}

if (runId) {
chrome.storage.sync.set({ [StorageKey.RUN_ID_GPT]: runId }, () => {
console.log('Saved runId (conversation id):', runId);
if (callback) callback();
});
}
else {
// Not in a valid ChatGPT chat/thread, clear runId
chrome.storage.sync.set({ [StorageKey.RUN_ID_GPT]: null }, () => {
console.log('Cleared runId (not in ChatGPT chat/thread)');
if (callback) callback();
});
}
}
console.log('ayush1');
extractAndStoreRunIdFromConversation();

// Robust SPA navigation detection for chatGPT
let lastChatGptUrl = window.location.href;
let pendingMemoryCapture = false;
let pendingMessage = '';
const chatGptUrlObserver = new MutationObserver(() => {
if (window.location.href !== lastChatGptUrl) {
lastChatGptUrl = window.location.href;
extractAndStoreRunIdFromConversation();
// If pending memory capture, run after runId is set
if (pendingMemoryCapture) {
setTimeout(() => {
captureAndStoreMemory();
pendingMemoryCapture = false;
pendingMessage = '';
}, 100); // Wait for runId to be stored
}
}
});
chatGptUrlObserver.observe(document.body, { childList: true, subtree: true });

/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { DEFAULT_USER_ID, type LoginData, MessageRole } from '../types/api';
import type { HistoryStateData } from '../types/browser';
Expand All @@ -12,6 +58,7 @@ import { OPENMEMORY_UI } from '../utils/util_positioning';

export {};


let isProcessingMem0: boolean = false;

// Initialize the MutationObserver variable
Expand Down Expand Up @@ -47,6 +94,13 @@ const chatgptSearch = createOrchestrator({
StorageKey.USER_ID,
StorageKey.SIMILARITY_THRESHOLD,
StorageKey.TOP_K,
StorageKey.RUN_ID_GPT,
StorageKey.RUN_ID_GEMINI,
StorageKey.RUN_ID_CLAUDE,
StorageKey.RUN_ID_DEEPSEEK,
StorageKey.RUN_ID_GROK,
StorageKey.RUN_ID_PERPLEXITY,
StorageKey.RUN_ID_REPLIT,
],
function (items) {
resolve(items as SearchStorage);
Expand Down Expand Up @@ -76,10 +130,18 @@ const chatgptSearch = createOrchestrator({
if (data[StorageKey.SELECTED_PROJECT]) {
optionalParams.project_id = data[StorageKey.SELECTED_PROJECT];
}
// Try to find any runId value in the storage data

const payload = {
query,
filters: { user_id: userId },
//filters: { user_id: userId, },
filters: {
"AND": [
{ user_id: userId },
{ run_id: '*' },
{ session_id: '*' }
]
},
rerank: true,
threshold: threshold,
top_k: topK,
Expand Down Expand Up @@ -1217,14 +1279,15 @@ function addSendButtonListener(): void {
if (sendButton && !sendButton.dataset.mem0Listener) {
sendButton.dataset.mem0Listener = 'true';
sendButton.addEventListener('click', function () {
// Capture and save memory asynchronously
captureAndStoreMemory();

// Clear all memories after sending
setTimeout(() => {
allMemories = [];
allMemoriesById.clear();
}, 100);
// Always update run_id, then capture memory
extractAndStoreRunIdFromConversation(undefined, () => {
captureAndStoreMemory();
// Clear all memories after sending
setTimeout(() => {
allMemories = [];
allMemoriesById.clear();
}, 100);
});
});

// Also handle Enter key press
Expand All @@ -1236,22 +1299,21 @@ function addSendButtonListener(): void {
if (inputElement && !inputElement.dataset.mem0KeyListener) {
inputElement.dataset.mem0KeyListener = 'true';
(inputElement as HTMLElement).addEventListener('keydown', function (event: KeyboardEvent) {
// Check if Enter was pressed without Shift (standard send behavior)

inputValueCopy =
(inputElement as HTMLTextAreaElement)?.value ||
inputElement.textContent ||
inputValueCopy;

if (event.key === 'Enter' && !event.shiftKey) {
// Capture and save memory asynchronously
captureAndStoreMemory();

// Clear all memories after sending
setTimeout(() => {
allMemories = [];
allMemoriesById.clear();
}, 100);
// Always update run_id, then capture memory
extractAndStoreRunIdFromConversation(undefined, () => {
captureAndStoreMemory();
// Clear all memories after sending
setTimeout(() => {
allMemories = [];
allMemoriesById.clear();
}, 100);
});
}
});
}
Expand All @@ -1260,6 +1322,8 @@ function addSendButtonListener(): void {

// Function to capture and store memory asynchronously
function captureAndStoreMemory(): void {
console.log('ayush5');
extractAndStoreRunIdFromConversation();
// Get the message content
// id is prompt-textarea
const inputElement =
Expand Down Expand Up @@ -1290,7 +1354,7 @@ function captureAndStoreMemory(): void {
if (!message || message.trim() === '') {
return;
}

// Asynchronously store the memory
chrome.storage.sync.get(
[
Expand All @@ -1301,6 +1365,7 @@ function captureAndStoreMemory(): void {
StorageKey.SELECTED_ORG,
StorageKey.SELECTED_PROJECT,
StorageKey.USER_ID,
StorageKey.RUN_ID_GPT,
],
function (items) {
// Skip if memory is disabled or no credentials
Expand Down Expand Up @@ -1331,9 +1396,11 @@ function captureAndStoreMemory(): void {
}

// Send memory to mem0 API asynchronously without waiting for response
const runId = items[StorageKey.RUN_ID_GPT];
const storagePayload = {
messages: messages,
user_id: userId,
run_id: runId,
infer: true,
metadata: {
provider: 'ChatGPT',
Expand Down Expand Up @@ -1451,6 +1518,7 @@ async function handleMem0Modal(sourceButtonId: string | null = null): Promise<vo
StorageKey.USER_ID,
StorageKey.SIMILARITY_THRESHOLD,
StorageKey.TOP_K,
StorageKey.RUN_ID_GPT,
],
function (items) {
resolve(items);
Expand Down Expand Up @@ -2102,6 +2170,8 @@ function handleSyncClick(): void {
// New function to send memories in batch
function sendMemoriesToMem0(memories: Array<{ role: string; content: string }>): Promise<void> {
return new Promise<void>((resolve, reject) => {
console.log('ayush6');
extractAndStoreRunIdFromConversation();
chrome.storage.sync.get(
[
StorageKey.API_KEY,
Expand All @@ -2110,6 +2180,7 @@ function sendMemoriesToMem0(memories: Array<{ role: string; content: string }>):
StorageKey.SELECTED_ORG,
StorageKey.SELECTED_PROJECT,
StorageKey.USER_ID,
StorageKey.RUN_ID_GPT,
],
function (items) {
if (items[StorageKey.API_KEY] || items[StorageKey.ACCESS_TOKEN]) {
Expand All @@ -2126,6 +2197,7 @@ function sendMemoriesToMem0(memories: Array<{ role: string; content: string }>):
if (items[StorageKey.SELECTED_PROJECT]) {
optionalParams.project_id = items[StorageKey.SELECTED_PROJECT];
}
const runId = items[StorageKey.RUN_ID_GPT];

fetch('https://api.mem0.ai/v1/memories/', {
method: 'POST',
Expand All @@ -2136,6 +2208,7 @@ function sendMemoriesToMem0(memories: Array<{ role: string; content: string }>):
body: JSON.stringify({
messages: memories,
user_id: userId,
run_id: runId,
infer: true,
metadata: {
provider: 'ChatGPT',
Expand Down Expand Up @@ -2222,6 +2295,8 @@ function sendMemoryToMem0(
infer: boolean = true
): Promise<void> {
return new Promise<void>((resolve, reject) => {
console.log('ayush7');
extractAndStoreRunIdFromConversation();
chrome.storage.sync.get(
[
StorageKey.API_KEY,
Expand All @@ -2230,6 +2305,7 @@ function sendMemoryToMem0(
StorageKey.SELECTED_ORG,
StorageKey.SELECTED_PROJECT,
StorageKey.USER_ID,
StorageKey.RUN_ID_GPT,
],
function (items) {
if (items[StorageKey.API_KEY] || items[StorageKey.ACCESS_TOKEN]) {
Expand All @@ -2246,7 +2322,7 @@ function sendMemoryToMem0(
if (items[StorageKey.SELECTED_PROJECT]) {
optionalParams.project_id = items[StorageKey.SELECTED_PROJECT];
}

const runId = items[StorageKey.RUN_ID_GPT];
fetch('https://api.mem0.ai/v1/memories/', {
method: 'POST',
headers: {
Expand All @@ -2256,6 +2332,7 @@ function sendMemoryToMem0(
body: JSON.stringify({
messages: [{ content: memory.content, role: MessageRole.User }],
user_id: userId,
run_id: runId,
infer: infer,
metadata: {
provider: 'ChatGPT',
Expand Down
53 changes: 49 additions & 4 deletions src/claude/content.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
// Extract and store runId (from Claude conversation id)
function extractAndStoreRunIdFromConversation(claudeCurrentUrl?: string): void {
let runId: string | null = null;
const url = claudeCurrentUrl || window.location.href;
if (url.includes('claude.ai/chat/')) {
const extracted = url.split('/chat/')[1]?.split('?')[0];
runId = extracted ? extracted : null;
}
if (runId) {
chrome.storage.sync.set({ [StorageKey.RUN_ID_CLAUDE]: runId }, () => {
console.log('Saved runId (Claude conversation id):', runId);
});
}
else {
// Not in a valid Claude chat/thread, clear runId
chrome.storage.sync.set({ [StorageKey.RUN_ID_CLAUDE]: null }, () => {
console.log('Cleared runId (not in Claude chat/thread)');
});
}
}
extractAndStoreRunIdFromConversation();
import { MessageRole } from '../types/api';
import type { HistoryStateData } from '../types/browser';
import type { ExtendedDocument, ExtendedElement } from '../types/dom';
Expand All @@ -10,6 +31,16 @@ import { SITE_CONFIG } from '../utils/site_config';
import { getBrowser, sendExtensionEvent } from '../utils/util_functions';
import { OPENMEMORY_UI, type Placement } from '../utils/util_positioning';

// Robust SPA navigation detection for Claude
let lastClaudeUrl = window.location.href;
const claudeUrlObserver = new MutationObserver(() => {
if (window.location.href !== lastClaudeUrl) {
lastClaudeUrl = window.location.href;
extractAndStoreRunIdFromConversation();
}
});
claudeUrlObserver.observe(document.body, { childList: true, subtree: true });

// Chrome runtime types
interface ChromeRuntimeLastError {
message?: string;
Expand Down Expand Up @@ -51,6 +82,13 @@ const claudeSearch = createOrchestrator({
StorageKey.USER_ID,
StorageKey.SIMILARITY_THRESHOLD,
StorageKey.TOP_K,
StorageKey.RUN_ID_GPT,
StorageKey.RUN_ID_GEMINI,
StorageKey.RUN_ID_CLAUDE,
StorageKey.RUN_ID_DEEPSEEK,
StorageKey.RUN_ID_GROK,
StorageKey.RUN_ID_PERPLEXITY,
StorageKey.RUN_ID_REPLIT,
],
function (items) {
resolve(items as SearchStorage);
Expand Down Expand Up @@ -2177,7 +2215,7 @@ async function handleMem0Modal(
console.log('❌ Memory disabled, not showing modal');
return; // Don't show modal or login popup if memory is disabled
}

extractAndStoreRunIdFromConversation();
isProcessingMem0 = true;

// Set loading state for button
Expand All @@ -2201,6 +2239,7 @@ async function handleMem0Modal(
StorageKey.USER_ID,
StorageKey.SIMILARITY_THRESHOLD,
StorageKey.TOP_K,
StorageKey.RUN_ID_CLAUDE, // Add RUN_ID_CLAUDE to the keys
],
function (items) {
resolve(items);
Expand Down Expand Up @@ -2306,17 +2345,19 @@ async function handleMem0Modal(
} catch (_) {
claudeSearch.runImmediate(message);
}

const runId = data[StorageKey.RUN_ID_CLAUDE];
// New add memory API call (non-blocking)
fetch('https://api.mem0.ai/v1/memories/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: authHeader,
},

body: JSON.stringify({
messages: messages,
user_id: userId,
run_id: runId,
infer: true,
metadata: {
provider: 'Claude',
Expand Down Expand Up @@ -3023,7 +3064,8 @@ async function captureAndStoreMemory(snapshot: string) {
if (!chrome || !chrome.storage) {
return;
}

// Ensure we have a run ID for this session
extractAndStoreRunIdFromConversation();
try {
// Check if memory is enabled
const memoryEnabled = await getMemoryEnabledState();
Expand Down Expand Up @@ -3097,6 +3139,7 @@ async function captureAndStoreMemory(snapshot: string) {
StorageKey.SELECTED_ORG,
StorageKey.SELECTED_PROJECT,
StorageKey.USER_ID,
StorageKey.RUN_ID_CLAUDE,
],
function (items) {
// Check for chrome.runtime.lastError which indicates extension context issues
Expand Down Expand Up @@ -3131,17 +3174,19 @@ async function captureAndStoreMemory(snapshot: string) {
if (items.selected_project) {
optionalParams.project_id = items.selected_project;
}

const runId = items[StorageKey.RUN_ID_CLAUDE];
// Send memory to mem0 API asynchronously without waiting for response
fetch('https://api.mem0.ai/v1/memories/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: authHeader,
},

body: JSON.stringify({
messages: contextMessages,
user_id: userId,
run_id: runId,
infer: true,
metadata: {
provider: 'Claude',
Expand Down
Loading