ai-helper-agent 2.2.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +94 -1
  2. package/README.zh-CN.md +94 -1
  3. package/dist/assets/{agent-store-DOyMdB_7.js → agent-store-DcAs6YyA.js} +2 -2
  4. package/dist/assets/{agent-store-DOyMdB_7.js.map → agent-store-DcAs6YyA.js.map} +1 -1
  5. package/dist/assets/background.js +83 -89
  6. package/dist/assets/background.js.map +1 -1
  7. package/dist/assets/content.js +81 -65
  8. package/dist/assets/{i18n-MmYITlVj.js → i18n-C4jSRoCH.js} +5 -5
  9. package/dist/assets/i18n-C4jSRoCH.js.map +1 -0
  10. package/dist/assets/{model-profiles-CV_sUuPf.js → model-profiles-Dl_MsXWa.js} +5 -5
  11. package/dist/assets/model-profiles-Dl_MsXWa.js.map +1 -0
  12. package/dist/assets/options-8-nu3P5K.css +1 -0
  13. package/dist/assets/options.js +264 -197
  14. package/dist/assets/options.js.map +1 -1
  15. package/dist/assets/{page-snapshot-renderer-CE53ibi-.js → page-snapshot-renderer-CVL6r6Kf.js} +2 -2
  16. package/dist/assets/{page-snapshot-renderer-CE53ibi-.js.map → page-snapshot-renderer-CVL6r6Kf.js.map} +1 -1
  17. package/dist/assets/{session-store-B0ZPGm7d.js → session-store-CzVQCgQ_.js} +4 -4
  18. package/dist/assets/session-store-CzVQCgQ_.js.map +1 -0
  19. package/dist/assets/side_panel.css +1 -1
  20. package/dist/assets/side_panel.js +327 -293
  21. package/dist/assets/side_panel.js.map +1 -1
  22. package/dist/assets/version-CNG2KGnJ.js +2 -0
  23. package/dist/assets/version-CNG2KGnJ.js.map +1 -0
  24. package/dist/libs/jspdf.min.js +1 -1
  25. package/dist/manifest.json +2 -2
  26. package/dist/offscreen/offscreen.js +4 -4
  27. package/dist/options.html +140 -3
  28. package/dist/side_panel.html +27 -4
  29. package/package.json +1 -1
  30. package/src/config.js +72 -24
  31. package/src/crypto-lite.js +31 -0
  32. package/src/locales/en.json +55 -0
  33. package/src/locales/zh.json +55 -0
  34. package/src/memory/embedding-config.js +95 -0
  35. package/src/memory/fusion.js +60 -0
  36. package/src/memory/injection.js +118 -0
  37. package/src/memory/routes.js +193 -0
  38. package/src/memory/service.js +957 -0
  39. package/src/memory/tags.js +62 -0
  40. package/src/memory/tier.js +37 -0
  41. package/src/memory/tokens.js +158 -0
  42. package/src/memory/vectors.js +128 -0
  43. package/src/rag/embedding/index.js +7 -0
  44. package/src/rag/embedding/local.js +25 -0
  45. package/src/rag/keyword-match.js +112 -0
  46. package/src/rag/manager.js +15 -0
  47. package/src/rag/searcher.js +15 -87
  48. package/src/server.js +6 -0
  49. package/dist/assets/i18n-MmYITlVj.js.map +0 -1
  50. package/dist/assets/model-profiles-CV_sUuPf.js.map +0 -1
  51. package/dist/assets/options-Df_1Dtn8.css +0 -1
  52. package/dist/assets/session-store-B0ZPGm7d.js.map +0 -1
  53. /package/dist/_locales/{zh → zh_CN}/messages.json +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":"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","names":["idb.deleteReactCheckpoint","idb.ensureMigration","idb.getAllSessions","idb.getActiveSessionId","state","idb.getSession","idb.putSession","idb.setActiveSessionId","idb.deleteSession","idb.getAllBookmarks","idb.deleteBookmark","idb.getAllArchivedSessions","idb.replaceArchivedSessions"],"ignoreList":[],"sources":["../../src/storage/db.js","../../src/storage/token-store.js","../../src/shared/agent-defaults.js","../../src/background/tools/rag-tools.js","../../src/side_panel/state.js","../../src/storage/session-store.js"],"sourcesContent":["import logger from '../shared/logger.js';\nimport { t, registerTranslations } from '../shared/i18n.js';\n\n// storage/db.js - IndexedDB 封装层\n// 提供 Promise 化的 IndexedDB 操作,支持 side panel 和 service worker 共享访问\n\nregisterTranslations('zh', {\n db: {\n sessionLabel: '会话 {id}',\n archivedLabel: '[归档] {label}',\n },\n});\n\nregisterTranslations('en', {\n db: {\n sessionLabel: 'Session {id}',\n archivedLabel: '[Archived] {label}',\n },\n});\n\nconst DB_NAME = 'ai-helper-db';\nconst DB_VERSION = 6;\n\n// ReAct Checkpoint TTL:7 天\nexport const REACT_CHECKPOINT_TTL_MS = 7 * 24 * 60 * 60 * 1000;\n\n// chrome.storage.local 中 checkpoint 的 key 前缀(后备存储)\n// 当 IndexedDB 的 reactCheckpoints store 不可用时,使用 chrome.storage.local 作为可靠后备\nconst CHECKPOINT_KEY_PREFIX = 'react_checkpoint_';\n\n/**\n * 获取数据库连接(单例)\n * @returns {Promise<IDBDatabase>}\n */\nfunction openDB() {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION);\n\n request.onupgradeneeded = (event) => {\n const db = event.target.result;\n\n // 活跃会话存储(keyPath: id)\n if (!db.objectStoreNames.contains('sessions')) {\n const sessionsStore = db.createObjectStore('sessions', { keyPath: 'id' });\n sessionsStore.createIndex('updatedAt', 'updatedAt', { unique: false });\n }\n\n // 活跃会话 ID(单条记录,keyPath: key)\n if (!db.objectStoreNames.contains('activeSession')) {\n db.createObjectStore('activeSession', { keyPath: 'key' });\n }\n\n // 归档会话存储(keyPath: id)\n if (!db.objectStoreNames.contains('archivedSessions')) {\n const archiveStore = db.createObjectStore('archivedSessions', { keyPath: 'id' });\n archiveStore.createIndex('createdAt', 'createdAt', { unique: false });\n }\n\n // UI 原型存储(keyPath: id)\n if (!db.objectStoreNames.contains('uiPrototypes')) {\n const prototypeStore = db.createObjectStore('uiPrototypes', { keyPath: 'id' });\n prototypeStore.createIndex('createdAt', 'createdAt', { unique: false });\n prototypeStore.createIndex('sessionId', 'sessionId', { unique: false });\n }\n\n // Token 使用统计存储(keyPath: id)\n if (!db.objectStoreNames.contains('tokenStats')) {\n const tokenStatsStore = db.createObjectStore('tokenStats', { keyPath: 'id' });\n tokenStatsStore.createIndex('sessionId', 'sessionId', { unique: false });\n tokenStatsStore.createIndex('timestamp', 'timestamp', { unique: false });\n }\n\n // ReAct Checkpoint 存储(keyPath: sessionId)—— 用于任务中断后续接\n if (!db.objectStoreNames.contains('reactCheckpoints')) {\n const checkpointStore = db.createObjectStore('reactCheckpoints', { keyPath: 'sessionId' });\n checkpointStore.createIndex('updatedAt', 'updatedAt', { unique: false });\n }\n\n // 消息收藏存储(keyPath: id)\n if (!db.objectStoreNames.contains('bookmarks')) {\n const bookmarksStore = db.createObjectStore('bookmarks', { keyPath: 'id' });\n bookmarksStore.createIndex('sessionId', 'sessionId', { unique: false });\n bookmarksStore.createIndex('createdAt', 'createdAt', { unique: false });\n }\n\n // 定时任务存储(keyPath: id)\n if (!db.objectStoreNames.contains('scheduledTasks')) {\n const tasksStore = db.createObjectStore('scheduledTasks', { keyPath: 'id' });\n tasksStore.createIndex('nextRunAt', 'nextRunAt', { unique: false });\n tasksStore.createIndex('enabled', 'enabled', { unique: false });\n }\n };\n\n request.onsuccess = (event) => {\n const db = event.target.result;\n\n // 关键:当其他连接(如 side_panel 持有的旧连接)触发版本变更时,\n // 主动关闭当前连接,避免阻塞 DB 升级。\n // 没有此处理会导致 SW 升级 DB 时被旧连接阻塞,reactCheckpoints store 无法创建,\n // 表现为 checkpoint 写入静默失败 → 恢复时\"未找到 checkpoint\"。\n db.onversionchange = (event) => {\n logger.debug('[IDB] detected versionchange event,maindynamicclosecurrent connection as allows upgrade');\n try { event.target.close(); } catch (e) {}\n // 重置单例缓存,下次 getDB() 会重新打开\n dbPromise = null;\n };\n\n resolve(db);\n };\n\n request.onerror = (event) => {\n logger.error('[IDB] opendatabase failed:', event.target.error);\n reject(event.target.error);\n };\n\n // 处理被阻塞的升级(其他连接持有旧版本时触发)\n request.onblocked = () => {\n logger.warn('[IDB] databaseupgrade blocked (other connections notclose),waitingretry...');\n // 不直接 reject,让调用方稍后重试 getDB() 时自然获得新连接\n };\n });\n}\n\nlet dbPromise = null;\n\n/**\n * 重置数据库连接缓存(用于 Service Worker 重启后恢复)\n */\nfunction resetDBConnection() {\n if (dbPromise) {\n dbPromise = null;\n logger.debug('[IDB] databaseconnectionreset');\n }\n}\n\n/**\n * 获取数据库实例(缓存单例)\n */\nexport function getDB() {\n if (!dbPromise) {\n dbPromise = openDB();\n }\n return dbPromise;\n}\n\n/**\n * 通用事务包装器(带连接恢复重试)\n * @param {string} storeName - objectStore 名称\n * @param {'readonly'|'readwrite'} mode\n * @param {function(IDBObjectStore, function): void} callback\n * @param {boolean} [isRetry=false] - 是否为重试调用(内部使用)\n * @returns {Promise<any>}\n */\nexport function withStore(storeName, mode, callback, isRetry = false) {\n return getDB().then((db) => {\n return new Promise((resolve, reject) => {\n let resolvedValue = null;\n let settled = false;\n\n // 检查 store 是否存在;不存在时强制重置连接并重试(触发 DB 升级创建缺失的 store)\n // 这是 \"reactCheckpoints store 不存在导致 checkpoint 静默失败\" 的根因修复\n if (!db.objectStoreNames.contains(storeName)) {\n logger.error(`[IDB] store \"${storeName}\" not found!current DB version: ${db.version}, store column table: [${Array.from(db.objectStoreNames).join(', ')}]`);\n if (!isRetry) {\n settled = true;\n // 强制重置连接,下次 getDB() 会重新打开并触发 onupgradeneeded(如果版本有变化)\n // 但如果 DB 已经是最新版本但 store 仍然缺失(连接陈旧),需要先关闭再重开\n try { db.close(); } catch (e) {}\n resetDBConnection();\n // 延迟重试,给 DB 重开一点时间\n setTimeout(() => {\n withStore(storeName, mode, callback, true)\n .then(resolve)\n .catch(reject);\n }, 50);\n return;\n } else {\n settled = true;\n reject(new Error(`Store \"${storeName}\" does not exist in database (version ${db.version})`));\n return;\n }\n }\n\n let tx;\n try {\n tx = db.transaction(storeName, mode);\n } catch (e) {\n // transaction() 可能抛出同步异常(如连接已关闭)\n logger.error(`[IDB] create transaction failed (${storeName}):`, e.message);\n if (!isRetry) {\n settled = true;\n resetDBConnection();\n setTimeout(() => {\n withStore(storeName, mode, callback, true)\n .then(resolve)\n .catch(reject);\n }, 50);\n return;\n } else {\n settled = true;\n reject(e);\n return;\n }\n }\n const store = tx.objectStore(storeName);\n\n // 回调中的 resolve 仅保存结果,实际 resolve 在 tx.oncomplete 中执行\n // 确保事务已提交后再返回,防止调用方读取到未持久化的数据\n callback(store, (result) => {\n resolvedValue = result;\n });\n\n tx.oncomplete = () => {\n if (!settled) {\n settled = true;\n resolve(resolvedValue);\n }\n };\n\n tx.onerror = (event) => {\n if (settled) return;\n settled = true;\n const err = event.target.error || new Error(`Transaction error on ${storeName}`);\n logger.error(`[IDB] transaction error (${storeName}):`, err);\n\n if (!isRetry) {\n // Service Worker 重启后连接可能已关闭,重置并重试一次\n resetDBConnection();\n withStore(storeName, mode, callback, true)\n .then(resolve)\n .catch(reject);\n } else {\n reject(err);\n }\n };\n\n tx.onabort = () => {\n if (settled) return;\n settled = true;\n\n if (!isRetry) {\n resetDBConnection();\n withStore(storeName, mode, callback, true)\n .then(resolve)\n .catch(reject);\n } else {\n reject(new Error(`Transaction aborted on ${storeName}`));\n }\n };\n });\n });\n}\n\n// ==================== Sessions CRUD ====================\n\n/**\n * 获取所有活跃会话\n */\nexport function getAllSessions() {\n return withStore('sessions', 'readonly', (store, resolve) => {\n const request = store.getAll();\n request.onsuccess = () => resolve(request.result || []);\n request.onerror = () => resolve([]);\n });\n}\n\n/**\n * 获取单个会话\n */\nexport function getSession(id) {\n return withStore('sessions', 'readonly', (store, resolve) => {\n const request = store.get(id);\n request.onsuccess = () => resolve(request.result || null);\n request.onerror = () => resolve(null);\n });\n}\n\n/**\n * 从会话的 messageHistory 中直接删除指定消息(不依赖 saveCurrentSession 的完整保存流程)\n * @param {string} sessionId\n * @param {string} messageId\n * @returns {Promise<boolean>}\n */\nexport async function deleteMessageFromSession(sessionId, messageId) {\n if (!sessionId || !messageId) return false;\n try {\n const session = await getSession(sessionId);\n if (!session || !session.messageHistory) return false;\n\n const idx = session.messageHistory.findIndex(\n msg => (msg.messageId || msg.id) === messageId\n );\n if (idx === -1) return false;\n\n session.messageHistory.splice(idx, 1);\n return await putSession(session);\n } catch (e) {\n logger.error('[DB] deleteMessageFromSession failed:', e);\n return false;\n }\n}\n\n/**\n * 保存/更新会话(put = insert or update)\n */\nexport function putSession(session) {\n return withStore('sessions', 'readwrite', (store, resolve) => {\n const request = store.put(session);\n request.onsuccess = () => resolve(true);\n request.onerror = () => resolve(false);\n });\n}\n\n/**\n * 删除会话\n */\nexport function deleteSession(id) {\n return withStore('sessions', 'readwrite', (store, resolve) => {\n const request = store.delete(id);\n request.onsuccess = () => resolve(true);\n request.onerror = () => resolve(false);\n });\n}\n\n/**\n * 批量保存/更新多个会话\n */\nexport function putSessions(sessions) {\n return getDB().then((db) => {\n return new Promise((resolve) => {\n const tx = db.transaction('sessions', 'readwrite');\n const store = tx.objectStore('sessions');\n sessions.forEach((s) => store.put(s));\n tx.oncomplete = () => resolve(true);\n tx.onerror = () => resolve(false);\n });\n });\n}\n\n// ==================== Active Session ID ====================\n\n/**\n * 获取当前活跃会话 ID\n */\nexport function getActiveSessionId() {\n return withStore('activeSession', 'readonly', (store, resolve) => {\n const request = store.get('active');\n request.onsuccess = () => resolve(request.result?.sessionId || null);\n request.onerror = () => resolve(null);\n });\n}\n\n/**\n * 设置当前活跃会话 ID\n */\nexport function setActiveSessionId(sessionId) {\n return withStore('activeSession', 'readwrite', (store, resolve) => {\n const request = store.put({ key: 'active', sessionId });\n request.onsuccess = () => resolve(true);\n request.onerror = () => resolve(false);\n });\n}\n\n// ==================== Archived Sessions CRUD ====================\n\n/**\n * 获取所有归档会话\n */\nexport function getAllArchivedSessions() {\n return withStore('archivedSessions', 'readonly', (store, resolve) => {\n const request = store.getAll();\n request.onsuccess = () => resolve(request.result || []);\n request.onerror = () => resolve([]);\n });\n}\n\n/**\n * 保存归档会话\n */\nexport function putArchivedSession(session) {\n return withStore('archivedSessions', 'readwrite', (store, resolve) => {\n const request = store.put(session);\n request.onsuccess = () => resolve(true);\n request.onerror = () => resolve(false);\n });\n}\n\n/**\n * 删除归档会话\n */\nexport function deleteArchivedSession(id) {\n return withStore('archivedSessions', 'readwrite', (store, resolve) => {\n const request = store.delete(id);\n request.onsuccess = () => resolve(true);\n request.onerror = () => resolve(false);\n });\n}\n\n/**\n * 清空并批量写入归档会话(用于截断超出 20 条的场景)\n */\nexport function replaceArchivedSessions(sessions) {\n return getDB().then((db) => {\n return new Promise((resolve) => {\n const tx = db.transaction('archivedSessions', 'readwrite');\n const store = tx.objectStore('archivedSessions');\n\n // 清空\n const clearReq = store.clear();\n clearReq.onsuccess = () => {\n // 批量写入\n sessions.forEach((s) => store.put(s));\n };\n tx.oncomplete = () => resolve(true);\n tx.onerror = () => resolve(false);\n });\n });\n}\n\n// ==================== UI Prototypes CRUD ====================\n\n/**\n * 保存 UI 原型\n */\nexport function saveUiPrototype(prototype) {\n const now = Date.now();\n const data = {\n ...prototype,\n createdAt: prototype.createdAt || now,\n updatedAt: now\n };\n return withStore('uiPrototypes', 'readwrite', (store, resolve) => {\n const request = store.put(data);\n request.onsuccess = () => resolve(data);\n request.onerror = () => resolve(null);\n });\n}\n\n/**\n * 获取单个 UI 原型\n */\nexport function getUiPrototype(id) {\n return withStore('uiPrototypes', 'readonly', (store, resolve) => {\n const request = store.get(id);\n request.onsuccess = () => resolve(request.result || null);\n request.onerror = () => resolve(null);\n });\n}\n\n/**\n * 获取所有 UI 原型(按创建时间倒序)\n */\nexport function listUiPrototypes(sessionId = null) {\n return withStore('uiPrototypes', 'readonly', (store, resolve) => {\n const request = store.getAll();\n request.onsuccess = () => {\n let results = request.result || [];\n if (sessionId) {\n results = results.filter(p => p.sessionId === sessionId);\n }\n results.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));\n resolve(results);\n };\n request.onerror = () => resolve([]);\n });\n}\n\n/**\n * 删除 UI 原型\n */\nexport function deleteUiPrototype(id) {\n return withStore('uiPrototypes', 'readwrite', (store, resolve) => {\n const request = store.delete(id);\n request.onsuccess = () => resolve(true);\n request.onerror = () => resolve(false);\n });\n}\n\n// ==================== 搜索 ====================\n\n/**\n * 从所有活跃会话中搜索消息\n * @param {string} sessionId - 限定会话 ID,null 表示搜索所有活跃会话\n * @returns {Promise<Array<{session: string, index: number, role: string, content: string}>>}\n */\nexport function searchActiveSessionsMessages(sessionId) {\n if (sessionId) {\n // 指定 sessionId 时使用 O(1) 主键查询,避免全量加载所有会话\n return getSession(sessionId).then((session) => {\n if (!session) return [];\n const label = session.title || t('db.sessionLabel', { id: session.id?.slice(0, 8) });\n return (session.messageHistory || []).filter(msg => msg.content).map((msg, idx) => ({\n session: session.id,\n sessionLabel: label,\n index: idx,\n role: msg.role,\n content: msg.content,\n }));\n });\n }\n return getAllSessions().then((all) => {\n const sessions = all;\n const results = [];\n sessions.forEach((session) => {\n const label = session.title || t('db.sessionLabel', { id: session.id?.slice(0, 8) });\n (session.messageHistory || []).forEach((msg, idx) => {\n if (msg.content) {\n results.push({\n session: session.id,\n sessionLabel: label,\n index: idx,\n role: msg.role,\n content: msg.content,\n });\n }\n });\n });\n return results;\n });\n}\n\n/**\n * 获取所有归档会话的消息\n * @returns {Promise<Array<{session: string, index: number, role: string, content: string}>>}\n */\nexport function getArchivedSessionsMessages() {\n return getAllArchivedSessions().then((archiveSessions) => {\n const results = [];\n archiveSessions.forEach((session) => {\n const label = t('db.archivedLabel', { label: session.title || t('db.sessionLabel', { id: session.id?.slice(0, 8) }) });\n (session.messages || []).forEach((msg, idx) => {\n if (msg.content) {\n results.push({\n session: session.id,\n sessionLabel: label,\n index: idx,\n role: msg.role,\n content: msg.content,\n });\n }\n });\n });\n return results;\n });\n}\n\n// ==================== 数据迁移 ====================\n\n/**\n * 从 chrome.storage.local 迁移数据到 IndexedDB\n * 迁移后清除 chrome.storage.local 中的大数据 key\n * @returns {Promise<boolean>}\n */\nexport function migrateFromChromeStorage() {\n return new Promise((resolve) => {\n chrome.storage.local.get(['sessions', 'conversationSessions'], async (result) => {\n let migrated = false;\n\n // 迁移活跃会话\n if (result.sessions?.list?.length > 0) {\n const { list, activeSessionId } = result.sessions;\n await putSessions(list);\n if (activeSessionId) {\n await setActiveSessionId(activeSessionId);\n }\n migrated = true;\n logger.debug('[IDB] migrated activesession:', list.length, '');\n }\n\n // 迁移归档会话\n if (result.conversationSessions?.sessions?.length > 0) {\n await replaceArchivedSessions(result.conversationSessions.sessions);\n migrated = true;\n logger.debug('[IDB] migrated archivesession:', result.conversationSessions.sessions.length, '');\n }\n\n if (migrated) {\n // 清除 chrome.storage 中的大数据 key,保留其他配置\n chrome.storage.local.remove(['sessions', 'conversationSessions', 'chatHistory'], () => {\n logger.debug('[IDB] cleaned chrome.storage.local olddata');\n });\n }\n\n resolve(migrated);\n });\n });\n}\n\n/**\n * 检查 IndexedDB 是否有数据,没有则尝试从 chrome.storage 迁移\n * @returns {Promise<void>}\n */\nexport async function ensureMigration() {\n const sessions = await getAllSessions();\n if (sessions.length > 0) return; // 已有数据\n\n const activeId = await getActiveSessionId();\n if (activeId) return; // 已有活跃会话标记\n\n // 尝试迁移\n await migrateFromChromeStorage();\n}\n\n// ==================== React Checkpoints CRUD ====================\n//\n// checkpoint 数据结构:\n// {\n// sessionId: string, // 主键\n// currentMessages: Array, // 完整的 API 消息数组(含 tool_calls/tool 消息对)\n// executionLog: Array, // 执行日志\n// iteration: number, // 当前迭代次数\n// globalIteration: { value }, // 全局迭代计数\n// subtaskPlan: Object|null, // 任务拆解计划\n// currentSubtaskIndex: number|null, // 当前子任务索引\n// totalReflectionRounds: number, // 反思轮数\n// model: string, // 使用的模型\n// apiParams: Object, // API 参数\n// taskContext: Object|null, // 任务上下文(子任务/子代理)\n// tabId: number|null, // 标签页 ID\n// callId: string|null, // 调用 ID\n// interruptedReason: string, // 中断原因\n// createdAt: number, // 创建时间戳\n// updatedAt: number, // 更新时间戳\n// }\n\n/**\n * 保存/更新 ReAct checkpoint\n * 采用双写策略:IndexedDB + chrome.storage.local(后备)\n * 当 IndexedDB 的 reactCheckpoints store 不存在或写入失败时,chrome.storage.local 确保数据不丢失\n * @param {Object} checkpoint - 必须包含 sessionId\n * @returns {Promise<boolean>}\n */\nexport async function saveReactCheckpoint(checkpoint) {\n if (!checkpoint?.sessionId) return false;\n const now = Date.now();\n const data = {\n ...checkpoint,\n updatedAt: now,\n createdAt: checkpoint.createdAt || now,\n };\n const key = CHECKPOINT_KEY_PREFIX + checkpoint.sessionId;\n\n // 策略:先写 chrome.storage.local(可靠),再尝试 IndexedDB(性能优化)\n // 这样即使 IndexedDB 完全不可用,checkpoint 也不会丢失\n let storageOk = false;\n try {\n await chrome.storage.local.set({ [key]: data });\n storageOk = true;\n } catch (e) {\n logger.warn('[Storage] chrome.storage.local save checkpoint failed:', e.message);\n }\n\n // 同时写入 IndexedDB(如果可用),供 getAllReactCheckpoints 高效查询\n let idbOk = false;\n try {\n idbOk = await withStore('reactCheckpoints', 'readwrite', (store, resolve) => {\n const request = store.put(data);\n request.onsuccess = () => resolve(true);\n request.onerror = () => resolve(false);\n });\n } catch (e) {\n // IndexedDB 不可用(store 不存在等),不影响主流程\n logger.warn('[Storage] IndexedDB save checkpoint failed (downgraded to chrome.storage.local):', e.message);\n }\n\n if (!storageOk && !idbOk) {\n logger.error('[Storage] checkpoint save complete failed:chrome.storage.local and IndexedDB averagenon- with ');\n return false;\n }\n return true;\n}\n\n/**\n * 获取指定会话的 ReAct checkpoint\n * 优先从 IndexedDB 读取,不存在时降级到 chrome.storage.local\n * @param {string} sessionId\n * @returns {Promise<Object|null>}\n */\nexport async function getReactCheckpoint(sessionId) {\n if (!sessionId) return null;\n\n // 优先尝试 IndexedDB\n try {\n const result = await withStore('reactCheckpoints', 'readonly', (store, resolve) => {\n const request = store.get(sessionId);\n request.onsuccess = () => resolve(request.result || null);\n request.onerror = () => resolve(null);\n });\n if (result) return result;\n } catch (e) {\n // IndexedDB 不可用,继续尝试 chrome.storage.local\n }\n\n // 降级:从 chrome.storage.local 读取\n try {\n const key = CHECKPOINT_KEY_PREFIX + sessionId;\n const data = await chrome.storage.local.get(key);\n return data[key] || null;\n } catch (e) {\n logger.warn('[Storage] chrome.storage.local read checkpoint failed:', e.message);\n return null;\n }\n}\n\n/**\n * 删除指定会话的 ReAct checkpoint\n * 同时从 IndexedDB 和 chrome.storage.local 中删除\n * @param {string} sessionId\n * @returns {Promise<boolean>}\n */\nexport async function deleteReactCheckpoint(sessionId) {\n if (!sessionId) return false;\n let ok = false;\n\n // 从 chrome.storage.local 删除\n try {\n const key = CHECKPOINT_KEY_PREFIX + sessionId;\n await chrome.storage.local.remove(key);\n ok = true;\n } catch (e) {\n logger.warn('[Storage] chrome.storage.local delete checkpoint failed:', e.message);\n }\n\n // 从 IndexedDB 删除\n try {\n await withStore('reactCheckpoints', 'readwrite', (store, resolve) => {\n const request = store.delete(sessionId);\n request.onsuccess = () => resolve(true);\n request.onerror = () => resolve(false);\n });\n ok = true;\n } catch (e) {\n // IndexedDB 不可用,不影响\n }\n\n return ok;\n}\n\n/**\n * 获取所有 ReAct checkpoint(用于 TTL 清理和诊断)\n * 合并 IndexedDB 和 chrome.storage.local 中的结果\n * @returns {Promise<Array>}\n */\nexport async function getAllReactCheckpoints() {\n // 从 chrome.storage.local 获取所有 checkpoint\n let storageCheckpoints = [];\n try {\n const all = await chrome.storage.local.get(null);\n for (const [key, value] of Object.entries(all)) {\n if (key.startsWith(CHECKPOINT_KEY_PREFIX) && value?.sessionId) {\n storageCheckpoints.push(value);\n }\n }\n } catch (e) {\n logger.warn('[Storage] chrome.storage.local get all checkpoint failed:', e.message);\n }\n\n // 从 IndexedDB 获取\n let idbCheckpoints = [];\n try {\n idbCheckpoints = await withStore('reactCheckpoints', 'readonly', (store, resolve) => {\n const request = store.getAll();\n request.onsuccess = () => resolve(request.result || []);\n request.onerror = () => resolve([]);\n });\n } catch (e) {\n // IndexedDB 不可用\n }\n\n // 合并去重(以 sessionId 为键,IndexedDB 优先)\n const merged = new Map();\n for (const cp of storageCheckpoints) {\n merged.set(cp.sessionId, cp);\n }\n for (const cp of idbCheckpoints) {\n merged.set(cp.sessionId, cp);\n }\n return Array.from(merged.values());\n}\n\n/**\n * 清理过期的 ReAct checkpoint(TTL: 7 天)\n * 应在 SW 启动时调用\n * @returns {Promise<number>} 清理的条数\n */\nexport async function cleanupExpiredReactCheckpoints() {\n const all = await getAllReactCheckpoints();\n const now = Date.now();\n const expired = all.filter(c => (c.updatedAt || 0) < now - REACT_CHECKPOINT_TTL_MS);\n if (expired.length === 0) return 0;\n\n let deleted = 0;\n for (const cp of expired) {\n const ok = await deleteReactCheckpoint(cp.sessionId);\n if (ok) deleted++;\n }\n logger.debug(`[IDB] cleanup ${deleted} expired ReAct checkpoint`);\n return deleted;\n}\n\n// ==================== Bookmarks CRUD ====================\n\n/**\n * 获取所有收藏\n * @returns {Promise<Array>}\n */\nexport function getAllBookmarks() {\n return withStore('bookmarks', 'readonly', (store, resolve) => {\n const request = store.getAll();\n request.onsuccess = () => resolve(request.result || []);\n request.onerror = () => resolve([]);\n });\n}\n\n/**\n * 保存收藏\n * @param {Object} bookmark - { id, sessionId, messageId, content, sessionTitle, createdAt, pinned }\n * @returns {Promise<boolean>}\n */\nexport function putBookmark(bookmark) {\n return withStore('bookmarks', 'readwrite', (store, resolve) => {\n const request = store.put(bookmark);\n request.onsuccess = () => resolve(true);\n request.onerror = () => resolve(false);\n });\n}\n\n/**\n * 删除收藏\n * @param {string} id\n * @returns {Promise<boolean>}\n */\nexport function deleteBookmark(id) {\n return withStore('bookmarks', 'readwrite', (store, resolve) => {\n const request = store.delete(id);\n request.onsuccess = () => resolve(true);\n request.onerror = () => resolve(false);\n });\n}\n\n/**\n * 更新收藏的置顶状态\n * @param {string} id\n * @param {boolean} pinned\n * @returns {Promise<boolean>}\n */\nexport function updateBookmarkPin(id, pinned) {\n return withStore('bookmarks', 'readwrite', (store, resolve) => {\n const getReq = store.get(id);\n getReq.onsuccess = () => {\n const bm = getReq.result;\n if (!bm) { resolve(false); return; }\n bm.pinned = pinned;\n const putReq = store.put(bm);\n putReq.onsuccess = () => resolve(true);\n putReq.onerror = () => resolve(false);\n };\n getReq.onerror = () => resolve(false);\n });\n}\n\n// ==================== Scheduled Tasks CRUD ====================\n\n/**\n * 获取单个定时任务\n */\nexport function getScheduledTask(id) {\n return withStore('scheduledTasks', 'readonly', (store, resolve) => {\n const request = store.get(id);\n request.onsuccess = () => resolve(request.result || null);\n request.onerror = () => resolve(null);\n });\n}\n\n/**\n * 获取所有定时任务\n */\nexport function getAllScheduledTasks() {\n return withStore('scheduledTasks', 'readonly', (store, resolve) => {\n const request = store.getAll();\n request.onsuccess = () => resolve(request.result || []);\n request.onerror = () => resolve([]);\n });\n}\n\n/**\n * 保存/更新定时任务(put = insert or update)\n */\nexport function putScheduledTask(task) {\n return withStore('scheduledTasks', 'readwrite', (store, resolve) => {\n const request = store.put(task);\n request.onsuccess = () => resolve(true);\n request.onerror = () => resolve(false);\n });\n}\n\n/**\n * 删除定时任务\n */\nexport function deleteScheduledTask(id) {\n return withStore('scheduledTasks', 'readwrite', (store, resolve) => {\n const request = store.delete(id);\n request.onsuccess = () => resolve(true);\n request.onerror = () => resolve(false);\n });\n}\n","// storage/token-store.js - Token 使用统计的 IndexedDB CRUD\n\nimport { getDB, withStore } from './db.js';\n\nconst STORE_NAME = 'tokenStats';\n\n// 全局汇总存储 key\nconst OVERALL_KEY = 'overall';\n\n// 系统级会话 ID:不计入\"总会话数\",但其 Token 消耗仍计入总量\nconst SYSTEM_SESSION_IDS = new Set(['selection_toolbar']);\n\n/**\n * 记录单次 API 调用的 token 使用\n * @param {Object} record\n * @param {string} record.sessionId\n * @param {string} record.model\n * @param {number} record.promptTokens\n * @param {number} record.completionTokens\n * @param {number} record.totalTokens\n * @param {number} record.contextWindow\n * @param {string} record.callType - 'react_loop' | 'non_stream' | 'reflection' | 'tool_reflection' | 'subtask_reflection'\n */\nexport function recordTokenCall(record) {\n const entry = {\n id: `tcr_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,\n sessionId: record.sessionId,\n timestamp: new Date().toISOString(),\n model: record.model || 'unknown',\n promptTokens: record.promptTokens || 0,\n completionTokens: record.completionTokens || 0,\n totalTokens: record.totalTokens || 0,\n contextWindow: record.contextWindow || 256000,\n contextUsageRate: record.contextWindow > 0\n ? ((record.promptTokens || 0) / record.contextWindow)\n : 0,\n callType: record.callType || 'unknown'\n };\n\n return withStore(STORE_NAME, 'readwrite', (store, resolve) => {\n const req = store.put(entry);\n req.onsuccess = () => resolve(true);\n req.onerror = () => resolve(false);\n });\n}\n\n/**\n * 获取某个会话的 token 汇总\n * @param {string} sessionId\n * @returns {Promise<Object>}\n */\nexport function getSessionTokenSummary(sessionId) {\n return withStore(STORE_NAME, 'readonly', (store, resolve) => {\n // 使用 sessionId 索引 + key range 缩小候选集(精确匹配 + 子任务前缀 sessionId_xxx),\n // 避免 getAll() 全量加载所有会话的记录\n const index = store.index('sessionId');\n const req = sessionId\n ? index.getAll(IDBKeyRange.bound(sessionId, sessionId + '\\uffff'))\n : store.getAll();\n req.onsuccess = () => {\n // 仍需精确过滤,确保语义与原实现一致(排除 sessionIdX 类前缀误匹配)\n const records = (req.result || []).filter(r => \n r.sessionId === sessionId || \n (sessionId && r.sessionId?.startsWith(sessionId + '_'))\n );\n if (records.length === 0) {\n resolve({\n sessionId,\n totalPromptTokens: 0,\n totalCompletionTokens: 0,\n totalTokens: 0,\n apiCallCount: 0,\n avgContextUsageRate: 0,\n maxContextUsageRate: 0,\n minContextUsageRate: 0,\n records: []\n });\n return;\n }\n\n const usageRates = records.map(r => r.contextUsageRate || 0).filter(r => r > 0);\n resolve({\n sessionId,\n totalPromptTokens: records.reduce((s, r) => s + (r.promptTokens || 0), 0),\n totalCompletionTokens: records.reduce((s, r) => s + (r.completionTokens || 0), 0),\n totalTokens: records.reduce((s, r) => s + (r.totalTokens || 0), 0),\n apiCallCount: records.length,\n avgContextUsageRate: usageRates.length > 0\n ? usageRates.reduce((s, r) => s + r, 0) / usageRates.length\n : 0,\n maxContextUsageRate: usageRates.length > 0 ? Math.max(...usageRates) : 0,\n minContextUsageRate: usageRates.length > 0 ? Math.min(...usageRates) : 0,\n records: records.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)).slice(0, 20)\n });\n };\n req.onerror = () => resolve(null);\n });\n}\n\n/**\n * 获取全局 token 汇总\n * @returns {Promise<Object>}\n */\nexport function getOverallTokenSummary() {\n return withStore(STORE_NAME, 'readonly', (store, resolve) => {\n const req = store.getAll();\n req.onsuccess = () => {\n const records = req.result || [];\n if (records.length === 0) {\n resolve({\n totalPromptTokens: 0,\n totalCompletionTokens: 0,\n totalTokens: 0,\n totalApiCalls: 0,\n totalSessions: 0,\n lastUpdated: null\n });\n return;\n }\n\n const sessionIds = new Set(\n records.map(r => r.sessionId).filter(id => id && !SYSTEM_SESSION_IDS.has(id))\n );\n resolve({\n totalPromptTokens: records.reduce((s, r) => s + (r.promptTokens || 0), 0),\n totalCompletionTokens: records.reduce((s, r) => s + (r.completionTokens || 0), 0),\n totalTokens: records.reduce((s, r) => s + (r.totalTokens || 0), 0),\n totalApiCalls: records.length,\n totalSessions: sessionIds.size,\n lastUpdated: records.reduce((latest, r) => {\n return r.timestamp > latest ? r.timestamp : latest;\n }, '')\n });\n };\n req.onerror = () => resolve(null);\n });\n}\n\n/**\n * 清除某个会话的 token 统计\n * @param {string} sessionId\n */\nexport function clearSessionTokenStats(sessionId) {\n return withStore(STORE_NAME, 'readwrite', (store, resolve) => {\n const req = store.getAll();\n req.onsuccess = () => {\n const records = req.result || [];\n const toDelete = records.filter(r => r.sessionId === sessionId);\n let deleted = 0;\n toDelete.forEach(r => {\n store.delete(r.id);\n deleted++;\n });\n resolve(deleted);\n };\n req.onerror = () => resolve(0);\n });\n}\n\n/**\n * 清除所有 token 统计\n */\nexport function clearAllTokenStats() {\n return withStore(STORE_NAME, 'readwrite', (store, resolve) => {\n const req = store.clear();\n req.onsuccess = () => resolve(true);\n req.onerror = () => resolve(false);\n });\n}\n\n/**\n * 获取最近 N 条 token 记录(全局)\n * @param {number} limit\n */\nexport function getRecentTokenRecords(limit = 20) {\n return withStore(STORE_NAME, 'readonly', (store, resolve) => {\n const req = store.getAll();\n req.onsuccess = () => {\n const records = req.result || [];\n records.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));\n resolve(records.slice(0, limit));\n };\n req.onerror = () => resolve([]);\n });\n}\n\n// ==================== 阶段二:新增聚合查询接口 ====================\n\n/**\n * 获取最近 N 天的每日 token 聚合\n * @param {number} days - 天数(默认 30)\n * @returns {Promise<Array<{date: string, totalTokens: number, apiCalls: number}>>}\n */\nexport function getDailyTokenSummary(days = 30) {\n return withStore(STORE_NAME, 'readonly', (store, resolve) => {\n const now = new Date();\n const startDate = new Date(now.getTime() - days * 24 * 60 * 60 * 1000);\n // 使用 timestamp 索引下界,只读取区间内记录(ISO 字符串可按字典序比较)\n const index = store.index('timestamp');\n const req = index.getAll(IDBKeyRange.lowerBound(startDate.toISOString()));\n req.onsuccess = () => {\n const records = req.result || [];\n \n // 按天聚合\n const dailyMap = new Map();\n records.forEach(r => {\n const recordDate = new Date(r.timestamp);\n if (recordDate < startDate) return;\n \n const dateKey = recordDate.toISOString().split('T')[0]; // YYYY-MM-DD\n if (!dailyMap.has(dateKey)) {\n dailyMap.set(dateKey, { date: dateKey, totalTokens: 0, apiCalls: 0 });\n }\n const dayData = dailyMap.get(dateKey);\n dayData.totalTokens += r.totalTokens || 0;\n dayData.apiCalls += 1;\n });\n \n // 转换为数组并按日期排序\n const result = Array.from(dailyMap.values()).sort((a, b) => a.date.localeCompare(b.date));\n resolve(result);\n };\n req.onerror = () => resolve([]);\n });\n}\n\n/**\n * 按时间范围过滤的全局汇总\n * @param {string} startDate - ISO 日期字符串\n * @param {string} endDate - ISO 日期字符串\n * @returns {Promise<Object>} 同 getOverallTokenSummary 结构\n */\nexport function getOverallTokenSummaryByRange(startDate, endDate) {\n return withStore(STORE_NAME, 'readonly', (store, resolve) => {\n // 使用 timestamp 索引限定区间,只读取范围内的记录\n const index = store.index('timestamp');\n const req = index.getAll(IDBKeyRange.bound(startDate, endDate));\n req.onsuccess = () => {\n const records = req.result || [];\n const start = new Date(startDate);\n const end = new Date(endDate);\n \n // 二次精确过滤(防御 ISO 格式差异导致的字典序偏差)\n const filtered = records.filter(r => {\n const recordDate = new Date(r.timestamp);\n return recordDate >= start && recordDate <= end;\n });\n \n if (filtered.length === 0) {\n resolve({\n totalPromptTokens: 0,\n totalCompletionTokens: 0,\n totalTokens: 0,\n totalApiCalls: 0,\n totalSessions: 0,\n lastUpdated: null\n });\n return;\n }\n \n const sessionIds = new Set(\n filtered.map(r => r.sessionId).filter(id => id && !SYSTEM_SESSION_IDS.has(id))\n );\n resolve({\n totalPromptTokens: filtered.reduce((s, r) => s + (r.promptTokens || 0), 0),\n totalCompletionTokens: filtered.reduce((s, r) => s + (r.completionTokens || 0), 0),\n totalTokens: filtered.reduce((s, r) => s + (r.totalTokens || 0), 0),\n totalApiCalls: filtered.length,\n totalSessions: sessionIds.size,\n lastUpdated: filtered.reduce((latest, r) => {\n return r.timestamp > latest ? r.timestamp : latest;\n }, '')\n });\n };\n req.onerror = () => resolve(null);\n });\n}\n\n/**\n * 按模型聚合的 token 分布\n * @returns {Promise<Array<{model: string, totalTokens: number, percentage: number}>>}\n */\nexport function getTokenSummaryByModel() {\n return withStore(STORE_NAME, 'readonly', (store, resolve) => {\n const req = store.getAll();\n req.onsuccess = () => {\n const records = req.result || [];\n const modelMap = new Map();\n \n records.forEach(r => {\n const model = r.model || 'unknown';\n if (!modelMap.has(model)) {\n modelMap.set(model, 0);\n }\n modelMap.set(model, modelMap.get(model) + (r.totalTokens || 0));\n });\n \n const totalTokens = Array.from(modelMap.values()).reduce((s, v) => s + v, 0);\n const result = Array.from(modelMap.entries())\n .map(([model, tokens]) => ({\n model,\n totalTokens: tokens,\n percentage: totalTokens > 0 ? (tokens / totalTokens) * 100 : 0\n }))\n .sort((a, b) => b.totalTokens - a.totalTokens);\n \n resolve(result);\n };\n req.onerror = () => resolve([]);\n });\n}\n\n/**\n * 按 callType 聚合的 token 分布\n * @returns {Promise<Array<{callType: string, totalTokens: number, percentage: number}>>}\n */\nexport function getTokenSummaryByCallType() {\n return withStore(STORE_NAME, 'readonly', (store, resolve) => {\n const req = store.getAll();\n req.onsuccess = () => {\n const records = req.result || [];\n const callTypeMap = new Map();\n \n records.forEach(r => {\n const callType = r.callType || 'unknown';\n if (!callTypeMap.has(callType)) {\n callTypeMap.set(callType, 0);\n }\n callTypeMap.set(callType, callTypeMap.get(callType) + (r.totalTokens || 0));\n });\n \n const totalTokens = Array.from(callTypeMap.values()).reduce((s, v) => s + v, 0);\n const result = Array.from(callTypeMap.entries())\n .map(([callType, tokens]) => ({\n callType,\n totalTokens: tokens,\n percentage: totalTokens > 0 ? (tokens / totalTokens) * 100 : 0\n }))\n .sort((a, b) => b.totalTokens - a.totalTokens);\n \n resolve(result);\n };\n req.onerror = () => resolve([]);\n });\n}\n","// shared/agent-defaults.js - 内置 Agent 定义和模板\n\n/**\n * 内置默认 Agent(不可删除)\n */\nexport const BUILTIN_AGENTS = [\n {\n id: 'default',\n name: 'Default Assistant',\n description: 'All-purpose AI assistant with all tool capabilities',\n icon: '🤖',\n systemPrompt: null, // null = 使用全局 systemPrompt\n toolIds: null, // null = 使用全局 enabledTools\n skillIds: null, // null = 使用全部启用技能\n isBuiltin: true,\n allowSubDispatch: false,\n model: null,\n temperature: null,\n topP: null,\n }\n];\n\n/**\n * Agent 模板(供用户快速创建参考)\n */\nexport const AGENT_TEMPLATES = [\n {\n name: 'Code Review Expert',\n icon: '🔍',\n description: 'Focused on code review and quality assurance',\n systemPrompt: `You are a senior Code Review Expert. Your responsibilities:\n1. Review code for logical correctness, performance, and security\n2. Check code style, naming conventions, and best practices\n3. Identify potential bugs, memory leaks, and concurrency issues\n4. Provide actionable improvement suggestions with example code\n\nResponse principles:\n- Always indicate severity (critical/moderate/suggestion)\n- Provide specific code modification suggestions, avoid vague comments\n- Focus on maintainability, not just functional correctness`,\n systemPromptZh: `你是一位资深代码审查专家。你的职责:\n1. 审查代码的逻辑正确性、性能和安全性\n2. 检查代码风格、命名规范和最佳实践\n3. 识别潜在的 Bug、内存泄漏和并发问题\n4. 提供可操作的改进建议,并附带示例代码\n\n回复原则:\n- 始终标注严重程度(严重/中等/建议)\n- 提供具体的代码修改建议,避免模糊评论\n- 关注可维护性,而非仅关注功能正确性`,\n toolIds: ['page_content', 'search_browser_data', 'agent_file', 'agent_search', 'search_chats', 'fetch_url', 'plan_task'],\n allowSubDispatch: false,\n },\n {\n name: 'Web Automation Assistant',\n icon: '🌐',\n description: 'Focused on web interaction and automation',\n systemPrompt: `You are a web automation expert. Your strengths:\n1. Automate web page interactions based on user needs (clicking, form filling, scrolling, etc.)\n2. Extract and analyze web page content\n3. Handle multi-step web interaction flows\n\nOperating principles:\n- Understand page structure before acting; prefer query_elements first\n- Verify results after actions to ensure they take effect\n- Analyze errors and try alternative approaches\n- Never assume elements exist; fetch page info first when uncertain\n- For dialogs (alert/confirm/prompt), call handle_dialog BEFORE the action that triggers them, then use interact_element to proceed`,\n systemPromptZh: `你是一位网页自动化专家。你的优势:\n1. 根据用户需求自动化网页交互(点击、表单填写、滚动等)\n2. 提取和分析网页内容\n3. 处理多步骤的网页交互流程\n\n操作原则:\n- 行动前先理解页面结构,优先使用 query_elements\n- 操作后验证结果,确保生效\n- 分析错误并尝试替代方案\n- 不要假设元素存在,不确定时先获取页面信息\n- 涉及 alert/confirm/prompt 弹窗时,先在触发弹窗的操作前调用 handle_dialog,再通过 interact_element 继续`,\n toolIds: ['page_content', 'query_elements', 'interact_element', 'fill_form', 'scroll_to', 'wait_element', 'keyboard_input', 'select_dropdown', 'capture_page', 'extract_data', 'search_in_page', 'wait_navigation', 'scroll_collect', 'drag_drop', 'file_upload', 'iframe_content', 'manage_tab', 'handle_dialog'],\n allowSubDispatch: false,\n },\n {\n name: 'Data Analyst',\n icon: '📊',\n description: 'Focused on data extraction, analysis, and visualization',\n systemPrompt: `You are a data analyst. Your strengths:\n1. Extract structured data from web pages (tables, lists, JSON)\n2. Analyze and summarize data patterns\n3. Present analysis results in clear formats\n\nAnalysis principles:\n- Understand data structure before starting analysis\n- Use the most appropriate extraction method (tables → extract_data with dataType=table; links/images/forms → extract_data with dataType=links/images/forms; text/HTML → page_content with format=text/html)\n- Support analysis results with data; avoid subjective assumptions\n- Present conclusions using tables or charts`,\n systemPromptZh: `你是一位数据分析师。你的优势:\n1. 从网页中提取结构化数据(表格、列表、JSON)\n2. 分析和总结数据模式\n3. 以清晰格式呈现分析结果\n\n分析原则:\n- 开始分析前先理解数据结构\n- 使用最合适的提取方法(表格使用 extract_data 配合 dataType=table;链接/图片/表单等结构化数据使用 extract_data 配合 dataType=links/images/forms;纯文本/HTML 使用 page_content 配合 format=text/html)\n- 用数据支撑分析结果,避免主观臆断\n- 使用表格或图表呈现结论`,\n toolIds: ['page_content', 'extract_data', 'query_elements', 'search_in_page', 'scroll_collect', 'fetch_url', 'iframe_content', 'clipboard'],\n allowSubDispatch: false,\n },\n {\n name: 'Documentation Assistant',\n icon: '📝',\n description: 'Focused on technical documentation and content organization',\n systemPrompt: `You are a technical documentation expert. Your strengths:\n1. Write clear technical documentation (API docs, READMEs, user guides)\n2. Distill and summarize technical information\n3. Transform complex concepts into easy-to-understand documentation\n\nWriting principles:\n- Clear structure: Use headings, lists, and tables to organize content\n- Examples first: Key concepts must include code or configuration examples\n- Reader-oriented: Adjust technical depth based on target audience\n- Stay concise: Avoid redundancy; every paragraph should carry information`,\n systemPromptZh: `你是一位技术文档专家。你的优势:\n1. 编写清晰的技术文档(API 文档、README、用户指南)\n2. 提炼和总结技术信息\n3. 将复杂概念转化为易于理解的文档\n\n编写原则:\n- 结构清晰:使用标题、列表和表格组织内容\n- 示例先行:关键概念必须附带代码或配置示例\n- 面向读者:根据目标受众调整技术深度\n- 保持简洁:避免冗余,每段文字都应承载信息`,\n toolIds: ['page_content', 'clipboard', 'search_browser_data', 'search_chats', 'fetch_url', 'agent_file', 'agent_search', 'extract_data', 'capture_page', 'search_in_page'],\n allowSubDispatch: false,\n },\n];\n\n/**\n * 生成新的 Agent ID\n */\nexport function generateAgentId() {\n return 'agent_' + Date.now().toString(36) + '_' + Math.random().toString(36).substring(2, 6);\n}\n","// rag-tools - RAG knowledge base tool definitions (3-in-1)\n// Backed by agent /api/rag/* endpoints (vectra index + transformers embedding)\n// Dynamically registered only when ragEnabled=true && agent /api/status ragAvailable=true\n\nexport const RAG_TOOLS = [\n {\n id: 'knowledge_search',\n category: 'knowledge',\n execution: 'background',\n parallelizable: true,\n requiresConfirmation: false,\n type: 'function',\n function: {\n name: 'knowledge_search',\n description: 'Search knowledge bases for relevant information. Use when you need factual grounding or the user asks about specific topics.',\n parameters: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Search query' },\n collectionId: { type: 'string', description: 'Knowledge base ID; omit to search all' },\n topK: { type: 'integer', description: 'Number of results to return, default 5' }\n },\n required: ['query']\n }\n }\n },\n {\n id: 'knowledge_ingest',\n category: 'knowledge',\n execution: 'background',\n parallelizable: false,\n requiresConfirmation: false,\n type: 'function',\n function: {\n name: 'knowledge_ingest',\n description: 'Ingest a document or text into a knowledge base for future retrieval. Use knowledge_list first to get the collectionId.',\n parameters: {\n type: 'object',\n properties: {\n collectionId: { type: 'string', description: 'Target knowledge base ID (from knowledge_list)' },\n type: { type: 'string', enum: ['text', 'file', 'url'], description: 'Source type: text (raw content), file (local file path), url (web page)' },\n content: { type: 'string', description: 'Raw text, local file path, or URL depending on type' },\n name: { type: 'string', description: 'Optional document name for display' },\n metadata: { type: 'object', description: 'Optional metadata to attach to chunks' }\n },\n required: ['collectionId', 'type', 'content']\n }\n }\n },\n {\n id: 'knowledge_list',\n category: 'knowledge',\n execution: 'background',\n parallelizable: true,\n requiresConfirmation: false,\n type: 'function',\n function: {\n name: 'knowledge_list',\n description: 'List available knowledge bases and their document counts',\n parameters: {\n type: 'object',\n properties: {}\n }\n }\n }\n];\n","// state.js - 全局状态变量\n// 同时导出命名 let 绑定(供 named import 使用)和 default 对象(供 import state 使用)\n// default 对象通过 getter/setter 代理到同名 let 变量,确保两种导入方式共享同一份数据\n\nexport let generatingSessionIds = new Set(); // Set of sessionIds currently generating\n// 后台任务已完成、但用户尚未切回查看的会话 ID 集合\n// 用于在会话标签上显示\"完成待查看\"提示,用户切回该会话后清除\nexport let completedSessionIds = new Set();\nexport let messageHistory = [];\nexport let currentModel = 'deepseek-v4-pro';\nexport let activeSessionId = null; // 当前活跃会话 ID\nexport let sessions = []; // 所有会话列表缓存\nexport let useTools = true;\nexport let isolateChat = true;\n// 当前会话的上下文压缩记录(会话级,随会话持久化;null 表示未压缩)\nexport let activeCompaction = null;\nexport let enableSelectionQuery = false;\nexport let currentTabId = null;\nexport let selectedContextText = '';\nexport let quotedContextText = '';\nexport let customPrompts = [];\nexport let selectedPromptIndex = -1;\nexport let selectedSkillIndex = -1; // 技能选择器当前选中索引\nexport let selectedSkills = []; // 当前选中的技能列表(支持多选)[{ name, description, type }]\nexport let selectedMcpServices = []; // 当前选中的 MCP 服务列表(支持多选)[{ serverId, serverName, toolCount }]\nexport let selectedMcpServiceIndex = -1; // MCP 服务选择器当前选中索引\nexport let selectedPage = null; // 当前选中的网页 { id, title, url, favIconUrl }\nexport let selectedPageIndex = -1; // 页面选择器当前选中索引\nexport let knowledgeRefs = []; // 已引用的知识库列表 [{ id, name }]\nexport let selectedKnowledgeAtIndex = -1; // @ 知识库选择器当前选中索引\nexport let activeDropdownTab = 'prompts'; // 下拉框当前激活的 Tab\nexport let lastActiveDropdownTab = 'prompts'; // 上次激活的 Tab(用于记忆)\nexport let showMergedList = false; // 是否显示合并列表(搜索模式)\nexport let selectedAgentAtIndex = -1; // @ Agent 选择器当前选中索引\nexport let selectedProxyAtIndex = -1; // @ 代理选择器当前选中索引\nexport let selectedFileAtIndex = -1; // $ 工作目录文件选择器当前选中索引\nexport let draggedItemIndex = null;\nexport let systemPrompt = '';\nexport let inputHistory = [];\nexport let inputHistoryIndex = -1;\n\n// 自定义 Agent 相关\nexport let activeAgentId = null; // 当前选中的 Agent ID\nexport let activeAgentToolIds = null; // 当前 Agent 限定的工具列表(null=继承全局)\nexport let activeAgentMcpExcludedServerIds = null; // 当前 Agent 排除的 MCP 服务 ID 列表(服务级;null=不限)\nexport let activeAgentSkillIds = null; // 当前 Agent 绑定的技能名称列表(null=全部启用)\nexport let customAgents = []; // 用户自定义 Agent 列表缓存\n\n// Agent 平台信息(从 Agent 获取,用于系统提示词注入)\nexport let agentPlatform = {\n platformName: 'Unknown',\n platform: 'unknown',\n arch: 'unknown',\n shell: '/bin/sh',\n homeDir: '/home/user',\n workdir: '',\n connected: false\n};\n\n// 配置常量 - 从 storage 获取,使用默认值作为后备\nexport let chatConfig = {\n maxMemoryMessages: 20,\n enableExecutionLog: false,\n showMessageTimestamp: true,\n // 上下文压缩:自动压缩默认关(额外调用),摘要沉淀默认开\n autoCompaction: false,\n autoCompactionThreshold: 0.85,\n persistSummaryToMemory: true\n};\n\n// 温度设置\nexport let temperature = 0.2;\nexport let topP = 1.0;\nexport let selectedTempIndex = 0;\n\n// 工具弹窗搜索和分类状态\nexport let currentCategory = 'all';\nexport let currentSearch = '';\n\n// 当前启用的工具列表(不含 MCP 工具,MCP 按服务级开关管理)\nexport let enabledTools = [];\n\n// 当前智能体上下文中被用户关闭的 MCP 服务 ID 列表(deny-list:不在列表中 = 开放)\nexport let mcpClosedServers = [];\n\n// 分类折叠状态\nexport const collapsedCategories = {};\n\n// 当前执行状态(按 sessionId 隔离,key 为 sessionId)\nexport let sessionExecutionStatus = new Map();\nexport let executionLogListener = null;\n\n// 当前 API 调用的取消函数,按 sessionId 隔离,防止多会话并行时互相覆盖\nexport let pendingCancelApiMap = new Map();\n\n// 记录有 pending callApi 的会话 ID 集合(支持多会话同时有后台任务)\nexport let pendingCallApiSessionIds = new Set();\n\n// 切换回原会话后,重新创建的加载指示器 ID 集合(按 sessionId 索引)\nexport let substituteLoadingIds = new Map();\n\n// 澄清对话框相关状态\nexport let currentClarifyToolCallId = null;\nexport let currentClarifySessionId = null; // 当前澄清所属的会话 ID\nexport let currentConfirmToolCallId = null; // 当前确认对话框所属的 toolCallId\nexport let currentConfirmSessionId = null; // 当前确认对话框所属的会话 ID\nexport let clarifyTimerInterval = null;\nexport let clarifyTimeoutValue = 300000; // 默认 5 分钟\n\n// 消息目录状态\nexport let messageTocContainer = null;\nexport let isMouseOverToc = false;\nexport let tocHideTimer = null;\n\n// 消息收藏\nexport let bookmarks = [];\n\n// 划词问答弹出菜单状态\nexport let lastSelectedText = '';\nexport let currentSelectionRange = null;\nexport let lastMouseX = 0;\nexport let lastMouseY = 0;\n\n// 待删除的提示词索引\nexport let pendingDeleteIndex = -1;\n\n// 图片识别\nexport let enableImageInput = false;\nexport let imageModelName = ''; // 图片识别模型,为空则使用 currentModel\nexport let imageApiBase = ''; // 图片识别独立 API Base,为空则使用主配置\nexport let imageApiKey = ''; // 图片识别独立 API Token,为空则使用主配置\nexport let attachedImages = []; // [{ originalUrl: 'data:image/jpeg;base64,...', compressedUrl: 'data:image/jpeg;base64,...' }]\n\n// 文件上传问答\nexport let enableFileInput = true; // 文件上传开关\nexport let attachedFiles = []; // [{ name, size, type, dataUrl, text, status: 'pending'|'extracting'|'done'|'error' }]\n\n// 代理版本号缓存 (agentId -> version string)\nexport let agentVersions = new Map();\n\n// 代理工作目录缓存 (agentId -> workdir string)\nexport let agentWorkdirs = new Map();\n\n// 代理用户主目录缓存 (agentId -> homeDir string),用于将工作目录前缀替换为 ~\nexport let agentHomeDirs = new Map();\n\n// 代理系统信息缓存 (agentId -> \"macOS (x64)\" 格式字符串)\nexport let agentSystemInfos = new Map();\n\n// 标志位\nexport let isScrolling = false;\nexport let isComposing = false;\n\n// 自定义模型上下文窗口映射(modelName → contextWindow)\nexport let customModelMap = new Map();\n\n// ============================================================\n// default 导出:通过 getter/setter 代理到同名 let 变量\n// 使用方式: import state from './state.js'; state.xxx = value;\n// ============================================================\nexport default {\n get isGenerating() { return generatingSessionIds.has(activeSessionId); },\n set isGenerating(v) { \n if (v) generatingSessionIds.add(activeSessionId); \n else generatingSessionIds.delete(activeSessionId);\n document.dispatchEvent(new CustomEvent('generating-state-changed'));\n },\n get generatingSessionIds() { return generatingSessionIds; },\n get completedSessionIds() { return completedSessionIds; },\n set completedSessionIds(v) { completedSessionIds = v; },\n get messageHistory() { return messageHistory; },\n set messageHistory(v) { messageHistory = v; },\n get currentModel() { return currentModel; },\n set currentModel(v) { currentModel = v; },\n get activeSessionId() { return activeSessionId; },\n set activeSessionId(v) { activeSessionId = v; },\n get sessions() { return sessions; },\n set sessions(v) { sessions = v; },\n get useTools() { return useTools; },\n set useTools(v) { useTools = v; },\n get isolateChat() { return isolateChat; },\n set isolateChat(v) { isolateChat = v; },\n get activeCompaction() { return activeCompaction; },\n set activeCompaction(v) { activeCompaction = v; },\n get enableSelectionQuery() { return enableSelectionQuery; },\n set enableSelectionQuery(v) { enableSelectionQuery = v; },\n get currentTabId() { return currentTabId; },\n set currentTabId(v) { currentTabId = v; },\n get selectedContextText() { return selectedContextText; },\n set selectedContextText(v) { selectedContextText = v; },\n get quotedContextText() { return quotedContextText; },\n set quotedContextText(v) { quotedContextText = v; },\n get customPrompts() { return customPrompts; },\n set customPrompts(v) { customPrompts = v; },\n get selectedPromptIndex() { return selectedPromptIndex; },\n set selectedPromptIndex(v) { selectedPromptIndex = v; },\n get selectedSkillIndex() { return selectedSkillIndex; },\n set selectedSkillIndex(v) { selectedSkillIndex = v; },\n get selectedSkills() { return selectedSkills; },\n set selectedSkills(v) { selectedSkills = v; },\n get selectedMcpServices() { return selectedMcpServices; },\n set selectedMcpServices(v) { selectedMcpServices = v; },\n get selectedMcpServiceIndex() { return selectedMcpServiceIndex; },\n set selectedMcpServiceIndex(v) { selectedMcpServiceIndex = v; },\n get selectedPage() { return selectedPage; },\n set selectedPage(v) { selectedPage = v; },\n get selectedPageIndex() { return selectedPageIndex; },\n set selectedPageIndex(v) { selectedPageIndex = v; },\n get knowledgeRefs() { return knowledgeRefs; },\n set knowledgeRefs(v) { knowledgeRefs = v; },\n get selectedKnowledgeAtIndex() { return selectedKnowledgeAtIndex; },\n set selectedKnowledgeAtIndex(v) { selectedKnowledgeAtIndex = v; },\n get activeDropdownTab() { return activeDropdownTab; },\n set activeDropdownTab(v) { activeDropdownTab = v; },\n get lastActiveDropdownTab() { return lastActiveDropdownTab; },\n set lastActiveDropdownTab(v) { lastActiveDropdownTab = v; },\n get showMergedList() { return showMergedList; },\n set showMergedList(v) { showMergedList = v; },\n get selectedAgentAtIndex() { return selectedAgentAtIndex; },\n set selectedAgentAtIndex(v) { selectedAgentAtIndex = v; },\n get selectedProxyAtIndex() { return selectedProxyAtIndex; },\n set selectedProxyAtIndex(v) { selectedProxyAtIndex = v; },\n get selectedFileAtIndex() { return selectedFileAtIndex; },\n set selectedFileAtIndex(v) { selectedFileAtIndex = v; },\n get draggedItemIndex() { return draggedItemIndex; },\n set draggedItemIndex(v) { draggedItemIndex = v; },\n get systemPrompt() { return systemPrompt; },\n set systemPrompt(v) { systemPrompt = v; },\n get activeAgentId() { return activeAgentId; },\n set activeAgentId(v) { activeAgentId = v; },\n get activeAgentToolIds() { return activeAgentToolIds; },\n set activeAgentToolIds(v) { activeAgentToolIds = v; },\n get activeAgentMcpExcludedServerIds() { return activeAgentMcpExcludedServerIds; },\n set activeAgentMcpExcludedServerIds(v) { activeAgentMcpExcludedServerIds = v; },\n get activeAgentSkillIds() { return activeAgentSkillIds; },\n set activeAgentSkillIds(v) { activeAgentSkillIds = v; },\n get customAgents() { return customAgents; },\n set customAgents(v) { customAgents = v; },\n get agentPlatform() { return agentPlatform; },\n set agentPlatform(v) { Object.assign(agentPlatform, v); },\n get inputHistory() { return inputHistory; },\n set inputHistory(v) { inputHistory = v; },\n get inputHistoryIndex() { return inputHistoryIndex; },\n set inputHistoryIndex(v) { inputHistoryIndex = v; },\n get chatConfig() { return chatConfig; },\n set chatConfig(v) { chatConfig = v; },\n get temperature() { return temperature; },\n set temperature(v) { temperature = v; },\n get topP() { return topP; },\n set topP(v) { topP = v; },\n get selectedTempIndex() { return selectedTempIndex; },\n set selectedTempIndex(v) { selectedTempIndex = v; },\n get currentCategory() { return currentCategory; },\n set currentCategory(v) { currentCategory = v; },\n get currentSearch() { return currentSearch; },\n set currentSearch(v) { currentSearch = v; },\n get enabledTools() { return enabledTools; },\n set enabledTools(v) { enabledTools = v; },\n get mcpClosedServers() { return mcpClosedServers; },\n set mcpClosedServers(v) { mcpClosedServers = v; },\n get collapsedCategories() { return collapsedCategories; },\n // collapsedCategories is const, no setter needed\n get sessionExecutionStatus() { return sessionExecutionStatus; },\n set sessionExecutionStatus(v) { sessionExecutionStatus = v; },\n // 兼容旧代码:currentExecutionStatus 通过 getter/setter 操作当前活跃会话\n get currentExecutionStatus() { return sessionExecutionStatus.get(activeSessionId) || null; },\n set currentExecutionStatus(v) { \n if (v === null) {\n sessionExecutionStatus.delete(activeSessionId);\n } else {\n sessionExecutionStatus.set(activeSessionId, v);\n }\n },\n get executionLogListener() { return executionLogListener; },\n set executionLogListener(v) { executionLogListener = v; },\n get pendingCancelApi() { return pendingCancelApiMap.get(activeSessionId) || null; },\n set pendingCancelApi(v) {\n if (v === null) {\n pendingCancelApiMap.delete(activeSessionId);\n } else {\n pendingCancelApiMap.set(activeSessionId, v);\n }\n },\n get pendingCancelApiMap() { return pendingCancelApiMap; },\n get pendingCallApiSessionIds() { return pendingCallApiSessionIds; },\n set pendingCallApiSessionIds(v) { pendingCallApiSessionIds = v; },\n get substituteLoadingIds() { return substituteLoadingIds; },\n set substituteLoadingIds(v) { substituteLoadingIds = v; },\n get currentClarifyToolCallId() { return currentClarifyToolCallId; },\n set currentClarifyToolCallId(v) { currentClarifyToolCallId = v; },\n get currentClarifySessionId() { return currentClarifySessionId; },\n set currentClarifySessionId(v) { currentClarifySessionId = v; },\n get currentConfirmToolCallId() { return currentConfirmToolCallId; },\n set currentConfirmToolCallId(v) { currentConfirmToolCallId = v; },\n get currentConfirmSessionId() { return currentConfirmSessionId; },\n set currentConfirmSessionId(v) { currentConfirmSessionId = v; },\n get clarifyTimerInterval() { return clarifyTimerInterval; },\n set clarifyTimerInterval(v) { clarifyTimerInterval = v; },\n get clarifyTimeoutValue() { return clarifyTimeoutValue; },\n set clarifyTimeoutValue(v) { clarifyTimeoutValue = v; },\n get messageTocContainer() { return messageTocContainer; },\n set messageTocContainer(v) { messageTocContainer = v; },\n get isMouseOverToc() { return isMouseOverToc; },\n set isMouseOverToc(v) { isMouseOverToc = v; },\n get tocHideTimer() { return tocHideTimer; },\n set tocHideTimer(v) { tocHideTimer = v; },\n get bookmarks() { return bookmarks; },\n set bookmarks(v) { bookmarks = v; },\n get lastSelectedText() { return lastSelectedText; },\n set lastSelectedText(v) { lastSelectedText = v; },\n get currentSelectionRange() { return currentSelectionRange; },\n set currentSelectionRange(v) { currentSelectionRange = v; },\n get lastMouseX() { return lastMouseX; },\n set lastMouseX(v) { lastMouseX = v; },\n get lastMouseY() { return lastMouseY; },\n set lastMouseY(v) { lastMouseY = v; },\n get pendingDeleteIndex() { return pendingDeleteIndex; },\n set pendingDeleteIndex(v) { pendingDeleteIndex = v; },\n get enableImageInput() { return enableImageInput; },\n set enableImageInput(v) { enableImageInput = v; },\n get imageModelName() { return imageModelName; },\n set imageModelName(v) { imageModelName = v; },\n get imageApiBase() { return imageApiBase; },\n set imageApiBase(v) { imageApiBase = v; },\n get imageApiKey() { return imageApiKey; },\n set imageApiKey(v) { imageApiKey = v; },\n get attachedImages() { return attachedImages; },\n set attachedImages(v) { attachedImages = v; },\n get enableFileInput() { return enableFileInput; },\n set enableFileInput(v) { enableFileInput = v; },\n get attachedFiles() { return attachedFiles; },\n set attachedFiles(v) { attachedFiles = v; },\n get agentVersions() { return agentVersions; },\n set agentVersions(v) { agentVersions = v; },\n get agentWorkdirs() { return agentWorkdirs; },\n set agentWorkdirs(v) { agentWorkdirs = v; },\n get agentHomeDirs() { return agentHomeDirs; },\n set agentHomeDirs(v) { agentHomeDirs = v; },\n get agentSystemInfos() { return agentSystemInfos; },\n set agentSystemInfos(v) { agentSystemInfos = v; },\n get isScrolling() { return isScrolling; },\n set isScrolling(v) { isScrolling = v; },\n get isComposing() { return isComposing; },\n set isComposing(v) { isComposing = v; },\n get customModelMap() { return customModelMap; },\n set customModelMap(v) { customModelMap = v; },\n};\n","// storage/session-store.js - 会话存储适配器\n// 对接 IndexedDB,提供与旧 chrome.storage 兼容的 API\n// 同时保留 chrome.storage.local 兜底(双写 + 读降级)\n\nimport * as idb from './db.js';\nimport state from '../side_panel/state.js';\nimport logger from '../shared/logger.js';\nimport { t, registerTranslations } from '../shared/i18n.js';\n\nregisterTranslations('zh', {\n session: {\n newSession: '新会话',\n importedSession: '导入的会话',\n unnamedSession: '未命名会话',\n restoredSession: '恢复的会话',\n forkSuffix: '分叉',\n sourceNotFound: '源会话不存在',\n scheduledSession: '定时任务',\n },\n});\nregisterTranslations('en', {\n session: {\n newSession: 'New session',\n importedSession: 'Imported session',\n unnamedSession: 'Unnamed session',\n restoredSession: 'Restored session',\n forkSuffix: 'Fork',\n sourceNotFound: 'Source session not found',\n scheduledSession: 'Scheduled task',\n },\n});\n\n// 会话删除时需要同步清理对应的 ReAct checkpoint\nasync function cleanupCheckpointForSession(sessionId) {\n try {\n await idb.deleteReactCheckpoint(sessionId);\n } catch (e) {\n logger.warn('[session-store] cleanup ReAct checkpoint failed:', e);\n }\n}\n\nlet migrated = false;\n\n/**\n * 初始化:尝试从 chrome.storage 迁移数据\n */\nexport async function init() {\n if (migrated) return;\n await idb.ensureMigration();\n migrated = true;\n\n // 清理孤儿 scrollPosition key(会话已被删除但 key 残留)\n cleanupOrphanScrollPositions();\n}\n\n/**\n * 清理 chrome.storage.local 中无对应会话的 scrollPosition_* key\n */\nasync function cleanupOrphanScrollPositions() {\n try {\n const sessions = await idb.getAllSessions();\n const validIds = new Set(sessions.map(s => s.id));\n // 'default' 是 fallback key,始终保留\n validIds.add('default');\n\n const allItems = await chrome.storage.local.get(null);\n const orphanKeys = Object.keys(allItems).filter(\n k => k.startsWith('scrollPosition_') && !validIds.has(k.slice('scrollPosition_'.length))\n );\n\n if (orphanKeys.length > 0) {\n await chrome.storage.local.remove(orphanKeys);\n logger.debug(`[session-store] cleanup ${orphanKeys.length} orphan scrollPosition key:`, orphanKeys);\n }\n } catch (e) {\n // 非关键路径,静默失败\n logger.warn('[session-store] cleanup orphaned scrollPosition failed:', e);\n }\n}\n\n/**\n * 加载所有会话(兼容旧 API 返回格式)\n * @returns {Promise<{activeSessionId: string|null, list: Array}>}\n */\nexport async function loadSessions() {\n await init();\n const [list, activeId] = await Promise.all([\n idb.getAllSessions(),\n idb.getActiveSessionId(),\n ]);\n // 按 order 排序,没有 order 的按 createdAt 兜底\n list.sort((a, b) => {\n if (a.order !== undefined && b.order !== undefined) return a.order - b.order;\n if (a.order !== undefined) return -1;\n if (b.order !== undefined) return 1;\n return new Date(a.createdAt) - new Date(b.createdAt);\n });\n return { activeSessionId: activeId, list };\n}\n\n/**\n * 保存所有活跃会话到存储\n * @param {{activeSessionId: string|null, list: Array}} sessionsData\n */\nexport async function saveSessions(sessionsData) {\n await init();\n const { list, activeSessionId } = sessionsData;\n await Promise.all([\n idb.putSessions(list),\n idb.setActiveSessionId(activeSessionId),\n ]);\n return true;\n}\n\n/**\n * 从消息 content 中提取纯文本(content 可能是数组格式含图片)\n */\nfunction getTextContent(content) {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n return content.filter(c => c.type === 'text').map(c => c.text).join('');\n }\n return '';\n}\n\n/**\n * 保存当前会话状态\n */\nexport async function saveCurrentSession() {\n if (!state.activeSessionId) return false;\n\n const currentSession = await idb.getSession(state.activeSessionId);\n if (!currentSession) {\n logger.warn('[SessionStore] saveCurrentSession cannot find currentsession:', state.activeSessionId);\n return false;\n }\n\n // 更新当前会话数据\n currentSession.model = state.currentModel;\n currentSession.useTools = state.useTools;\n currentSession.enabledTools = [...state.enabledTools];\n currentSession.agentId = state.activeAgentId || null;\n currentSession.temperature = state.temperature;\n currentSession.topP = state.topP;\n // 上下文压缩记录(会话级;undefined 时 JSON 序列化自动省略字段)\n currentSession.contextCompaction = state.activeCompaction || undefined;\n\n currentSession.messageHistory = state.messageHistory.map((msg) => ({\n role: msg.role,\n content: msg.content || '',\n executionLog: msg.executionLog || [],\n reflectionScore: msg.reflectionScore,\n wasRevised: msg.wasRevised || false,\n htmlContent: msg.htmlContent || undefined,\n messageId: msg.messageId || undefined,\n timestamp: msg.timestamp || undefined,\n contextBubbles: msg.contextBubbles || undefined,\n resumable: msg.resumable || false,\n }));\n\n currentSession.updatedAt = new Date().toISOString();\n\n // 更新标题(取第一条用户消息的前 50 个字符)\n // 分叉/复制的会话保留其自定义标题(含\"- 分叉\"/\"- 副本\"后缀),不自动覆盖\n if (!currentSession.forkMetadata) {\n const firstUserMsg = state.messageHistory.find((m) => m.role === 'user');\n if (firstUserMsg) {\n currentSession.title = getTextContent(firstUserMsg.content).substring(0, 50).replace(/\\n/g, ' ');\n }\n }\n\n const saved = await idb.putSession(currentSession);\n if (!saved) {\n logger.error('[SessionStore] putSession failed, sessionId:', state.activeSessionId);\n return false;\n }\n return true;\n}\n\n/**\n * 创建一个新会话\n * @returns {Promise<Object>} 新会话对象\n */\nexport async function createSession() {\n await init();\n const sessionId = generateSessionId();\n const newSession = {\n id: sessionId,\n title: t('session.newSession'),\n model: state.currentModel,\n useTools: state.useTools,\n enabledTools: [...state.enabledTools],\n agentId: state.activeAgentId || null,\n temperature: state.temperature,\n topP: state.topP,\n messageHistory: [],\n scrollPosition: 0,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n order: Date.now(),\n isGenerating: false,\n lastExecutionLog: [],\n };\n\n await idb.putSession(newSession);\n await idb.setActiveSessionId(sessionId);\n return newSession;\n}\n\n/**\n * 从导出的会话数据导入为新的活跃会话\n * @param {Array} sessionsData - 导出的会话数据数组(不含 id/order,导入时重新生成)\n * @returns {Promise<Array>} 新创建的会话数组\n */\nexport async function importSessions(sessionsData) {\n await init();\n const createdSessions = [];\n\n for (const sessionData of sessionsData) {\n const sessionId = generateSessionId();\n const newSession = {\n id: sessionId,\n title: sessionData.title || t('session.importedSession'),\n model: sessionData.model || state.currentModel,\n useTools: sessionData.useTools !== undefined ? sessionData.useTools : true,\n enabledTools: sessionData.enabledTools || [...state.enabledTools],\n temperature: sessionData.temperature !== undefined ? sessionData.temperature : state.temperature,\n topP: sessionData.topP !== undefined ? sessionData.topP : state.topP,\n agentId: sessionData.agentId || null,\n messageHistory: (sessionData.messageHistory || []).map((msg) => ({\n role: msg.role,\n content: msg.content || '',\n executionLog: msg.executionLog || [],\n reflectionScore: msg.reflectionScore,\n wasRevised: msg.wasRevised || false,\n htmlContent: msg.htmlContent || undefined,\n messageId: msg.messageId || undefined,\n timestamp: msg.timestamp || undefined,\n })),\n scrollPosition: 0,\n createdAt: sessionData.createdAt || new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n order: Date.now() + createdSessions.length,\n isGenerating: false,\n lastExecutionLog: [],\n };\n\n await idb.putSession(newSession);\n createdSessions.push(newSession);\n }\n\n // 如果当前没有活跃会话,自动切换到第一个导入的会话\n const activeId = await idb.getActiveSessionId();\n if (!activeId && createdSessions.length > 0) {\n await idb.setActiveSessionId(createdSessions[0].id);\n }\n\n return createdSessions;\n}\n\n/**\n * 将一条消息追加到指定会话的消息历史中\n * 用于切换会话后,后台任务仍在执行时保存任务结果\n * @param {string} sessionId 目标会话 ID\n * @param {Object} message 消息对象 { role, content, executionLog }\n */\nexport async function appendMessageToSession(sessionId, message) {\n const session = await idb.getSession(sessionId);\n if (!session) return false;\n\n session.messageHistory = session.messageHistory || [];\n session.messageHistory.push({\n role: message.role,\n content: message.content || '',\n executionLog: message.executionLog || [],\n reflectionScore: message.reflectionScore,\n wasRevised: message.wasRevised || false,\n htmlContent: message.htmlContent || undefined,\n messageId: message.messageId || undefined,\n timestamp: message.timestamp || undefined,\n contextBubbles: message.contextBubbles || undefined,\n });\n\n session.updatedAt = new Date().toISOString();\n session.isGenerating = false;\n\n await idb.putSession(session);\n return true;\n}\n\n/**\n * 生成唯一会话 ID\n */\nfunction generateSessionId() {\n return 'sess_' + Date.now().toString(36) + '_' + Math.random().toString(36).substring(2, 8);\n}\n\n/**\n * 切换到指定会话\n * @returns {Promise<Object|false>}\n */\nexport async function switchToSession(sessionId) {\n if (sessionId === state.activeSessionId) return;\n\n // 并发化:保存当前会话与读取目标会话互相独立,同时发起。\n // 红线保留:saveCurrentSession 仍是第一个动作(读旧 state 保存),\n // 且所有 state 赋值都在 Promise.all 完成后执行;默认 Agent 的全局模型\n // 配置读取也无副作用,一并提前发起(结果仍在后文分支中应用)。\n const savePromise = saveCurrentSession();\n const globalConfigPromise = chrome.storage.local.get(['modelName', 'temperature', 'topP']).catch(() => ({}));\n\n const [targetSession] = await Promise.all([idb.getSession(sessionId), savePromise]);\n if (!targetSession) {\n logger.error('[SessionStore] find not to session:', sessionId);\n return false;\n }\n\n await idb.setActiveSessionId(sessionId);\n\n state.activeSessionId = sessionId;\n state.messageHistory = targetSession.messageHistory || [];\n // 恢复会话级上下文压缩记录(无则置 null)\n state.activeCompaction = targetSession.contextCompaction || null;\n // 会话切换时从会话恢复 model/temperature\n // 如果会话绑定了 Agent,优先使用 Agent 配置;否则使用会话存储的值\n state.currentModel = targetSession.model || state.currentModel;\n state.temperature = targetSession.temperature !== undefined ? targetSession.temperature : state.temperature;\n state.topP = targetSession.topP !== undefined ? targetSession.topP : state.topP;\n state.useTools = targetSession.useTools !== undefined ? targetSession.useTools : state.useTools;\n state.activeAgentId = targetSession.agentId || null;\n // 持久化当前智能体 ID,避免刷新后丢失\n if (targetSession.agentId) {\n chrome.storage.local.set({ activeAssistantId: targetSession.agentId });\n } else {\n chrome.storage.local.remove('activeAssistantId');\n }\n\n // 如果会话绑定了自定义 Agent,尝试从 Agent 配置中加载模型/温度\n // 注意:自定义 Agent 的参数只更新 state,不写入 chrome.storage.local 全局键,\n // 避免覆盖默认 Agent 的全局模型/温度值\n if (targetSession.agentId && targetSession.agentId !== 'default') {\n try {\n const { getAgent } = await import('../side_panel/agent-store.js');\n const agent = await getAgent(targetSession.agentId);\n if (agent) {\n if (agent.model) {\n state.currentModel = agent.model;\n }\n if (agent.temperature !== null && agent.temperature !== undefined) {\n state.temperature = agent.temperature;\n state.topP = agent.topP !== null && agent.topP !== undefined ? agent.topP : 1.0;\n }\n // 触发 UI 更新\n document.dispatchEvent(new CustomEvent('agent-model-changed'));\n }\n } catch { /* Agent 加载失败,使用会话存储值 */ }\n } else {\n // 默认 Agent:从 chrome.storage.local 读取全局模型/温度(所有默认 Agent 会话共享)\n // 注:该读取已随切换流程并行发起(见函数开头),此处仅应用结果\n const global = await globalConfigPromise;\n if (global.modelName) state.currentModel = global.modelName;\n if (global.temperature !== undefined) state.temperature = global.temperature;\n if (global.topP !== undefined) state.topP = global.topP;\n // 触发 UI 更新,确保弹窗 slider/输入框与图标一致\n document.dispatchEvent(new CustomEvent('agent-model-changed'));\n }\n // 使用按会话隔离的 generatingSessionIds Set 恢复生成状态\n // 仅当 pendingCallApiSessionIds 中也存在该 session 时才恢复,防止 SW 重启后\n // DB 中残留的 isGenerating=true 导致虚假的\"生成中\"状态\n if (targetSession.isGenerating && state.pendingCallApiSessionIds.has(sessionId)) {\n state.generatingSessionIds.add(sessionId);\n } else if (targetSession.isGenerating) {\n // DB 中有生成标记但实际无后台任务,异步清理过期状态(不阻塞切换)\n logger.debug('[SessionStore] detected expiredperiod isGenerating mark,async cleanup:', sessionId);\n (async () => {\n try {\n const session = await idb.getSession(sessionId);\n if (session) {\n session.isGenerating = false;\n await idb.putSession(session);\n }\n } catch (e) {\n logger.warn('[SessionStore] cleanup expired isGenerating mark failed:', e);\n }\n })();\n }\n\n return targetSession;\n}\n\n/**\n * 删除指定会话\n */\nexport async function deleteStoreSession(sessionId) {\n const allSessions = await idb.getAllSessions();\n const activeId = await idb.getActiveSessionId();\n\n await idb.deleteSession(sessionId);\n\n // 清理该会话的 scrollPosition 存储,防止泄露\n chrome.storage.local.remove('scrollPosition_' + sessionId);\n\n // 同步清理该会话的 ReAct checkpoint\n await cleanupCheckpointForSession(sessionId);\n\n // 清理该会话的收藏\n try {\n const allBookmarks = await idb.getAllBookmarks();\n const toDelete = allBookmarks.filter(b => b.sessionId === sessionId);\n for (const bm of toDelete) {\n await idb.deleteBookmark(bm.id);\n }\n if (toDelete.length > 0) {\n logger.info(`[SessionStore] cleaned ${toDelete.length} bookmark (session ${sessionId} deleted)`);\n }\n } catch (e) {\n logger.warn('[SessionStore] cleanup bookmarks failed:', e);\n }\n\n // 如果删除的是当前活跃会话,就近激活:优先右侧紧邻,无右侧则左侧紧邻\n if (activeId === sessionId) {\n // 按显示顺序排序(与 loadSessions 保持一致)\n const sorted = [...allSessions].sort((a, b) => {\n if (a.order !== undefined && b.order !== undefined) return a.order - b.order;\n if (a.order !== undefined) return -1;\n if (b.order !== undefined) return 1;\n return new Date(a.createdAt) - new Date(b.createdAt);\n });\n const deletedIndex = sorted.findIndex(s => s.id === sessionId);\n const remaining = sorted.filter((s) => s.id !== sessionId);\n\n if (remaining.length > 0) {\n // 就近激活:取同位置(右侧邻居),若已超出则取最后一个(左侧邻居)\n const fallbackIndex = Math.min(deletedIndex, remaining.length - 1);\n await idb.setActiveSessionId(remaining[fallbackIndex].id);\n } else {\n await idb.setActiveSessionId(null);\n }\n }\n\n return true;\n}\n\n/**\n * 重命名会话\n */\nexport async function renameSession(sessionId, newTitle) {\n const session = await idb.getSession(sessionId);\n if (!session) return false;\n session.title = newTitle;\n session.updatedAt = new Date().toISOString();\n await idb.putSession(session);\n return true;\n}\n\n/**\n * 重新排序会话\n * @param {string[]} orderedIds - 按新顺序排列的会话 ID 列表\n */\nexport async function reorderSessions(orderedIds) {\n const allSessions = await idb.getAllSessions();\n const sessionMap = new Map(allSessions.map(s => [s.id, s]));\n\n const updates = [];\n orderedIds.forEach((id, index) => {\n const session = sessionMap.get(id);\n if (session) {\n session.order = index;\n updates.push(idb.putSession(session));\n }\n });\n\n await Promise.all(updates);\n return true;\n}\n\n/**\n * 归档当前会话\n */\nexport async function archiveCurrentSession() {\n if (!state.messageHistory || state.messageHistory.length === 0) return;\n\n const currentSession = await idb.getSession(state.activeSessionId);\n if (!currentSession) return;\n\n const firstUserMsg = state.messageHistory.find((m) => m.role === 'user');\n const title = firstUserMsg\n ? getTextContent(firstUserMsg.content).substring(0, 50).replace(/\\n/g, ' ')\n : currentSession.title || t('session.unnamedSession');\n\n const archivedId = Date.now().toString(36) + Math.random().toString(36).substring(2, 8);\n\n const sanitizedMessages = state.messageHistory.map((msg) => ({\n role: msg.role,\n content: msg.content || '',\n executionLog: msg.executionLog || [],\n reflectionScore: msg.reflectionScore,\n wasRevised: msg.wasRevised || false,\n htmlContent: msg.htmlContent || undefined,\n }));\n\n // 保存到归档\n const archiveSessions = await idb.getAllArchivedSessions();\n archiveSessions.push({\n id: archivedId,\n title,\n createdAt: new Date().toISOString(),\n messages: sanitizedMessages,\n });\n\n // 最多保留 20 个归档\n if (archiveSessions.length > 20) {\n archiveSessions.splice(0, archiveSessions.length - 20);\n }\n\n await idb.replaceArchivedSessions(archiveSessions);\n\n // 从活跃会话列表中移除,保留一个空的新会话\n const newEmptySession = await createSession();\n await deleteStoreSession(currentSession.id);\n await switchToSession(newEmptySession.id);\n\n return true;\n}\n\n/**\n * 恢复归档会话为活跃会话\n */\nexport async function restoreArchivedSession(archivedId) {\n const archiveSessions = await idb.getAllArchivedSessions();\n const archived = archiveSessions.find((s) => s.id === archivedId);\n\n if (!archived) return false;\n\n // 创建新活跃会话\n const sessionId = generateSessionId();\n const newSession = {\n id: sessionId,\n title: archived.title || t('session.restoredSession'),\n model: state.currentModel,\n useTools: state.useTools,\n enabledTools: [...state.enabledTools],\n temperature: state.temperature,\n topP: state.topP,\n messageHistory: archived.messages || [],\n scrollPosition: 0,\n createdAt: archived.createdAt || new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n isGenerating: false,\n lastExecutionLog: [],\n };\n\n await idb.putSession(newSession);\n await idb.setActiveSessionId(sessionId);\n\n // 从归档中移除\n const archiveIdx = archiveSessions.findIndex((s) => s.id === archivedId);\n if (archiveIdx !== -1) {\n archiveSessions.splice(archiveIdx, 1);\n }\n await idb.replaceArchivedSessions(archiveSessions);\n\n return newSession;\n}\n\n/**\n * 复制会话(完整快照或消息级截断,作为对话分支使用)\n * 完整继承源会话的消息历史与配置(模型/工具/Agent/温度等)\n * @param {string} sourceSessionId - 源会话 ID\n * @param {string|null} [upToMessageId=null] - 消息级分叉:截断到此 messageId(含)为止\n * 传入时仅复制到该消息(含),不传则完整复制全部历史\n * @returns {Promise<Object>} 新创建的会话\n */\nexport async function duplicateSession(sourceSessionId, upToMessageId = null) {\n await init();\n\n const source = await idb.getSession(sourceSessionId);\n if (!source) throw new Error(t('session.sourceNotFound'));\n\n const newSessionId = generateSessionId();\n const now = new Date().toISOString();\n\n // 消息级分叉:截断到指定 messageId(含);找不到则回退完整复制\n let srcMessages = source.messageHistory || [];\n let isMessageFork = false;\n if (upToMessageId) {\n const idx = srcMessages.findIndex(m => m.messageId === upToMessageId);\n if (idx >= 0) {\n srcMessages = srcMessages.slice(0, idx + 1); // 含该消息\n isMessageFork = true;\n }\n }\n\n // 完整复制消息历史,深拷贝避免引用共享;重新生成 messageId 避免冲突\n const clonedMessages = srcMessages.map((msg) => ({\n ...msg,\n executionLog: Array.isArray(msg.executionLog) ? [...msg.executionLog] : [],\n contextBubbles: Array.isArray(msg.contextBubbles) ? [...msg.contextBubbles] : undefined,\n // 重新生成 messageId(遵循项目约定:所有消息必须有唯一 messageId)\n messageId: 'msg_' + Date.now().toString(36) + '_' + Math.random().toString(36).substring(2, 8),\n }));\n\n const newSession = {\n ...source,\n id: newSessionId,\n title: `${source.title || t('session.newSession')} - ${t('session.forkSuffix')}`,\n messageHistory: clonedMessages,\n // 重置运行时状态\n isGenerating: false,\n lastExecutionLog: [],\n scrollPosition: 0,\n createdAt: now,\n updatedAt: now,\n order: Date.now(),\n // 分支元数据:记录来源与分叉点\n forkMetadata: {\n sourceSessionId,\n forkedAt: now,\n ...(isMessageFork ? { upToMessageId } : {}),\n },\n };\n\n await idb.putSession(newSession);\n await idb.setActiveSessionId(newSessionId);\n\n return newSession;\n}\n\n/**\n * 为定时任务创建专属宿主会话(当任务绑定的会话被删除时自愈使用)\n * 与普通会话一致,仅额外标记 scheduledTaskId,便于识别 与 后续清理\n * @param {Object} task - 定时任务对象\n * @returns {Promise<Object>} 新会话对象\n */\nexport async function createScheduledSession(task) {\n await init();\n const sessionId = generateSessionId();\n const newSession = {\n id: sessionId,\n title: task.name || t('session.scheduledSession'),\n // 注意:定时任务自建会话不写入占位配置,null 表示\"执行时继承全局\",\n // 避免 SW 环境下的默认值(state 占位)覆盖真实全局配置\n model: task.model || null,\n useTools: task.useTools != null ? task.useTools : null,\n enabledTools: null,\n agentId: task.agentId || null,\n temperature: task.temperature != null ? task.temperature : null,\n topP: task.topP != null ? task.topP : null,\n messageHistory: [],\n scrollPosition: 0,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n order: Date.now(),\n isGenerating: false,\n lastExecutionLog: [],\n scheduledTaskId: task.id,\n };\n\n await idb.putSession(newSession);\n return newSession;\n}\n"],"file":"session-store-CzVQCgQ_.js"}