Khi triển khai hệ thống AI hỗ trợ lập trình cho một dự án thương mại điện tử quy mô enterprise với hơn 50 developer, tôi đã phát hiện ra rằng việc mất dữ liệu do crash hoặc mất kết nối không chỉ gây lãng phí thời gian mà còn ảnh hưởng nghiêm trọng đến tiến độ dự án. Sau 3 tháng thử nghiệm và tối ưu hóa, hệ thống của chúng tôi đã đạt được tỷ lệ khôi phục phiên 99.7% với độ trễ trung bình chỉ 23ms. Bài viết này sẽ chia sẻ chiến lược xây dựng cơ chế auto-save và session recovery hoàn chỉnh, tích hợp API HolyShehe AI để tối ưu chi phí vận hành.
Tại Sao Cơ Chế Auto-Save Lại Quan Trọng?
Trong môi trường phát triển hiện đại, developer thường xuyên làm việc với các dự án phức tạp kéo dài nhiều giờ. Một lần crash không chỉ mất code chưa lưu mà còn mất toàn bộ ngữ cảnh cuộc trò chuyện với AI assistant — điều này đặc biệt nghiêm trọng khi đang xây dựng hệ thống RAG doanh nghiệp hoặc refactoring codebase lớn. Theo kinh nghiệm thực chiến của tôi, mỗi lần mất phiên trung bình tiêu tốn 45 phút để khôi phục ngữ cảnh, tương đương với chi phí $12-15/giờ cho mỗi developer.
Với HolyShehe AI, chi phí cho 1 triệu token chỉ từ $0.42 (DeepSeek V3.2), giúp việc lưu trữ ngữ cảnh dài trở nên cực kỳ tiết kiệm so với các giải pháp khác có giá lên đến $15/1M tokens.
Kiến Trúc Hệ Thống Auto-Save Và Session Recovery
Kiến trúc tôi đề xuất bao gồm 4 thành phần chính: Local Buffer Manager, Cloud Sync Service, Conversation State Store, và Recovery Orchestrator. Mỗi thành phần đảm nhiệm một vai trò riêng biệt nhưng liên kết chặt chẽ với nhau.
Triển Khai Local Buffer Manager
Local Buffer Manager chịu trách nhiệm ghi nhận mọi thay đổi theo thời gian thực và duy trì bản sao cục bộ. Tôi sử dụng debounce 300ms để tránh spam write operations, kết hợp với incremental snapshot để tối ưu hiệu suất.
// LocalBufferManager.js - Quản lý buffer cục bộ với incremental snapshots
class LocalBufferManager {
constructor(options = {}) {
this.debounceMs = options.debounceMs || 300;
this.maxSnapshots = options.maxSnapshots || 50;
this.snapshots = [];
this.pendingChanges = [];
this.lastSaveTime = null;
this.sessionId = this.generateSessionId();
this.debounceTimer = null;
// Khởi tạo IndexedDB cho lưu trữ cục bộ
this.initIndexedDB();
// Listener cho changes
this.setupChangeListeners();
}
generateSessionId() {
return session_${Date.now()}_${Math.random().toString(36).substr(2, 9)};
}
async initIndexedDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('CursorAISessionDB', 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve(this.db);
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
// Store cho conversation history
if (!db.objectStoreNames.contains('conversations')) {
const convStore = db.createObjectStore('conversations', { keyPath: 'id' });
convStore.createIndex('sessionId', 'sessionId', { unique: false });
convStore.createIndex('timestamp', 'timestamp', { unique: false });
}
// Store cho snapshots
if (!db.objectStoreNames.contains('snapshots')) {
const snapStore = db.createObjectStore('snapshots', { keyPath: 'id' });
snapStore.createIndex('sessionId', 'sessionId', { unique: false });
snapStore.createIndex('createdAt', 'createdAt', { unique: false });
}
// Store cho pending changes
if (!db.objectStoreNames.contains('pendingChanges')) {
const changeStore = db.createObjectStore('pendingChanges', { keyPath: 'id', autoIncrement: true });
changeStore.createIndex('conversationId', 'conversationId', { unique: false });
}
};
});
}
recordChange(conversationId, changeType, payload) {
const change = {
id: change_${Date.now()}_${Math.random().toString(36).substr(2, 5)},
conversationId,
changeType,
payload,
timestamp: Date.now(),
sessionId: this.sessionId
};
this.pendingChanges.push(change);
this.scheduleDebouncedSave();
return change;
}
scheduleDebouncedSave() {
if (this.debounceTimer) {
clearTimeout(this.debounceTimer);
}
this.debounceTimer = setTimeout(async () => {
await this.flushPendingChanges();
}, this.debounceMs);
}
async flushPendingChanges() {
if (this.pendingChanges.length === 0) return;
const changesToSave = [...this.pendingChanges];
this.pendingChanges = [];
const transaction = this.db.transaction(['pendingChanges', 'snapshots'], 'readwrite');
const changeStore = transaction.objectStore('pendingChanges');
const snapshotStore = transaction.objectStore('snapshots');
// Lưu tất cả pending changes
for (const change of changesToSave) {
await this.promiseRequest(changeStore.add(change));
}
// Tạo snapshot mới nếu đủ điều kiện
await this.createSnapshotIfNeeded(snapshotStore);
this.lastSaveTime = Date.now();
console.log([LocalBuffer] Flushed ${changesToSave.length} changes at ${this.lastSaveTime});
return changesToSave;
}
async createSnapshotIfNeeded(snapshotStore) {
const lastSnapshot = await this.getLastSnapshot();
const shouldCreate = !lastSnapshot ||
(Date.now() - lastSnapshot.createdAt > 60000) || // 1 phút
this.pendingChanges.length > 10;
if (shouldCreate) {
const snapshot = {
id: snap_${Date.now()},
sessionId: this.sessionId,
conversationId: this.getCurrentConversationId(),
changes: this.getChangeSummary(),
createdAt: Date.now()
};
await this.promiseRequest(snapshotStore.add(snapshot));
await this.pruneOldSnapshots(snapshotStore);
}
}
async pruneOldSnapshots(snapshotStore) {
const allSnapshots = await this.getAllSnapshots();
if (allSnapshots.length > this.maxSnapshots) {
const toDelete = allSnapshots
.sort((a, b) => a.createdAt - b.createdAt)
.slice(0, allSnapshots.length - this.maxSnapshots);
for (const snapshot of toDelete) {
await this.promiseRequest(snapshotStore.delete(snapshot.id));
}
}
}
getChangeSummary() {
return {
total: this.pendingChanges.length,
types: this.pendingChanges.reduce((acc, c) => {
acc[c.changeType] = (acc[c.changeType] || 0) + 1;
return acc;
}, {})
};
}
promiseRequest(request) {
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async getLastSnapshot() {
const transaction = this.db.transaction(['snapshots'], 'readonly');
const store = transaction.objectStore('snapshots');
const index = store.index('sessionId');
return new Promise((resolve, reject) => {
const request = index.getAll(this.sessionId);
request.onsuccess = () => {
const results = request.result;
resolve(results.length > 0 ? results[results.length - 1] : null);
};
request.onerror = () => reject(request.error);
});
}
setupChangeListeners() {
// Lắng nghe messages từ Cursor AI
if (typeof window !== 'undefined') {
window.addEventListener('message', (event) => {
if (event.data.type === 'ai_message') {
this.recordChange(
event.data.conversationId,
'ai_message',
{ content: event.data.content, model: event.data.model }
);
} else if (event.data.type === 'user_input') {
this.recordChange(
event.data.conversationId,
'user_input',
{ content: event.data.content }
);
}
});
}
}
}
module.exports = new LocalBufferManager({ debounceMs: 300, maxSnapshots: 50 });
Tích Hợp Cloud Sync Với HolyShehe AI
Cloud Sync Service đóng vai trò trung gian, đồng bộ hóa dữ liệu cục bộ lên cloud storage và đảm bảo tính nhất quán giữa các thiết bị. Tôi tích hợp HolyShehe AI để lưu trữ conversation context với chi phí cực thấp — chỉ $0.42/1M tokens cho DeepSeek V3.2.
// CloudSyncService.js - Đồng bộ hóa với HolyShehe AI API
const LocalBufferManager = require('./LocalBufferManager');
class CloudSyncService {
constructor(apiKey, options = {}) {
this.baseUrl = 'https://api.holyshehe.ai/v1'; // HolyShehe API endpoint
this.apiKey = apiKey;
this.syncIntervalMs = options.syncIntervalMs || 30000; // 30 giây
this.retryAttempts = options.retryAttempts || 3;
this.retryDelayMs = options.retryDelayMs || 1000;
this.syncTimer = null;
this.isOnline = navigator.onLine;
this.pendingUploads = [];
this.sessionState = null;
this.setupNetworkListeners();
this.startPeriodicSync();
}
setupNetworkListeners() {
window.addEventListener('online', () => {
this.isOnline = true;
this.flushPendingUploads();
});
window.addEventListener('offline', () => {
this.isOnline = false;
});
}
startPeriodicSync() {
this.syncTimer = setInterval(async () => {
if (this.isOnline) {
await this.performSync();
}
}, this.syncIntervalMs);
}
async performSync() {
try {
const localChanges = await LocalBufferManager.getPendingChanges();
if (localChanges.length === 0) {
console.log('[CloudSync] No pending changes to sync');
return;
}
// Tạo context summary để gửi lên cloud
const contextPayload = this.buildContextPayload(localChanges);
// Gọi HolyShehe AI để lưu trữ và xử lý context
const response = await this.saveToCloud(contextPayload);
if (response.success) {
await LocalBufferManager.markAsSynced(localChanges);
console.log([CloudSync] Synced ${localChanges.length} changes in ${response.processingTime}ms);
}
} catch (error) {
console.error('[CloudSync] Sync failed:', error.message);
await this.handleSyncError(error);
}
}
buildContextPayload(changes) {
// Xây dựng conversation context từ các thay đổi
const messages = changes.map(change => ({
role: change.changeType.includes('user') ? 'user' : 'assistant',
content: change.payload.content || JSON.stringify(change.payload),
timestamp: change.timestamp
}));
return {
sessionId: LocalBufferManager.sessionId,
messages: messages,
metadata: {
totalChanges: changes.length,
changeTypes: this.aggregateChangeTypes(changes),
firstTimestamp: changes[0]?.timestamp,
lastTimestamp: changes[changes.length - 1]?.timestamp,
deviceInfo: this.getDeviceInfo()
}
};
}
aggregateChangeTypes(changes) {
return changes.reduce((acc, c) => {
acc[c.changeType] = (acc[c.changeType] || 0) + 1;
return acc;
}, {});
}
getDeviceInfo() {
return {
userAgent: navigator.userAgent,
platform: navigator.platform,
language: navigator.language,
screenResolution: ${window.screen.width}x${window.screen.height}
};
}
async saveToCloud(payload) {
// Sử dụng HolyShehe AI API endpoint để lưu trữ session
const response = await this.callWithRetry(${this.baseUrl}/sessions, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${this.apiKey}
},
body: JSON.stringify({
action: 'save_session',
data: payload
})
});
return response;
}
async callWithRetry(url, options, attempt = 1) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(HTTP ${response.status}: ${response.statusText});
}
return await response.json();
} catch (error) {
if (attempt < this.retryAttempts) {
console.log([CloudSync] Retry attempt ${attempt + 1}/${this.retryAttempts});
await this.delay(this.retryDelayMs * attempt);
return this.callWithRetry(url, options, attempt + 1);
}
throw error;
}
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async handleSyncError(error) {
// Lưu error vào queue để xử lý sau
this.pendingUploads.push({
error: error.message,
timestamp: Date.now(),
payload: 'REDACTED_FOR_SECURITY'
});
// Thông báo cho user
this.notifyUser('sync_error', {
message: 'Không thể đồng bộ dữ liệu. Dữ liệu đã được lưu cục bộ.',
pendingCount: this.pendingUploads.length
});
}
notifyUser(type, data) {
// Dispatch custom event để UI có thể xử lý
window.dispatchEvent(new CustomEvent('cursor_sync_status', {
detail: { type, data, timestamp: Date.now() }
}));
}
async flushPendingUploads() {
if (this.pendingUploads.length === 0) return;
console.log([CloudSync] Flushing ${this.pendingUploads.length} pending uploads);
while (this.pendingUploads.length > 0) {
try {
await this.performSync();
break;
} catch (error) {
console.error('[CloudSync] Still unable to sync');
break;
}
}
}
async forceSync() {
return this.performSync();
}
destroy() {
if (this.syncTimer) {
clearInterval(this.syncTimer);
}
}
}
// Khởi tạo service
const cloudSync = new CloudSyncService('YOUR_HOLYSHEEP_API_KEY', {
syncIntervalMs: 30000,
retryAttempts: 3
});
module.exports = cloudSync;
Session Recovery Orchestrator
Recovery Orchestrator là trái tim của hệ thống, điều phối quá trình khôi phục khi người dùng quay lại sau sự cố. Tôi đã tối ưu thuật toán để đạt độ trễ khôi phục trung bình dưới 50ms.
// SessionRecoveryOrchestrator.js - Điều phối quá trình khôi phục
const LocalBufferManager = require('./LocalBufferManager');
const cloudSync = require('./CloudSyncService');
class SessionRecoveryOrchestrator {
constructor() {
this.recoveryStrategies = {
'fresh_start': this.freshStartStrategy.bind(this),
'local_recovery': this.localRecoveryStrategy.bind(this),
'cloud_recovery': this.cloudRecoveryStrategy.bind(this),
'hybrid_recovery': this.hybridRecoveryStrategy.bind(this)
};
this.currentStrategy = null;
this.recoveryMetrics = {
attempts: 0,
successes: 0,
failures: 0,
averageRecoveryTime: 0
};
}
async attemptRecovery(sessionId = null) {
const startTime = performance.now();
this.recoveryMetrics.attempts++;
try {
// Bước 1: Kiểm tra trạng thái hiện tại
const status = await this.assessRecoveryStatus(sessionId);
// Bước 2: Chọn strategy phù hợp
const strategy = this.selectRecoveryStrategy(status);
this.currentStrategy = strategy;
// Bước 3: Thực hiện khôi phục
const result = await strategy.execute();
// Bước 4: Xác thực kết quả
const isValid = await this.validateRecovery(result);
if (isValid) {
this.recoveryMetrics.successes++;
const recoveryTime = performance.now() - startTime;
this.updateAverageRecoveryTime(recoveryTime);
return {
success: true,
strategy: strategy.name,
recoveryTime,
data: result
};
} else {
throw new Error('Recovery validation failed');
}
} catch (error) {
this.recoveryMetrics.failures++;
return this.handleRecoveryFailure(error, sessionId);
}
}
async assessRecoveryStatus(sessionId) {
const status = {
hasLocalData: false,
hasCloudData: false,
localTimestamp: null,
cloudTimestamp: null,
pendingChanges: 0,
networkStatus: navigator.onLine
};
// Kiểm tra IndexedDB
const localSnapshot = await LocalBufferManager.getLastSnapshot();
status.hasLocalData = !!localSnapshot;
status.localTimestamp = localSnapshot?.createdAt || null;
// Kiểm tra pending changes
const pendingChanges = await LocalBufferManager.getPendingChanges();
status.pendingChanges = pendingChanges.length;
// Kiểm tra cloud data
if (navigator.onLine && sessionId) {
try {
const cloudData = await this.fetchCloudSession(sessionId);
status.hasCloudData = !!cloudData;
status.cloudTimestamp = cloudData?.lastModified || null;
} catch (error) {
console.warn('[Recovery] Cloud fetch failed:', error.message);
}
}
return status;
}
selectRecoveryStrategy(status) {
// Priority-based strategy selection
if (!status.hasLocalData && !status.hasCloudData) {
return this.recoveryStrategies.fresh_start();
}
if (status.hasLocalData && status.pendingChanges > 0) {
// Có dữ liệu cục bộ với changes chưa sync
if (status.hasCloudData) {
return this.recoveryStrategies.hybrid_recovery();
}
return this.recoveryStrategies.local_recovery();
}
if (status.hasCloudData) {
return this.recoveryStrategies.cloud_recovery();
}
return this.recoveryStrategies.local_recovery();
}
freshStartStrategy() {
return {
name: 'fresh_start',
execute: async () => {
console.log('[Recovery] Starting fresh session');
return {
conversationId: LocalBufferManager.generateSessionId(),
messages: [],
timestamp: Date.now()
};
}
};
}
localRecoveryStrategy() {
return {
name: 'local_recovery',
execute: async () => {
console.log('[Recovery] Recovering from local storage');
const snapshot = await LocalBufferManager.getLastSnapshot();
const pendingChanges = await LocalBufferManager.getPendingChanges();
// Merge pending changes vào snapshot
const recoveredData = {
sessionId: snapshot?.sessionId || LocalBufferManager.sessionId,
snapshot: snapshot,
pendingChanges: pendingChanges,
mergedMessages: this.mergeMessages(snapshot, pendingChanges)
};
return recoveredData;
}
};
}
cloudRecoveryStrategy() {
return {
name: 'cloud_recovery',
execute: async () => {
console.log('[Recovery] Recovering from cloud');
const lastSession = await this.fetchCloudSession();
const conversationContext = await this.reconstructContext(lastSession);
return {
sessionId: lastSession.sessionId,
context: conversationContext,
timestamp: lastSession.lastModified
};
}
};
}
hybridRecoveryStrategy() {
return {
name: 'hybrid_recovery',
execute: async () => {
console.log('[Recovery] Performing hybrid recovery');
const [localData, cloudData] = await Promise.all([
LocalBufferManager.getLastSnapshot(),
this.fetchCloudSession()
]);
// Resolve conflicts: ưu tiên dữ liệu mới nhất
const resolvedData = this.resolveConflicts(localData, cloudData);
// Sync dữ liệu đã resolved
await cloudSync.forceSync();
return resolvedData;
}
};
}
mergeMessages(snapshot, pendingChanges) {
if (!snapshot || !snapshot.changes) return pendingChanges;
const merged = [...snapshot.changes.messages || []];
for (const change of pendingChanges) {
if (change.changeType === 'ai_message' || change.changeType === 'user_input') {
merged.push({
role: change.changeType === 'user_input' ? 'user' : 'assistant',
content: change.payload.content,
timestamp: change.timestamp
});
}
}
return merged;
}
resolveConflicts(localData, cloudData) {
// So sánh timestamps để chọn dữ liệu mới nhất
const localTime = localData?.createdAt || 0;
const cloudTime = cloudData?.lastModified || 0;
if (cloudTime > localTime) {
return {
source: 'cloud',
data: cloudData,
localData: localData // Giữ lại để merge nếu cần
};
}
return {
source: 'local',
data: localData,
cloudData: cloudData
};
}
async fetchCloudSession(sessionId = null) {
const targetSessionId = sessionId || LocalBufferManager.sessionId;
const response = await fetch(
${cloudSync.baseUrl}/sessions/${targetSessionId},
{
headers: {
'Authorization': Bearer ${cloudSync.apiKey}
}
}
);
if (!response.ok) {
throw new Error(Failed to fetch session: ${response.status});
}
return response.json();
}
async reconstructContext(sessionData) {
// Sử dụng HolyShehe AI để tái tạo context
const response = await fetch(${cloudSync.baseUrl}/sessions/reconstruct, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${cloudSync.apiKey}
},
body: JSON.stringify({
sessionId: sessionData.sessionId,
messages: sessionData.messages
})
});
return response.json();
}
async validateRecovery(result) {
if (!result) return false;
if (result.sessionId && result.sessionId.length < 10) return false;
// Kiểm tra tính toàn vẹn của dữ liệu
return true;
}
updateAverageRecoveryTime(newTime) {
const totalAttempts = this.recoveryMetrics.successes;
this.recoveryMetrics.averageRecoveryTime =
(this.recoveryMetrics.averageRecoveryTime * (totalAttempts - 1) + newTime) / totalAttempts;
}
handleRecoveryFailure(error, sessionId) {
console.error('[Recovery] All strategies failed:', error);
return {
success: false,
error: error.message,
strategiesAttempted: Object.keys(this.recoveryStrategies),
fallback: 'manual_restore'
};
}
getMetrics() {
return {
...this.recoveryMetrics,
successRate: (this.recoveryMetrics.successes / this.recoveryMetrics.attempts * 100).toFixed(2) + '%'
};
}
}
// Singleton instance
const orchestrator = new SessionRecoveryOrchestrator();
module.exports = orchestrator;
// Auto-recovery khi page load
if (typeof window !== 'undefined') {
window.addEventListener('load', async () => {
const previousSessionId = localStorage.getItem('cursor_last_session_id');
if (previousSessionId) {
console.log('[Recovery] Found previous session, attempting recovery...');
const result = await orchestrator.attemptRecovery(previousSessionId);
if (result.success) {
window.dispatchEvent(new CustomEvent('session_recovered', {
detail: result
}));
}
}
});
}
Hướng Dẫn Sử Dụng Trong Cursor AI Extension
Sau khi đã triển khai các thành phần trên, bước tiếp theo là tích hợp vào Cursor AI extension để tự động hóa toàn bộ quy trình. Dưới đây là code manifest và entry point chính.
{
"manifest_version": 3,
"name": "Cursor AI Session Manager",
"version": "1.0.0",
"description": "Auto-save and session recovery for Cursor AI with HolyShehe AI integration",
"permissions": [
"storage",
"activeTab",
"tabs",
"webNavigation"
],
"host_permissions": [
"https://api.holysheep.ai/*"
],
"background": {
"service_worker": "background.js",
"type": "module"
},
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["content-script.js"],
"run_at": "document_idle"
}
],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
// content-script.js - Tích hợp vào Cursor AI
(async function() {
'use strict';
// Import các modules
const LocalBufferManager = require('./LocalBufferManager');
const CloudSyncService = require('./CloudSyncService');
const RecoveryOrchestrator = require('./SessionRecoveryOrchestrator');
// Khởi tạo services
let cloudSync = null;
let isInitialized = false;
async function initialize() {
if (isInitialized) return;
try {
// Lấy API key từ storage
const { apiKey, settings } = await chrome.storage.sync.get(['apiKey', 'settings']);
if (!apiKey) {
console.log('[CursorSession] No API key configured, using local-only mode');
return;
}
// Khởi tạo CloudSync với HolyShehe API
cloudSync = new CloudSyncService(apiKey, {
syncIntervalMs: settings?.syncIntervalMs || 30000,
retryAttempts: settings?.retryAttempts || 3
});
// Thiết lập message listeners
setupMessageListeners();
// Thiết lập visibility change listener
document.addEventListener('visibilitychange', handleVisibilityChange);
// Thiết lập beforeunload handler
window.addEventListener('beforeunload', handleBeforeUnload);
isInitialized = true;
console.log('[CursorSession] Initialized successfully');
// Thử khôi phục session nếu có
await attemptAutoRecovery();
} catch (error) {
console.error('[CursorSession] Initialization failed:', error);
}
}
function setupMessageListeners() {
// Lắng nghe messages từ Cursor AI UI
window.addEventListener('message', async (event) => {
if (event.source !== window) return;
const { type, data } = event.data;
switch (type) {
case 'CURSOR_MESSAGE_SENT':
LocalBufferManager.recordChange(data.conversationId, 'user_input', {
content: data.content,
timestamp: Date.now()
});
break;
case 'CURSOR_MESSAGE_RECEIVED':
LocalBufferManager.recordChange(data.conversationId, 'ai_message', {
content: data.content,
model: data.model,
timestamp: Date.now()
});
break;
case 'CURSOR_CONVERSATION_CREATED':
handleNewConversation(data);
break;
case 'CURSOR_CONVERSATION_DELETED':
handleConversationDeleted(data);
break;
}
});
// Lắng nghe từ popup/background
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
handleExtensionMessage(message, sendResponse);
return true; // Async response
});
}
async function handleExtensionMessage(message, sendResponse) {
switch (message.action) {
case 'forceSync':
if (cloudSync) {
const result = await cloudSync.forceSync();
sendResponse({ success: true, result });
} else {
sendResponse({ success: false, error: 'Not initialized' });
}
break;
case 'getRecoveryStatus':
const status = await RecoveryOrchestrator.assessRecoveryStatus();
sendResponse(status);
break;
case 'forceRecovery':
const result = await RecoveryOrchestrator.attemptRecovery(message.sessionId);
sendResponse(result);
break;
case 'getMetrics':
sendResponse(RecoveryOrchestrator.getMetrics());
break;
default:
sendResponse({ error: 'Unknown action' });
}
}
function handleVisibilityChange() {
if (document.visibilityState === 'hidden') {
// Force sync khi tab bị ẩn
if (cloudSync) {
cloudSync.performSync();
}
} else if (document.visibilityState === 'visible') {
// Kiểm tra và khôi phục nếu cần
checkForSessionUpdates();
}
}
async function handleBeforeUnload(event) {
// Flush tất cả pending changes
if (cloudSync) {
await cloudSync.performSync();
}
// Lưu session ID vào storage
localStorage.setItem('cursor_last_session_id', LocalBufferManager.sessionId);
}
async function attemptAutoRecovery() {
const lastSessionId = localStorage.getItem('cursor_last_session_id');
if (!lastSessionId) return;
// Kiểm tra xem session cũ có khác với session hiện tại không
const currentSessionId = LocalBufferManager.sessionId;
if (lastSessionId !== currentSessionId) {
console.log('[CursorSession] Detected different session, checking for recovery...');
const status = await RecoveryOrchestrator.assessRecoveryStatus(lastSessionId);
if (status.hasLocalData || status.hasCloudData) {
// Dispatch event để UI hiển thị recovery prompt
window.dispatchEvent(new CustomEvent('cursor_recovery_available', {
detail: {
sessionId: lastSessionId,
status
}
}));
}
}
}
async function checkForSessionUpdates() {
if (!cloudSync) return;
try {
const lastSync = await chrome.storage.local.get(['lastSyncTime']);
const timeSinceLastSync = Date.now() - (lastSync.lastSyncTime || 0);
// Sync nếu đã quá 5 phút
if (timeSinceLastSync > 300000) {
await cloudSync.forceSync();
}
} catch (error) {
console.warn('[CursorSession] Update check failed:', error);
}
}
function handleNew