chatlab-cli 0.29.0 → 0.30.0

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 (101) hide show
  1. package/dist/{chat-command-JKG3FY5B.mjs → chat-command-UZ23GKO7.mjs} +8 -8
  2. package/dist/{chunk-KL2NGIMQ.mjs → chunk-4IZ3WKYU.mjs} +429 -272
  3. package/dist/chunk-4IZ3WKYU.mjs.map +1 -0
  4. package/dist/{chunk-CIB23U32.mjs → chunk-5UBHNQFZ.mjs} +101 -41
  5. package/dist/chunk-5UBHNQFZ.mjs.map +1 -0
  6. package/dist/{chunk-WPI6YSAR.mjs → chunk-7BZLS2KW.mjs} +4114 -3962
  7. package/dist/chunk-7BZLS2KW.mjs.map +1 -0
  8. package/dist/{chunk-QQK7MLCR.mjs → chunk-A2SANKG4.mjs} +121 -32
  9. package/dist/chunk-A2SANKG4.mjs.map +1 -0
  10. package/dist/{chunk-D4WTE7LV.mjs → chunk-CNSYXRJP.mjs} +2 -2
  11. package/dist/{chunk-I35WHWA6.mjs → chunk-FCCL5Z4W.mjs} +3 -3
  12. package/dist/chunk-M55KW2HA.mjs +2323 -0
  13. package/dist/chunk-M55KW2HA.mjs.map +1 -0
  14. package/dist/{chunk-2MZ6G4AC.mjs → chunk-MUPILWAZ.mjs} +2 -2
  15. package/dist/{chunk-7RW4CQAO.mjs → chunk-P3ZOLTGS.mjs} +3 -3
  16. package/dist/{chunk-6FPEP2ZX.mjs → chunk-RDR6PS2R.mjs} +2 -2
  17. package/dist/{chunk-SKUG5XVD.mjs → chunk-ZFU2CHW3.mjs} +5 -4
  18. package/dist/cli.mjs +7 -7
  19. package/dist/contacts-worker.mjs +2 -2
  20. package/dist/{http-5E7ARYFW.mjs → http-UWRA5FLB.mjs} +11 -11
  21. package/dist/{import-3KG6VK6O.mjs → import-745KQL7N.mjs} +7 -7
  22. package/dist/index.mjs +11 -11
  23. package/dist/{mcp-JBI4CUQT.mjs → mcp-UHN5JOHC.mjs} +8 -8
  24. package/dist/people-relationships-worker.mjs +2 -2
  25. package/dist/semantic-index-worker.mjs +1 -1
  26. package/dist/{src-UTBQAOJ3.mjs → src-Y7MDRXLJ.mjs} +12 -6
  27. package/dist/{stream-import-7HZ4IRYJ.mjs → stream-import-3JMXQD7P.mjs} +7 -7
  28. package/dist/{worker-runtime-ALS4MR2A.mjs → worker-runtime-63RYMGZI.mjs} +5 -5
  29. package/dist-web/assets/{BufferResource-MsqP5sq3.js → BufferResource-BVdJ_I5X.js} +1 -1
  30. package/dist-web/assets/CanvasRenderer-BaRuT54R.js +1 -0
  31. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-N5CoiJzN.js +110 -0
  32. package/dist-web/assets/{Filter-wRJtdXx9.js → Filter-B7cNFtuE.js} +1 -1
  33. package/dist-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-D_dJuLWJ.js +1 -0
  34. package/dist-web/assets/{MessageExportModal-BCrMf5Kw.js → MessageExportModal-B2Qm6VVj.js} +1 -1
  35. package/dist-web/assets/{PageHeader-C_yw54le.js → PageHeader-PjlWy9fC.js} +1 -1
  36. package/dist-web/assets/{RelationshipGalaxyCanvas-DTzJq7q7.js → RelationshipGalaxyCanvas-11MafciW.js} +3 -3
  37. package/dist-web/assets/{RelationshipGalaxyThreeCanvas-DIE-0_iM.js → RelationshipGalaxyThreeCanvas-C1I4B8NX.js} +1 -1
  38. package/dist-web/assets/{RenderTargetSystem-BSAQFamI.js → RenderTargetSystem-Chkz1WlI.js} +1 -1
  39. package/dist-web/assets/{WebGLRenderer-lfzmCZNT.js → WebGLRenderer-xK19ru58.js} +1 -1
  40. package/dist-web/assets/{WebGPURenderer-BnEdfdr_.js → WebGPURenderer-CL5ZaKsn.js} +1 -1
  41. package/dist-web/assets/{browserAll-gRNfDyPu.js → browserAll-CyyNzRD_.js} +1 -1
  42. package/dist-web/assets/electron-MXaTKaMu.js +1 -0
  43. package/dist-web/assets/{fetch-BW3aO7PS.js → fetch-D1dDiFO4.js} +1 -1
  44. package/dist-web/assets/{fetch-DPCJ-1l-.js → fetch-DDnVXGte.js} +1 -1
  45. package/dist-web/assets/{fetch-C1fI_KOX.js → fetch-DTwdRTXM.js} +1 -1
  46. package/dist-web/assets/{fetch-CQt-gb-f.js → fetch-DaaJwT-5.js} +1 -1
  47. package/dist-web/assets/{fetch-gMB4wuBd.js → fetch-DeA0ztwT.js} +1 -1
  48. package/dist-web/assets/fetch-DpnIGZSN.js +17 -0
  49. package/dist-web/assets/{fetch-CFRoSIAn.js → fetch-DsE-xcP2.js} +1 -1
  50. package/dist-web/assets/{fetch-mFlEBZyB.js → fetch-DsTgYu_X.js} +1 -1
  51. package/dist-web/assets/{fetch-CN91TIwV.js → fetch-hKE53UO4.js} +1 -1
  52. package/dist-web/assets/{fetch-BwF5hNuF.js → fetch-u9GTMhMF.js} +1 -1
  53. package/dist-web/assets/index-BVkE4qis.js +22 -0
  54. package/dist-web/assets/index-BcXLcFnV.css +1 -0
  55. package/dist-web/assets/{index-Bg4GGPXv.js → index-BnHKyESZ.js} +1 -1
  56. package/dist-web/assets/index-Btr5H7d-.js +2 -0
  57. package/dist-web/assets/{index-MBRD3wPd.css → index-C1p9CbmN.css} +1 -1
  58. package/dist-web/assets/index-COpza1A4.css +1 -0
  59. package/dist-web/assets/index-D04LWJop.js +173 -0
  60. package/dist-web/assets/index-DFCYmzGE.js +2 -0
  61. package/dist-web/assets/{index-DveHdPfK.js → index-DqXHRIhI.js} +1 -1
  62. package/dist-web/assets/{index-ZdCB7xcf.js → index-DqtXt3wl.js} +4 -4
  63. package/dist-web/assets/index-Dszv-QFp.js +64 -0
  64. package/dist-web/assets/index-DzLrWGXu.js +1 -0
  65. package/dist-web/assets/{vendor-nuxt-ui-Dr2T8_OZ.js → vendor-nuxt-ui-DaLxYaC4.js} +1 -1
  66. package/dist-web/assets/{web-BakeV58u.js → web-DPVWfPS-.js} +1 -1
  67. package/dist-web/assets/{webworkerAll-D_6PgURS.js → webworkerAll-DF5QV_vN.js} +1 -1
  68. package/dist-web/index.html +3 -3
  69. package/package.json +1 -1
  70. package/dist/chunk-ARZM6HBX.mjs +0 -542
  71. package/dist/chunk-ARZM6HBX.mjs.map +0 -1
  72. package/dist/chunk-CIB23U32.mjs.map +0 -1
  73. package/dist/chunk-KL2NGIMQ.mjs.map +0 -1
  74. package/dist/chunk-QQK7MLCR.mjs.map +0 -1
  75. package/dist/chunk-WPI6YSAR.mjs.map +0 -1
  76. package/dist-web/assets/CanvasRenderer-BKCvcc2p.js +0 -1
  77. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-HGJb0Et1.js +0 -110
  78. package/dist-web/assets/electron-BHaEaaZP.js +0 -1
  79. package/dist-web/assets/fetch-CBZbgZqk.js +0 -17
  80. package/dist-web/assets/index--6C0sK6a.css +0 -1
  81. package/dist-web/assets/index-B6wEGata.js +0 -173
  82. package/dist-web/assets/index-BgVbj5qX.js +0 -12
  83. package/dist-web/assets/index-BkoXaBV_.js +0 -64
  84. package/dist-web/assets/index-C56IuV5U.css +0 -1
  85. package/dist-web/assets/index-CLRaKwpT.js +0 -2
  86. package/dist-web/assets/index-Dj1BSD7O.js +0 -1
  87. package/dist-web/assets/index-RoDgJcHU.js +0 -2
  88. package/dist-web/assets/index-aljYO5ms.js +0 -1
  89. /package/dist/{chat-command-JKG3FY5B.mjs.map → chat-command-UZ23GKO7.mjs.map} +0 -0
  90. /package/dist/{chunk-D4WTE7LV.mjs.map → chunk-CNSYXRJP.mjs.map} +0 -0
  91. /package/dist/{chunk-I35WHWA6.mjs.map → chunk-FCCL5Z4W.mjs.map} +0 -0
  92. /package/dist/{chunk-2MZ6G4AC.mjs.map → chunk-MUPILWAZ.mjs.map} +0 -0
  93. /package/dist/{chunk-7RW4CQAO.mjs.map → chunk-P3ZOLTGS.mjs.map} +0 -0
  94. /package/dist/{chunk-6FPEP2ZX.mjs.map → chunk-RDR6PS2R.mjs.map} +0 -0
  95. /package/dist/{chunk-SKUG5XVD.mjs.map → chunk-ZFU2CHW3.mjs.map} +0 -0
  96. /package/dist/{http-5E7ARYFW.mjs.map → http-UWRA5FLB.mjs.map} +0 -0
  97. /package/dist/{import-3KG6VK6O.mjs.map → import-745KQL7N.mjs.map} +0 -0
  98. /package/dist/{mcp-JBI4CUQT.mjs.map → mcp-UHN5JOHC.mjs.map} +0 -0
  99. /package/dist/{src-UTBQAOJ3.mjs.map → src-Y7MDRXLJ.mjs.map} +0 -0
  100. /package/dist/{stream-import-7HZ4IRYJ.mjs.map → stream-import-3JMXQD7P.mjs.map} +0 -0
  101. /package/dist/{worker-runtime-ALS4MR2A.mjs.map → worker-runtime-63RYMGZI.mjs.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../packages/config/src/schema.ts","../../../packages/config/src/loader.ts","../../../packages/config/src/auth-profiles.ts","../../../packages/config/src/migrations/runner.ts","../../../packages/config/src/migrations/m001-legacy-to-multi-config.ts","../../../packages/config/src/migrations/m002-schema-v2.ts","../../../packages/config/src/migrations/m003-schema-v3.ts","../../../packages/config/src/migrations/m004-encrypted-keys-to-auth-profiles.ts","../../../packages/config/src/migrations/crypto-legacy.ts","../../../packages/config/src/migrations/index.ts","../../../packages/node-runtime/src/better-sqlite3-adapter.ts","../../../packages/core/src/query/filters.ts","../../../packages/core/src/query/basic-queries.ts","../../../packages/core/src/query/session-queries.ts","../../../packages/core/src/query/message-sql.ts","../../../packages/core/src/query/message-queries.ts","../../../packages/core/src/query/advanced/social.ts","../../../packages/core/src/query/contact-queries.ts","../../../packages/core/src/query/contact-scoring.ts","../../../packages/core/src/query/member-ops.ts","../../../packages/core/src/query/advanced/text-filters.ts","../../../packages/core/src/query/advanced/repeat.ts","../../../packages/core/src/query/advanced/relationship.ts","../../../packages/core/src/query/advanced/languagePreference.ts","../../../packages/core/src/query/advanced/ranking.ts","../../../packages/core/src/ai/tool-catalog.ts","../../../packages/core/src/ai/provider-registry.ts","../../../packages/core/src/ai/model-catalog.ts","../../../packages/core/src/ai/content-parser.ts","../../../packages/core/src/ai/streaming-think-parser.ts","../../../packages/core/src/ai/thinking.ts","../../../packages/core/src/ai/tool-result-text.ts","../../../packages/core/src/version.ts","../../../packages/core/src/chart/index.ts","../../../packages/core/src/nlp/pos-tags.ts","../../../packages/core/src/nlp/stopwords.ts","../../../packages/core/src/nlp/text-utils.ts","../../../packages/core/src/owner/owner-matching.ts","../../../packages/core/src/import/dedup.ts","../../../packages/core/src/merger/algorithms.ts","../../../packages/core/src/schema/tables.ts","../../../packages/core/src/schema/migrations.ts","../../../packages/node-runtime/src/nlp/segmenter.ts","../../../packages/node-runtime/src/nlp/fts-tokenizer.ts","../../../packages/node-runtime/src/ai/ai-logger.ts","../../../packages/node-runtime/src/logging/app-logger.ts","../../../packages/node-runtime/src/migrations/electron-data-migration.ts","../../../packages/node-runtime/src/cache/session-cache.ts","../../../packages/node-runtime/src/data-dir-compat.ts","../../../packages/node-runtime/src/migrations/chat-db-migrations.ts","../../../packages/node-runtime/src/services/contacts/paths.ts","../../../packages/node-runtime/src/services/people/relationships/paths.ts","../../../packages/node-runtime/src/database-manager.ts","../../../packages/node-runtime/src/cache/analytics-cache.ts","../../../packages/node-runtime/src/semantic-index/static-path-provider.ts","../../../packages/node-runtime/src/services/adapters/database-manager-adapter.ts","../../../packages/shared-types/index.ts"],"sourcesContent":["/**\n * 配置 Zod Schema 定义\n *\n * 同时服务于 TOML 文件校验和环境变量解析。\n */\n\nimport { z } from 'zod'\n\nexport const DEFAULT_API_PORT = 3110\n\nexport const llmConfigSchema = z.object({\n provider: z.string().default(''),\n model: z.string().default(''),\n base_url: z.string().default(''),\n})\n\nexport const dataConfigSchema = z.object({\n user_data_dir: z.string().default(''),\n electron_migration_done: z.boolean().default(false),\n})\n\nexport const apiConfigSchema = z.object({\n port: z.number().int().min(1).max(65535).default(DEFAULT_API_PORT),\n host: z.string().default('127.0.0.1'),\n token: z.string().default(''),\n require_auth: z.boolean().default(false),\n})\n\nexport const localeConfigSchema = z.object({\n lang: z.string().default(''),\n})\n\nexport const uiConfigSchema = z.object({\n default_session_tab: z.enum(['overview', 'ai-chat']).default('overview'),\n session_gap_threshold: z.number().int().min(60).max(86400).default(1800),\n summary_strategy: z.enum(['brief', 'standard']).default('standard'),\n})\n\nexport const configSchema = z.object({\n llm: llmConfigSchema.default({}),\n data: dataConfigSchema.default({}),\n api: apiConfigSchema.default({}),\n locale: localeConfigSchema.default({}),\n ui: uiConfigSchema.default({}),\n})\n\nexport type ChatLabConfig = z.infer<typeof configSchema>\nexport type LlmConfig = z.infer<typeof llmConfigSchema>\nexport type DataConfig = z.infer<typeof dataConfigSchema>\nexport type ApiConfig = z.infer<typeof apiConfigSchema>\nexport type LocaleConfig = z.infer<typeof localeConfigSchema>\nexport type UiConfig = z.infer<typeof uiConfigSchema>\n","/**\n * 配置加载器\n *\n * 优先级(从高到低):\n * 1. CHATLAB_* 环境变量\n * 2. ~/.chatlab/config.toml(或 config.json)\n * 3. Zod schema 默认值\n */\n\nimport * as fs from 'fs'\nimport * as os from 'os'\nimport * as path from 'path'\nimport { parse as parseToml } from 'smol-toml'\nimport { configSchema, type ChatLabConfig } from './schema'\n\nconst CONFIG_DIR = path.join(os.homedir(), '.chatlab')\nconst CONFIG_TOML = path.join(CONFIG_DIR, 'config.toml')\nconst CONFIG_JSON = path.join(CONFIG_DIR, 'config.json')\n\n/**\n * 加载完整配置\n */\nexport function loadConfig(overrides?: Partial<ChatLabConfig>): ChatLabConfig {\n const fileConfig = loadConfigFile()\n const envConfig = loadEnvConfig()\n\n const merged = deepMerge(deepMerge(fileConfig, envConfig), overrides ?? {})\n return configSchema.parse(merged)\n}\n\n/**\n * 获取配置文件路径(返回实际存在的路径或默认 TOML 路径)\n */\nexport function getConfigPath(): string {\n if (fs.existsSync(CONFIG_TOML)) return CONFIG_TOML\n if (fs.existsSync(CONFIG_JSON)) return CONFIG_JSON\n return CONFIG_TOML\n}\n\n/**\n * 获取配置目录路径\n */\nexport function getConfigDir(): string {\n return CONFIG_DIR\n}\n\n/**\n * 从文件加载配置\n */\nfunction loadConfigFile(): Record<string, unknown> {\n if (fs.existsSync(CONFIG_TOML)) {\n try {\n const content = fs.readFileSync(CONFIG_TOML, 'utf-8')\n return parseToml(content) as Record<string, unknown>\n } catch (err) {\n console.warn(`[Config] Failed to parse ${CONFIG_TOML}:`, err)\n }\n }\n\n if (fs.existsSync(CONFIG_JSON)) {\n try {\n const content = fs.readFileSync(CONFIG_JSON, 'utf-8')\n return JSON.parse(content) as Record<string, unknown>\n } catch (err) {\n console.warn(`[Config] Failed to parse ${CONFIG_JSON}:`, err)\n }\n }\n\n return {}\n}\n\n/**\n * 从 CHATLAB_* 环境变量加载配置\n *\n * 映射规则:\n * - CHATLAB_DATA_DIR -> data.user_data_dir\n * - CHATLAB_API_PORT -> api.port\n * - CHATLAB_API_HOST -> api.host\n * - CHATLAB_LLM_PROVIDER -> llm.provider\n * - CHATLAB_LLM_MODEL -> llm.model\n * - CHATLAB_LLM_BASE_URL -> llm.base_url\n * - CHATLAB_LOCALE_LANG -> locale.lang\n */\nfunction loadEnvConfig(): Record<string, unknown> {\n const result: Record<string, Record<string, unknown>> = {}\n\n const envMap: Array<{ env: string; section: string; key: string; transform?: (v: string) => unknown }> = [\n { env: 'CHATLAB_DATA_DIR', section: 'data', key: 'user_data_dir' },\n { env: 'CHATLAB_API_PORT', section: 'api', key: 'port', transform: (v) => parseInt(v, 10) },\n { env: 'CHATLAB_API_HOST', section: 'api', key: 'host' },\n { env: 'CHATLAB_LLM_PROVIDER', section: 'llm', key: 'provider' },\n { env: 'CHATLAB_LLM_MODEL', section: 'llm', key: 'model' },\n { env: 'CHATLAB_LLM_BASE_URL', section: 'llm', key: 'base_url' },\n { env: 'CHATLAB_LOCALE_LANG', section: 'locale', key: 'lang' },\n ]\n\n for (const { env, section, key, transform } of envMap) {\n const value = process.env[env]\n if (value !== undefined && value !== '') {\n if (!result[section]) result[section] = {}\n result[section][key] = transform ? transform(value) : value\n }\n }\n\n return result\n}\n\n/**\n * 写入 config.toml 的某个字段\n *\n * 如果文件不存在则创建,如果已存在则保留其他内容,只更新指定字段。\n * 采用简单的 TOML 序列化:仅支持一级 section + 字符串/数字值。\n */\nexport function writeConfigField(section: string, key: string, value: string | number | boolean): void {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true })\n }\n\n let existing: Record<string, Record<string, unknown>> = {}\n if (fs.existsSync(CONFIG_TOML)) {\n try {\n const content = fs.readFileSync(CONFIG_TOML, 'utf-8')\n existing = parseToml(content) as Record<string, Record<string, unknown>>\n } catch {\n // 解析失败时从空开始,旧文件会被覆盖\n }\n }\n\n if (!existing[section] || typeof existing[section] !== 'object') {\n existing[section] = {}\n }\n existing[section][key] = value\n\n const lines: string[] = []\n for (const [sec, entries] of Object.entries(existing)) {\n if (typeof entries !== 'object' || entries === null) continue\n lines.push(`[${sec}]`)\n for (const [k, v] of Object.entries(entries as Record<string, unknown>)) {\n if (typeof v === 'string') {\n lines.push(`${k} = ${JSON.stringify(v)}`)\n } else if (typeof v === 'number' || typeof v === 'boolean') {\n lines.push(`${k} = ${v}`)\n }\n }\n lines.push('')\n }\n\n fs.writeFileSync(CONFIG_TOML, lines.join('\\n'), 'utf-8')\n}\n\nfunction deepMerge(base: Record<string, unknown>, override: Record<string, unknown>): Record<string, unknown> {\n const result = { ...base }\n for (const [key, value] of Object.entries(override)) {\n if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n result[key] = { ...(result[key] as object), ...(value as object) }\n } else if (value !== undefined) {\n result[key] = value\n }\n }\n return result\n}\n","/**\n * auth-profiles.json 凭证管理\n *\n * 存储位置:~/.chatlab/auth-profiles.json\n * 用途:将 API Key 等敏感凭证与主配置文件分离\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { getConfigDir } from './loader'\n\nexport interface AuthProfile {\n type: 'api_key'\n provider: string\n key: string\n}\n\nexport interface AuthProfilesData {\n version: number\n profiles: Record<string, AuthProfile>\n}\n\nconst AUTH_PROFILES_FILE = 'auth-profiles.json'\n\nfunction getAuthProfilesPath(): string {\n return path.join(getConfigDir(), AUTH_PROFILES_FILE)\n}\n\n/**\n * 加载 auth-profiles.json\n */\nexport function loadAuthProfiles(): AuthProfilesData {\n const filePath = getAuthProfilesPath()\n if (!fs.existsSync(filePath)) {\n return { version: 1, profiles: {} }\n }\n\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const data = JSON.parse(content) as AuthProfilesData\n if (!data.profiles || typeof data.profiles !== 'object') {\n return { version: 1, profiles: {} }\n }\n return data\n } catch {\n return { version: 1, profiles: {} }\n }\n}\n\n/**\n * 按 profile 名称获取 API Key\n */\nexport function getApiKeyByProfile(profileName: string): string {\n const data = loadAuthProfiles()\n const profile = data.profiles[profileName]\n return profile?.key || ''\n}\n\n/**\n * 按 provider 名称获取 API Key(模糊匹配,取第一个匹配的 profile)\n */\nexport function getApiKeyByProvider(provider: string): string {\n const data = loadAuthProfiles()\n for (const profile of Object.values(data.profiles)) {\n if (profile.provider === provider) {\n return profile.key || ''\n }\n }\n return ''\n}\n\n/**\n * 查找 API Key:先按 authProfile 精确匹配,再按 provider 兜底\n */\nexport function resolveApiKey(provider: string, authProfile?: string): string {\n if (authProfile) {\n const key = getApiKeyByProfile(authProfile)\n if (key) return key\n }\n return getApiKeyByProvider(provider)\n}\n\n/**\n * 写入/更新一个 auth profile\n */\nexport function writeAuthProfile(name: string, profile: AuthProfile): void {\n const configDir = getConfigDir()\n if (!fs.existsSync(configDir)) {\n fs.mkdirSync(configDir, { recursive: true })\n }\n\n const data = loadAuthProfiles()\n data.profiles[name] = profile\n\n const filePath = getAuthProfilesPath()\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), { encoding: 'utf-8', mode: 0o600 })\n}\n\n/**\n * 删除一个 auth profile\n */\nexport function deleteAuthProfile(name: string): boolean {\n const data = loadAuthProfiles()\n if (!(name in data.profiles)) return false\n\n delete data.profiles[name]\n const filePath = getAuthProfilesPath()\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), { encoding: 'utf-8', mode: 0o600 })\n return true\n}\n","/**\n * MigrationRunner\n *\n * 在应用启动时执行所有待执行的数据迁移。\n * 版本号存储在 ~/.chatlab/.migration-version 文件中。\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport type { Migration, MigrationContext } from './types'\n\nconst VERSION_FILE = '.migration-version'\n\nexport class MigrationRunner {\n private migrations: Migration[]\n private context: MigrationContext\n\n constructor(migrations: Migration[], context: MigrationContext) {\n this.migrations = [...migrations].sort((a, b) => a.version - b.version)\n this.context = context\n }\n\n getCurrentVersion(): number {\n const versionPath = path.join(this.context.dataDir, VERSION_FILE)\n try {\n if (fs.existsSync(versionPath)) {\n const raw = fs.readFileSync(versionPath, 'utf-8').trim()\n const ver = parseInt(raw, 10)\n return Number.isFinite(ver) ? ver : 0\n }\n } catch {\n // version file unreadable, treat as 0\n }\n return 0\n }\n\n private writeVersion(version: number): void {\n const versionPath = path.join(this.context.dataDir, VERSION_FILE)\n try {\n if (!fs.existsSync(this.context.dataDir)) {\n fs.mkdirSync(this.context.dataDir, { recursive: true })\n }\n fs.writeFileSync(versionPath, String(version), 'utf-8')\n } catch (err) {\n this.context.logger.error('Migration', `Failed to write version file: ${err}`)\n }\n }\n\n async run(): Promise<{ executed: number; currentVersion: number }> {\n const currentVersion = this.getCurrentVersion()\n const pending = this.migrations.filter((m) => m.version > currentVersion)\n\n if (pending.length === 0) {\n return { executed: 0, currentVersion }\n }\n\n this.context.logger.info('Migration', `${pending.length} pending migration(s), current version: ${currentVersion}`)\n\n let lastVersion = currentVersion\n let executed = 0\n\n for (const migration of pending) {\n this.context.logger.info('Migration', `Running: v${lastVersion}→v${migration.version} ${migration.name}`)\n try {\n await migration.up(this.context)\n lastVersion = migration.version\n this.writeVersion(lastVersion)\n executed++\n this.context.logger.info('Migration', `Completed: ${migration.name}`)\n } catch (err) {\n this.context.logger.error('Migration', `Failed: ${migration.name}`, err)\n break\n }\n }\n\n return { executed, currentVersion: lastVersion }\n }\n}\n","/**\n * Migration v0→v1: Legacy 单配置 → 多配置格式\n *\n * 旧格式:{ provider, apiKey, model, ... }(扁平对象)\n * 新格式:{ configs: [{ id, name, provider, apiKey, model, ... }], defaultAssistant, fastModel }\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { randomUUID } from 'crypto'\nimport type { Migration, MigrationContext } from './types'\n\ninterface LegacyFlatConfig {\n provider: string\n apiKey: string\n model?: string\n maxTokens?: number\n [key: string]: unknown\n}\n\nfunction isLegacyFlatConfig(data: unknown): data is LegacyFlatConfig {\n if (!data || typeof data !== 'object') return false\n const obj = data as Record<string, unknown>\n return 'provider' in obj && 'apiKey' in obj && !('configs' in obj)\n}\n\nexport const m001LegacyToMultiConfig: Migration = {\n version: 1,\n name: 'legacy-to-multi-config',\n description: 'Convert legacy flat LLM config to multi-config format',\n\n async up(ctx: MigrationContext) {\n const configPath = path.join(ctx.aiDataDir, 'llm-config.json')\n if (!fs.existsSync(configPath)) return\n\n const raw = fs.readFileSync(configPath, 'utf-8')\n let data: unknown\n try {\n data = JSON.parse(raw)\n } catch {\n return\n }\n\n if (!isLegacyFlatConfig(data)) return\n\n ctx.logger.info('Migration', 'Converting legacy flat config to multi-config format')\n\n const now = Date.now()\n const newConfig = {\n id: randomUUID(),\n name: data.provider,\n provider: data.provider,\n apiKey: data.apiKey || '',\n model: data.model || '',\n maxTokens: data.maxTokens,\n createdAt: now,\n updatedAt: now,\n }\n\n const migrated = {\n schemaVersion: 1,\n configs: [newConfig],\n defaultAssistant: { configId: newConfig.id, modelId: newConfig.model },\n fastModel: null,\n }\n\n fs.writeFileSync(configPath, JSON.stringify(migrated, null, 2), 'utf-8')\n ctx.logger.info('Migration', 'Legacy config migrated to multi-config format')\n },\n}\n","/**\n * Migration v1→v2: Schema v2 升级\n *\n * - 移除 configs 中的 disableThinking / isReasoningModel 字段\n * - 更新 schemaVersion 为 2\n *\n * 注意:原 Electron 版还包含自定义 provider/model 创建逻辑,\n * 该逻辑依赖 Electron-specific 模块,在共享 migration 中跳过。\n * Electron 端在 loadConfigStore 中已有回退处理。\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport type { Migration, MigrationContext } from './types'\n\nexport const m002SchemaV2: Migration = {\n version: 2,\n name: 'schema-v2',\n description: 'Upgrade LLM config to schema v2 (remove deprecated fields)',\n\n async up(ctx: MigrationContext) {\n const configPath = path.join(ctx.aiDataDir, 'llm-config.json')\n if (!fs.existsSync(configPath)) return\n\n let data: Record<string, unknown>\n try {\n data = JSON.parse(fs.readFileSync(configPath, 'utf-8'))\n } catch {\n return\n }\n\n const schemaVersion = (data.schemaVersion as number) || 0\n if (schemaVersion >= 2) return\n\n ctx.logger.info('Migration', 'Upgrading LLM config to schema v2')\n\n const configs = (data.configs as Record<string, unknown>[]) || []\n const cleanedConfigs = configs.map((c) => {\n const { disableThinking: _dt, isReasoningModel: _rm, ...rest } = c\n return rest\n })\n\n data.configs = cleanedConfigs\n data.schemaVersion = 2\n\n fs.writeFileSync(configPath, JSON.stringify(data, null, 2), 'utf-8')\n },\n}\n","/**\n * Migration v2→v3: Schema v3 升级 — 双 slot 模型选择\n *\n * - 将 activeConfigId → defaultAssistant { configId, modelId }\n * - 新增 fastModel slot(默认 null)\n * - 更新 schemaVersion 为 3\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport type { Migration, MigrationContext } from './types'\n\nexport const m003SchemaV3: Migration = {\n version: 3,\n name: 'schema-v3-dual-slot',\n description: 'Upgrade LLM config to schema v3 (dual-slot model selection)',\n\n async up(ctx: MigrationContext) {\n const configPath = path.join(ctx.aiDataDir, 'llm-config.json')\n if (!fs.existsSync(configPath)) return\n\n let data: Record<string, unknown>\n try {\n data = JSON.parse(fs.readFileSync(configPath, 'utf-8'))\n } catch {\n return\n }\n\n const schemaVersion = (data.schemaVersion as number) || 0\n if (schemaVersion >= 3) return\n\n ctx.logger.info('Migration', 'Upgrading LLM config to schema v3 (dual-slot)')\n\n const configs = (data.configs as Array<{ id: string; model?: string }>) || []\n const activeConfigId = data.activeConfigId as string | null | undefined\n\n const resolvedConfig =\n activeConfigId && configs.find((c) => c.id === activeConfigId)\n ? configs.find((c) => c.id === activeConfigId)!\n : (configs[0] ?? null)\n\n data.defaultAssistant = resolvedConfig ? { configId: resolvedConfig.id, modelId: resolvedConfig.model || '' } : null\n data.fastModel = data.fastModel ?? null\n data.schemaVersion = 3\n delete data.activeConfigId\n\n fs.writeFileSync(configPath, JSON.stringify(data, null, 2), 'utf-8')\n },\n}\n","/**\n * Migration v3→v4: 加密 API Key → auth-profiles.json\n *\n * - 从 llm-config.json 读取加密的 apiKey(enc: 前缀)\n * - 使用 device-key + legacy machine-id 尝试解密\n * - 解密成功的写入 auth-profiles.json\n * - 清空 llm-config.json 中的 apiKey 字段\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport type { Migration, MigrationContext } from './types'\nimport { isEncrypted, decryptApiKey } from './crypto-legacy'\nimport { writeAuthProfile } from '../auth-profiles'\n\n/**\n * Derive a human-readable profile name from config fields.\n * Official providers → provider id (e.g. \"deepseek\")\n * openai-compatible → hostname from baseUrl (e.g. \"api.example.com\") or config name\n */\nfunction deriveProfileName(provider: string, config: Record<string, unknown>): string {\n if (provider !== 'openai-compatible') {\n return provider.toLowerCase().replace(/\\s+/g, '-')\n }\n\n const baseUrl = config.baseUrl as string\n if (baseUrl) {\n try {\n return new URL(baseUrl).hostname.toLowerCase()\n } catch {\n // invalid URL, fall through\n }\n }\n\n const name = config.name as string\n if (name) return name.toLowerCase().replace(/\\s+/g, '-')\n\n return 'custom'\n}\n\nexport const m004EncryptedKeysToAuthProfiles: Migration = {\n version: 4,\n name: 'encrypted-keys-to-auth-profiles',\n description: 'Migrate encrypted API keys from llm-config.json to auth-profiles.json',\n\n async up(ctx: MigrationContext) {\n const configPath = path.join(ctx.aiDataDir, 'llm-config.json')\n if (!fs.existsSync(configPath)) return\n\n let data: Record<string, unknown>\n try {\n data = JSON.parse(fs.readFileSync(configPath, 'utf-8'))\n } catch {\n return\n }\n\n const configs = (data.configs as Array<Record<string, unknown>>) || []\n let migrated = false\n const usedProfileNames = new Set<string>()\n\n for (const config of configs) {\n const apiKey = config.apiKey as string\n if (!apiKey) continue\n\n const provider = (config.provider as string) || 'unknown'\n let plainKey: string | null = null\n\n if (isEncrypted(apiKey)) {\n plainKey = decryptApiKey(apiKey)\n if (!plainKey) {\n ctx.logger.warn('Migration', `Failed to decrypt API key for \"${config.name}\", skipping`)\n continue\n }\n } else if (apiKey.length > 0) {\n plainKey = apiKey\n }\n\n if (plainKey) {\n const baseName = deriveProfileName(provider, config)\n\n let profileName = baseName\n if (usedProfileNames.has(profileName)) {\n let i = 2\n while (usedProfileNames.has(`${profileName}-${i}`)) i++\n profileName = `${profileName}-${i}`\n }\n usedProfileNames.add(profileName)\n\n writeAuthProfile(profileName, { type: 'api_key', provider, key: plainKey })\n config.apiKey = ''\n migrated = true\n ctx.logger.info('Migration', `Migrated API key → profile \"${profileName}\"`)\n }\n }\n\n if (migrated) {\n fs.writeFileSync(configPath, JSON.stringify(data, null, 2), 'utf-8')\n ctx.logger.info('Migration', 'Cleared API keys from llm-config.json')\n }\n },\n}\n","/**\n * Legacy crypto 解密模块(仅用于迁移)\n *\n * 从 electron/main/ai/llm/crypto.ts + device-key.ts 提取,\n * 不依赖 Electron API,可在 CLI 和 Electron 中共用。\n */\n\nimport * as fs from 'fs'\nimport * as os from 'os'\nimport * as path from 'path'\nimport { createDecipheriv, createHash } from 'crypto'\nimport { execSync } from 'child_process'\n\nconst ENCRYPTED_PREFIX = 'enc:'\nconst ALGORITHM = 'aes-256-gcm'\nconst SALT = 'chatlab-api-key-encryption-v1'\nconst DEVICE_KEY_FILE = '.device-key'\n\nexport function isEncrypted(value: string): boolean {\n return value?.startsWith(ENCRYPTED_PREFIX) ?? false\n}\n\nfunction readKeyFromFile(keyPath: string): string | null {\n try {\n if (fs.existsSync(keyPath)) {\n const key = fs.readFileSync(keyPath, 'utf-8').trim()\n if (key.length >= 32) return key\n }\n } catch {\n // ignore\n }\n return null\n}\n\nfunction getAllDeviceKeyPaths(): string[] {\n const home = os.homedir()\n const paths = [path.join(home, '.chatlab', DEVICE_KEY_FILE)]\n\n if (process.platform === 'darwin') {\n paths.push(path.join(home, 'Library', 'Application Support', 'ChatLab', 'data', DEVICE_KEY_FILE))\n } else if (process.platform === 'win32') {\n const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming')\n paths.push(path.join(appData, 'ChatLab', 'data', DEVICE_KEY_FILE))\n } else {\n paths.push(path.join(home, '.config', 'ChatLab', 'data', DEVICE_KEY_FILE))\n }\n\n return paths\n}\n\nfunction getAllDeviceKeys(): string[] {\n const keys: string[] = []\n const seen = new Set<string>()\n for (const p of getAllDeviceKeyPaths()) {\n const k = readKeyFromFile(p)\n if (k && !seen.has(k)) {\n seen.add(k)\n keys.push(k)\n }\n }\n return keys\n}\n\nfunction deriveLegacyKeys(): Buffer[] {\n const keys: Buffer[] = []\n try {\n let cmd: string | null = null\n if (process.platform === 'linux') {\n cmd = '( cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || hostname ) | head -n 1 || :'\n } else if (process.platform === 'darwin') {\n cmd = 'ioreg -rd1 -c IOPlatformExpertDevice'\n } else if (process.platform === 'win32') {\n cmd = 'REG.exe QUERY HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\Microsoft\\\\Cryptography /v MachineGuid'\n }\n\n if (cmd) {\n const raw = execSync(cmd).toString()\n let machineId: string\n if (process.platform === 'darwin') {\n machineId =\n raw\n .split('IOPlatformUUID')[1]\n ?.split('\\n')[0]\n ?.replace(/[=\\s\"]/g, '')\n ?.toLowerCase() || ''\n } else if (process.platform === 'win32') {\n machineId =\n raw\n .split('REG_SZ')[1]\n ?.replace(/[\\r\\n\\s]/g, '')\n ?.toLowerCase() || ''\n } else {\n machineId = raw.replace(/[\\r\\n\\s]/g, '').toLowerCase()\n }\n\n if (machineId) {\n const hashed = createHash('sha256').update(machineId).digest('hex')\n keys.push(\n createHash('sha256')\n .update(hashed + SALT)\n .digest()\n )\n }\n }\n } catch {\n // ignore\n }\n\n keys.push(\n createHash('sha256')\n .update('chatlab-fallback-key' + SALT)\n .digest()\n )\n return keys\n}\n\nfunction tryDecryptWithKey(encrypted: string, key: Buffer): string | null {\n try {\n const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(':')\n if (parts.length !== 3) return null\n\n const [ivBase64, authTagBase64, ciphertext] = parts\n const iv = Buffer.from(ivBase64, 'base64')\n const authTag = Buffer.from(authTagBase64, 'base64')\n\n const decipher = createDecipheriv(ALGORITHM, key, iv)\n decipher.setAuthTag(authTag)\n\n let decrypted = decipher.update(ciphertext, 'base64', 'utf8')\n decrypted += decipher.final('utf8')\n return decrypted\n } catch {\n return null\n }\n}\n\n/**\n * 解密旧加密 API Key,尝试所有可用的 device key 和 legacy machine-id 密钥\n */\nexport function decryptApiKey(encrypted: string): string {\n if (!encrypted || !isEncrypted(encrypted)) return encrypted || ''\n\n for (const dk of getAllDeviceKeys()) {\n const derived = createHash('sha256')\n .update(dk + SALT)\n .digest()\n const result = tryDecryptWithKey(encrypted, derived)\n if (result !== null) return result\n }\n\n for (const legacyKey of deriveLegacyKeys()) {\n const result = tryDecryptWithKey(encrypted, legacyKey)\n if (result !== null) return result\n }\n\n return ''\n}\n","/**\n * Migration Runner 导出\n *\n * ALL_MIGRATIONS 按版本号排列,由 MigrationRunner 在应用启动时执行。\n */\n\nexport { MigrationRunner } from './runner'\nexport type { Migration, MigrationContext, Logger } from './types'\n\nimport { m001LegacyToMultiConfig } from './m001-legacy-to-multi-config'\nimport { m002SchemaV2 } from './m002-schema-v2'\nimport { m003SchemaV3 } from './m003-schema-v3'\nimport { m004EncryptedKeysToAuthProfiles } from './m004-encrypted-keys-to-auth-profiles'\nimport type { Migration } from './types'\n\nexport const ALL_MIGRATIONS: Migration[] = [\n m001LegacyToMultiConfig,\n m002SchemaV2,\n m003SchemaV3,\n m004EncryptedKeysToAuthProfiles,\n]\n","/**\n * DatabaseAdapter 的 better-sqlite3 实现\n *\n * 薄包装层:better-sqlite3 的 API 与 DatabaseAdapter 接口天然匹配,\n * 本适配器主要做类型桥接,几乎零额外逻辑。\n */\n\nimport Database from 'better-sqlite3'\nimport type { DatabaseAdapter, PreparedStatement, RunResult } from '@openchatlab/core'\n\nclass BetterSqlitePreparedStatement implements PreparedStatement {\n readonly?: boolean\n\n constructor(private stmt: Database.Statement) {\n this.readonly = stmt.readonly\n }\n\n get(...params: unknown[]): Record<string, unknown> | undefined {\n return this.stmt.get(...params) as Record<string, unknown> | undefined\n }\n\n all(...params: unknown[]): Record<string, unknown>[] {\n return this.stmt.all(...params) as Record<string, unknown>[]\n }\n\n run(...params: unknown[]): RunResult {\n const result = this.stmt.run(...params)\n return {\n changes: result.changes,\n lastInsertRowid: result.lastInsertRowid,\n }\n }\n}\n\n/**\n * 基于 better-sqlite3 的 DatabaseAdapter 实现\n */\nexport class BetterSqliteAdapter implements DatabaseAdapter {\n readonly?: boolean\n\n constructor(private db: Database.Database) {\n this.readonly = db.readonly\n }\n\n exec(sql: string): void {\n this.db.exec(sql)\n }\n\n prepare(sql: string): PreparedStatement {\n return new BetterSqlitePreparedStatement(this.db.prepare(sql))\n }\n\n transaction<T>(fn: () => T): T {\n return this.db.transaction(fn)()\n }\n\n pragma(pragma: string): unknown {\n return this.db.pragma(pragma)\n }\n\n close(): void {\n this.db.close()\n }\n}\n\n/**\n * 从文件路径打开数据库并返回适配器\n *\n * @param options.nativeBinding - 指定 better-sqlite3 原生模块路径,\n * 用于在独立 Node.js 环境中加载与 Electron 隔离的二进制。\n */\nexport function openBetterSqliteDatabase(\n filePath: string,\n options?: { readonly?: boolean; nativeBinding?: string }\n): BetterSqliteAdapter {\n const db = new Database(filePath, {\n readonly: options?.readonly ?? false,\n nativeBinding: options?.nativeBinding,\n })\n if (!options?.readonly) {\n db.pragma('journal_mode = WAL')\n }\n return new BetterSqliteAdapter(db)\n}\n","/**\n * SQL 查询过滤条件构建工具\n *\n * 平台无关的 WHERE 子句构建器和 schema 检测工具,供所有查询模块复用。\n */\n\nimport type { TimeFilter } from '@openchatlab/shared-types'\nimport type { DatabaseAdapter } from '../interfaces'\n\n/**\n * 构建时间过滤 WHERE 子句\n * @param filter 时间过滤器(包含时间范围和成员筛选)\n * @param tableAlias 表别名,用于多表 JOIN 场景避免列名歧义(如 'msg')\n */\nexport function buildTimeFilter(\n filter?: TimeFilter,\n tableAlias?: string\n): { clause: string; params: (number | string)[] } {\n const conditions: string[] = []\n const params: (number | string)[] = []\n\n const tsColumn = tableAlias ? `${tableAlias}.ts` : 'ts'\n const senderIdColumn = tableAlias ? `${tableAlias}.sender_id` : 'sender_id'\n\n if (filter?.startTs !== undefined) {\n conditions.push(`${tsColumn} >= ?`)\n params.push(filter.startTs)\n }\n if (filter?.endTs !== undefined) {\n conditions.push(`${tsColumn} <= ?`)\n params.push(filter.endTs)\n }\n if (filter?.memberId !== undefined && filter?.memberId !== null) {\n conditions.push(`${senderIdColumn} = ?`)\n params.push(filter.memberId)\n }\n\n return {\n clause: conditions.length > 0 ? ` WHERE ${conditions.join(' AND ')}` : '',\n params,\n }\n}\n\n/**\n * Check if a table exists in the database\n */\nexport function hasTable(db: DatabaseAdapter, tableName: string): boolean {\n const row = db.prepare(\"SELECT 1 FROM sqlite_master WHERE type='table' AND name=?\").get(tableName)\n return row !== undefined\n}\n\n/**\n * Check if a column exists in a table\n */\nexport function hasColumn(db: DatabaseAdapter, tableName: string, columnName: string): boolean {\n const rows = db.pragma(`table_info(${tableName})`) as Array<{ name: string }>\n if (!Array.isArray(rows)) return false\n return rows.some((r) => r.name === columnName)\n}\n\n/**\n * 构建排除系统消息的过滤条件\n */\nexport function buildSystemMessageFilter(existingClause: string): string {\n const systemFilter = \"COALESCE(m.account_name, '') != '系统消息'\"\n\n if (existingClause.includes('WHERE')) {\n return existingClause + ' AND ' + systemFilter\n } else {\n return ' WHERE ' + systemFilter\n }\n}\n","/**\n * 基础统计查询模块(平台无关)\n *\n * 提供活跃度排行、时段分布等核心统计查询。\n * 所有函数接收 DatabaseAdapter 参数,不依赖全局状态。\n */\n\nimport type { TimeFilter } from '@openchatlab/shared-types'\nimport type { DatabaseAdapter } from '../interfaces'\nimport { buildTimeFilter, buildSystemMessageFilter } from './filters'\n\nexport interface MemberActivity {\n memberId: number\n platformId: string\n name: string\n avatar: string | null\n messageCount: number\n percentage: number\n}\n\nexport interface HourlyActivity {\n hour: number\n messageCount: number\n}\n\nexport interface DailyActivity {\n date: string\n messageCount: number\n}\n\nexport interface WeekdayActivity {\n weekday: number\n messageCount: number\n}\n\nexport interface MonthlyActivity {\n month: number\n messageCount: number\n}\n\nexport interface YearlyActivity {\n year: number\n messageCount: number\n}\n\nexport interface MessageLengthDistribution {\n detail: Array<{ len: number; count: number }>\n grouped: Array<{ range: string; count: number }>\n}\n\nexport interface MessageTypeStats {\n type: number\n count: number\n}\n\nexport interface TextStats {\n textCount: number\n avgLength: number\n maxLength: number\n shortCount: number\n}\n\nexport interface TextLengthPercentiles {\n p25: number\n p50: number\n p75: number\n p90: number\n}\n\nexport interface MemberMonthlyTrend {\n month: string\n memberId: number\n memberName: string\n count: number\n}\n\n/**\n * 获取消息时间范围\n */\nexport function getTimeRange(db: DatabaseAdapter): { start: number; end: number } | null {\n const row = db.prepare('SELECT MIN(ts) as start, MAX(ts) as end FROM message').get() as\n | { start: number | null; end: number | null }\n | undefined\n if (!row || row.start == null || row.end == null) return null\n return { start: row.start, end: row.end }\n}\n\n/**\n * 获取可用的年份列表\n */\nexport function getAvailableYears(db: DatabaseAdapter): number[] {\n const rows = db\n .prepare(\n `SELECT DISTINCT CAST(strftime('%Y', ts, 'unixepoch', 'localtime') AS INTEGER) as year\n FROM message\n ORDER BY year DESC`\n )\n .all() as Array<{ year: number }>\n\n return rows.map((r) => r.year)\n}\n\n/**\n * 获取成员活跃度排行\n */\nexport function getMemberActivity(db: DatabaseAdapter, filter?: TimeFilter): MemberActivity[] {\n const { clause, params } = buildTimeFilter(filter)\n\n const msgFilterBase = clause ? clause.replace('WHERE', 'AND') : ''\n const msgFilterWithSystem = msgFilterBase + \" AND COALESCE(m.account_name, '') != '系统消息'\"\n\n const totalClauseWithSystem = buildSystemMessageFilter(clause)\n const totalMessages = (\n db\n .prepare(\n `SELECT COUNT(*) as count\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ${totalClauseWithSystem}`\n )\n .get(...params) as { count: number }\n ).count\n\n const rows = db\n .prepare(\n `SELECT\n m.id as memberId,\n m.platform_id as platformId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,\n m.avatar as avatar,\n COUNT(msg.id) as messageCount\n FROM member m\n LEFT JOIN message msg ON m.id = msg.sender_id ${msgFilterWithSystem}\n WHERE COALESCE(m.account_name, '') != '系统消息'\n GROUP BY m.id\n HAVING messageCount > 0\n ORDER BY messageCount DESC`\n )\n .all(...params) as Array<{\n memberId: number\n platformId: string\n name: string\n avatar: string | null\n messageCount: number\n }>\n\n return rows.map((row) => ({\n ...row,\n percentage: totalMessages > 0 ? Math.round((row.messageCount / totalMessages) * 10000) / 100 : 0,\n }))\n}\n\n/**\n * 获取每小时活跃度分布\n */\nexport function getHourlyActivity(db: DatabaseAdapter, filter?: TimeFilter): HourlyActivity[] {\n const { clause, params } = buildTimeFilter(filter)\n const clauseWithSystem = buildSystemMessageFilter(clause)\n\n const rows = db\n .prepare(\n `SELECT\n CAST(strftime('%H', msg.ts, 'unixepoch', 'localtime') AS INTEGER) as hour,\n COUNT(*) as messageCount\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ${clauseWithSystem}\n GROUP BY hour\n ORDER BY hour`\n )\n .all(...params) as Array<{ hour: number; messageCount: number }>\n\n const result: HourlyActivity[] = []\n for (let h = 0; h < 24; h++) {\n const found = rows.find((r) => r.hour === h)\n result.push({ hour: h, messageCount: found ? found.messageCount : 0 })\n }\n return result\n}\n\n/**\n * 获取每日活跃度趋势\n */\nexport function getDailyActivity(db: DatabaseAdapter, filter?: TimeFilter): DailyActivity[] {\n const { clause, params } = buildTimeFilter(filter)\n const clauseWithSystem = buildSystemMessageFilter(clause)\n\n return db\n .prepare(\n `SELECT\n strftime('%Y-%m-%d', msg.ts, 'unixepoch', 'localtime') as date,\n COUNT(*) as messageCount\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ${clauseWithSystem}\n GROUP BY date\n ORDER BY date`\n )\n .all(...params) as unknown as DailyActivity[]\n}\n\n/**\n * 获取星期活跃度分布\n */\nexport function getWeekdayActivity(db: DatabaseAdapter, filter?: TimeFilter): WeekdayActivity[] {\n const { clause, params } = buildTimeFilter(filter)\n const clauseWithSystem = buildSystemMessageFilter(clause)\n\n const rows = db\n .prepare(\n `SELECT\n CASE\n WHEN CAST(strftime('%w', msg.ts, 'unixepoch', 'localtime') AS INTEGER) = 0 THEN 7\n ELSE CAST(strftime('%w', msg.ts, 'unixepoch', 'localtime') AS INTEGER)\n END as weekday,\n COUNT(*) as messageCount\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ${clauseWithSystem}\n GROUP BY weekday\n ORDER BY weekday`\n )\n .all(...params) as Array<{ weekday: number; messageCount: number }>\n\n const result: WeekdayActivity[] = []\n for (let w = 1; w <= 7; w++) {\n const found = rows.find((r) => r.weekday === w)\n result.push({ weekday: w, messageCount: found ? found.messageCount : 0 })\n }\n return result\n}\n\n/**\n * 获取消息类型分布\n */\nexport function getMessageTypeStats(db: DatabaseAdapter, filter?: TimeFilter): MessageTypeStats[] {\n const { clause, params } = buildTimeFilter(filter)\n const clauseWithSystem = buildSystemMessageFilter(clause)\n\n return db\n .prepare(\n `SELECT\n msg.type as type,\n COUNT(*) as count\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ${clauseWithSystem}\n GROUP BY msg.type\n ORDER BY count DESC`\n )\n .all(...params) as unknown as MessageTypeStats[]\n}\n\n/**\n * 获取月份活跃度分布\n */\nexport function getMonthlyActivity(db: DatabaseAdapter, filter?: TimeFilter): MonthlyActivity[] {\n const { clause, params } = buildTimeFilter(filter)\n const clauseWithSystem = buildSystemMessageFilter(clause)\n\n const rows = db\n .prepare(\n `SELECT\n CAST(strftime('%m', msg.ts, 'unixepoch', 'localtime') AS INTEGER) as month,\n COUNT(*) as messageCount\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ${clauseWithSystem}\n GROUP BY month\n ORDER BY month`\n )\n .all(...params) as Array<{ month: number; messageCount: number }>\n\n const result: MonthlyActivity[] = []\n for (let m = 1; m <= 12; m++) {\n const found = rows.find((r) => r.month === m)\n result.push({ month: m, messageCount: found ? found.messageCount : 0 })\n }\n return result\n}\n\n/**\n * 获取年份活跃度分布\n */\nexport function getYearlyActivity(db: DatabaseAdapter, filter?: TimeFilter): YearlyActivity[] {\n const { clause, params } = buildTimeFilter(filter)\n const clauseWithSystem = buildSystemMessageFilter(clause)\n\n return db\n .prepare(\n `SELECT\n CAST(strftime('%Y', msg.ts, 'unixepoch', 'localtime') AS INTEGER) as year,\n COUNT(*) as messageCount\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ${clauseWithSystem}\n GROUP BY year\n ORDER BY year`\n )\n .all(...params) as unknown as YearlyActivity[]\n}\n\n/**\n * 获取消息长度分布(仅统计文字消息 type=0)\n */\nexport function getMessageLengthDistribution(db: DatabaseAdapter, filter?: TimeFilter): MessageLengthDistribution {\n const { clause, params } = buildTimeFilter(filter)\n const clauseWithSystem = buildSystemMessageFilter(clause)\n\n const typeCondition = clauseWithSystem\n ? clauseWithSystem + ' AND msg.type = 0 AND msg.content IS NOT NULL AND LENGTH(msg.content) > 0'\n : 'WHERE msg.type = 0 AND msg.content IS NOT NULL AND LENGTH(msg.content) > 0'\n\n const rows = db\n .prepare(\n `SELECT LENGTH(msg.content) as len, COUNT(*) as count\n FROM message msg JOIN member m ON msg.sender_id = m.id\n ${typeCondition}\n GROUP BY len ORDER BY len`\n )\n .all(...params) as Array<{ len: number; count: number }>\n\n const detail: Array<{ len: number; count: number }> = []\n for (let i = 1; i <= 25; i++) {\n const found = rows.find((r) => r.len === i)\n detail.push({ len: i, count: found ? found.count : 0 })\n }\n\n const ranges = [\n { min: 1, max: 5, label: '1-5' },\n { min: 6, max: 10, label: '6-10' },\n { min: 11, max: 15, label: '11-15' },\n { min: 16, max: 20, label: '16-20' },\n { min: 21, max: 25, label: '21-25' },\n { min: 26, max: 30, label: '26-30' },\n { min: 31, max: 35, label: '31-35' },\n { min: 36, max: 40, label: '36-40' },\n { min: 41, max: 45, label: '41-45' },\n { min: 46, max: 50, label: '46-50' },\n { min: 51, max: 60, label: '51-60' },\n { min: 61, max: 70, label: '61-70' },\n { min: 71, max: 80, label: '71-80' },\n { min: 81, max: 100, label: '81-100' },\n { min: 101, max: Infinity, label: '100+' },\n ]\n\n const grouped: Array<{ range: string; count: number }> = ranges.map((r) => ({\n range: r.label,\n count: rows.filter((row) => row.len >= r.min && row.len <= r.max).reduce((sum, row) => sum + row.count, 0),\n }))\n\n return { detail, grouped }\n}\n\n/**\n * 获取文字消息统计(仅 type=0):数量、平均长度、最大长度、短消息(≤5 字)数\n */\nexport function getTextStats(db: DatabaseAdapter, filter?: TimeFilter): TextStats {\n const { clause, params } = buildTimeFilter(filter)\n const typeCondition =\n buildSystemMessageFilter(clause) + ' AND msg.type = 0 AND msg.content IS NOT NULL AND LENGTH(msg.content) > 0'\n\n const row = db\n .prepare(\n `SELECT\n COUNT(*) as textCount,\n ROUND(AVG(LENGTH(msg.content)), 1) as avgLength,\n MAX(LENGTH(msg.content)) as maxLength,\n SUM(CASE WHEN LENGTH(msg.content) <= 5 THEN 1 ELSE 0 END) as shortCount\n FROM message msg JOIN member m ON msg.sender_id = m.id\n ${typeCondition}`\n )\n .get(...params) as\n | { textCount: number; avgLength: number | null; maxLength: number | null; shortCount: number | null }\n | undefined\n\n return {\n textCount: row?.textCount ?? 0,\n avgLength: row?.avgLength ?? 0,\n maxLength: row?.maxLength ?? 0,\n shortCount: row?.shortCount ?? 0,\n }\n}\n\n/**\n * 获取长消息(小作文)数量,minLength 为字符阈值(默认 30),仅统计文字消息\n */\nexport function getLongMessageCount(db: DatabaseAdapter, filter?: TimeFilter, minLength = 30): number {\n const { clause, params } = buildTimeFilter(filter)\n const typeCondition =\n buildSystemMessageFilter(clause) + ' AND msg.type = 0 AND msg.content IS NOT NULL AND LENGTH(msg.content) >= ?'\n\n const row = db\n .prepare(\n `SELECT COUNT(*) as cnt\n FROM message msg JOIN member m ON msg.sender_id = m.id\n ${typeCondition}`\n )\n .get(...params, minLength) as { cnt: number } | undefined\n\n return row?.cnt ?? 0\n}\n\n/**\n * 获取成员月度消息趋势(按月 × 发送者)\n */\nexport function getMemberMonthlyTrend(db: DatabaseAdapter, filter?: TimeFilter): MemberMonthlyTrend[] {\n const { clause, params } = buildTimeFilter(filter)\n const clauseWithSystem = buildSystemMessageFilter(clause)\n\n return db\n .prepare(\n `SELECT\n strftime('%Y-%m', msg.ts, 'unixepoch', 'localtime') as month,\n msg.sender_id as memberId,\n m.account_name as memberName,\n COUNT(*) as count\n FROM message msg JOIN member m ON msg.sender_id = m.id\n ${clauseWithSystem}\n GROUP BY month, msg.sender_id\n ORDER BY month`\n )\n .all(...params) as unknown as MemberMonthlyTrend[]\n}\n\n/**\n * 获取文字消息长度百分位(P25/P50/P75/P90),仅统计文字消息\n */\nexport function getTextLengthPercentiles(db: DatabaseAdapter, filter?: TimeFilter): TextLengthPercentiles {\n const { clause, params } = buildTimeFilter(filter)\n const typeCondition =\n buildSystemMessageFilter(clause) + ' AND msg.type = 0 AND msg.content IS NOT NULL AND LENGTH(msg.content) > 0'\n\n const rows = db\n .prepare(\n `SELECT LENGTH(msg.content) as len\n FROM message msg JOIN member m ON msg.sender_id = m.id\n ${typeCondition}\n ORDER BY len`\n )\n .all(...params) as Array<{ len: number }>\n\n if (rows.length === 0) return { p25: 0, p50: 0, p75: 0, p90: 0 }\n\n const lengths = rows.map((r) => r.len)\n const getPercentile = (arr: number[], p: number) => {\n const idx = Math.ceil((p / 100) * arr.length) - 1\n return arr[Math.max(0, idx)]\n }\n\n return {\n p25: getPercentile(lengths, 25),\n p50: getPercentile(lengths, 50),\n p75: getPercentile(lengths, 75),\n p90: getPercentile(lengths, 90),\n }\n}\n","/**\n * 会话查询模块(平台无关)\n *\n * 纯 SQL 查询函数,接收 DatabaseAdapter 参数,不依赖全局状态。\n * 这些函数是 CLI/MCP/HTTP API 查询会话数据的基础。\n */\n\nimport type { TimeFilter } from '@openchatlab/shared-types'\nimport type { DatabaseAdapter } from '../interfaces'\nimport { hasTable } from './filters'\nimport { getMemberActivity } from './basic-queries'\n\nexport interface SessionMeta {\n name: string\n platform: string\n type: string\n importedAt: number\n groupId: string | null\n groupAvatar: string | null\n ownerId: string | null\n}\n\nexport interface SessionOverview {\n totalMessages: number\n totalMembers: number\n firstMessageTs: number | null\n lastMessageTs: number | null\n}\n\nexport interface SessionInfo extends SessionMeta {\n id: string\n overview: SessionOverview\n}\n\n// ==================== Flat session info (for AnalysisSession-like consumers) ====================\n\n/**\n * Platform-agnostic session info built from meta + overview.\n * Covers all AnalysisSession fields that are queryable from a single DB.\n * Platform-specific fields (dbPath, memberAvatar, aiConversationCount) are left to callers.\n */\nexport interface CoreSessionInfo {\n name: string\n platform: string\n type: string\n importedAt: number\n messageCount: number\n memberCount: number\n groupId: string | null\n groupAvatar: string | null\n ownerId: string | null\n firstMessageTs: number | null\n lastMessageTs: number | null\n summaryCount: number\n}\n\n/**\n * Pure mapper: compose SessionMeta + SessionOverview into flat CoreSessionInfo.\n * Callers provide the inputs which may come from cache or fresh SQL.\n */\nexport function buildSessionInfo(\n meta: SessionMeta,\n overview: SessionOverview,\n summaryCount: number = 0\n): CoreSessionInfo {\n return {\n name: meta.name,\n platform: meta.platform,\n type: meta.type,\n importedAt: meta.importedAt,\n messageCount: overview.totalMessages,\n memberCount: overview.totalMembers,\n groupId: meta.groupId,\n groupAvatar: meta.groupAvatar,\n ownerId: meta.ownerId,\n firstMessageTs: overview.firstMessageTs,\n lastMessageTs: overview.lastMessageTs,\n summaryCount,\n }\n}\n\n/**\n * Convenience: read meta + overview from DB and return flat CoreSessionInfo.\n */\nexport function getSessionInfo(db: DatabaseAdapter): CoreSessionInfo | null {\n const meta = getSessionMeta(db)\n if (!meta) return null\n const overview = getSessionOverview(db)\n const sc = getSummaryCount(db)\n return buildSessionInfo(meta, overview, sc)\n}\n\n/**\n * Count of chat sessions that have an AI-generated summary.\n */\nexport function getSummaryCount(db: DatabaseAdapter): number {\n if (!hasTable(db, 'segment')) return 0\n const row = db.prepare(\"SELECT COUNT(*) as count FROM segment WHERE summary IS NOT NULL AND summary != ''\").get() as\n | { count: number }\n | undefined\n return row?.count ?? 0\n}\n\n/**\n * Get the latest platform_message_id (used as incremental import boundary).\n */\nexport function getLastPlatformMessageId(db: DatabaseAdapter): string | null {\n const row = db\n .prepare('SELECT platform_message_id FROM message WHERE platform_message_id IS NOT NULL ORDER BY ts DESC LIMIT 1')\n .get() as { platform_message_id: string } | undefined\n return row?.platform_message_id ?? null\n}\n\n// ==================== Core identification ====================\n\n/**\n * 判断数据库是否为聊天会话数据库\n * 通过核心三表(meta/member/message)存在性快速识别\n */\nexport function isChatSessionDb(db: DatabaseAdapter): boolean {\n const row = db\n .prepare(\"SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name IN ('meta', 'member', 'message')\")\n .get() as { cnt: number } | undefined\n return row?.cnt === 3\n}\n\n/**\n * 读取会话元信息\n */\nexport function getSessionMeta(db: DatabaseAdapter): SessionMeta | null {\n const row = db.prepare('SELECT * FROM meta LIMIT 1').get() as Record<string, unknown> | undefined\n if (!row) return null\n\n return {\n name: row.name as string,\n platform: row.platform as string,\n type: row.type as string,\n importedAt: row.imported_at as number,\n groupId: (row.group_id as string) || null,\n groupAvatar: (row.group_avatar as string) || null,\n ownerId: (row.owner_id as string) || null,\n }\n}\n\n/**\n * 查询会话基础统计(消息数、成员数、时间范围)\n */\nexport function getSessionOverview(db: DatabaseAdapter): SessionOverview {\n const msgRow = db\n .prepare(\n `SELECT COUNT(*) as count\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE COALESCE(m.account_name, '') != '系统消息'`\n )\n .get() as { count: number }\n\n const memberRow = db\n .prepare(\n `SELECT COUNT(*) as count\n FROM member\n WHERE COALESCE(account_name, '') != '系统消息'`\n )\n .get() as { count: number }\n\n const firstTs = (db.prepare('SELECT MIN(ts) as v FROM message').get() as { v: number | null })?.v ?? null\n const lastTs = (db.prepare('SELECT MAX(ts) as v FROM message').get() as { v: number | null })?.v ?? null\n\n return {\n totalMessages: msgRow.count,\n totalMembers: memberRow.count,\n firstMessageTs: firstTs,\n lastMessageTs: lastTs,\n }\n}\n\n/**\n * 获取数据库中的表结构(Schema)\n */\nexport function getDatabaseSchema(db: DatabaseAdapter): Array<{ name: string; sql: string }> {\n return db\n .prepare(\"SELECT name, sql FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name\")\n .all() as Array<{ name: string; sql: string }>\n}\n\n// ==================== Chat Overview & Session Queries ====================\n\nexport interface ChatOverviewData {\n name: string\n platform: string\n type: string\n totalMessages: number\n totalMembers: number\n firstMessageTs: number | null\n lastMessageTs: number | null\n topMembers: Array<{ id: number; name: string; count: number }>\n summaryCount: number\n}\n\nexport interface SessionPreviewMessage {\n id: number\n senderId: number\n senderName: string\n senderPlatformId: string\n content: string | null\n timestamp: number\n}\n\nexport interface SegmentMessagesData {\n segmentId: number\n startTs: number\n endTs: number\n messageCount: number\n returnedCount: number\n participants: string[]\n messages: SessionPreviewMessage[]\n}\n\nexport interface SegmentSummaryData {\n id: number\n startTs: number\n endTs: number\n messageCount: number\n participants: string[]\n summary: string | null\n}\n\n/**\n * Get chat overview by composing meta, overview stats, and top members.\n * Simpler than Electron version — no cache layer, direct SQL.\n */\nexport function getChatOverview(db: DatabaseAdapter, topN: number = 10): ChatOverviewData | null {\n const meta = getSessionMeta(db)\n if (!meta) return null\n\n const overview = getSessionOverview(db)\n const members = getMemberActivity(db)\n const summaryCount = getSummaryCount(db)\n\n const topMembers = members.slice(0, topN).map((m) => ({\n id: m.memberId,\n name: m.name,\n count: m.messageCount,\n }))\n\n return {\n name: meta.name,\n platform: meta.platform,\n type: meta.type,\n totalMessages: overview.totalMessages,\n totalMembers: overview.totalMembers,\n firstMessageTs: overview.firstMessageTs,\n lastMessageTs: overview.lastMessageTs,\n topMembers,\n summaryCount,\n }\n}\n\n/**\n * Get messages for a specific chat session\n */\nexport function getSegmentMessages(\n db: DatabaseAdapter,\n segmentId: number,\n limit: number = 500\n): SegmentMessagesData | null {\n if (!hasTable(db, 'segment')) return null\n\n const session = db\n .prepare(\n `SELECT id, start_ts as startTs, end_ts as endTs, message_count as messageCount\n FROM segment WHERE id = ?`\n )\n .get(segmentId) as { id: number; startTs: number; endTs: number; messageCount: number } | undefined\n\n if (!session) return null\n\n const messages = db\n .prepare(\n `SELECT m.id, mb.id as senderId,\n COALESCE(mb.group_nickname, mb.account_name, mb.platform_id) as senderName,\n mb.platform_id as senderPlatformId,\n m.content, m.ts as timestamp\n FROM message_context mc\n JOIN message m ON m.id = mc.message_id\n JOIN member mb ON mb.id = m.sender_id\n WHERE mc.segment_id = ? ORDER BY m.ts ASC LIMIT ?`\n )\n .all(segmentId, limit) as unknown as SessionPreviewMessage[]\n\n const participantsSet = new Set<string>()\n for (const msg of messages) {\n participantsSet.add(msg.senderName)\n }\n\n return {\n segmentId: session.id,\n startTs: session.startTs,\n endTs: session.endTs,\n messageCount: session.messageCount,\n returnedCount: messages.length,\n participants: Array.from(participantsSet),\n messages,\n }\n}\n\n/**\n * Get session summaries (only sessions that have AI-generated summaries)\n */\nexport function getSegmentSummaries(\n db: DatabaseAdapter,\n options?: { limit?: number; timeFilter?: TimeFilter }\n): SegmentSummaryData[] {\n if (!hasTable(db, 'segment')) return []\n\n const { limit = 50, timeFilter } = options ?? {}\n\n let sql = `\n SELECT cs.id, cs.start_ts as startTs, cs.end_ts as endTs,\n cs.message_count as messageCount, cs.summary\n FROM segment cs\n WHERE cs.summary IS NOT NULL AND cs.summary != ''\n `\n const params: unknown[] = []\n\n if (timeFilter?.startTs !== undefined) {\n sql += ' AND cs.start_ts >= ?'\n params.push(timeFilter.startTs)\n }\n if (timeFilter?.endTs !== undefined) {\n sql += ' AND cs.start_ts <= ?'\n params.push(timeFilter.endTs)\n }\n\n sql += ' ORDER BY cs.start_ts DESC LIMIT ?'\n params.push(limit)\n\n const sessions = db.prepare(sql).all(...params) as Array<{\n id: number\n startTs: number\n endTs: number\n messageCount: number\n summary: string | null\n }>\n\n const participantsSql = `\n SELECT DISTINCT COALESCE(mb.group_nickname, mb.account_name, mb.platform_id) as name\n FROM message_context mc\n JOIN message m ON m.id = mc.message_id\n JOIN member mb ON mb.id = m.sender_id\n WHERE mc.segment_id = ? LIMIT 10\n `\n\n return sessions.map((session) => {\n const participants = db.prepare(participantsSql).all(session.id) as Array<{ name: string }>\n\n return {\n id: session.id,\n startTs: session.startTs,\n endTs: session.endTs,\n messageCount: session.messageCount,\n participants: participants.map((p) => p.name),\n summary: session.summary,\n }\n })\n}\n\n// ==================== Session Index (segment) ====================\n\n/** Default gap threshold for session segmentation: 30 minutes (seconds) */\nexport const DEFAULT_SESSION_GAP_THRESHOLD = 1800\n\nexport interface ChatSessionItem {\n id: number\n startTs: number\n endTs: number\n messageCount: number\n firstMessageId: number\n summary?: string | null\n}\n\nexport interface SessionIndexStats {\n sessionCount: number\n hasIndex: boolean\n gapThreshold: number\n}\n\n/**\n * Check whether the segment table exists and has at least one row.\n */\nexport function hasSessionIndex(db: DatabaseAdapter): boolean {\n if (!hasTable(db, 'segment')) return false\n try {\n const row = db.prepare('SELECT COUNT(*) as count FROM segment').get() as { count: number } | undefined\n return (row?.count ?? 0) > 0\n } catch {\n return false\n }\n}\n\n/**\n * Session index statistics: count, existence flag, and gap threshold from meta.\n */\nexport function getSessionIndexStats(db: DatabaseAdapter): SessionIndexStats {\n let sessionCount = 0\n if (hasTable(db, 'segment')) {\n try {\n const row = db.prepare('SELECT COUNT(*) as count FROM segment').get() as { count: number } | undefined\n sessionCount = row?.count ?? 0\n } catch {\n /* table may not exist */\n }\n }\n\n let gapThreshold = DEFAULT_SESSION_GAP_THRESHOLD\n try {\n const meta = db.prepare('SELECT session_gap_threshold FROM meta LIMIT 1').get() as\n | { session_gap_threshold: number | null }\n | undefined\n if (meta?.session_gap_threshold) {\n gapThreshold = meta.session_gap_threshold\n }\n } catch {\n /* column may not exist */\n }\n\n return { sessionCount, hasIndex: sessionCount > 0, gapThreshold }\n}\n\n/**\n * Query chat sessions within a time range.\n */\nexport function getSessionsByTimeRange(db: DatabaseAdapter, startTs: number, endTs: number): ChatSessionItem[] {\n if (!hasTable(db, 'segment')) return []\n try {\n return db\n .prepare(\n `SELECT\n id, start_ts as startTs, end_ts as endTs,\n message_count as messageCount, summary,\n (SELECT mc.message_id FROM message_context mc\n WHERE mc.segment_id = cs.id ORDER BY mc.message_id LIMIT 1) as firstMessageId\n FROM segment cs\n WHERE start_ts >= ? AND start_ts <= ?\n ORDER BY start_ts DESC`\n )\n .all(startTs, endTs) as unknown as ChatSessionItem[]\n } catch {\n return []\n }\n}\n\n/**\n * Get the most recent N chat sessions.\n */\nexport function getRecentChatSessions(db: DatabaseAdapter, limit: number): ChatSessionItem[] {\n if (!hasTable(db, 'segment')) return []\n try {\n return db\n .prepare(\n `SELECT\n id, start_ts as startTs, end_ts as endTs,\n message_count as messageCount, summary,\n (SELECT mc.message_id FROM message_context mc\n WHERE mc.segment_id = cs.id ORDER BY mc.message_id LIMIT 1) as firstMessageId\n FROM segment cs\n ORDER BY start_ts DESC\n LIMIT ?`\n )\n .all(limit) as unknown as ChatSessionItem[]\n } catch {\n return []\n }\n}\n\n/**\n * Timeline list of chat sessions with first message id and summary.\n */\nexport function getChatSessionList(db: DatabaseAdapter): ChatSessionItem[] {\n if (!hasTable(db, 'segment')) return []\n try {\n return db\n .prepare(\n `SELECT\n cs.id,\n cs.start_ts as startTs,\n cs.end_ts as endTs,\n cs.message_count as messageCount,\n cs.summary,\n (SELECT mc.message_id FROM message_context mc\n WHERE mc.segment_id = cs.id ORDER BY mc.message_id LIMIT 1) as firstMessageId\n FROM segment cs\n ORDER BY cs.start_ts ASC`\n )\n .all() as unknown as ChatSessionItem[]\n } catch {\n return []\n }\n}\n\n/**\n * Load messages for a chat session (for summary generation).\n */\nexport function loadSegmentMessages(\n db: DatabaseAdapter,\n segmentId: number,\n limit: number = 500\n): Array<{ senderName: string; content: string | null }> | null {\n try {\n return db\n .prepare(\n `SELECT\n COALESCE(mb.group_nickname, mb.account_name, mb.platform_id) as senderName,\n m.content\n FROM message_context mc\n JOIN message m ON m.id = mc.message_id\n JOIN member mb ON mb.id = m.sender_id\n WHERE mc.segment_id = ?\n ORDER BY m.ts ASC\n LIMIT ?`\n )\n .all(segmentId, limit) as unknown as Array<{ senderName: string; content: string | null }>\n } catch {\n return null\n }\n}\n\n/**\n * Get summary text for a single chat session.\n */\nexport function getSegmentSummary(db: DatabaseAdapter, segmentId: number): string | null {\n try {\n const row = db.prepare('SELECT summary FROM segment WHERE id = ?').get(segmentId) as\n | { summary: string | null }\n | undefined\n return row?.summary ?? null\n } catch {\n return null\n }\n}\n\n/**\n * Save summary text for a chat session.\n */\nexport function saveSegmentSummary(db: DatabaseAdapter, segmentId: number, summary: string): void {\n db.prepare('UPDATE segment SET summary = ? WHERE id = ?').run(summary, segmentId)\n}\n\n/**\n * Update gap threshold in meta table.\n */\nexport function updateSessionGapThreshold(db: DatabaseAdapter, gapThreshold: number | null): void {\n db.prepare('UPDATE meta SET session_gap_threshold = ?').run(gapThreshold)\n}\n\n/**\n * Update session owner_id in meta table.\n */\nexport function updateSessionOwnerId(db: DatabaseAdapter, ownerId: string | null): void {\n db.prepare('UPDATE meta SET owner_id = ?').run(ownerId)\n}\n\n/**\n * Rename a session (update name in meta table).\n */\nexport function renameSession(db: DatabaseAdapter, newName: string): void {\n db.prepare('UPDATE meta SET name = ?').run(newName)\n}\n\n/**\n * Delete all session index data (segment + message_context).\n */\nfunction clearSessionIndexRows(db: DatabaseAdapter): void {\n db.exec('DELETE FROM message_context')\n db.exec('DELETE FROM segment')\n}\n\nexport function clearSessionIndex(db: DatabaseAdapter): void {\n db.transaction(() => clearSessionIndexRows(db))\n}\n\n/**\n * Generate session index using gap-based segmentation.\n * Pure SQL+JS algorithm — caller provides an open DatabaseAdapter.\n *\n * @returns number of sessions created\n */\nexport function generateSessionIndex(\n db: DatabaseAdapter,\n gapThreshold: number = DEFAULT_SESSION_GAP_THRESHOLD,\n onProgress?: (current: number, total: number) => void\n): number {\n const countRow = db.prepare('SELECT COUNT(*) as count FROM message').get() as { count: number } | undefined\n if (!countRow || countRow.count === 0) return 0\n\n const sessionMarkSQL = `\n WITH message_ordered AS (\n SELECT id, ts, LAG(ts) OVER (ORDER BY ts, id) AS prev_ts FROM message\n ),\n session_marks AS (\n SELECT id, ts,\n CASE WHEN prev_ts IS NULL OR (ts - prev_ts) > ? THEN 1 ELSE 0 END AS is_new_session\n FROM message_ordered\n ),\n session_ids AS (\n SELECT id, ts, SUM(is_new_session) OVER (ORDER BY ts, id) AS session_num\n FROM session_marks\n )\n SELECT id, ts, session_num FROM session_ids\n `\n\n const messages = db.prepare(sessionMarkSQL).all(gapThreshold) as Array<{\n id: number\n ts: number\n session_num: number\n }>\n\n if (messages.length === 0) return 0\n\n const sessionMap = new Map<number, { startTs: number; endTs: number; messageIds: number[] }>()\n for (const msg of messages) {\n const session = sessionMap.get(msg.session_num)\n if (!session) {\n sessionMap.set(msg.session_num, { startTs: msg.ts, endTs: msg.ts, messageIds: [msg.id] })\n } else {\n session.endTs = msg.ts\n session.messageIds.push(msg.id)\n }\n }\n\n const insertSession = db.prepare(\n 'INSERT INTO segment (start_ts, end_ts, message_count, is_manual, summary) VALUES (?, ?, ?, 0, NULL)'\n )\n const insertContext = db.prepare('INSERT INTO message_context (message_id, segment_id, topic_id) VALUES (?, ?, NULL)')\n\n return db.transaction(() => {\n clearSessionIndexRows(db)\n\n let processed = 0\n const total = sessionMap.size\n for (const [, data] of sessionMap) {\n const result = insertSession.run(data.startTs, data.endTs, data.messageIds.length)\n const newId = (result.lastInsertRowid ?? 0) as number\n for (const mid of data.messageIds) {\n insertContext.run(mid, newId)\n }\n processed++\n if (onProgress && processed % 100 === 0) onProgress(processed, total)\n }\n if (onProgress) onProgress(total, total)\n return total\n })\n}\n\n/**\n * Incremental session index generation — only processes unindexed messages.\n * Preserves existing sessions and their summaries.\n *\n * @returns number of NEW sessions created (appended messages don't count)\n */\nexport function generateIncrementalSessionIndex(\n db: DatabaseAdapter,\n gapThreshold: number = DEFAULT_SESSION_GAP_THRESHOLD\n): number {\n const indexedIds = new Set<number>()\n if (hasTable(db, 'message_context')) {\n const rows = db.prepare('SELECT message_id FROM message_context').all() as Array<{ message_id: number }>\n for (const r of rows) indexedIds.add(r.message_id)\n }\n\n const allMessages = db.prepare('SELECT id, ts FROM message ORDER BY ts, id').all() as Array<{\n id: number\n ts: number\n }>\n const newMessages = allMessages.filter((m) => !indexedIds.has(m.id))\n if (newMessages.length === 0) return 0\n\n const lastSession = hasTable(db, 'segment')\n ? (db.prepare('SELECT id, end_ts FROM segment ORDER BY end_ts DESC LIMIT 1').get() as\n | { id: number; end_ts: number }\n | undefined)\n : undefined\n\n newMessages.sort((a, b) => a.ts - b.ts || a.id - b.id)\n\n const insertSession = db.prepare(\n 'INSERT INTO segment (start_ts, end_ts, message_count, is_manual, summary) VALUES (?, ?, ?, 0, NULL)'\n )\n const insertContext = db.prepare('INSERT INTO message_context (message_id, segment_id, topic_id) VALUES (?, ?, NULL)')\n const updateSession = db.prepare('UPDATE segment SET end_ts = ?, message_count = message_count + ? WHERE id = ?')\n\n return db.transaction(() => {\n let newSessionCount = 0\n let currentSessionId: number | null = null\n let currentEndTs = 0\n let appendCount = 0\n\n for (let i = 0; i < newMessages.length; i++) {\n const msg = newMessages[i]\n let needNew = false\n\n if (i === 0) {\n if (lastSession && msg.ts - lastSession.end_ts <= gapThreshold) {\n currentSessionId = lastSession.id\n currentEndTs = lastSession.end_ts\n appendCount = 0\n } else {\n needNew = true\n }\n } else {\n if (msg.ts - newMessages[i - 1].ts > gapThreshold) {\n if (currentSessionId && appendCount > 0) {\n updateSession.run(currentEndTs, appendCount, currentSessionId)\n appendCount = 0\n }\n needNew = true\n }\n }\n\n if (needNew) {\n const result = insertSession.run(msg.ts, msg.ts, 1)\n currentSessionId = (result.lastInsertRowid ?? 0) as number\n currentEndTs = msg.ts\n newSessionCount++\n appendCount = 0\n } else {\n currentEndTs = msg.ts\n appendCount++\n }\n\n insertContext.run(msg.id, currentSessionId)\n }\n\n if (currentSessionId && appendCount > 0) {\n updateSession.run(currentEndTs, appendCount, currentSessionId)\n }\n\n return newSessionCount\n })\n}\n\n// ==================== Export data ====================\n\nexport interface ExportSessionData {\n meta: {\n name: string\n platform: string\n type: string\n groupId?: string\n groupAvatar?: string\n }\n members: Array<{\n platformId: string\n accountName: string\n groupNickname?: string\n avatar?: string\n }>\n messages: Array<{\n sender: string\n accountName: string\n groupNickname?: string\n timestamp: number\n type: number\n content: string | null\n }>\n}\n\n/**\n * Query all data needed for session export (meta + members + messages).\n */\nexport function getExportSessionData(db: DatabaseAdapter): ExportSessionData {\n const meta = db.prepare('SELECT * FROM meta').get() as {\n name: string\n platform: string\n type: string\n group_id?: string\n group_avatar?: string\n }\n if (!meta) throw new Error('Cannot read session meta')\n\n const members = db.prepare('SELECT platform_id, account_name, group_nickname, avatar FROM member').all() as Array<{\n platform_id: string\n account_name?: string\n group_nickname?: string\n avatar?: string\n }>\n\n const messages = db\n .prepare(\n `SELECT\n m.platform_id as sender,\n msg.sender_account_name as accountName,\n msg.sender_group_nickname as groupNickname,\n msg.ts as timestamp,\n msg.type,\n msg.content\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ORDER BY msg.ts`\n )\n .all() as unknown as Array<{\n sender: string\n accountName?: string\n groupNickname?: string\n timestamp: number\n type: number\n content?: string\n }>\n\n return {\n meta: {\n name: meta.name,\n platform: meta.platform,\n type: meta.type,\n groupId: meta.group_id,\n groupAvatar: meta.group_avatar,\n },\n members: members.map((m) => ({\n platformId: m.platform_id,\n accountName: m.account_name || m.platform_id,\n groupNickname: m.group_nickname || undefined,\n avatar: m.avatar,\n })),\n messages: messages.map((msg) => ({\n sender: msg.sender,\n accountName: msg.accountName || msg.sender,\n groupNickname: msg.groupNickname || undefined,\n timestamp: msg.timestamp,\n type: msg.type,\n content: msg.content ?? null,\n })),\n }\n}\n\n/**\n * Get private chat partner's avatar.\n * Finds the \"other\" member (not the owner) in a private chat, falling back to name match or first with avatar.\n */\nexport function getPrivateChatMemberAvatar(\n db: DatabaseAdapter,\n sessionName: string,\n ownerId: string | null | undefined\n): string | null {\n const members = db\n .prepare(\n `SELECT\n m.platform_id as platformId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,\n m.avatar\n FROM member m\n WHERE COALESCE(m.account_name, '') != '系统消息'\n ORDER BY (SELECT COUNT(*) FROM message WHERE sender_id = m.id) DESC`\n )\n .all() as unknown as Array<{ platformId: string; name: string; avatar: string | null }>\n\n if (members.length === 0) return null\n\n if (ownerId) {\n const other = members.find((m) => m.platformId !== ownerId)\n if (other?.avatar) return other.avatar\n }\n\n const sameName = members.find((m) => m.name === sessionName)\n if (sameName?.avatar) return sameName.avatar\n\n const firstWithAvatar = members.find((m) => m.avatar)\n return firstWithAvatar?.avatar || null\n}\n","/**\n * Shared full-message SQL template, types, and row mapper.\n *\n * Single source of truth for all platforms (Electron worker, CLI Web FetchMessageAdapter).\n * Each platform imports these constants and the mapper; only the SQL execution\n * mechanism differs (direct db.prepare vs pluginQuery HTTP).\n */\n\n// ==================== SQL fragments ====================\n\nexport const FULL_MSG_COLUMNS = `\n msg.id,\n m.id as senderId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as senderName,\n m.platform_id as senderPlatformId,\n COALESCE(m.aliases, '[]') as aliasesJson,\n m.avatar as senderAvatar,\n msg.content,\n msg.ts as timestamp,\n msg.type,\n msg.reply_to_message_id as replyToMessageId,\n reply_msg.content as replyToContent,\n COALESCE(reply_m.group_nickname, reply_m.account_name, reply_m.platform_id) as replyToSenderName`\n\nexport const FULL_MSG_FROM = `\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n LEFT JOIN message reply_msg ON msg.reply_to_message_id = reply_msg.platform_message_id\n LEFT JOIN member reply_m ON reply_msg.sender_id = reply_m.id`\n\nexport const FULL_MSG_SELECT = `SELECT ${FULL_MSG_COLUMNS} ${FULL_MSG_FROM}`\n\nexport const MSG_COUNT_FROM = `FROM message msg JOIN member m ON msg.sender_id = m.id`\n\nexport const SYSTEM_MSG_FILTER = \"COALESCE(m.account_name, '') != '系统消息'\"\nexport const TEXT_ONLY_FILTER = \"msg.type = 0 AND msg.content IS NOT NULL AND msg.content != ''\"\n\n// ==================== Types ====================\n\nexport interface FullMessageRow {\n id: number\n senderId: number\n senderName: string\n senderPlatformId: string\n aliasesJson: string\n senderAvatar: string | null\n content: string | null\n timestamp: number\n type: number\n replyToMessageId: string | null\n replyToContent: string | null\n replyToSenderName: string | null\n}\n\nexport interface MappedMessage {\n id: number\n senderId: number\n senderName: string\n senderPlatformId: string\n senderAliases: string[]\n senderAvatar: string | null\n content: string\n timestamp: number\n type: number\n replyToMessageId: string | null\n replyToContent: string | null\n replyToSenderName: string | null\n}\n\n// ==================== Row mapper ====================\n\nexport function mapMessageRow(row: FullMessageRow): MappedMessage {\n let senderAliases: string[] = []\n try {\n const parsed = JSON.parse(row.aliasesJson || '[]')\n if (Array.isArray(parsed)) senderAliases = parsed\n } catch {\n /* ignore malformed JSON */\n }\n\n return {\n id: Number(row.id),\n senderId: Number(row.senderId),\n senderName: String(row.senderName || ''),\n senderPlatformId: String(row.senderPlatformId || ''),\n senderAliases,\n senderAvatar: row.senderAvatar || null,\n content: row.content != null ? String(row.content) : '',\n timestamp: Number(row.timestamp),\n type: Number(row.type),\n replyToMessageId: row.replyToMessageId || null,\n replyToContent: row.replyToContent || null,\n replyToSenderName: row.replyToSenderName || null,\n }\n}\n\n// ==================== Query builders ====================\n\nexport interface MsgQueryConditions {\n clause: string\n params: unknown[]\n}\n\nexport function buildMsgConditions(options?: {\n startTs?: number\n endTs?: number\n senderId?: number\n memberId?: number | null\n keywords?: string[]\n systemFilter?: boolean\n textOnlyFilter?: boolean\n}): MsgQueryConditions {\n const conds: string[] = []\n const params: unknown[] = []\n\n if (options?.startTs != null) {\n conds.push('msg.ts >= ?')\n params.push(options.startTs)\n }\n if (options?.endTs != null) {\n conds.push('msg.ts <= ?')\n params.push(options.endTs)\n }\n if (options?.senderId != null) {\n conds.push('msg.sender_id = ?')\n params.push(options.senderId)\n }\n if (options?.memberId != null) {\n conds.push('msg.sender_id = ?')\n params.push(options.memberId)\n }\n if (options?.keywords && options.keywords.length > 0) {\n const kwConds = options.keywords.map(() => 'msg.content LIKE ?')\n conds.push(`(${kwConds.join(' OR ')})`)\n params.push(...options.keywords.map((k) => `%${k}%`))\n }\n if (options?.systemFilter) {\n conds.push(SYSTEM_MSG_FILTER)\n }\n if (options?.textOnlyFilter) {\n conds.push(TEXT_ONLY_FILTER)\n }\n\n return {\n clause: conds.length > 0 ? 'AND ' + conds.join(' AND ') : '',\n params,\n }\n}\n","/**\n * 消息查询模块(平台无关)\n *\n * 提供消息搜索、分页等基础查询能力。\n * 复杂的 FTS 搜索留在 Electron/Server 层处理(依赖分词器)。\n */\n\nimport type { TimeFilter } from '@openchatlab/shared-types'\nimport type { DatabaseAdapter } from '../interfaces'\nimport { buildTimeFilter, hasTable, hasColumn } from './filters'\nimport {\n FULL_MSG_SELECT,\n mapMessageRow,\n buildMsgConditions,\n type FullMessageRow,\n type MappedMessage,\n} from './message-sql'\n\nexport interface MessageResult {\n id: number\n senderId: number\n senderName: string\n senderPlatformId: string\n content: string\n timestamp: number\n type: number\n}\n\nexport interface PaginatedMessages {\n messages: MessageResult[]\n hasMore: boolean\n total?: number\n}\n\nexport interface QueryMessagesOptions {\n keyword?: string\n startTs?: number\n endTs?: number\n senderId?: number\n limit?: number\n offset?: number\n}\n\nexport interface QueryMessagesResult {\n messages: MessageResult[]\n total: number\n page: number\n limit: number\n totalPages: number\n}\n\n/**\n * 通用分页消息查询\n * 支持关键词、时间范围、发送者过滤\n */\nexport function queryMessages(db: DatabaseAdapter, options?: QueryMessagesOptions): QueryMessagesResult {\n const limit = Math.min(1000, Math.max(1, options?.limit ?? 100))\n const offset = options?.offset ?? 0\n const page = Math.floor(offset / limit) + 1\n\n const conditions: string[] = [\"COALESCE(m.account_name, '') != '系统消息'\"]\n const params: unknown[] = []\n\n if (options?.keyword) {\n conditions.push('msg.content LIKE ?')\n params.push(`%${options.keyword}%`)\n }\n if (options?.startTs !== undefined) {\n conditions.push('msg.ts >= ?')\n params.push(options.startTs)\n }\n if (options?.endTs !== undefined) {\n conditions.push('msg.ts <= ?')\n params.push(options.endTs)\n }\n if (options?.senderId !== undefined) {\n conditions.push('msg.sender_id = ?')\n params.push(options.senderId)\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''\n\n const countRow = db\n .prepare(\n `SELECT COUNT(*) as total\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ${where}`\n )\n .get(...params) as { total: number }\n\n const rows = db\n .prepare(\n `SELECT\n msg.id as id,\n m.id as senderId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as senderName,\n m.platform_id as senderPlatformId,\n msg.content as content,\n msg.ts as timestamp,\n msg.type as type\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ${where}\n ORDER BY msg.ts DESC\n LIMIT ? OFFSET ?`\n )\n .all(...params, limit, offset) as Array<{\n id: number\n senderId: number\n senderName: string\n senderPlatformId: string\n content: string\n timestamp: number\n type: number\n }>\n\n const messages = rows.map((row) => ({\n id: Number(row.id),\n senderId: Number(row.senderId),\n senderName: String(row.senderName || ''),\n senderPlatformId: String(row.senderPlatformId || ''),\n content: row.content != null ? String(row.content) : '',\n timestamp: Number(row.timestamp),\n type: Number(row.type),\n }))\n\n const total = countRow.total\n return {\n messages,\n total,\n page,\n limit,\n totalPages: Math.ceil(total / limit),\n }\n}\n\n/**\n * 基于 LIKE 的简单关键词搜索\n * 不依赖 FTS 索引,适用于 CLI/MCP 场景\n */\nexport function searchMessagesLike(\n db: DatabaseAdapter,\n keyword: string,\n options?: { limit?: number; offset?: number }\n): PaginatedMessages {\n const limit = options?.limit ?? 50\n const offset = options?.offset ?? 0\n\n const countRow = db\n .prepare(\n `SELECT COUNT(*) as total\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE msg.content LIKE ? AND COALESCE(m.account_name, '') != '系统消息'`\n )\n .get(`%${keyword}%`) as { total: number }\n\n const rows = db\n .prepare(\n `SELECT\n msg.id as id,\n m.id as senderId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as senderName,\n m.platform_id as senderPlatformId,\n msg.content as content,\n msg.ts as timestamp,\n msg.type as type\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE msg.content LIKE ? AND COALESCE(m.account_name, '') != '系统消息'\n ORDER BY msg.ts DESC\n LIMIT ? OFFSET ?`\n )\n .all(`%${keyword}%`, limit + 1, offset) as Array<{\n id: number\n senderId: number\n senderName: string\n senderPlatformId: string\n content: string\n timestamp: number\n type: number\n }>\n\n const hasMore = rows.length > limit\n const messages = rows.slice(0, limit).map((row) => ({\n id: Number(row.id),\n senderId: Number(row.senderId),\n senderName: String(row.senderName || ''),\n senderPlatformId: String(row.senderPlatformId || ''),\n content: row.content != null ? String(row.content) : '',\n timestamp: Number(row.timestamp),\n type: Number(row.type),\n }))\n\n return { messages, hasMore, total: countRow.total }\n}\n\n/**\n * 多关键词 LIKE 子串搜索(关键词之间 OR,命中任一即返回)\n *\n * 适用于 CLI/MCP/Web 等无 FTS 的场景,行为与 Electron worker 的\n * searchMessagesLikeAsync 一致:支持时间范围、发送者过滤,并排除系统消息。\n */\nexport function searchMessagesByKeywords(\n db: DatabaseAdapter,\n keywords: string[],\n options?: { startTs?: number; endTs?: number; senderId?: number; limit?: number; offset?: number }\n): PaginatedMessages {\n const limit = options?.limit ?? 50\n const offset = options?.offset ?? 0\n const cleaned = keywords.map((k) => k.trim()).filter((k) => k.length > 0)\n\n const { clause, params } = buildMsgConditions({\n startTs: options?.startTs,\n endTs: options?.endTs,\n senderId: options?.senderId,\n keywords: cleaned.length > 0 ? cleaned : undefined,\n systemFilter: true,\n })\n\n const countRow = db\n .prepare(`SELECT COUNT(*) as total FROM message msg JOIN member m ON msg.sender_id = m.id WHERE 1=1 ${clause}`)\n .get(...params) as { total: number }\n\n const rows = db\n .prepare(`${FULL_MSG_SELECT} WHERE 1=1 ${clause} ORDER BY msg.ts DESC LIMIT ? OFFSET ?`)\n .all(...params, limit + 1, offset) as unknown as FullMessageRow[]\n\n const hasMore = rows.length > limit\n const messages = rows.slice(0, limit).map((row) => {\n const mapped = mapMessageRow(row)\n return {\n id: mapped.id,\n senderId: mapped.senderId,\n senderName: mapped.senderName,\n senderPlatformId: mapped.senderPlatformId,\n content: mapped.content,\n timestamp: mapped.timestamp,\n type: mapped.type,\n }\n })\n\n return { messages, hasMore, total: countRow.total }\n}\n\n/**\n * 获取最近 N 条消息\n */\nexport function getRecentMessages(db: DatabaseAdapter, options?: { limit?: number }): MessageResult[] {\n const limit = options?.limit ?? 50\n\n const rows = db\n .prepare(\n `SELECT\n msg.id as id,\n m.id as senderId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as senderName,\n m.platform_id as senderPlatformId,\n msg.content as content,\n msg.ts as timestamp,\n msg.type as type\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE COALESCE(m.account_name, '') != '系统消息'\n ORDER BY msg.ts DESC\n LIMIT ?`\n )\n .all(limit) as Array<{\n id: number\n senderId: number\n senderName: string\n senderPlatformId: string\n content: string\n timestamp: number\n type: number\n }>\n\n return rows.map((row) => ({\n id: Number(row.id),\n senderId: Number(row.senderId),\n senderName: String(row.senderName || ''),\n senderPlatformId: String(row.senderPlatformId || ''),\n content: row.content != null ? String(row.content) : '',\n timestamp: Number(row.timestamp),\n type: Number(row.type),\n }))\n}\n\n/**\n * 获取成员列表\n */\nexport function getMembers(\n db: DatabaseAdapter\n): Array<{ id: number; platformId: string; name: string; messageCount: number }> {\n return db\n .prepare(\n `SELECT\n m.id as id,\n m.platform_id as platformId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,\n (SELECT COUNT(*) FROM message WHERE sender_id = m.id) as messageCount\n FROM member m\n WHERE COALESCE(m.account_name, '') != '系统消息'\n ORDER BY messageCount DESC`\n )\n .all() as Array<{ id: number; platformId: string; name: string; messageCount: number }>\n}\n\nexport interface MemberDetailed {\n id: number\n platformId: string\n accountName: string\n groupNickname: string | null\n messageCount: number\n}\n\n/**\n * 获取完整字段的成员列表(含 accountName、groupNickname)\n */\nexport function getMembersDetailed(db: DatabaseAdapter): MemberDetailed[] {\n const rows = db\n .prepare(\n `SELECT\n m.id as id,\n m.platform_id as platformId,\n COALESCE(m.account_name, m.platform_id) as accountName,\n m.group_nickname as groupNickname,\n (SELECT COUNT(*) FROM message WHERE sender_id = m.id) as messageCount\n FROM member m\n WHERE COALESCE(m.account_name, '') != '系统消息'\n ORDER BY messageCount DESC`\n )\n .all() as Array<{\n id: number\n platformId: string\n accountName: string\n groupNickname: string | null\n messageCount: number\n }>\n\n return rows.map((row) => ({\n id: Number(row.id),\n platformId: String(row.platformId),\n accountName: String(row.accountName || row.platformId),\n groupNickname: row.groupNickname ? String(row.groupNickname) : null,\n messageCount: Number(row.messageCount),\n }))\n}\n\n/**\n * 执行只读 SQL 查询(SQL Lab)— 保留向后兼容签名\n */\nexport function executeReadonlySql(\n db: DatabaseAdapter,\n sql: string,\n maxRows: number = 1000\n): { columns: string[]; rows: Record<string, unknown>[]; rowCount: number; truncated: boolean } {\n const result = executeSql(db, sql, { maxRows })\n return {\n columns: result.columns,\n rows: result.rows as Record<string, unknown>[],\n rowCount: result.rowCount,\n truncated: result.truncated,\n }\n}\n\n// ==================== Unified SQL Execution ====================\n\nexport interface SqlExecutionOptions {\n /** Maximum rows to return. 0 = no limit. Default: 1000. */\n maxRows?: number\n /** Return rows as 2D arrays instead of objects. Default: false. */\n columnar?: boolean\n /** Include execution duration in result. Default: false. */\n timing?: boolean\n}\n\nexport interface SqlExecutionResult {\n columns: string[]\n rows: Record<string, unknown>[] | unknown[][]\n rowCount: number\n truncated: boolean\n duration?: number\n}\n\n/**\n * Unified readonly SQL execution.\n *\n * Safety: uses stmt.readonly when available (better-sqlite3 native check),\n * falls back to keyword denylist for adapters that don't expose it.\n */\nexport function executeSql(db: DatabaseAdapter, sql: string, options?: SqlExecutionOptions): SqlExecutionResult {\n const maxRows = options?.maxRows ?? 1000\n const columnar = options?.columnar ?? false\n const timing = options?.timing ?? false\n\n const trimmed = sql.trim()\n\n const startTime = timing ? Date.now() : 0\n\n const stmt = db.prepare(trimmed)\n\n if (stmt.readonly === false) {\n throw new Error('Only read-only statements are allowed (SELECT / WITH)')\n }\n\n if (stmt.readonly === undefined) {\n const forbidden = /^\\s*(INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|ATTACH|DETACH|REINDEX|VACUUM|PRAGMA)/i\n if (forbidden.test(trimmed)) {\n throw new Error('Only SELECT queries are allowed')\n }\n }\n\n const needsLimit = maxRows > 0 && !/\\bLIMIT\\b/i.test(trimmed)\n let allRows: Record<string, unknown>[]\n\n if (needsLimit) {\n const safeSql = `${trimmed} LIMIT ${maxRows + 1}`\n allRows = db.prepare(safeSql).all() as Record<string, unknown>[]\n } else {\n allRows = stmt.all() as Record<string, unknown>[]\n }\n\n const truncated = maxRows > 0 && allRows.length > maxRows\n const resultRows = truncated ? allRows.slice(0, maxRows) : allRows\n const columns = resultRows.length > 0 ? Object.keys(resultRows[0]) : []\n\n const duration = timing ? Date.now() - startTime : undefined\n\n if (columnar) {\n const rows2d = resultRows.map((row) => columns.map((col) => row[col]))\n return { columns, rows: rows2d, rowCount: rows2d.length, truncated, duration }\n }\n\n return { columns, rows: resultRows, rowCount: resultRows.length, truncated, duration }\n}\n\n/** Table schema with column details */\nexport interface TableSchema {\n name: string\n columns: Array<{\n name: string\n type: string\n notnull: boolean\n pk: boolean\n }>\n}\n\n/**\n * Get database schema with column-level details.\n * Returns table names + full column info via PRAGMA table_info.\n */\nexport function getSchemaDetailed(db: DatabaseAdapter): TableSchema[] {\n const tables = db\n .prepare(\n `SELECT name FROM sqlite_master\n WHERE type='table' AND name NOT LIKE 'sqlite_%'\n ORDER BY name`\n )\n .all() as Array<{ name: string }>\n\n return tables.map((table) => {\n const columns = db.prepare(`PRAGMA table_info('${table.name}')`).all() as Array<{\n cid: number\n name: string\n type: string\n notnull: number\n dflt_value: unknown\n pk: number\n }>\n\n return {\n name: table.name,\n columns: columns.map((col) => ({\n name: col.name,\n type: col.type,\n notnull: col.notnull === 1,\n pk: col.pk === 1,\n })),\n }\n })\n}\n\n// ==================== Context & Conversation Queries ====================\n\nexport interface ContextMessage {\n id: number\n senderId: number\n senderName: string\n senderPlatformId: string\n content: string | null\n timestamp: number\n}\n\nexport interface ConversationData {\n messages: ContextMessage[]\n total: number\n member1Name: string\n member2Name: string\n}\n\nexport interface MemberNameHistoryEntry {\n nameType: string\n name: string\n startTs: number\n endTs: number | null\n}\n\nfunction getMemberNameHistoryFromMessages(db: DatabaseAdapter, memberId: number): MemberNameHistoryEntry[] {\n const rows = db\n .prepare(\n `SELECT\n sender_account_name as accountName,\n sender_group_nickname as groupNickname,\n MIN(ts) as startTs,\n MAX(ts) as endTs\n FROM message\n WHERE sender_id = ?\n GROUP BY sender_account_name, sender_group_nickname\n ORDER BY startTs`\n )\n .all(memberId) as unknown as Array<{\n accountName: string | null\n groupNickname: string | null\n startTs: number\n endTs: number | null\n }>\n\n const history: MemberNameHistoryEntry[] = []\n for (const row of rows) {\n if (row.accountName) {\n history.push({ nameType: 'account_name', name: row.accountName, startTs: row.startTs, endTs: row.endTs })\n }\n if (row.groupNickname) {\n history.push({ nameType: 'group_nickname', name: row.groupNickname, startTs: row.startTs, endTs: row.endTs })\n }\n }\n return history\n}\n\nexport interface MemberWithAliases {\n id: number\n platformId: string\n accountName: string | null\n groupNickname: string | null\n aliases: string[]\n avatar: string | null\n messageCount: number\n}\n\nexport interface MembersPaginationParams {\n page?: number\n pageSize?: number\n search?: string\n sortOrder?: 'asc' | 'desc'\n}\n\nexport interface MembersPaginatedResult {\n members: MemberWithAliases[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\n/**\n * Batch-load messages by IDs with full sender info (avatar, aliases, reply, etc.)\n */\nfunction hydrateMessagesByIds(db: DatabaseAdapter, ids: number[]): MappedMessage[] {\n if (ids.length === 0) return []\n const placeholders = ids.map(() => '?').join(', ')\n const rows = db\n .prepare(`${FULL_MSG_SELECT} WHERE msg.id IN (${placeholders}) ORDER BY msg.id ASC`)\n .all(...ids) as unknown as FullMessageRow[]\n return rows.map(mapMessageRow)\n}\n\n/**\n * Get surrounding context messages for given message IDs.\n * Uses simple id-based ordering (not session-aware).\n */\nexport function getMessageContext(\n db: DatabaseAdapter,\n messageIds: number[],\n contextSize: number = 20\n): MappedMessage[] {\n if (messageIds.length === 0) return []\n\n const contextIds = new Set<number>()\n\n for (const messageId of messageIds) {\n contextIds.add(messageId)\n\n const beforeRows = db\n .prepare('SELECT id FROM message WHERE id < ? ORDER BY id DESC LIMIT ?')\n .all(messageId, contextSize) as { id: number }[]\n beforeRows.forEach((r) => contextIds.add(r.id))\n\n const afterRows = db\n .prepare('SELECT id FROM message WHERE id > ? ORDER BY id ASC LIMIT ?')\n .all(messageId, contextSize) as { id: number }[]\n afterRows.forEach((r) => contextIds.add(r.id))\n }\n\n return hydrateMessagesByIds(db, Array.from(contextIds))\n}\n\n/**\n * Get context messages around search results.\n * Session-aware when message_context table is available, falls back to id-based ordering.\n */\nexport function getSearchMessageContext(\n db: DatabaseAdapter,\n messageIds: number[],\n contextBefore: number = 2,\n contextAfter: number = 2\n): MappedMessage[] {\n if (messageIds.length === 0) return []\n\n const contextIds = new Set<number>()\n\n const hasSessionData =\n hasTable(db, 'message_context') &&\n (db.prepare('SELECT 1 FROM message_context LIMIT 1').get() as Record<string, unknown> | undefined) !== undefined\n\n for (const messageId of messageIds) {\n contextIds.add(messageId)\n\n if (hasSessionData) {\n const sessionRow = db.prepare('SELECT segment_id FROM message_context WHERE message_id = ?').get(messageId) as\n | { segment_id: number }\n | undefined\n\n if (sessionRow) {\n if (contextBefore > 0) {\n const rows = db\n .prepare(\n `SELECT mc.message_id as id FROM message_context mc\n WHERE mc.segment_id = ? AND mc.message_id < ?\n ORDER BY mc.message_id DESC LIMIT ?`\n )\n .all(sessionRow.segment_id, messageId, contextBefore) as { id: number }[]\n rows.forEach((r) => contextIds.add(r.id))\n }\n if (contextAfter > 0) {\n const rows = db\n .prepare(\n `SELECT mc.message_id as id FROM message_context mc\n WHERE mc.segment_id = ? AND mc.message_id > ?\n ORDER BY mc.message_id ASC LIMIT ?`\n )\n .all(sessionRow.segment_id, messageId, contextAfter) as { id: number }[]\n rows.forEach((r) => contextIds.add(r.id))\n }\n continue\n }\n }\n\n if (contextBefore > 0) {\n const rows = db\n .prepare('SELECT id FROM message WHERE id < ? ORDER BY id DESC LIMIT ?')\n .all(messageId, contextBefore) as { id: number }[]\n rows.forEach((r) => contextIds.add(r.id))\n }\n if (contextAfter > 0) {\n const rows = db\n .prepare('SELECT id FROM message WHERE id > ? ORDER BY id ASC LIMIT ?')\n .all(messageId, contextAfter) as { id: number }[]\n rows.forEach((r) => contextIds.add(r.id))\n }\n }\n\n return hydrateMessagesByIds(db, Array.from(contextIds))\n}\n\n/**\n * Get conversation messages between two members\n */\nexport function getConversationBetween(\n db: DatabaseAdapter,\n memberId1: number,\n memberId2: number,\n filter?: TimeFilter,\n limit: number = 100\n): ConversationData {\n const member1 = db\n .prepare('SELECT COALESCE(group_nickname, account_name, platform_id) as name FROM member WHERE id = ?')\n .get(memberId1) as { name: string } | undefined\n\n const member2 = db\n .prepare('SELECT COALESCE(group_nickname, account_name, platform_id) as name FROM member WHERE id = ?')\n .get(memberId2) as { name: string } | undefined\n\n if (!member1 || !member2) {\n return { messages: [], total: 0, member1Name: '', member2Name: '' }\n }\n\n const { clause: timeClause, params: timeParams } = buildTimeFilter(filter, 'msg')\n const timeCondition = timeClause ? timeClause.replace('WHERE', 'AND') : ''\n\n const countSql = `\n SELECT COUNT(*) as total FROM message msg\n WHERE msg.sender_id IN (?, ?) ${timeCondition}\n AND msg.content IS NOT NULL AND msg.content != ''\n `\n const totalRow = db.prepare(countSql).get(memberId1, memberId2, ...timeParams) as { total: number }\n\n const sql = `\n SELECT\n msg.id as id, m.id as senderId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as senderName,\n m.platform_id as senderPlatformId,\n msg.content as content, msg.ts as timestamp\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE msg.sender_id IN (?, ?) ${timeCondition}\n AND msg.content IS NOT NULL AND msg.content != ''\n ORDER BY msg.ts DESC LIMIT ?\n `\n const rows = db.prepare(sql).all(memberId1, memberId2, ...timeParams, limit) as unknown as ContextMessage[]\n\n return {\n messages: rows.reverse(),\n total: totalRow?.total || 0,\n member1Name: member1.name,\n member2Name: member2.name,\n }\n}\n\n/**\n * Get name change history for a member\n */\nexport function getMemberNameHistory(db: DatabaseAdapter, memberId: number): MemberNameHistoryEntry[] {\n if (!hasTable(db, 'member_name_history')) return getMemberNameHistoryFromMessages(db, memberId)\n\n const history = db\n .prepare(\n `SELECT name_type as nameType, name, start_ts as startTs, end_ts as endTs\n FROM member_name_history WHERE member_id = ? ORDER BY start_ts DESC`\n )\n .all(memberId) as unknown as MemberNameHistoryEntry[]\n\n return history.length > 0 ? history : getMemberNameHistoryFromMessages(db, memberId)\n}\n\n/**\n * Get member list with aliases, avatar and detailed info\n */\nexport function getMembersWithAliases(db: DatabaseAdapter): MemberWithAliases[] {\n const aliasesAvailable = hasColumn(db, 'member', 'aliases')\n const avatarAvailable = hasColumn(db, 'member', 'avatar')\n\n const aliasesSelect = aliasesAvailable ? 'm.aliases' : 'NULL as aliases'\n const avatarSelect = avatarAvailable ? 'm.avatar' : 'NULL as avatar'\n const rows = db\n .prepare(\n `SELECT\n m.id, m.platform_id as platformId,\n m.account_name as accountName, m.group_nickname as groupNickname,\n ${aliasesSelect}, ${avatarSelect},\n COUNT(msg.id) as messageCount\n FROM member m\n LEFT JOIN message msg ON m.id = msg.sender_id\n WHERE COALESCE(m.group_nickname, m.account_name, m.platform_id) != '系统消息'\n GROUP BY m.id ORDER BY messageCount DESC`\n )\n .all() as Array<{\n id: number\n platformId: string\n accountName: string | null\n groupNickname: string | null\n aliases: string | null\n avatar: string | null\n messageCount: number\n }>\n\n return rows.map(mapMemberRow)\n}\n\n/**\n * Paginated member list with search and sort.\n */\nexport function getMembersPaginated(db: DatabaseAdapter, params: MembersPaginationParams): MembersPaginatedResult {\n const page = Math.max(1, params.page ?? 1)\n const pageSize = Math.min(100, Math.max(1, params.pageSize ?? 20))\n const search = params.search?.trim() || ''\n const sortDirection = params.sortOrder === 'asc' ? 'ASC' : 'DESC'\n\n const aliasesAvailable = hasColumn(db, 'member', 'aliases')\n const avatarAvailable = hasColumn(db, 'member', 'avatar')\n const aliasesSelect = aliasesAvailable ? 'm.aliases' : 'NULL as aliases'\n const avatarSelect = avatarAvailable ? 'm.avatar' : 'NULL as avatar'\n\n let searchClause = ''\n const searchParams: unknown[] = []\n if (search) {\n const clauses = ['m.account_name LIKE ?', 'm.group_nickname LIKE ?', 'm.platform_id LIKE ?']\n const like = `%${search}%`\n searchParams.push(like, like, like)\n if (aliasesAvailable) {\n clauses.push('m.aliases LIKE ?')\n searchParams.push(like)\n }\n searchClause = `AND (${clauses.join(' OR ')})`\n }\n\n const systemFilter = \"COALESCE(m.group_nickname, m.account_name, m.platform_id) != '系统消息'\"\n\n const countRow = db\n .prepare(\n `SELECT COUNT(*) as total FROM (\n SELECT m.id FROM member m\n LEFT JOIN message msg ON m.id = msg.sender_id\n WHERE ${systemFilter} ${searchClause}\n GROUP BY m.id\n )`\n )\n .get(...searchParams) as { total: number } | undefined\n\n const total = countRow?.total ?? 0\n const totalPages = Math.ceil(total / pageSize)\n const offset = (page - 1) * pageSize\n\n const rows = db\n .prepare(\n `SELECT\n m.id, m.platform_id as platformId,\n m.account_name as accountName, m.group_nickname as groupNickname,\n ${aliasesSelect}, ${avatarSelect},\n COUNT(msg.id) as messageCount\n FROM member m\n LEFT JOIN message msg ON m.id = msg.sender_id\n WHERE ${systemFilter} ${searchClause}\n GROUP BY m.id\n ORDER BY messageCount ${sortDirection}\n LIMIT ? OFFSET ?`\n )\n .all(...searchParams, pageSize, offset) as Array<{\n id: number\n platformId: string\n accountName: string | null\n groupNickname: string | null\n aliases: string | null\n avatar: string | null\n messageCount: number\n }>\n\n return { members: rows.map(mapMemberRow), total, page, pageSize, totalPages }\n}\n\nfunction mapMemberRow(row: {\n id: number\n platformId: string\n accountName: string | null\n groupNickname: string | null\n aliases: string | null\n avatar: string | null\n messageCount: number\n}): MemberWithAliases {\n return {\n id: row.id,\n platformId: row.platformId,\n accountName: row.accountName,\n groupNickname: row.groupNickname,\n aliases: row.aliases ? JSON.parse(row.aliases) : [],\n avatar: row.avatar ?? null,\n messageCount: row.messageCount,\n }\n}\n\n/**\n * Execute a parameterized read-only SQL query with named bindings.\n * Used by SQL analysis tools to run predefined queries with user-supplied parameters.\n */\nexport function executeParameterizedSql<T = Record<string, unknown>>(\n db: DatabaseAdapter,\n query: string,\n params: Record<string, unknown> = {}\n): T[] {\n const trimmed = query.trim()\n\n const forbidden = /^\\s*(INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|ATTACH|DETACH|REINDEX|VACUUM|PRAGMA)/i\n if (forbidden.test(trimmed)) {\n throw new Error('Only SELECT queries are allowed')\n }\n\n const stmt = db.prepare(trimmed)\n\n if (stmt.readonly === false) {\n throw new Error('Only READ-ONLY statements are allowed')\n }\n\n return stmt.all(params) as T[]\n}\n","/**\n * 社交分析模块(平台无关)\n * 包含:@ 互动分析、含笑量分析、小团体关系图\n */\n\nimport type { TimeFilter } from '@openchatlab/shared-types'\nimport type { DatabaseAdapter } from '../../interfaces'\nimport { buildTimeFilter } from '../filters'\n\n// ==================== @ 互动分析 ====================\n\nexport function getMentionAnalysis(db: DatabaseAdapter, filter?: TimeFilter): any {\n const emptyResult = {\n topMentioners: [],\n topMentioned: [],\n oneWay: [],\n twoWay: [],\n totalMentions: 0,\n memberDetails: [],\n }\n\n const members = db\n .prepare(\n `SELECT id, platform_id as platformId, COALESCE(group_nickname, account_name, platform_id) as name\n FROM member WHERE COALESCE(account_name, '') != '系统消息'`\n )\n .all() as Array<{ id: number; platformId: string; name: string }>\n\n if (members.length === 0) return emptyResult\n\n const nameToMemberId = new Map<string, number>()\n const memberIdToInfo = new Map<number, { platformId: string; name: string }>()\n\n for (const member of members) {\n memberIdToInfo.set(member.id, { platformId: member.platformId, name: member.name })\n nameToMemberId.set(member.name, member.id)\n\n const history = db.prepare('SELECT name FROM member_name_history WHERE member_id = ?').all(member.id) as Array<{\n name: string\n }>\n\n for (const h of history) {\n if (!nameToMemberId.has(h.name)) {\n nameToMemberId.set(h.name, member.id)\n }\n }\n }\n\n const { clause, params } = buildTimeFilter(filter)\n let whereClause = clause\n if (whereClause.includes('WHERE')) {\n whereClause +=\n \" AND COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL AND msg.content LIKE '%@%'\"\n } else {\n whereClause =\n \" WHERE COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL AND msg.content LIKE '%@%'\"\n }\n\n const messages = db\n .prepare(\n `SELECT msg.sender_id as senderId, msg.content\n FROM message msg JOIN member m ON msg.sender_id = m.id ${whereClause}`\n )\n .all(...params) as Array<{ senderId: number; content: string }>\n\n const mentionMatrix = new Map<number, Map<number, number>>()\n const mentionedCount = new Map<number, number>()\n const mentionerCount = new Map<number, number>()\n let totalMentions = 0\n const mentionRegex = /@([^\\s@]+)/g\n\n for (const msg of messages) {\n const matches = msg.content.matchAll(mentionRegex)\n const mentionedInThisMsg = new Set<number>()\n\n for (const match of matches) {\n const mentionedId = nameToMemberId.get(match[1])\n if (mentionedId && mentionedId !== msg.senderId && !mentionedInThisMsg.has(mentionedId)) {\n mentionedInThisMsg.add(mentionedId)\n totalMentions++\n\n if (!mentionMatrix.has(msg.senderId)) mentionMatrix.set(msg.senderId, new Map())\n const fromMap = mentionMatrix.get(msg.senderId)!\n fromMap.set(mentionedId, (fromMap.get(mentionedId) || 0) + 1)\n\n mentionerCount.set(msg.senderId, (mentionerCount.get(msg.senderId) || 0) + 1)\n mentionedCount.set(mentionedId, (mentionedCount.get(mentionedId) || 0) + 1)\n }\n }\n }\n\n if (totalMentions === 0) return emptyResult\n\n const topMentioners: any[] = []\n for (const [memberId, count] of mentionerCount.entries()) {\n const info = memberIdToInfo.get(memberId)!\n topMentioners.push({\n memberId,\n platformId: info.platformId,\n name: info.name,\n count,\n percentage: Math.round((count / totalMentions) * 10000) / 100,\n })\n }\n topMentioners.sort((a, b) => b.count - a.count)\n\n const topMentioned: any[] = []\n for (const [memberId, count] of mentionedCount.entries()) {\n const info = memberIdToInfo.get(memberId)!\n topMentioned.push({\n memberId,\n platformId: info.platformId,\n name: info.name,\n count,\n percentage: Math.round((count / totalMentions) * 10000) / 100,\n })\n }\n topMentioned.sort((a, b) => b.count - a.count)\n\n const oneWay: any[] = []\n const processedPairs = new Set<string>()\n for (const [fromId, toMap] of mentionMatrix.entries()) {\n for (const [toId, fromToCount] of toMap.entries()) {\n const pairKey = `${Math.min(fromId, toId)}-${Math.max(fromId, toId)}`\n if (processedPairs.has(pairKey)) continue\n processedPairs.add(pairKey)\n const toFromCount = mentionMatrix.get(toId)?.get(fromId) || 0\n const total = fromToCount + toFromCount\n if (total < 3) continue\n const ratio = fromToCount / total\n if (ratio >= 0.8) {\n const fromInfo = memberIdToInfo.get(fromId)!\n const toInfo = memberIdToInfo.get(toId)!\n oneWay.push({\n fromMemberId: fromId,\n fromName: fromInfo.name,\n toMemberId: toId,\n toName: toInfo.name,\n fromToCount,\n toFromCount,\n ratio: Math.round(ratio * 100) / 100,\n })\n } else if (ratio <= 0.2) {\n const fromInfo = memberIdToInfo.get(fromId)!\n const toInfo = memberIdToInfo.get(toId)!\n oneWay.push({\n fromMemberId: toId,\n fromName: toInfo.name,\n toMemberId: fromId,\n toName: fromInfo.name,\n fromToCount: toFromCount,\n toFromCount: fromToCount,\n ratio: Math.round((1 - ratio) * 100) / 100,\n })\n }\n }\n }\n oneWay.sort((a, b) => b.fromToCount - a.fromToCount)\n\n const twoWay: any[] = []\n processedPairs.clear()\n for (const [fromId, toMap] of mentionMatrix.entries()) {\n for (const [toId, fromToCount] of toMap.entries()) {\n const pairKey = `${Math.min(fromId, toId)}-${Math.max(fromId, toId)}`\n if (processedPairs.has(pairKey)) continue\n processedPairs.add(pairKey)\n const toFromCount = mentionMatrix.get(toId)?.get(fromId) || 0\n const total = fromToCount + toFromCount\n if (total < 5 || toFromCount === 0 || fromToCount === 0) continue\n const ratio = Math.min(fromToCount, toFromCount) / Math.max(fromToCount, toFromCount)\n if (ratio >= 0.3) {\n const m1Info = memberIdToInfo.get(fromId)!\n const m2Info = memberIdToInfo.get(toId)!\n twoWay.push({\n member1Id: fromId,\n member1Name: m1Info.name,\n member2Id: toId,\n member2Name: m2Info.name,\n member1To2: fromToCount,\n member2To1: toFromCount,\n total,\n balance: Math.round(ratio * 100) / 100,\n })\n }\n }\n }\n twoWay.sort((a, b) => b.total - a.total)\n\n const memberDetails: any[] = []\n for (const member of members) {\n const info = memberIdToInfo.get(member.id)!\n const topMentionedByThis: any[] = []\n const toMap = mentionMatrix.get(member.id)\n if (toMap) {\n for (const [toId, count] of toMap.entries()) {\n const toInfo = memberIdToInfo.get(toId)!\n topMentionedByThis.push({\n fromMemberId: member.id,\n fromName: info.name,\n toMemberId: toId,\n toName: toInfo.name,\n count,\n })\n }\n topMentionedByThis.sort((a, b) => b.count - a.count)\n }\n const topMentionersOfThis: any[] = []\n for (const [fromId, fToMap] of mentionMatrix.entries()) {\n const count = fToMap.get(member.id)\n if (count) {\n const fromInfo = memberIdToInfo.get(fromId)!\n topMentionersOfThis.push({\n fromMemberId: fromId,\n fromName: fromInfo.name,\n toMemberId: member.id,\n toName: info.name,\n count,\n })\n }\n }\n topMentionersOfThis.sort((a, b) => b.count - a.count)\n if (topMentionedByThis.length > 0 || topMentionersOfThis.length > 0) {\n memberDetails.push({\n memberId: member.id,\n name: info.name,\n topMentioned: topMentionedByThis.slice(0, 5),\n topMentioners: topMentionersOfThis.slice(0, 5),\n })\n }\n }\n\n return { topMentioners, topMentioned, oneWay, twoWay, totalMentions, memberDetails }\n}\n\n// ==================== @ 互动关系图数据 ====================\n\nexport interface MentionGraphNode {\n id: number\n name: string\n value: number\n symbolSize: number\n}\n\nexport interface MentionGraphLink {\n source: string\n target: string\n value: number\n}\n\nexport interface MentionGraphData {\n nodes: MentionGraphNode[]\n links: MentionGraphLink[]\n maxLinkValue: number\n}\n\nexport function getMentionGraph(db: DatabaseAdapter, filter?: TimeFilter): MentionGraphData {\n const emptyResult: MentionGraphData = { nodes: [], links: [], maxLinkValue: 0 }\n\n const { clause, params } = buildTimeFilter(filter)\n const msgFilterBase = clause ? clause.replace('WHERE', 'AND') : ''\n const msgFilterWithSystem = msgFilterBase + \" AND COALESCE(m.account_name, '') != '系统消息'\"\n\n const members = db\n .prepare(\n `SELECT m.id, m.platform_id as platformId, COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,\n COUNT(msg.id) as messageCount\n FROM member m LEFT JOIN message msg ON m.id = msg.sender_id ${msgFilterWithSystem}\n WHERE COALESCE(m.account_name, '') != '系统消息' GROUP BY m.id`\n )\n .all(...params) as Array<{ id: number; platformId: string; name: string; messageCount: number }>\n\n if (members.length === 0) return emptyResult\n\n const nameToMemberId = new Map<string, number>()\n const memberIdToInfo = new Map<number, { name: string; messageCount: number }>()\n\n for (const member of members) {\n memberIdToInfo.set(member.id, { name: member.name, messageCount: member.messageCount })\n nameToMemberId.set(member.name, member.id)\n const history = db.prepare('SELECT name FROM member_name_history WHERE member_id = ?').all(member.id) as Array<{\n name: string\n }>\n for (const h of history) {\n if (!nameToMemberId.has(h.name)) nameToMemberId.set(h.name, member.id)\n }\n }\n\n let whereClause = clause\n if (whereClause.includes('WHERE')) {\n whereClause +=\n \" AND COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL AND msg.content LIKE '%@%'\"\n } else {\n whereClause =\n \" WHERE COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL AND msg.content LIKE '%@%'\"\n }\n\n const messages = db\n .prepare(\n `SELECT msg.sender_id as senderId, msg.content FROM message msg JOIN member m ON msg.sender_id = m.id ${whereClause}`\n )\n .all(...params) as Array<{ senderId: number; content: string }>\n\n const mentionMatrix = new Map<number, Map<number, number>>()\n const mentionRegex = /@([^\\s@]+)/g\n\n for (const msg of messages) {\n const matches = msg.content.matchAll(mentionRegex)\n const mentionedInThisMsg = new Set<number>()\n for (const match of matches) {\n const mentionedId = nameToMemberId.get(match[1])\n if (mentionedId && mentionedId !== msg.senderId && !mentionedInThisMsg.has(mentionedId)) {\n mentionedInThisMsg.add(mentionedId)\n if (!mentionMatrix.has(msg.senderId)) mentionMatrix.set(msg.senderId, new Map())\n const fromMap = mentionMatrix.get(msg.senderId)!\n fromMap.set(mentionedId, (fromMap.get(mentionedId) || 0) + 1)\n }\n }\n }\n\n const involvedMemberIds = new Set<number>()\n for (const [fromId, toMap] of mentionMatrix.entries()) {\n involvedMemberIds.add(fromId)\n for (const toId of toMap.keys()) involvedMemberIds.add(toId)\n }\n\n const maxMessageCount = Math.max(...members.filter((m) => involvedMemberIds.has(m.id)).map((m) => m.messageCount), 1)\n const nodes: MentionGraphNode[] = []\n for (const memberId of involvedMemberIds) {\n const info = memberIdToInfo.get(memberId)\n if (info) {\n const symbolSize = 20 + (info.messageCount / maxMessageCount) * 40\n nodes.push({ id: memberId, name: info.name, value: info.messageCount, symbolSize: Math.round(symbolSize) })\n }\n }\n\n const links: MentionGraphLink[] = []\n let maxLinkValue = 0\n for (const [fromId, toMap] of mentionMatrix.entries()) {\n const fromInfo = memberIdToInfo.get(fromId)\n if (!fromInfo) continue\n for (const [toId, count] of toMap.entries()) {\n const toInfo = memberIdToInfo.get(toId)\n if (!toInfo) continue\n links.push({ source: fromInfo.name, target: toInfo.name, value: count })\n maxLinkValue = Math.max(maxLinkValue, count)\n }\n }\n\n return { nodes, links, maxLinkValue }\n}\n\n// ==================== 含笑量分析 ====================\n\nfunction keywordToPattern(keyword: string): string {\n const escaped = keyword.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n if (keyword === '哈哈') return '哈哈+'\n return escaped\n}\n\nexport function getLaughAnalysis(db: DatabaseAdapter, filter?: TimeFilter, keywords?: string[]): any {\n const emptyResult = {\n rankByRate: [],\n rankByCount: [],\n typeDistribution: [],\n totalLaughs: 0,\n totalMessages: 0,\n groupLaughRate: 0,\n }\n const laughKeywords = keywords && keywords.length > 0 ? keywords : []\n const patterns = laughKeywords.map(keywordToPattern)\n const laughRegex = new RegExp(`(${patterns.join('|')})`, 'gi')\n\n const { clause, params } = buildTimeFilter(filter)\n let whereClause = clause\n if (whereClause.includes('WHERE')) {\n whereClause += \" AND COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL\"\n } else {\n whereClause = \" WHERE COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL\"\n }\n\n const messages = db\n .prepare(\n `SELECT msg.sender_id as senderId, msg.content, m.platform_id as platformId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as name\n FROM message msg JOIN member m ON msg.sender_id = m.id ${whereClause}`\n )\n .all(...params) as Array<{ senderId: number; content: string; platformId: string; name: string }>\n\n if (messages.length === 0) return emptyResult\n\n const memberStats = new Map<\n number,\n { platformId: string; name: string; laughCount: number; messageCount: number; keywordCounts: Map<string, number> }\n >()\n const typeCount = new Map<string, number>()\n let totalLaughs = 0\n\n for (const msg of messages) {\n if (!memberStats.has(msg.senderId)) {\n memberStats.set(msg.senderId, {\n platformId: msg.platformId,\n name: msg.name,\n laughCount: 0,\n messageCount: 0,\n keywordCounts: new Map(),\n })\n }\n const stats = memberStats.get(msg.senderId)!\n stats.messageCount++\n const matches = msg.content.match(laughRegex)\n if (matches) {\n stats.laughCount += matches.length\n totalLaughs += matches.length\n for (const match of matches) {\n let matchedType = '其他'\n for (const keyword of laughKeywords) {\n if (new RegExp(`^${keywordToPattern(keyword)}$`, 'i').test(match)) {\n matchedType = keyword\n break\n }\n }\n typeCount.set(matchedType, (typeCount.get(matchedType) || 0) + 1)\n stats.keywordCounts.set(matchedType, (stats.keywordCounts.get(matchedType) || 0) + 1)\n }\n }\n }\n\n if (totalLaughs === 0) return emptyResult\n\n const rankItems: any[] = []\n for (const [memberId, stats] of memberStats.entries()) {\n if (stats.laughCount > 0) {\n const keywordDistribution: Array<{ keyword: string; count: number; percentage: number }> = []\n for (const keyword of laughKeywords) {\n const count = stats.keywordCounts.get(keyword) || 0\n if (count > 0)\n keywordDistribution.push({ keyword, count, percentage: Math.round((count / stats.laughCount) * 10000) / 100 })\n }\n const otherCount = stats.keywordCounts.get('其他') || 0\n if (otherCount > 0)\n keywordDistribution.push({\n keyword: '其他',\n count: otherCount,\n percentage: Math.round((otherCount / stats.laughCount) * 10000) / 100,\n })\n\n rankItems.push({\n memberId,\n platformId: stats.platformId,\n name: stats.name,\n laughCount: stats.laughCount,\n messageCount: stats.messageCount,\n laughRate: Math.round((stats.laughCount / stats.messageCount) * 10000) / 100,\n percentage: Math.round((stats.laughCount / totalLaughs) * 10000) / 100,\n keywordDistribution,\n })\n }\n }\n\n const typeDistribution: any[] = []\n for (const [type, count] of typeCount.entries()) {\n typeDistribution.push({ type, count, percentage: Math.round((count / totalLaughs) * 10000) / 100 })\n }\n typeDistribution.sort((a, b) => b.count - a.count)\n\n return {\n rankByRate: [...rankItems].sort((a, b) => b.laughRate - a.laughRate),\n rankByCount: [...rankItems].sort((a, b) => b.laughCount - a.laughCount),\n typeDistribution,\n totalLaughs,\n totalMessages: messages.length,\n groupLaughRate: Math.round((totalLaughs / messages.length) * 10000) / 100,\n }\n}\n\n// ==================== 小团体关系图 ====================\n\nexport interface ClusterGraphOptions {\n lookAhead?: number\n decaySeconds?: number\n topEdges?: number\n}\n\nexport interface ClusterGraphNode {\n id: number\n name: string\n messageCount: number\n symbolSize: number\n degree: number\n normalizedDegree: number\n}\n\nexport interface ClusterGraphLink {\n source: string\n target: string\n value: number\n rawScore: number\n expectedScore: number\n coOccurrenceCount: number\n}\n\nexport interface ClusterGraphData {\n nodes: ClusterGraphNode[]\n links: ClusterGraphLink[]\n maxLinkValue: number\n communities: Array<{ id: number; name: string; size: number }>\n stats: {\n totalMembers: number\n totalMessages: number\n involvedMembers: number\n edgeCount: number\n communityCount: number\n }\n}\n\nexport interface CoOccurrenceMessage {\n senderId: number\n ts: number\n}\n\nexport interface CoOccurrencePairStats {\n sourceId: number\n targetId: number\n rawScore: number\n coOccurrenceCount: number\n lastOccurrenceTs: number\n}\n\nconst DEFAULT_CLUSTER_OPTIONS = { lookAhead: 3, decaySeconds: 120, topEdges: 100 }\n\nfunction roundNum(value: number, digits = 4): number {\n const factor = 10 ** digits\n return Math.round(value * factor) / factor\n}\n\nfunction clusterPairKey(aId: number, bId: number): string {\n return aId < bId ? `${aId}-${bId}` : `${bId}-${aId}`\n}\n\nexport function accumulateCoOccurrencePairs(\n messages: CoOccurrenceMessage[],\n options?: ClusterGraphOptions\n): CoOccurrencePairStats[] {\n const opts = { ...DEFAULT_CLUSTER_OPTIONS, ...options }\n const pairRawScore = new Map<string, number>()\n const pairCoOccurrence = new Map<string, number>()\n const pairLastOccurrenceTs = new Map<string, number>()\n\n // 与小团体图保持同一口径:按消息顺序向后寻找不同发言人,并用时间衰减和位置权重累计关系强度。\n for (let i = 0; i < messages.length - 1; i++) {\n const anchor = messages[i]\n const seenPartners = new Set<number>()\n let partnersFound = 0\n for (let j = i + 1; j < messages.length && partnersFound < opts.lookAhead; j++) {\n const candidate = messages[j]\n if (candidate.senderId === anchor.senderId || seenPartners.has(candidate.senderId)) continue\n seenPartners.add(candidate.senderId)\n partnersFound++\n const deltaSeconds = candidate.ts - anchor.ts\n const decayWeight = Math.exp(-deltaSeconds / opts.decaySeconds)\n const positionWeight = 1 - (partnersFound - 1) * 0.2\n const weight = decayWeight * positionWeight\n const key = clusterPairKey(anchor.senderId, candidate.senderId)\n pairRawScore.set(key, (pairRawScore.get(key) || 0) + weight)\n pairCoOccurrence.set(key, (pairCoOccurrence.get(key) || 0) + 1)\n pairLastOccurrenceTs.set(key, Math.max(pairLastOccurrenceTs.get(key) ?? 0, candidate.ts))\n }\n }\n\n const pairs: CoOccurrencePairStats[] = []\n for (const [key, rawScore] of pairRawScore) {\n const [sourceIdStr, targetIdStr] = key.split('-')\n pairs.push({\n sourceId: parseInt(sourceIdStr),\n targetId: parseInt(targetIdStr),\n rawScore,\n coOccurrenceCount: pairCoOccurrence.get(key) || 0,\n lastOccurrenceTs: pairLastOccurrenceTs.get(key) ?? 0,\n })\n }\n\n return pairs\n}\n\nexport function getClusterGraph(\n db: DatabaseAdapter,\n filter?: TimeFilter,\n options?: ClusterGraphOptions\n): ClusterGraphData {\n const opts = { ...DEFAULT_CLUSTER_OPTIONS, ...options }\n const emptyResult: ClusterGraphData = {\n nodes: [],\n links: [],\n maxLinkValue: 0,\n communities: [],\n stats: { totalMembers: 0, totalMessages: 0, involvedMembers: 0, edgeCount: 0, communityCount: 0 },\n }\n\n const members = db\n .prepare(\n `SELECT id, platform_id as platformId, COALESCE(group_nickname, account_name, platform_id) as name,\n (SELECT COUNT(*) FROM message WHERE sender_id = member.id) as messageCount\n FROM member WHERE COALESCE(account_name, '') != '系统消息'`\n )\n .all() as Array<{ id: number; platformId: string; name: string; messageCount: number }>\n\n if (members.length < 2) return { ...emptyResult, stats: { ...emptyResult.stats, totalMembers: members.length } }\n\n const memberInfo = new Map<number, { name: string; platformId: string; messageCount: number }>()\n for (const m of members)\n memberInfo.set(m.id, { name: m.name, platformId: m.platformId, messageCount: m.messageCount })\n\n const { clause, params } = buildTimeFilter(filter)\n let whereClause = clause\n if (whereClause.includes('WHERE')) {\n whereClause += \" AND COALESCE(m.account_name, '') != '系统消息'\"\n } else {\n whereClause = \" WHERE COALESCE(m.account_name, '') != '系统消息'\"\n }\n\n const messages = db\n .prepare(\n `SELECT msg.sender_id as senderId, msg.ts as ts FROM message msg JOIN member m ON msg.sender_id = m.id\n ${whereClause} ORDER BY msg.ts ASC, msg.id ASC`\n )\n .all(...params) as Array<{ senderId: number; ts: number }>\n\n if (messages.length < 2)\n return {\n ...emptyResult,\n stats: { ...emptyResult.stats, totalMembers: members.length, totalMessages: messages.length },\n }\n\n const memberMsgCount = new Map<number, number>()\n for (const msg of messages) memberMsgCount.set(msg.senderId, (memberMsgCount.get(msg.senderId) || 0) + 1)\n const totalMessages = messages.length\n\n const lookAheadFactor = opts.lookAhead * 0.8\n const rawEdges: Array<{\n sourceId: number\n targetId: number\n rawScore: number\n expectedScore: number\n normalizedScore: number\n coOccurrenceCount: number\n }> = []\n\n for (const pair of accumulateCoOccurrencePairs(messages, opts)) {\n const aId = pair.sourceId\n const bId = pair.targetId\n const aMsgCount = memberMsgCount.get(aId) || 0\n const bMsgCount = memberMsgCount.get(bId) || 0\n const expectedScore = ((aMsgCount * bMsgCount) / totalMessages) * lookAheadFactor\n const normalizedScore = expectedScore > 0 ? pair.rawScore / expectedScore : 0\n rawEdges.push({\n sourceId: aId,\n targetId: bId,\n rawScore: pair.rawScore,\n expectedScore,\n normalizedScore,\n coOccurrenceCount: pair.coOccurrenceCount,\n })\n }\n\n const maxRawScore = Math.max(...rawEdges.map((e) => e.rawScore), 1)\n const maxNormalizedScore = Math.max(...rawEdges.map((e) => e.normalizedScore), 1)\n\n const edges = rawEdges.map((e) => {\n const hybridScore = 0.5 * (e.rawScore / maxRawScore) + 0.5 * (e.normalizedScore / maxNormalizedScore)\n return {\n ...e,\n rawScore: roundNum(e.rawScore),\n expectedScore: roundNum(e.expectedScore),\n normalizedScore: roundNum(e.normalizedScore),\n hybridScore: roundNum(hybridScore),\n }\n })\n\n edges.sort((a, b) => b.hybridScore - a.hybridScore)\n const keptEdges = edges.slice(0, opts.topEdges)\n\n if (keptEdges.length === 0)\n return {\n ...emptyResult,\n stats: { ...emptyResult.stats, totalMembers: members.length, totalMessages: messages.length },\n }\n\n const involvedIds = new Set<number>()\n for (const edge of keptEdges) {\n involvedIds.add(edge.sourceId)\n involvedIds.add(edge.targetId)\n }\n\n const nodeDegree = new Map<number, number>()\n for (const edge of keptEdges) {\n nodeDegree.set(edge.sourceId, (nodeDegree.get(edge.sourceId) || 0) + edge.hybridScore)\n nodeDegree.set(edge.targetId, (nodeDegree.get(edge.targetId) || 0) + edge.hybridScore)\n }\n const maxDegree = Math.max(...nodeDegree.values(), 1)\n\n const nameCount = new Map<string, number>()\n for (const id of involvedIds) {\n const name = memberInfo.get(id)?.name || String(id)\n nameCount.set(name, (nameCount.get(name) || 0) + 1)\n }\n\n const displayNames = new Map<number, string>()\n for (const id of involvedIds) {\n const info = memberInfo.get(id)\n const baseName = info?.name || String(id)\n displayNames.set(\n id,\n (nameCount.get(baseName) || 0) > 1 ? `${baseName}#${(info?.platformId || String(id)).slice(-4)}` : baseName\n )\n }\n\n const maxMsgCount = Math.max(...[...involvedIds].map((id) => memberInfo.get(id)?.messageCount || 0), 1)\n const nodes: ClusterGraphNode[] = [...involvedIds].map((id) => {\n const info = memberInfo.get(id)!\n const degree = nodeDegree.get(id) || 0\n const normalizedDegree = degree / maxDegree\n const msgNorm = info.messageCount / maxMsgCount\n const symbolSize = 20 + (0.7 * normalizedDegree + 0.3 * msgNorm) * 35\n return {\n id,\n name: displayNames.get(id)!,\n messageCount: info.messageCount,\n symbolSize: Math.round(symbolSize),\n degree: roundNum(degree),\n normalizedDegree: roundNum(normalizedDegree),\n }\n })\n nodes.sort((a, b) => b.degree - a.degree)\n\n const maxLinkValue = keptEdges.length > 0 ? Math.max(...keptEdges.map((e) => e.hybridScore)) : 0\n const links: ClusterGraphLink[] = keptEdges.map((e) => ({\n source: displayNames.get(e.sourceId)!,\n target: displayNames.get(e.targetId)!,\n value: e.hybridScore,\n rawScore: e.rawScore,\n expectedScore: e.expectedScore,\n coOccurrenceCount: e.coOccurrenceCount,\n }))\n\n return {\n nodes,\n links,\n maxLinkValue: roundNum(maxLinkValue),\n communities: [],\n stats: {\n totalMembers: members.length,\n totalMessages: messages.length,\n involvedMembers: involvedIds.size,\n edgeCount: keptEdges.length,\n communityCount: 0,\n },\n }\n}\n","import type { DatabaseAdapter } from '../interfaces'\nimport { hasColumn } from './filters'\nimport { accumulateCoOccurrencePairs } from './advanced/social'\n\nconst SYSTEM_MESSAGE_TYPES = [80, 81] as const\nconst SYSTEM_MESSAGE_TYPES_SQL = SYSTEM_MESSAGE_TYPES.join(', ')\nconst LEGACY_SYSTEM_ACCOUNT_NAME = '系统消息'\n\nexport interface ContactMemberRef {\n id: number\n platformId: string\n name: string\n aliases: string[]\n avatar: string | null\n}\n\nexport interface ContactFactsOptions {\n startTs?: number | null\n}\n\nexport type PrivateContactFacts =\n | {\n type: 'ok'\n contact: ContactMemberRef\n privateMessageCount: number\n activeMonths: string[]\n lastMessageTs: number | null\n }\n | { type: 'missing' }\n | { type: 'ambiguous'; candidates: ContactMemberRef[] }\n\nexport interface GroupContactFacts {\n contact: ContactMemberRef\n messageCount: number\n coOccurrenceCount: number\n coOccurrenceRawScore: number\n replyInteractionCount: number\n repliesFromOwnerToContact: number\n repliesFromContactToOwner: number\n lastInteractionTs: number | null\n}\n\nexport interface RelationshipGraphMemberFact {\n contact: ContactMemberRef\n messageCount: number\n lastMessageTs: number | null\n}\n\nexport interface RelationshipGraphEdgeFact {\n source: ContactMemberRef\n target: ContactMemberRef\n coOccurrenceCount: number\n coOccurrenceRawScore: number\n replyInteractionCount: number\n repliesFromSourceToTarget: number\n repliesFromTargetToSource: number\n lastInteractionTs: number | null\n}\n\nexport interface GroupRelationshipGraphFacts {\n members: RelationshipGraphMemberFact[]\n edges: RelationshipGraphEdgeFact[]\n ownerMessageCount: number\n}\n\nexport function isValidContactPlatformId(platformId: string | null | undefined): platformId is string {\n return typeof platformId === 'string' && platformId.trim().length > 0\n}\n\nexport function resolveOwnerMember(db: DatabaseAdapter): ContactMemberRef | null {\n const meta = db.prepare('SELECT owner_id FROM meta LIMIT 1').get() as { owner_id: string | null } | undefined\n if (!isValidContactPlatformId(meta?.owner_id)) return null\n const aliasesSelect = hasColumn(db, 'member', 'aliases') ? 'aliases' : 'NULL as aliases'\n\n const row = db\n .prepare(\n `SELECT\n id,\n platform_id as platformId,\n COALESCE(group_nickname, account_name, platform_id) as name,\n ${aliasesSelect},\n avatar\n FROM member m\n WHERE platform_id = ? AND ${nonSystemContactMemberCondition(db, 'm')}\n LIMIT 1`\n )\n .get(meta.owner_id) as ContactMemberRow | undefined\n\n return row ? mapContactMemberRow(row) : null\n}\n\nexport function getNonSystemMembersForContacts(db: DatabaseAdapter): ContactMemberRef[] {\n const aliasesSelect = hasColumn(db, 'member', 'aliases') ? 'aliases' : 'NULL as aliases'\n const rows = db\n .prepare(\n `SELECT\n id,\n platform_id as platformId,\n COALESCE(group_nickname, account_name, platform_id) as name,\n ${aliasesSelect},\n avatar\n FROM member m\n WHERE ${nonSystemContactMemberCondition(db, 'm')}\n ORDER BY id ASC`\n )\n .all() as unknown as ContactMemberRow[]\n\n return rows.map(mapContactMemberRow).filter((row) => isValidContactPlatformId(row.platformId))\n}\n\nexport function getLatestContactMessageTs(db: DatabaseAdapter): number | null {\n const row = db\n .prepare(\n `SELECT MAX(msg.ts) as ts\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE ${nonSystemMessageCondition(db, 'msg', 'm')}`\n )\n .get() as { ts: number | null } | undefined\n\n return row?.ts ?? null\n}\n\nexport function getPrivateContactFacts(\n db: DatabaseAdapter,\n ownerMemberId: number,\n options: ContactFactsOptions = {}\n): PrivateContactFacts {\n const candidates = getNonSystemMembersForContacts(db).filter((member) => member.id !== ownerMemberId)\n if (candidates.length === 0) return { type: 'missing' }\n\n const timeFilter = createMessageTimeFilter('msg', options.startTs)\n const candidateById = new Map(candidates.map((candidate) => [candidate.id, candidate]))\n const activeCandidateRows = db\n .prepare(\n `SELECT msg.sender_id as senderId\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE ${nonSystemMessageCondition(db, 'msg', 'm')}\n AND msg.sender_id <> ?${timeFilter.sql}\n GROUP BY msg.sender_id`\n )\n .all(ownerMemberId, ...timeFilter.params) as Array<{ senderId: number }>\n const activeCandidates = activeCandidateRows\n .map((row) => candidateById.get(row.senderId))\n .filter((candidate): candidate is ContactMemberRef => Boolean(candidate))\n const resolvedCandidates = activeCandidates.length > 0 ? activeCandidates : candidates\n if (resolvedCandidates.length > 1) return { type: 'ambiguous', candidates: resolvedCandidates }\n\n const countRow = db\n .prepare(\n `SELECT COUNT(*) as count\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE ${nonSystemMessageCondition(db, 'msg', 'm')}${timeFilter.sql}`\n )\n .get(...timeFilter.params) as { count: number } | undefined\n\n const monthRows = db\n .prepare(\n `SELECT DISTINCT strftime('%Y-%m', msg.ts, 'unixepoch', 'localtime') as month\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE ${nonSystemMessageCondition(db, 'msg', 'm')}${timeFilter.sql}\n ORDER BY month ASC`\n )\n .all(...timeFilter.params) as Array<{ month: string }>\n\n const lastRow = db\n .prepare(\n `SELECT MAX(msg.ts) as lastMessageTs\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE ${nonSystemMessageCondition(db, 'msg', 'm')}${timeFilter.sql}`\n )\n .get(...timeFilter.params) as { lastMessageTs: number | null } | undefined\n\n return {\n type: 'ok',\n contact: resolvedCandidates[0],\n privateMessageCount: countRow?.count ?? 0,\n activeMonths: monthRows.map((row) => row.month).filter(Boolean),\n lastMessageTs: lastRow?.lastMessageTs ?? null,\n }\n}\n\nexport function getGroupContactFacts(\n db: DatabaseAdapter,\n ownerMemberId: number,\n options: ContactFactsOptions = {}\n): GroupContactFacts[] {\n const contacts = getNonSystemMembersForContacts(db).filter((member) => member.id !== ownerMemberId)\n const messageTimeFilter = createMessageTimeFilter('msg', options.startTs)\n const messageRows = db\n .prepare(\n `SELECT msg.sender_id as senderId, COUNT(*) as messageCount\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE ${nonSystemMessageCondition(db, 'msg', 'm')}${messageTimeFilter.sql}\n GROUP BY msg.sender_id`\n )\n .all(...messageTimeFilter.params) as Array<{ senderId: number; messageCount: number }>\n\n const messageCounts = new Map(messageRows.map((row) => [row.senderId, row.messageCount]))\n const contactById = new Map(contacts.map((contact) => [contact.id, contact]))\n const coOccurrenceRows = db\n .prepare(\n `SELECT msg.sender_id as senderId, msg.ts as ts\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE ${nonSystemMessageCondition(db, 'msg', 'm')}${messageTimeFilter.sql}\n ORDER BY msg.ts ASC, msg.id ASC`\n )\n .all(...messageTimeFilter.params) as Array<{ senderId: number; ts: number }>\n const coOccurrenceStats = new Map<\n number,\n { coOccurrenceCount: number; coOccurrenceRawScore: number; lastOccurrenceTs: number }\n >()\n\n // 共现算法会产出任意成员对;联系人页只消费 owner 与候选联系人的关系边。\n for (const pair of accumulateCoOccurrencePairs(coOccurrenceRows)) {\n const contactId =\n pair.sourceId === ownerMemberId && contactById.has(pair.targetId)\n ? pair.targetId\n : pair.targetId === ownerMemberId && contactById.has(pair.sourceId)\n ? pair.sourceId\n : null\n if (contactId === null) continue\n coOccurrenceStats.set(contactId, {\n coOccurrenceCount: pair.coOccurrenceCount,\n coOccurrenceRawScore: pair.rawScore,\n lastOccurrenceTs: pair.lastOccurrenceTs,\n })\n }\n const replyStats = new Map<\n number,\n {\n repliesFromOwnerToContact: number\n repliesFromContactToOwner: number\n lastInteractionTs: number | null\n }\n >()\n\n const replyTimeFilter = createReplyTimeFilter(options.startTs)\n const replyRows = db\n .prepare(\n `SELECT\n msg.sender_id as replySenderId,\n msg.ts as replyTs,\n target.sender_id as targetSenderId\n FROM message msg\n JOIN message target ON msg.reply_to_message_id = target.platform_message_id\n JOIN member sender ON msg.sender_id = sender.id\n JOIN member targetMember ON target.sender_id = targetMember.id\n WHERE msg.reply_to_message_id IS NOT NULL\n AND ${nonSystemMessageCondition(db, 'msg', 'sender')}\n AND ${nonSystemMessageCondition(db, 'target', 'targetMember')}${replyTimeFilter.sql}`\n )\n .all(...replyTimeFilter.params) as Array<{ replySenderId: number; replyTs: number; targetSenderId: number }>\n\n const ensureReplyStats = (contactId: number) => {\n const existing = replyStats.get(contactId)\n if (existing) return existing\n const created = { repliesFromOwnerToContact: 0, repliesFromContactToOwner: 0, lastInteractionTs: null }\n replyStats.set(contactId, created)\n return created\n }\n\n for (const row of replyRows) {\n if (row.replySenderId === ownerMemberId && contactById.has(row.targetSenderId)) {\n const stats = ensureReplyStats(row.targetSenderId)\n stats.repliesFromOwnerToContact++\n stats.lastInteractionTs = Math.max(stats.lastInteractionTs ?? 0, row.replyTs)\n } else if (row.targetSenderId === ownerMemberId && contactById.has(row.replySenderId)) {\n const stats = ensureReplyStats(row.replySenderId)\n stats.repliesFromContactToOwner++\n stats.lastInteractionTs = Math.max(stats.lastInteractionTs ?? 0, row.replyTs)\n }\n }\n\n return contacts.map((contact) => {\n const stats = replyStats.get(contact.id) ?? {\n repliesFromOwnerToContact: 0,\n repliesFromContactToOwner: 0,\n lastInteractionTs: null,\n }\n const coOccurrence = coOccurrenceStats.get(contact.id)\n const replyInteractionCount = stats.repliesFromOwnerToContact + stats.repliesFromContactToOwner\n return {\n contact,\n messageCount: messageCounts.get(contact.id) ?? 0,\n coOccurrenceCount: coOccurrence?.coOccurrenceCount ?? 0,\n coOccurrenceRawScore: coOccurrence?.coOccurrenceRawScore ?? 0,\n replyInteractionCount,\n repliesFromOwnerToContact: stats.repliesFromOwnerToContact,\n repliesFromContactToOwner: stats.repliesFromContactToOwner,\n lastInteractionTs: stats.lastInteractionTs ?? coOccurrence?.lastOccurrenceTs ?? null,\n }\n })\n}\n\nexport function getGroupRelationshipGraphFacts(\n db: DatabaseAdapter,\n ownerMemberId: number,\n options: ContactFactsOptions = {}\n): GroupRelationshipGraphFacts {\n const contacts = getNonSystemMembersForContacts(db).filter((member) => member.id !== ownerMemberId)\n const contactById = new Map(contacts.map((contact) => [contact.id, contact]))\n const messageTimeFilter = createMessageTimeFilter('msg', options.startTs)\n const messageRows = db\n .prepare(\n `SELECT msg.sender_id as senderId, COUNT(*) as messageCount, MAX(msg.ts) as lastMessageTs\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE ${nonSystemMessageCondition(db, 'msg', 'm')}${messageTimeFilter.sql}\n GROUP BY msg.sender_id`\n )\n .all(...messageTimeFilter.params) as Array<{ senderId: number; messageCount: number; lastMessageTs: number | null }>\n\n const memberStats = new Map<number, { messageCount: number; lastMessageTs: number | null }>()\n let ownerMessageCount = 0\n for (const row of messageRows) {\n if (row.senderId === ownerMemberId) {\n ownerMessageCount = row.messageCount\n continue\n }\n if (!contactById.has(row.senderId)) continue\n memberStats.set(row.senderId, {\n messageCount: row.messageCount,\n lastMessageTs: row.lastMessageTs ?? null,\n })\n }\n\n const coOccurrenceRows = db\n .prepare(\n `SELECT msg.sender_id as senderId, msg.ts as ts\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE ${nonSystemMessageCondition(db, 'msg', 'm')}${messageTimeFilter.sql}\n ORDER BY msg.ts ASC, msg.id ASC`\n )\n .all(...messageTimeFilter.params) as Array<{ senderId: number; ts: number }>\n\n const edgeStats = new Map<\n string,\n {\n sourceId: number\n targetId: number\n coOccurrenceCount: number\n coOccurrenceRawScore: number\n repliesFromSourceToTarget: number\n repliesFromTargetToSource: number\n lastInteractionTs: number | null\n }\n >()\n\n const ensureEdge = (aId: number, bId: number) => {\n const sourceId = Math.min(aId, bId)\n const targetId = Math.max(aId, bId)\n const key = `${sourceId}:${targetId}`\n const existing = edgeStats.get(key)\n if (existing) return existing\n const created = {\n sourceId,\n targetId,\n coOccurrenceCount: 0,\n coOccurrenceRawScore: 0,\n repliesFromSourceToTarget: 0,\n repliesFromTargetToSource: 0,\n lastInteractionTs: null,\n }\n edgeStats.set(key, created)\n return created\n }\n\n for (const pair of accumulateCoOccurrencePairs(coOccurrenceRows)) {\n if (!contactById.has(pair.sourceId) || !contactById.has(pair.targetId)) continue\n const edge = ensureEdge(pair.sourceId, pair.targetId)\n edge.coOccurrenceCount += pair.coOccurrenceCount\n edge.coOccurrenceRawScore += pair.rawScore\n edge.lastInteractionTs = Math.max(edge.lastInteractionTs ?? 0, pair.lastOccurrenceTs)\n }\n\n const replyTimeFilter = createReplyTimeFilter(options.startTs)\n const replyRows = db\n .prepare(\n `SELECT\n msg.sender_id as replySenderId,\n msg.ts as replyTs,\n target.sender_id as targetSenderId\n FROM message msg\n JOIN message target ON msg.reply_to_message_id = target.platform_message_id\n JOIN member sender ON msg.sender_id = sender.id\n JOIN member targetMember ON target.sender_id = targetMember.id\n WHERE msg.reply_to_message_id IS NOT NULL\n AND ${nonSystemMessageCondition(db, 'msg', 'sender')}\n AND ${nonSystemMessageCondition(db, 'target', 'targetMember')}${replyTimeFilter.sql}`\n )\n .all(...replyTimeFilter.params) as Array<{ replySenderId: number; replyTs: number; targetSenderId: number }>\n\n for (const row of replyRows) {\n if (!contactById.has(row.replySenderId) || !contactById.has(row.targetSenderId)) continue\n if (row.replySenderId === row.targetSenderId) continue\n const edge = ensureEdge(row.replySenderId, row.targetSenderId)\n if (row.replySenderId === edge.sourceId) edge.repliesFromSourceToTarget++\n else edge.repliesFromTargetToSource++\n edge.lastInteractionTs = Math.max(edge.lastInteractionTs ?? 0, row.replyTs)\n }\n\n const members = contacts.map((contact) => {\n const stats = memberStats.get(contact.id)\n return {\n contact,\n messageCount: stats?.messageCount ?? 0,\n lastMessageTs: stats?.lastMessageTs ?? null,\n }\n })\n\n const edges: RelationshipGraphEdgeFact[] = []\n for (const edge of edgeStats.values()) {\n const source = contactById.get(edge.sourceId)\n const target = contactById.get(edge.targetId)\n if (!source || !target) continue\n const replyInteractionCount = edge.repliesFromSourceToTarget + edge.repliesFromTargetToSource\n if (edge.coOccurrenceCount <= 0 && replyInteractionCount <= 0) continue\n edges.push({\n source,\n target,\n coOccurrenceCount: edge.coOccurrenceCount,\n coOccurrenceRawScore: edge.coOccurrenceRawScore,\n replyInteractionCount,\n repliesFromSourceToTarget: edge.repliesFromSourceToTarget,\n repliesFromTargetToSource: edge.repliesFromTargetToSource,\n lastInteractionTs: edge.lastInteractionTs,\n })\n }\n\n return { members, edges, ownerMessageCount }\n}\n\nfunction createMessageTimeFilter(\n alias: string,\n startTs: number | null | undefined\n): { sql: string; params: unknown[] } {\n return typeof startTs === 'number' ? { sql: ` AND ${alias}.ts >= ?`, params: [startTs] } : { sql: '', params: [] }\n}\n\nfunction createReplyTimeFilter(startTs: number | null | undefined): { sql: string; params: unknown[] } {\n return typeof startTs === 'number'\n ? { sql: ' AND msg.ts >= ? AND target.ts >= ?', params: [startTs, startTs] }\n : { sql: '', params: [] }\n}\n\ninterface ContactMemberRow {\n id: number\n platformId: string\n name: string\n aliases: string | null\n avatar: string | null\n}\n\nfunction mapContactMemberRow(row: ContactMemberRow): ContactMemberRef {\n return {\n id: row.id,\n platformId: row.platformId,\n name: row.name,\n aliases: parseContactAliases(row.aliases),\n avatar: row.avatar ?? null,\n }\n}\n\nfunction parseContactAliases(value: string | null): string[] {\n if (!value) return []\n try {\n const parsed = JSON.parse(value)\n return Array.isArray(parsed)\n ? parsed.filter((alias): alias is string => typeof alias === 'string' && alias.length > 0)\n : []\n } catch {\n return []\n }\n}\nfunction nonSystemContactMemberCondition(db: DatabaseAdapter, memberAlias: string): string {\n // 系统消息名称会随平台和导出语言变化;联系人候选优先用稳定 sender identity 和消息类型识别伪成员。\n return `(${nonSystemMemberIdentityCondition(db, memberAlias)}\n AND (\n NOT EXISTS (\n SELECT 1 FROM message system_msg\n WHERE system_msg.sender_id = ${memberAlias}.id\n AND system_msg.type IN (${SYSTEM_MESSAGE_TYPES_SQL})\n )\n OR EXISTS (\n SELECT 1 FROM message non_system_msg\n WHERE non_system_msg.sender_id = ${memberAlias}.id\n AND non_system_msg.type NOT IN (${SYSTEM_MESSAGE_TYPES_SQL})\n )\n ))`\n}\n\nfunction nonSystemMessageCondition(db: DatabaseAdapter, messageAlias: string, memberAlias: string): string {\n return `(${messageAlias}.type NOT IN (${SYSTEM_MESSAGE_TYPES_SQL})\n AND ${nonSystemMemberIdentityCondition(db, memberAlias)})`\n}\n\nfunction nonSystemMemberIdentityCondition(db: DatabaseAdapter, memberAlias: string): string {\n return `(LOWER(COALESCE(${memberAlias}.platform_id, '')) != 'system'\n AND COALESCE(${memberAlias}.account_name, '') != '${LEGACY_SYSTEM_ACCOUNT_NAME}'\n AND ${notGroupSelfMemberCondition(db, memberAlias)})`\n}\n\nfunction notGroupSelfMemberCondition(db: DatabaseAdapter, memberAlias: string): string {\n const clauses = [\n `(TRIM(COALESCE(session_meta.name, '')) != ''\n AND LOWER(TRIM(COALESCE(${memberAlias}.platform_id, ''))) = LOWER(TRIM(session_meta.name))\n AND LOWER(TRIM(COALESCE(${memberAlias}.account_name, ''))) = LOWER(TRIM(session_meta.name)))`,\n ]\n\n if (hasColumn(db, 'meta', 'group_id')) {\n clauses.unshift(\n `(TRIM(COALESCE(session_meta.group_id, '')) != ''\n AND LOWER(TRIM(COALESCE(${memberAlias}.platform_id, ''))) = LOWER(TRIM(session_meta.group_id)))`\n )\n }\n\n return `NOT EXISTS (\n SELECT 1 FROM meta session_meta\n WHERE LOWER(COALESCE(session_meta.type, '')) = 'group'\n AND (${clauses.join(' OR ')})\n )`\n}\n","import type { ContactScoreBreakdown } from '@openchatlab/shared-types'\n\nexport const MIN_PRIVATE_SESSIONS_FOR_CONTACTS = 10\n\nconst FRIEND_SCORE_WEIGHTS = {\n privateMessage: 0.55,\n privateRegularity: 0.25,\n commonGroup: 0.2,\n}\n\nconst NON_FRIEND_SCORE_WEIGHTS = {\n coOccurrence: 0.5,\n commonGroup: 0.25,\n replyInteraction: 0.25,\n}\n\nexport interface FriendScoreInput {\n privateMessageCount: number\n activeMonths: readonly string[]\n commonGroupCount: number\n}\n\nexport interface NonFriendScoreInput {\n coOccurrenceRawScore: number\n commonGroupCount: number\n replyInteractionCount: number\n coOccurrenceCount?: number\n repliesFromOwnerToContact?: number\n repliesFromContactToOwner?: number\n}\n\nexport interface FriendScoreComponents {\n privateMessageScore: number\n privateRegularityScore: number\n commonGroupScore: number\n privateMessageCount?: number\n activePrivateMonths?: number\n commonGroupCount?: number\n}\n\nexport interface NonFriendScoreComponents {\n coOccurrenceScore: number\n commonGroupScore: number\n replyInteractionScore: number\n commonGroupCount?: number\n coOccurrenceCount?: number\n coOccurrenceRawScore?: number\n replyInteractionCount?: number\n repliesFromOwnerToContact?: number\n repliesFromContactToOwner?: number\n}\n\nexport interface ContactScoringResult {\n score: number\n scoreBreakdown: ContactScoreBreakdown\n}\n\nfunction clamp01(value: number): number {\n if (!Number.isFinite(value)) return 0\n if (value < 0) return 0\n if (value > 1) return 1\n return value\n}\n\nfunction nonNegative(value: number | null | undefined): number {\n return Number.isFinite(value) && value !== null && value !== undefined ? Math.max(0, value) : 0\n}\n\nfunction parseMonthIndex(month: string): number | null {\n const match = /^(\\d{4})-(\\d{2})$/.exec(month)\n if (!match) return null\n\n const year = Number.parseInt(match[1], 10)\n const monthNumber = Number.parseInt(match[2], 10)\n if (!Number.isFinite(year) || monthNumber < 1 || monthNumber > 12) return null\n\n return year * 12 + monthNumber - 1\n}\n\nfunction getActiveMonthIndexes(activeMonths: readonly string[]): number[] {\n return [...new Set(activeMonths.map(parseMonthIndex).filter((month): month is number => month !== null))].sort(\n (a, b) => a - b\n )\n}\n\nexport function rankPercentiles<T>(items: readonly T[], valueSelector: (item: T) => number): Map<T, number> {\n const result = new Map<T, number>()\n if (items.length === 0) return result\n\n const values = items.map((item) => ({ item, value: nonNegative(valueSelector(item)) }))\n const min = Math.min(...values.map((entry) => entry.value))\n const max = Math.max(...values.map((entry) => entry.value))\n\n if (min === max) {\n const percentile = max > 0 ? 1 : 0\n for (const entry of values) result.set(entry.item, percentile)\n return result\n }\n\n const sorted = [...values].sort((a, b) => a.value - b.value)\n let index = 0\n while (index < sorted.length) {\n let end = index\n while (end + 1 < sorted.length && sorted[end + 1].value === sorted[index].value) end++\n const percentile = (index + end) / 2 / (sorted.length - 1)\n for (let i = index; i <= end; i++) result.set(sorted[i].item, percentile)\n index = end + 1\n }\n\n return result\n}\n\nexport function computePrivateRegularity(activeMonths: readonly string[]): number {\n const monthIndexes = getActiveMonthIndexes(activeMonths)\n if (monthIndexes.length === 0) return 0\n\n const activeCount = monthIndexes.length\n const spanMonths = monthIndexes[monthIndexes.length - 1] - monthIndexes[0] + 1\n\n // 定期性衡量持续规律:活跃月越多越高,跨度很大但只零星出现会被均匀度压低。\n return activeCount * (activeCount / spanMonths)\n}\n\nexport function computeFriendScore(components: FriendScoreComponents): ContactScoringResult {\n const privateMessageScore = clamp01(components.privateMessageScore)\n const privateRegularityScore = clamp01(components.privateRegularityScore)\n const commonGroupScore = clamp01(components.commonGroupScore)\n const score =\n FRIEND_SCORE_WEIGHTS.privateMessage * privateMessageScore +\n FRIEND_SCORE_WEIGHTS.privateRegularity * privateRegularityScore +\n FRIEND_SCORE_WEIGHTS.commonGroup * commonGroupScore\n\n return {\n score,\n scoreBreakdown: {\n privateMessageScore,\n privateRegularityScore,\n commonGroupScore,\n privateMessageCount: components.privateMessageCount,\n activePrivateMonths: components.activePrivateMonths,\n commonGroupCount: components.commonGroupCount,\n },\n }\n}\n\nexport function computeFriendScores<T extends FriendScoreInput>(items: readonly T[]): Map<T, ContactScoringResult> {\n const messageScores = rankPercentiles(items, (item) => Math.log1p(nonNegative(item.privateMessageCount)))\n const regularityByItem = new Map(items.map((item) => [item, computePrivateRegularity(item.activeMonths)]))\n const regularityScores = rankPercentiles(items, (item) => regularityByItem.get(item) ?? 0)\n const groupScores = rankPercentiles(items, (item) => nonNegative(item.commonGroupCount))\n const result = new Map<T, ContactScoringResult>()\n\n for (const item of items) {\n result.set(\n item,\n computeFriendScore({\n privateMessageScore: messageScores.get(item) ?? 0,\n privateRegularityScore: regularityScores.get(item) ?? 0,\n commonGroupScore: groupScores.get(item) ?? 0,\n privateMessageCount: nonNegative(item.privateMessageCount),\n activePrivateMonths: getActiveMonthIndexes(item.activeMonths).length,\n commonGroupCount: nonNegative(item.commonGroupCount),\n })\n )\n }\n\n return result\n}\n\nexport function computeNonFriendScore(components: NonFriendScoreComponents): ContactScoringResult {\n const coOccurrenceScore = clamp01(components.coOccurrenceScore)\n const commonGroupScore = clamp01(components.commonGroupScore)\n const replyInteractionScore = clamp01(components.replyInteractionScore)\n const score =\n NON_FRIEND_SCORE_WEIGHTS.coOccurrence * coOccurrenceScore +\n NON_FRIEND_SCORE_WEIGHTS.commonGroup * commonGroupScore +\n NON_FRIEND_SCORE_WEIGHTS.replyInteraction * replyInteractionScore\n\n return {\n score,\n scoreBreakdown: {\n coOccurrenceScore,\n commonGroupScore,\n replyInteractionScore,\n commonGroupCount: components.commonGroupCount,\n coOccurrenceCount: components.coOccurrenceCount,\n coOccurrenceRawScore: components.coOccurrenceRawScore,\n replyInteractionCount: components.replyInteractionCount,\n repliesFromOwnerToContact: components.repliesFromOwnerToContact,\n repliesFromContactToOwner: components.repliesFromContactToOwner,\n },\n }\n}\n\nexport function computeNonFriendScores<T extends NonFriendScoreInput>(\n items: readonly T[]\n): Map<T, ContactScoringResult> {\n const coOccurrenceScores = rankPercentiles(items, (item) => nonNegative(item.coOccurrenceRawScore))\n const groupScores = rankPercentiles(items, (item) => nonNegative(item.commonGroupCount))\n const replyScores = rankPercentiles(items, (item) => nonNegative(item.replyInteractionCount))\n const result = new Map<T, ContactScoringResult>()\n\n for (const item of items) {\n result.set(\n item,\n computeNonFriendScore({\n coOccurrenceScore: coOccurrenceScores.get(item) ?? 0,\n commonGroupScore: groupScores.get(item) ?? 0,\n replyInteractionScore: replyScores.get(item) ?? 0,\n commonGroupCount: nonNegative(item.commonGroupCount),\n coOccurrenceCount: nonNegative(item.coOccurrenceCount),\n coOccurrenceRawScore: nonNegative(item.coOccurrenceRawScore),\n replyInteractionCount: nonNegative(item.replyInteractionCount),\n repliesFromOwnerToContact: nonNegative(item.repliesFromOwnerToContact),\n repliesFromContactToOwner: nonNegative(item.repliesFromContactToOwner),\n })\n )\n }\n\n return result\n}\n","/**\n * Member write operations (merge, delete, update aliases).\n *\n * All functions accept a DatabaseAdapter, keeping them platform-agnostic.\n * The caller is responsible for opening/closing the DB connection.\n */\n\nimport type { DatabaseAdapter } from '../interfaces/database-adapter'\n\n// ==================== Helpers ====================\n\nfunction parseAliases(raw: string | null): string[] {\n if (!raw) return []\n try {\n const parsed = JSON.parse(raw)\n if (!Array.isArray(parsed)) return []\n return parsed.filter((item): item is string => typeof item === 'string' && item.trim().length > 0)\n } catch {\n return []\n }\n}\n\n// ==================== Public API ====================\n\nexport function updateMemberAliases(db: DatabaseAdapter, memberId: number, aliases: string[]): boolean {\n try {\n db.prepare('UPDATE member SET aliases = ? WHERE id = ?').run(JSON.stringify(aliases), memberId)\n return true\n } catch {\n return false\n }\n}\n\ninterface MemberMergeRow {\n id: number\n platformId: string\n accountName: string | null\n groupNickname: string | null\n aliases: string | null\n avatar: string | null\n messageCount: number\n}\n\n/**\n * Merge two members — messages and name history are reassigned to the one\n * with more messages (or the lower id on tie). The secondary member is deleted.\n */\nexport function mergeMembers(db: DatabaseAdapter, memberId1: number, memberId2: number): boolean {\n if (memberId1 === memberId2) return false\n\n try {\n const rows = db\n .prepare(\n `\n SELECT\n m.id,\n m.platform_id as platformId,\n m.account_name as accountName,\n m.group_nickname as groupNickname,\n m.aliases,\n m.avatar,\n COUNT(msg.id) as messageCount\n FROM member m\n LEFT JOIN message msg ON m.id = msg.sender_id\n WHERE m.id IN (?, ?)\n GROUP BY m.id\n `\n )\n .all(memberId1, memberId2) as unknown as MemberMergeRow[]\n\n if (rows.length !== 2) return false\n\n const [memberA, memberB] = rows\n let primary = memberA\n let secondary = memberB\n\n if (\n memberB.messageCount > memberA.messageCount ||\n (memberB.messageCount === memberA.messageCount && memberB.id < memberA.id)\n ) {\n primary = memberB\n secondary = memberA\n }\n\n const mergedAliases = Array.from(new Set([...parseAliases(primary.aliases), ...parseAliases(secondary.aliases)]))\n const mergedAccountName = primary.accountName || secondary.accountName\n const mergedGroupNickname = primary.groupNickname || secondary.groupNickname\n const mergedAvatar = primary.avatar || secondary.avatar\n\n db.transaction(() => {\n db.prepare('UPDATE message SET sender_id = ? WHERE sender_id = ?').run(primary.id, secondary.id)\n db.prepare('UPDATE member_name_history SET member_id = ? WHERE member_id = ?').run(primary.id, secondary.id)\n db.prepare('UPDATE meta SET owner_id = ? WHERE owner_id = ?').run(primary.platformId, secondary.platformId)\n db.prepare(`UPDATE member SET account_name = ?, group_nickname = ?, avatar = ?, aliases = ? WHERE id = ?`).run(\n mergedAccountName,\n mergedGroupNickname,\n mergedAvatar,\n JSON.stringify(mergedAliases),\n primary.id\n )\n db.prepare('DELETE FROM member WHERE id = ?').run(secondary.id)\n })\n\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Delete a member and all their messages / name history.\n */\nexport function deleteMember(db: DatabaseAdapter, memberId: number): boolean {\n try {\n db.transaction(() => {\n db.prepare('DELETE FROM message WHERE sender_id = ?').run(memberId)\n db.prepare('DELETE FROM member_name_history WHERE member_id = ?').run(memberId)\n db.prepare('DELETE FROM member WHERE id = ?').run(memberId)\n })\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Ensure the `aliases` column exists on the `member` table (DDL migration).\n */\nexport function ensureAliasesColumn(db: DatabaseAdapter): boolean {\n const columns = db.prepare('PRAGMA table_info(member)').all() as unknown as Array<{ name: string }>\n const has = columns.some((col) => col.name === 'aliases')\n if (!has) {\n db.exec(\"ALTER TABLE member ADD COLUMN aliases TEXT DEFAULT '[]'\")\n return true\n }\n return false\n}\n\n/**\n * Ensure the `avatar` column exists on the `member` table (DDL migration).\n */\nexport function ensureAvatarColumn(db: DatabaseAdapter): boolean {\n const columns = db.prepare('PRAGMA table_info(member)').all() as unknown as Array<{ name: string }>\n const has = columns.some((col) => col.name === 'avatar')\n if (!has) {\n db.exec('ALTER TABLE member ADD COLUMN avatar TEXT')\n return true\n }\n return false\n}\n","const SYSTEM_PLACEHOLDER_CONTENTS = new Set([\n '[图片]',\n '[视频]',\n '[语音]',\n '[文件]',\n '[动画表情]',\n '[表情]',\n '[链接]',\n '[位置]',\n '[地理位置]',\n '[名片]',\n '[红包]',\n '[转账]',\n '[音乐]',\n '[回复消息]',\n '[Image]',\n '[Photo]',\n '[Video]',\n '[Voice]',\n '[File]',\n '[Sticker]',\n '[Link]',\n '[Location]',\n])\n\nexport function isSystemPlaceholderContent(content: string): boolean {\n return SYSTEM_PLACEHOLDER_CONTENTS.has(content.trim())\n}\n","/**\n * 口头禅分析模块(平台无关)\n */\n\nimport type { TimeFilter } from '@openchatlab/shared-types'\nimport type { DatabaseAdapter } from '../../interfaces'\nimport { buildTimeFilter } from '../filters'\nimport { isSystemPlaceholderContent } from './text-filters'\n\nexport interface CatchphraseItem {\n content: string\n count: number\n}\n\nexport interface MemberCatchphrase {\n memberId: number\n platformId: string\n name: string\n catchphrases: CatchphraseItem[]\n}\n\nexport interface CatchphraseAnalysis {\n members: MemberCatchphrase[]\n}\n\nexport function getCatchphraseAnalysis(db: DatabaseAdapter, filter?: TimeFilter): CatchphraseAnalysis {\n const { clause, params } = buildTimeFilter(filter)\n\n let whereClause = clause\n if (whereClause.includes('WHERE')) {\n whereClause +=\n \" AND COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL AND LENGTH(TRIM(msg.content)) >= 2\"\n } else {\n whereClause =\n \" WHERE COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL AND LENGTH(TRIM(msg.content)) >= 2\"\n }\n\n const rows = db\n .prepare(\n `\n SELECT\n m.id as memberId,\n m.platform_id as platformId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,\n TRIM(msg.content) as content,\n COUNT(*) as count\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n ${whereClause}\n GROUP BY m.id, TRIM(msg.content)\n ORDER BY m.id, count DESC\n `\n )\n .all(...params) as Array<{\n memberId: number\n platformId: string\n name: string\n content: string\n count: number\n }>\n\n const memberMap = new Map<number, MemberCatchphrase>()\n\n for (const row of rows) {\n if (isSystemPlaceholderContent(row.content)) continue\n\n if (!memberMap.has(row.memberId)) {\n memberMap.set(row.memberId, {\n memberId: row.memberId,\n platformId: row.platformId,\n name: row.name,\n catchphrases: [],\n })\n }\n\n const member = memberMap.get(row.memberId)!\n if (member.catchphrases.length < 100) {\n member.catchphrases.push({ content: row.content, count: row.count })\n }\n }\n\n const members = Array.from(memberMap.values())\n members.sort((a, b) => {\n const aTotal = a.catchphrases.reduce((sum, c) => sum + c.count, 0)\n const bTotal = b.catchphrases.reduce((sum, c) => sum + c.count, 0)\n return bTotal - aTotal\n })\n\n return { members }\n}\n","/**\n * 关系分析模块(私聊专属,平台无关)\n * 基于会话索引统计双方的主动发起、收尾、破冰、响应时延、锲而不舍行为\n */\n\nimport type { TimeFilter } from '@openchatlab/shared-types'\nimport type { DatabaseAdapter } from '../../interfaces'\n\nexport interface RelationshipMonthStats {\n month: string\n members: Array<{ memberId: number; name: string; initiateCount: number; closeCount: number }>\n totalSessions: number\n}\n\nexport interface IceBreakerItem {\n month: string\n memberId: number\n name: string\n count: number\n}\n\nexport interface ResponseLatencyMember {\n memberId: number\n name: string\n avgResponseTime: number\n totalResponses: number\n}\n\nexport interface PerseveranceMember {\n memberId: number\n name: string\n totalDoubleTexts: number\n}\n\nexport interface MonthlyResponseLatency {\n month: string\n members: Array<{ memberId: number; name: string; avgResponseTime: number; responseCount: number }>\n}\n\nexport interface MonthlyPerseverance {\n month: string\n members: Array<{ memberId: number; name: string; doubleTextCount: number }>\n}\n\nexport interface RelationshipOptions {\n perseveranceThreshold?: number\n}\n\nexport interface RelationshipStats {\n months: RelationshipMonthStats[]\n members: Array<{ memberId: number; name: string; totalInitiateCount: number; totalCloseCount: number }>\n totalSessions: number\n hasSessionIndex: boolean\n iceBreakers: IceBreakerItem[]\n totalIceBreaks: number\n responseLatency: ResponseLatencyMember[]\n perseverance: PerseveranceMember[]\n totalDoubleTexts: number\n monthlyResponseLatency: MonthlyResponseLatency[]\n monthlyPerseverance: MonthlyPerseverance[]\n perseveranceThreshold: number\n}\n\nconst ICE_BREAK_THRESHOLD = 24 * 60 * 60\nconst DEFAULT_PERSEVERANCE_THRESHOLD = 300\n\nexport function getRelationshipStats(\n db: DatabaseAdapter,\n filter?: TimeFilter,\n options?: RelationshipOptions\n): RelationshipStats {\n const perseveranceThreshold = options?.perseveranceThreshold ?? DEFAULT_PERSEVERANCE_THRESHOLD\n const emptyResult: RelationshipStats = {\n months: [],\n members: [],\n totalSessions: 0,\n hasSessionIndex: false,\n iceBreakers: [],\n totalIceBreaks: 0,\n responseLatency: [],\n perseverance: [],\n totalDoubleTexts: 0,\n monthlyResponseLatency: [],\n monthlyPerseverance: [],\n perseveranceThreshold,\n }\n\n const sessionCount = db.prepare('SELECT COUNT(*) as count FROM segment').get() as { count: number } | undefined\n if (!sessionCount || sessionCount.count === 0) return emptyResult\n\n const timeConditions: string[] = []\n const params: (number | string)[] = []\n\n if (filter?.startTs !== undefined) {\n timeConditions.push('cs.start_ts >= ?')\n params.push(filter.startTs)\n }\n if (filter?.endTs !== undefined) {\n timeConditions.push('cs.start_ts <= ?')\n params.push(filter.endTs)\n }\n\n const whereClause = timeConditions.length > 0 ? `WHERE ${timeConditions.join(' AND ')}` : ''\n\n const segmentRows = db\n .prepare(\n `SELECT cs.id AS segment_id, cs.start_ts, cs.end_ts,\n (SELECT m.sender_id FROM message_context mc JOIN message m ON m.id = mc.message_id\n WHERE mc.segment_id = cs.id ORDER BY m.ts ASC, m.id ASC LIMIT 1) AS initiator_id,\n (SELECT m.sender_id FROM message_context mc JOIN message m ON m.id = mc.message_id\n WHERE mc.segment_id = cs.id ORDER BY m.ts DESC, m.id DESC LIMIT 1) AS closer_id\n FROM segment cs ${whereClause} ORDER BY cs.start_ts ASC`\n )\n .all(...params) as Array<{\n segment_id: number\n start_ts: number\n end_ts: number\n initiator_id: number | null\n closer_id: number | null\n }>\n\n const memberNames = new Map<number, string>()\n const memberRows = db\n .prepare('SELECT id, COALESCE(group_nickname, account_name, platform_id) as name FROM member')\n .all() as Array<{ id: number; name: string }>\n for (const row of memberRows) memberNames.set(row.id, row.name)\n\n const monthMap = new Map<\n string,\n { initiateMap: Map<number, number>; closeMap: Map<number, number>; totalSessions: number }\n >()\n const memberInitTotals = new Map<number, number>()\n const memberCloseTotals = new Map<number, number>()\n const iceBreakMap = new Map<string, Map<number, number>>()\n let totalIceBreaks = 0\n let prevEndTs: number | null = null\n\n for (const row of segmentRows) {\n const month = toLocalMonth(row.start_ts)\n if (!monthMap.has(month)) monthMap.set(month, { initiateMap: new Map(), closeMap: new Map(), totalSessions: 0 })\n const ms = monthMap.get(month)!\n ms.totalSessions++\n\n if (row.initiator_id !== null) {\n ms.initiateMap.set(row.initiator_id, (ms.initiateMap.get(row.initiator_id) ?? 0) + 1)\n memberInitTotals.set(row.initiator_id, (memberInitTotals.get(row.initiator_id) ?? 0) + 1)\n }\n if (row.closer_id !== null) {\n ms.closeMap.set(row.closer_id, (ms.closeMap.get(row.closer_id) ?? 0) + 1)\n memberCloseTotals.set(row.closer_id, (memberCloseTotals.get(row.closer_id) ?? 0) + 1)\n }\n if (prevEndTs !== null && row.initiator_id !== null && row.start_ts - prevEndTs > ICE_BREAK_THRESHOLD) {\n if (!iceBreakMap.has(month)) iceBreakMap.set(month, new Map())\n const mMap = iceBreakMap.get(month)!\n mMap.set(row.initiator_id, (mMap.get(row.initiator_id) ?? 0) + 1)\n totalIceBreaks++\n }\n prevEndTs = row.end_ts\n }\n\n const allMemberIds = new Set<number>()\n for (const id of memberInitTotals.keys()) allMemberIds.add(id)\n for (const id of memberCloseTotals.keys()) allMemberIds.add(id)\n\n const monthKeys = Array.from(monthMap.keys()).sort((a, b) => b.localeCompare(a))\n const months: RelationshipMonthStats[] = monthKeys.map((month) => {\n const ms = monthMap.get(month)!\n const members = Array.from(allMemberIds).map((memberId) => ({\n memberId,\n name: memberNames.get(memberId) ?? `Unknown(${memberId})`,\n initiateCount: ms.initiateMap.get(memberId) ?? 0,\n closeCount: ms.closeMap.get(memberId) ?? 0,\n }))\n return { month, members, totalSessions: ms.totalSessions }\n })\n\n const members = Array.from(allMemberIds)\n .map((memberId) => ({\n memberId,\n name: memberNames.get(memberId) ?? `Unknown(${memberId})`,\n totalInitiateCount: memberInitTotals.get(memberId) ?? 0,\n totalCloseCount: memberCloseTotals.get(memberId) ?? 0,\n }))\n .sort((a, b) => b.totalInitiateCount - a.totalInitiateCount)\n\n const iceBreakers: IceBreakerItem[] = []\n for (const month of monthKeys) {\n const mMap = iceBreakMap.get(month)\n if (!mMap) continue\n for (const [memberId, count] of mMap) {\n iceBreakers.push({ month, memberId, name: memberNames.get(memberId) ?? `Unknown(${memberId})`, count })\n }\n }\n\n const segmentIdList = segmentRows.map((r) => r.segment_id)\n const msgStats = queryMessageLevelStats(db, segmentIdList, memberNames, perseveranceThreshold)\n\n return {\n months,\n members,\n totalSessions: segmentRows.length,\n hasSessionIndex: true,\n iceBreakers,\n totalIceBreaks,\n ...msgStats,\n perseveranceThreshold,\n }\n}\n\nfunction queryMessageLevelStats(\n db: DatabaseAdapter,\n segmentIds: number[],\n memberNames: Map<number, string>,\n perseveranceThreshold: number\n): {\n responseLatency: ResponseLatencyMember[]\n perseverance: PerseveranceMember[]\n totalDoubleTexts: number\n monthlyResponseLatency: MonthlyResponseLatency[]\n monthlyPerseverance: MonthlyPerseverance[]\n} {\n const empty = {\n responseLatency: [],\n perseverance: [],\n totalDoubleTexts: 0,\n monthlyResponseLatency: [],\n monthlyPerseverance: [],\n }\n if (segmentIds.length === 0) return empty\n\n const BATCH_SIZE = 500\n const responseTotals = new Map<number, { sum: number; count: number }>()\n const dtTotals = new Map<number, number>()\n const monthlyRespMap = new Map<string, Map<number, { sum: number; count: number }>>()\n const monthlyDtMap = new Map<string, Map<number, number>>()\n\n for (let i = 0; i < segmentIds.length; i += BATCH_SIZE) {\n const batch = segmentIds.slice(i, i + BATCH_SIZE)\n const placeholders = batch.map(() => '?').join(',')\n\n const latencyRows = db\n .prepare(\n `WITH msg_lag AS (\n SELECT m.sender_id, m.ts,\n LAG(m.sender_id) OVER (PARTITION BY mc.segment_id ORDER BY m.ts, m.id) AS prev_sender_id,\n LAG(m.ts) OVER (PARTITION BY mc.segment_id ORDER BY m.ts, m.id) AS prev_ts\n FROM message_context mc JOIN message m ON m.id = mc.message_id\n WHERE mc.segment_id IN (${placeholders}) AND m.type = 0\n )\n SELECT strftime('%Y-%m', datetime(ts, 'unixepoch', 'localtime')) AS month,\n sender_id AS responder_id, SUM(ts - prev_ts) AS total_time, COUNT(*) AS response_count\n FROM msg_lag WHERE prev_sender_id IS NOT NULL AND sender_id != prev_sender_id\n GROUP BY month, responder_id`\n )\n .all(...batch) as Array<{ month: string; responder_id: number; total_time: number; response_count: number }>\n\n for (const row of latencyRows) {\n const existing = responseTotals.get(row.responder_id)\n if (existing) {\n existing.sum += row.total_time\n existing.count += row.response_count\n } else responseTotals.set(row.responder_id, { sum: row.total_time, count: row.response_count })\n\n if (!monthlyRespMap.has(row.month)) monthlyRespMap.set(row.month, new Map())\n const mMap = monthlyRespMap.get(row.month)!\n const mExisting = mMap.get(row.responder_id)\n if (mExisting) {\n mExisting.sum += row.total_time\n mExisting.count += row.response_count\n } else mMap.set(row.responder_id, { sum: row.total_time, count: row.response_count })\n }\n\n const dtRows = db\n .prepare(\n `WITH msg_lag AS (\n SELECT m.sender_id, m.ts,\n LAG(m.sender_id) OVER (PARTITION BY mc.segment_id ORDER BY m.ts, m.id) AS prev_sender_id,\n LAG(m.ts) OVER (PARTITION BY mc.segment_id ORDER BY m.ts, m.id) AS prev_ts\n FROM message_context mc JOIN message m ON m.id = mc.message_id\n WHERE mc.segment_id IN (${placeholders}) AND m.type = 0\n )\n SELECT strftime('%Y-%m', datetime(ts, 'unixepoch', 'localtime')) AS month,\n sender_id, COUNT(*) AS double_text_count\n FROM msg_lag WHERE prev_sender_id IS NOT NULL AND sender_id = prev_sender_id AND (ts - prev_ts) >= ?\n GROUP BY month, sender_id`\n )\n .all(...batch, perseveranceThreshold) as Array<{ month: string; sender_id: number; double_text_count: number }>\n\n for (const row of dtRows) {\n dtTotals.set(row.sender_id, (dtTotals.get(row.sender_id) ?? 0) + row.double_text_count)\n if (!monthlyDtMap.has(row.month)) monthlyDtMap.set(row.month, new Map())\n const mMap = monthlyDtMap.get(row.month)!\n mMap.set(row.sender_id, (mMap.get(row.sender_id) ?? 0) + row.double_text_count)\n }\n }\n\n const responseLatency: ResponseLatencyMember[] = Array.from(responseTotals.entries())\n .map(([memberId, { sum, count }]) => ({\n memberId,\n name: memberNames.get(memberId) ?? `Unknown(${memberId})`,\n avgResponseTime: Math.round(sum / count),\n totalResponses: count,\n }))\n .sort((a, b) => a.avgResponseTime - b.avgResponseTime)\n\n let totalDoubleTexts = 0\n const perseverance: PerseveranceMember[] = Array.from(dtTotals.entries())\n .map(([memberId, count]) => {\n totalDoubleTexts += count\n return { memberId, name: memberNames.get(memberId) ?? `Unknown(${memberId})`, totalDoubleTexts: count }\n })\n .sort((a, b) => b.totalDoubleTexts - a.totalDoubleTexts)\n\n const monthlyResponseLatency: MonthlyResponseLatency[] = Array.from(monthlyRespMap.entries())\n .sort(([a], [b]) => b.localeCompare(a))\n .map(([month, mMap]) => ({\n month,\n members: Array.from(mMap.entries())\n .map(([memberId, { sum, count }]) => ({\n memberId,\n name: memberNames.get(memberId) ?? `Unknown(${memberId})`,\n avgResponseTime: Math.round(sum / count),\n responseCount: count,\n }))\n .sort((a, b) => a.avgResponseTime - b.avgResponseTime),\n }))\n\n const monthlyPerseverance: MonthlyPerseverance[] = Array.from(monthlyDtMap.entries())\n .sort(([a], [b]) => b.localeCompare(a))\n .map(([month, mMap]) => ({\n month,\n members: Array.from(mMap.entries())\n .map(([memberId, count]) => ({\n memberId,\n name: memberNames.get(memberId) ?? `Unknown(${memberId})`,\n doubleTextCount: count,\n }))\n .sort((a, b) => b.doubleTextCount - a.doubleTextCount),\n }))\n\n return { responseLatency, perseverance, totalDoubleTexts, monthlyResponseLatency, monthlyPerseverance }\n}\n\nfunction toLocalMonth(ts: number): string {\n const d = new Date(ts * 1000)\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`\n}\n","/**\n * 语言偏好分析模块(私聊专用,平台无关)\n *\n * NLP 能力通过 NlpProvider 接口注入,不直接依赖 @node-rs/jieba。\n * 调用方负责提供对应平台的实现(Electron / Server 用 jieba,浏览器可降级)。\n */\n\nimport type { TimeFilter } from '@openchatlab/shared-types'\nimport type { DatabaseAdapter } from '../../interfaces'\nimport { buildTimeFilter } from '../filters'\nimport { isSystemPlaceholderContent } from './text-filters'\n\n// ==================== NLP Provider 接口 ====================\n\nexport interface PosTagResult {\n word: string\n tag: string\n}\n\nexport interface NlpProvider {\n tag(text: string): PosTagResult[]\n isStopword(word: string, locale: string): boolean\n meaningfulPosTags: Set<string>\n}\n\n// ==================== 内部常量 ====================\n\nconst RE_ELLIPSIS = /\\.{2,}|…+|。{2,}/g\nconst RE_EXCLAMATION = /[!!]+/g\nconst RE_QUESTION = /[??]+/g\nconst RE_TILDE = /[~~]+/g\nconst RE_PERIOD = /[.。](?![.。])/g\nconst RE_ENDS_WITH_PUNCT = /[.。!!??~~…,,;;::、)\\])】》\"'」』\\-—]$/\n\nconst NOUN_TAGS = new Set(['n', 'nr', 'ns', 'nt', 'nz', 'nw'])\nconst VERB_TAGS = new Set(['v', 'vn', 'vd', 'vg'])\nconst ADJ_TAGS = new Set(['a', 'an', 'ad', 'ag'])\nconst ADV_TAGS = new Set(['d'])\nconst MODAL_TAGS = new Set(['y', 'e'])\n\nconst RE_URL = /https?:\\/\\/[^\\s]+/g\nconst RE_MENTION = /@[^\\s@]+/g\nconst RE_EMOJI =\n /[\\u{1F600}-\\u{1F64F}\\u{1F300}-\\u{1F5FF}\\u{1F680}-\\u{1F6FF}\\u{1F1E0}-\\u{1F1FF}\\u{2600}-\\u{26FF}\\u{2700}-\\u{27BF}]/gu\nconst RE_PUNCTUATION = /[!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{|}~,。!?、;:\"\"''()【】《》…—~·\\s]/g\nconst RE_PURE_NUMBER = /^\\d+$/\n\nfunction cleanTextForNlp(text: string): string {\n return text\n .replace(RE_URL, ' ')\n .replace(RE_MENTION, ' ')\n .replace(RE_EMOJI, ' ')\n .replace(RE_PUNCTUATION, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\nfunction countMatches(text: string, regex: RegExp): number {\n regex.lastIndex = 0\n const m = text.match(regex)\n return m ? m.length : 0\n}\n\nfunction cosineSimilarity(a: number[], b: number[]): number {\n let dot = 0,\n magA = 0,\n magB = 0\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i]\n magA += a[i] * a[i]\n magB += b[i] * b[i]\n }\n const denom = Math.sqrt(magA) * Math.sqrt(magB)\n return denom === 0 ? 0 : dot / denom\n}\n\n// ==================== 主入口 ====================\n\nexport interface LanguagePreferenceParams {\n locale: string\n timeFilter?: TimeFilter\n nlpProvider?: NlpProvider\n}\n\nexport function getLanguagePreferenceAnalysis(db: DatabaseAdapter, params: LanguagePreferenceParams): any {\n const { locale, timeFilter, nlpProvider } = params\n\n const { clause, params: filterParams } = buildTimeFilter(timeFilter)\n let whereClause = clause\n const textFilter =\n \" COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL AND LENGTH(TRIM(msg.content)) >= 2\"\n if (whereClause.includes('WHERE')) {\n whereClause += ' AND ' + textFilter\n } else {\n whereClause = ' WHERE ' + textFilter\n }\n\n const rows = db\n .prepare(\n `SELECT m.id as memberId, COALESCE(m.group_nickname, m.account_name, m.platform_id) as name, msg.content as content\n FROM message msg JOIN member m ON msg.sender_id = m.id ${whereClause} ORDER BY m.id`\n )\n .all(...filterParams) as Array<{ memberId: number; name: string; content: string }>\n\n if (rows.length === 0) return { members: [], sharedWords: [], similarityScore: 0 }\n\n const memberMessages = new Map<number, { name: string; messages: string[] }>()\n for (const row of rows) {\n if (isSystemPlaceholderContent(row.content)) continue\n\n let entry = memberMessages.get(row.memberId)\n if (!entry) {\n entry = { name: row.name, messages: [] }\n memberMessages.set(row.memberId, entry)\n }\n entry.messages.push(row.content)\n }\n\n const isChinese = locale.startsWith('zh')\n const minWordLength = isChinese ? 2 : 3\n\n const memberProfiles: any[] = []\n\n for (const [memberId, { name, messages }] of memberMessages) {\n const wordFreq = new Map<string, number>()\n const posCount = { noun: 0, verb: 0, adjective: 0, adverb: 0, modalParticle: 0, interjection: 0, other: 0 }\n const modalFreq = new Map<string, number>()\n let totalWordCount = 0\n const punct = { ellipsis: 0, exclamation: 0, question: 0, tilde: 0, period: 0, noPunct: 0, total: 0 }\n const phraseFreq = new Map<string, number>()\n\n for (const content of messages) {\n punct.ellipsis += countMatches(content, RE_ELLIPSIS)\n punct.exclamation += countMatches(content, RE_EXCLAMATION)\n punct.question += countMatches(content, RE_QUESTION)\n punct.tilde += countMatches(content, RE_TILDE)\n punct.period += countMatches(content, RE_PERIOD)\n const trimmed = content.trim()\n\n if (trimmed.length > 0 && !RE_ENDS_WITH_PUNCT.test(trimmed)) punct.noPunct++\n punct.total++\n\n if (trimmed.length >= 2) phraseFreq.set(trimmed, (phraseFreq.get(trimmed) || 0) + 1)\n\n const cleaned = cleanTextForNlp(content)\n if (!cleaned) continue\n\n if (isChinese && nlpProvider) {\n try {\n const tagged = nlpProvider.tag(cleaned)\n for (const { word, tag } of tagged) {\n if (!word || word.trim().length === 0 || RE_PURE_NUMBER.test(word)) continue\n if (word.length < minWordLength && !MODAL_TAGS.has(tag)) continue\n\n if (NOUN_TAGS.has(tag)) posCount.noun++\n else if (VERB_TAGS.has(tag)) posCount.verb++\n else if (ADJ_TAGS.has(tag)) posCount.adjective++\n else if (ADV_TAGS.has(tag)) posCount.adverb++\n else if (tag === 'y') posCount.modalParticle++\n else if (tag === 'e') posCount.interjection++\n else posCount.other++\n\n if (MODAL_TAGS.has(tag)) modalFreq.set(word, (modalFreq.get(word) || 0) + 1)\n\n if (nlpProvider.meaningfulPosTags.has(tag) || MODAL_TAGS.has(tag)) {\n if (!nlpProvider.isStopword(word, locale)) {\n wordFreq.set(word, (wordFreq.get(word) || 0) + 1)\n totalWordCount++\n }\n }\n }\n } catch {\n /* jieba failure — skip */\n }\n } else {\n try {\n const segmenter = new Intl.Segmenter(locale, { granularity: 'word' })\n for (const seg of segmenter.segment(cleaned)) {\n if (!seg.isWordLike) continue\n const w = seg.segment.toLowerCase()\n if (w.length < minWordLength || RE_PURE_NUMBER.test(w)) continue\n if (nlpProvider?.isStopword(w, locale)) continue\n wordFreq.set(w, (wordFreq.get(w) || 0) + 1)\n totalWordCount++\n posCount.other++\n }\n } catch {\n /* fallback */\n }\n }\n }\n\n const filteredWords = [...wordFreq.entries()].filter(([, c]) => c >= 2).sort((a, b) => b[1] - a[1])\n const uniqueWords = filteredWords.length\n const topWords = filteredWords.slice(0, 100).map(([word, count]) => ({ word, count }))\n const lexicalDiversity = totalWordCount > 0 ? Math.round((uniqueWords / totalWordCount) * 10000) / 100 : 0\n\n const modalParticles = [...modalFreq.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 20)\n .map(([word, count]) => ({ word, count }))\n const catchphrases = [...phraseFreq.entries()]\n .filter(([, c]) => c >= 2)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 50)\n .map(([content, count]) => ({ content, count }))\n\n memberProfiles.push({\n memberId,\n name,\n totalMessages: messages.length,\n totalWords: totalWordCount,\n uniqueWords,\n lexicalDiversity,\n topWords,\n posDistribution: posCount,\n modalParticles,\n punctuation: punct,\n catchphrases,\n })\n }\n\n memberProfiles.sort((a, b) => b.totalMessages - a.totalMessages)\n\n let sharedWords: any[] = []\n let similarityScore = 0\n\n if (memberProfiles.length >= 2) {\n const a = memberProfiles[0]\n const b = memberProfiles[1]\n const wordsA = new Map<string, number>(a.topWords.map((w: any) => [w.word, w.count]))\n const wordsB = new Map<string, number>(b.topWords.map((w: any) => [w.word, w.count]))\n const shared: Array<{ word: string; countA: number; countB: number }> = []\n for (const [word, countA] of wordsA) {\n const countB = wordsB.get(word)\n if (countB) shared.push({ word, countA, countB })\n }\n shared.sort((x, y) => y.countA + y.countB - (x.countA + x.countB))\n sharedWords = shared.slice(0, 30)\n\n const posKeys = ['noun', 'verb', 'adjective', 'adverb', 'modalParticle', 'interjection', 'other'] as const\n const vecA = posKeys.map((k) => a.posDistribution[k] as number)\n const vecB = posKeys.map((k) => b.posDistribution[k] as number)\n similarityScore = Math.round(cosineSimilarity(vecA, vecB) * 100)\n }\n\n return { members: memberProfiles, sharedWords, similarityScore }\n}\n","/**\n * 榜单分析模块(平台无关)\n *\n * 包含:龙王、潜水、打卡、斗图、夜猫、复读分析。\n * 算法从原前端 pluginCompute 移植,行为与现状保持一致(含原有时区/边界处理)。\n * 所有函数接收 DatabaseAdapter,不依赖全局状态。\n */\n\nimport type { TimeFilter } from '@openchatlab/shared-types'\nimport type { DatabaseAdapter } from '../../interfaces'\n\n// ==================== 类型定义 ====================\n\nexport type NightOwlTitle = '养生达人' | '偶尔失眠' | '经常失眠' | '夜猫子' | '秃头预备役' | '修仙练习生' | '守夜冠军'\n\nexport interface NightOwlRankItem {\n memberId: number\n platformId: string\n name: string\n totalNightMessages: number\n title: NightOwlTitle\n hourlyBreakdown: {\n h23: number\n h0: number\n h1: number\n h2: number\n h3to4: number\n }\n percentage: number\n}\n\nexport interface TimeRankItem {\n memberId: number\n platformId: string\n name: string\n count: number\n avgTime: string\n extremeTime: string\n percentage: number\n}\n\nexport interface ConsecutiveNightRecord {\n memberId: number\n platformId: string\n name: string\n maxConsecutiveDays: number\n currentStreak: number\n}\n\nexport interface NightOwlChampion {\n memberId: number\n platformId: string\n name: string\n score: number\n nightMessages: number\n lastSpeakerCount: number\n consecutiveDays: number\n}\n\nexport interface NightOwlAnalysis {\n nightOwlRank: NightOwlRankItem[]\n lastSpeakerRank: TimeRankItem[]\n firstSpeakerRank: TimeRankItem[]\n consecutiveRecords: ConsecutiveNightRecord[]\n champions: NightOwlChampion[]\n totalDays: number\n}\n\nexport interface DragonKingRankItem {\n memberId: number\n platformId: string\n name: string\n count: number\n percentage: number\n}\n\nexport interface DragonKingAnalysis {\n rank: DragonKingRankItem[]\n totalDays: number\n}\n\nexport interface DivingRankItem {\n memberId: number\n platformId: string\n name: string\n lastMessageTs: number\n daysSinceLastMessage: number\n}\n\nexport interface DivingAnalysis {\n rank: DivingRankItem[]\n}\n\nexport interface RepeatStatItem {\n memberId: number\n platformId: string\n name: string\n count: number\n percentage: number\n}\n\nexport interface RepeatRateItem {\n memberId: number\n platformId: string\n name: string\n count: number\n totalMessages: number\n rate: number\n}\n\nexport interface ChainLengthDistribution {\n length: number\n count: number\n}\n\nexport interface HotRepeatContent {\n content: string\n count: number\n maxChainLength: number\n originatorName: string\n lastTs: number\n firstMessageId: number\n}\n\nexport interface FastestRepeaterItem {\n memberId: number\n platformId: string\n name: string\n count: number\n avgTimeDiff: number\n}\n\nexport interface RepeatAnalysis {\n originators: RepeatStatItem[]\n initiators: RepeatStatItem[]\n breakers: RepeatStatItem[]\n fastestRepeaters: FastestRepeaterItem[]\n originatorRates: RepeatRateItem[]\n initiatorRates: RepeatRateItem[]\n breakerRates: RepeatRateItem[]\n chainLengthDistribution: ChainLengthDistribution[]\n hotContents: HotRepeatContent[]\n avgChainLength: number\n totalRepeatChains: number\n}\n\nexport interface MemeBattleRankItem {\n memberId: number\n platformId: string\n name: string\n count: number\n percentage: number\n}\n\nexport interface MemeBattleRecord {\n startTime: number\n endTime: number\n totalImages: number\n participantCount: number\n participants: Array<{\n memberId: number\n name: string\n imageCount: number\n }>\n}\n\nexport interface MemeBattleAnalysis {\n topBattles: MemeBattleRecord[]\n rankByCount: MemeBattleRankItem[]\n rankByImageCount: MemeBattleRankItem[]\n totalBattles: number\n}\n\nexport interface StreakRankItem {\n memberId: number\n name: string\n maxStreak: number\n maxStreakStart: string\n maxStreakEnd: string\n currentStreak: number\n}\n\nexport interface LoyaltyRankItem {\n memberId: number\n name: string\n totalDays: number\n percentage: number\n}\n\nexport interface CheckInAnalysis {\n streakRank: StreakRankItem[]\n loyaltyRank: LoyaltyRankItem[]\n totalDays: number\n}\n\n// ==================== 过滤条件构建 ====================\n\n/** 系统消息过滤条件(始终排除),与前端历史实现保持一致 */\nconst SYSTEM_FILTER = \"AND COALESCE(m.account_name, '') != '系统消息'\"\n\n/** 构建 AND 形式的时间/成员过滤条件(msg 别名),与原前端 SQL 保持等价 */\nfunction buildFilter(filter?: TimeFilter): { conditions: string; params: (number | string)[] } {\n const parts: string[] = []\n const params: (number | string)[] = []\n if (filter?.startTs != null) {\n parts.push('AND msg.ts >= ?')\n params.push(filter.startTs)\n }\n if (filter?.endTs != null) {\n parts.push('AND msg.ts <= ?')\n params.push(filter.endTs)\n }\n if (filter?.memberId != null) {\n parts.push('AND msg.sender_id = ?')\n params.push(filter.memberId)\n }\n return { conditions: parts.join(' '), params }\n}\n\n// ==================== 龙王分析 ====================\n\nexport function getDragonKingAnalysis(db: DatabaseAdapter, filter?: TimeFilter): DragonKingAnalysis {\n const { conditions, params } = buildFilter(filter)\n\n const rankRows = db\n .prepare(\n `WITH daily_counts AS (\n SELECT\n strftime('%Y-%m-%d', msg.ts, 'unixepoch', 'localtime') as date,\n msg.sender_id,\n m.platform_id,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,\n COUNT(*) as msg_count\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE 1=1 ${SYSTEM_FILTER} ${conditions}\n GROUP BY date, msg.sender_id\n ),\n daily_max AS (\n SELECT date, MAX(msg_count) as max_count\n FROM daily_counts\n GROUP BY date\n )\n SELECT dc.sender_id, dc.platform_id, dc.name, COUNT(*) as dragon_days\n FROM daily_counts dc\n JOIN daily_max dm ON dc.date = dm.date AND dc.msg_count = dm.max_count\n GROUP BY dc.sender_id\n ORDER BY dragon_days DESC`\n )\n .all(...params) as Array<{ sender_id: number; platform_id: string; name: string; dragon_days: number }>\n\n const totalRow = db\n .prepare(\n `SELECT COUNT(DISTINCT strftime('%Y-%m-%d', msg.ts, 'unixepoch', 'localtime')) as total\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE 1=1 ${SYSTEM_FILTER} ${conditions}`\n )\n .get(...params) as { total: number } | undefined\n\n const totalDays = totalRow?.total ?? 0\n const rank: DragonKingRankItem[] = rankRows.map((item) => ({\n memberId: item.sender_id,\n platformId: item.platform_id,\n name: item.name,\n count: item.dragon_days,\n percentage: totalDays > 0 ? Math.round((item.dragon_days / totalDays) * 10000) / 100 : 0,\n }))\n\n return { rank, totalDays }\n}\n\n// ==================== 潜水分析 ====================\n\nexport function getDivingAnalysis(db: DatabaseAdapter, filter?: TimeFilter): DivingAnalysis {\n const { conditions, params } = buildFilter(filter)\n\n const rows = db\n .prepare(\n `SELECT\n m.id as member_id,\n m.platform_id,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,\n MAX(msg.ts) as last_ts\n FROM member m\n JOIN message msg ON m.id = msg.sender_id\n WHERE 1=1 ${SYSTEM_FILTER} ${conditions}\n GROUP BY m.id\n ORDER BY last_ts ASC`\n )\n .all(...params) as Array<{ member_id: number; platform_id: string; name: string; last_ts: number }>\n\n const now = Math.floor(Date.now() / 1000)\n const rank: DivingRankItem[] = rows.map((item) => ({\n memberId: item.member_id,\n platformId: item.platform_id,\n name: item.name,\n lastMessageTs: item.last_ts,\n daysSinceLastMessage: Math.floor((now - item.last_ts) / 86400),\n }))\n\n return { rank }\n}\n\n// ==================== 打卡分析 ====================\n\nfunction computeCheckIn(dailyActivity: Array<{ senderId: number; name: string; day: string }>): CheckInAnalysis {\n if (dailyActivity.length === 0) {\n return { streakRank: [], loyaltyRank: [], totalDays: 0 }\n }\n\n const allDays = new Set(dailyActivity.map((r) => r.day))\n const totalDays = allDays.size\n const sortedDays = Array.from(allDays).sort()\n const lastDay = sortedDays[sortedDays.length - 1]\n\n const memberDays = new Map<number, { name: string; days: Set<string> }>()\n for (const record of dailyActivity) {\n if (!memberDays.has(record.senderId)) {\n memberDays.set(record.senderId, { name: record.name, days: new Set() })\n }\n memberDays.get(record.senderId)!.days.add(record.day)\n }\n\n const streakData: StreakRankItem[] = []\n const loyaltyData: Array<{ memberId: number; name: string; totalDays: number }> = []\n\n for (const [memberId, data] of memberDays) {\n const sortedMemberDays = Array.from(data.days).sort()\n const totalMemberDays = sortedMemberDays.length\n\n let maxStreak = 1\n let maxStreakStart = sortedMemberDays[0]\n let maxStreakEnd = sortedMemberDays[0]\n let currentStreakCount = 1\n let currentStreakStart = sortedMemberDays[0]\n\n for (let i = 1; i < sortedMemberDays.length; i++) {\n const prevDate = new Date(sortedMemberDays[i - 1])\n const currDate = new Date(sortedMemberDays[i])\n const diffDays = Math.round((currDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24))\n\n if (diffDays === 1) {\n currentStreakCount++\n } else {\n if (currentStreakCount > maxStreak) {\n maxStreak = currentStreakCount\n maxStreakStart = currentStreakStart\n maxStreakEnd = sortedMemberDays[i - 1]\n }\n currentStreakCount = 1\n currentStreakStart = sortedMemberDays[i]\n }\n }\n\n if (currentStreakCount > maxStreak) {\n maxStreak = currentStreakCount\n maxStreakStart = currentStreakStart\n maxStreakEnd = sortedMemberDays[sortedMemberDays.length - 1]\n }\n\n let finalCurrentStreak = 0\n if (sortedMemberDays[sortedMemberDays.length - 1] === lastDay) {\n finalCurrentStreak = 1\n for (let i = sortedMemberDays.length - 2; i >= 0; i--) {\n const currDate = new Date(sortedMemberDays[i + 1])\n const prevDate = new Date(sortedMemberDays[i])\n const diffDays = Math.round((currDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24))\n if (diffDays === 1) {\n finalCurrentStreak++\n } else {\n break\n }\n }\n }\n\n streakData.push({\n memberId,\n name: data.name,\n maxStreak,\n maxStreakStart,\n maxStreakEnd,\n currentStreak: finalCurrentStreak,\n })\n\n loyaltyData.push({\n memberId,\n name: data.name,\n totalDays: totalMemberDays,\n })\n }\n\n const streakRank = streakData.sort((a, b) => b.maxStreak - a.maxStreak)\n const sortedLoyalty = loyaltyData.sort((a, b) => b.totalDays - a.totalDays)\n const maxLoyaltyDays = sortedLoyalty.length > 0 ? sortedLoyalty[0].totalDays : 1\n const loyaltyRank: LoyaltyRankItem[] = sortedLoyalty.map((item) => ({\n ...item,\n percentage: Math.round((item.totalDays / maxLoyaltyDays) * 100),\n }))\n\n return { streakRank, loyaltyRank, totalDays }\n}\n\nexport function getCheckInAnalysis(db: DatabaseAdapter, filter?: TimeFilter): CheckInAnalysis {\n const { conditions, params } = buildFilter(filter)\n\n const dailyActivity = db\n .prepare(\n `SELECT\n msg.sender_id as senderId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,\n DATE(msg.ts, 'unixepoch', 'localtime') as day\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE 1=1 ${SYSTEM_FILTER} ${conditions}\n GROUP BY msg.sender_id, day\n ORDER BY msg.sender_id, day`\n )\n .all(...params) as Array<{ senderId: number; name: string; day: string }>\n\n if (dailyActivity.length === 0) {\n return { streakRank: [], loyaltyRank: [], totalDays: 0 }\n }\n\n return computeCheckIn(dailyActivity)\n}\n\n// ==================== 斗图分析 ====================\n\nfunction computeMemeBattle(\n messages: Array<{ senderId: number; type: number; ts: number; platformId: string; name: string }>\n): MemeBattleAnalysis {\n const emptyResult: MemeBattleAnalysis = {\n topBattles: [],\n rankByCount: [],\n rankByImageCount: [],\n totalBattles: 0,\n }\n\n const battles: Array<{\n startTime: number\n endTime: number\n msgs: Array<{ senderId: number; name: string; platformId: string }>\n }> = []\n\n let currentChain: Array<{ senderId: number; name: string; platformId: string; ts: number }> = []\n\n const processChain = () => {\n if (currentChain.length >= 3) {\n const senders = new Set(currentChain.map((m) => m.senderId))\n if (senders.size >= 2) {\n battles.push({\n startTime: currentChain[0].ts,\n endTime: currentChain[currentChain.length - 1].ts,\n msgs: currentChain.map(({ senderId, name, platformId }) => ({ senderId, name, platformId })),\n })\n }\n }\n currentChain = []\n }\n\n for (const msg of messages) {\n if (msg.type === 1 || msg.type === 5) {\n currentChain.push({\n senderId: msg.senderId,\n name: msg.name,\n platformId: msg.platformId,\n ts: msg.ts,\n })\n } else {\n processChain()\n }\n }\n processChain()\n\n if (battles.length === 0) return emptyResult\n\n const topBattles: MemeBattleRecord[] = battles\n .map((battle) => ({\n startTime: battle.startTime,\n endTime: battle.endTime,\n totalImages: battle.msgs.length,\n participantCount: new Set(battle.msgs.map((m) => m.senderId)).size,\n participants: Object.values(\n battle.msgs.reduce((acc: Record<number, { memberId: number; name: string; imageCount: number }>, curr) => {\n if (!acc[curr.senderId]) {\n acc[curr.senderId] = { memberId: curr.senderId, name: curr.name, imageCount: 0 }\n }\n acc[curr.senderId].imageCount++\n return acc\n }, {})\n ).sort((a, b) => b.imageCount - a.imageCount),\n }))\n .sort((a, b) => b.totalImages - a.totalImages)\n .slice(0, 30)\n\n const memberStats = new Map<\n number,\n { memberId: number; platformId: string; name: string; battleCount: number; imageCount: number }\n >()\n\n for (const battle of battles) {\n const participantsInBattle = new Set<number>()\n for (const msg of battle.msgs) {\n if (!memberStats.has(msg.senderId)) {\n memberStats.set(msg.senderId, {\n memberId: msg.senderId,\n platformId: msg.platformId,\n name: msg.name,\n battleCount: 0,\n imageCount: 0,\n })\n }\n memberStats.get(msg.senderId)!.imageCount++\n participantsInBattle.add(msg.senderId)\n }\n for (const memberId of participantsInBattle) {\n memberStats.get(memberId)!.battleCount++\n }\n }\n\n const allStats = Array.from(memberStats.values())\n\n const rankByCount: MemeBattleRankItem[] = [...allStats]\n .sort((a, b) => b.battleCount - a.battleCount)\n .map((item) => ({\n memberId: item.memberId,\n platformId: item.platformId,\n name: item.name,\n count: item.battleCount,\n percentage: battles.length > 0 ? Math.round((item.battleCount / battles.length) * 10000) / 100 : 0,\n }))\n\n const totalBattleImages = battles.reduce((sum, b) => sum + b.msgs.length, 0)\n const rankByImageCount: MemeBattleRankItem[] = [...allStats]\n .sort((a, b) => b.imageCount - a.imageCount)\n .map((item) => ({\n memberId: item.memberId,\n platformId: item.platformId,\n name: item.name,\n count: item.imageCount,\n percentage: totalBattleImages > 0 ? Math.round((item.imageCount / totalBattleImages) * 10000) / 100 : 0,\n }))\n\n return {\n topBattles,\n rankByCount,\n rankByImageCount,\n totalBattles: battles.length,\n }\n}\n\nexport function getMemeBattleAnalysis(db: DatabaseAdapter, filter?: TimeFilter): MemeBattleAnalysis {\n // 斗图分析过滤条件与众不同:不排除系统消息,仅排除 type=6(链接)\n const { conditions, params } = buildFilter(filter)\n\n const messages = db\n .prepare(\n `SELECT\n msg.sender_id as senderId,\n msg.type,\n msg.ts,\n m.platform_id as platformId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as name\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE msg.type != 6 ${conditions}\n ORDER BY msg.ts ASC`\n )\n .all(...params) as Array<{ senderId: number; type: number; ts: number; platformId: string; name: string }>\n\n if (messages.length === 0) {\n return { topBattles: [], rankByCount: [], rankByImageCount: [], totalBattles: 0 }\n }\n\n return computeMemeBattle(messages)\n}\n\n// ==================== 夜猫分析 ====================\n\nfunction computeNightOwl(\n messages: Array<{ id: number; senderId: number; ts: number; platformId: string; name: string }>\n): NightOwlAnalysis {\n const emptyResult: NightOwlAnalysis = {\n nightOwlRank: [],\n lastSpeakerRank: [],\n firstSpeakerRank: [],\n consecutiveRecords: [],\n champions: [],\n totalDays: 0,\n }\n\n if (messages.length === 0) return emptyResult\n\n function getNightOwlTitleByCount(count: number): NightOwlTitle {\n if (count === 0) return '养生达人'\n if (count <= 20) return '偶尔失眠'\n if (count <= 50) return '经常失眠'\n if (count <= 100) return '夜猫子'\n if (count <= 200) return '秃头预备役'\n if (count <= 500) return '修仙练习生'\n return '守夜冠军'\n }\n\n function getAdjustedDate(ts: number): string {\n const date = new Date(ts * 1000)\n const hour = date.getHours()\n if (hour < 5) {\n date.setDate(date.getDate() - 1)\n }\n return date.toISOString().split('T')[0]\n }\n\n function formatMinutes(minutes: number): string {\n const h = Math.floor(minutes / 60)\n const m = Math.round(minutes % 60)\n return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`\n }\n\n const memberInfo = new Map<number, { platformId: string; name: string }>()\n const nightStats = new Map<\n number,\n { total: number; h23: number; h0: number; h1: number; h2: number; h3to4: number; totalMessages: number }\n >()\n const dailyMessages = new Map<string, Array<{ senderId: number; ts: number; hour: number; minute: number }>>()\n const memberNightDays = new Map<number, Set<string>>()\n\n for (const msg of messages) {\n if (!memberInfo.has(msg.senderId)) {\n memberInfo.set(msg.senderId, { platformId: msg.platformId, name: msg.name })\n }\n\n const date = new Date(msg.ts * 1000)\n const hour = date.getHours()\n const minute = date.getMinutes()\n const adjustedDate = getAdjustedDate(msg.ts)\n\n if (!nightStats.has(msg.senderId)) {\n nightStats.set(msg.senderId, { total: 0, h23: 0, h0: 0, h1: 0, h2: 0, h3to4: 0, totalMessages: 0 })\n }\n const stats = nightStats.get(msg.senderId)!\n stats.totalMessages++\n\n if (hour === 23) {\n stats.h23++\n stats.total++\n } else if (hour === 0) {\n stats.h0++\n stats.total++\n } else if (hour === 1) {\n stats.h1++\n stats.total++\n } else if (hour === 2) {\n stats.h2++\n stats.total++\n } else if (hour >= 3 && hour < 5) {\n stats.h3to4++\n stats.total++\n }\n\n if (hour >= 23 || hour < 5) {\n if (!memberNightDays.has(msg.senderId)) {\n memberNightDays.set(msg.senderId, new Set())\n }\n memberNightDays.get(msg.senderId)!.add(adjustedDate)\n }\n\n if (!dailyMessages.has(adjustedDate)) {\n dailyMessages.set(adjustedDate, [])\n }\n dailyMessages.get(adjustedDate)!.push({ senderId: msg.senderId, ts: msg.ts, hour, minute })\n }\n\n const totalDays = dailyMessages.size\n\n const nightOwlRank: NightOwlAnalysis['nightOwlRank'] = []\n for (const [memberId, stats] of nightStats.entries()) {\n if (stats.total === 0) continue\n const info = memberInfo.get(memberId)!\n nightOwlRank.push({\n memberId,\n platformId: info.platformId,\n name: info.name,\n totalNightMessages: stats.total,\n title: getNightOwlTitleByCount(stats.total),\n hourlyBreakdown: { h23: stats.h23, h0: stats.h0, h1: stats.h1, h2: stats.h2, h3to4: stats.h3to4 },\n percentage: stats.totalMessages > 0 ? Math.round((stats.total / stats.totalMessages) * 10000) / 100 : 0,\n })\n }\n nightOwlRank.sort((a, b) => b.totalNightMessages - a.totalNightMessages)\n\n const lastSpeakerStats = new Map<number, { count: number; times: number[] }>()\n const firstSpeakerStats = new Map<number, { count: number; times: number[] }>()\n\n for (const [, dayMessages] of dailyMessages.entries()) {\n if (dayMessages.length === 0) continue\n const lastMsg = dayMessages[dayMessages.length - 1]\n if (!lastSpeakerStats.has(lastMsg.senderId)) {\n lastSpeakerStats.set(lastMsg.senderId, { count: 0, times: [] })\n }\n const lastStats = lastSpeakerStats.get(lastMsg.senderId)!\n lastStats.count++\n lastStats.times.push(lastMsg.hour * 60 + lastMsg.minute)\n\n const firstMsg = dayMessages[0]\n if (!firstSpeakerStats.has(firstMsg.senderId)) {\n firstSpeakerStats.set(firstMsg.senderId, { count: 0, times: [] })\n }\n const firstStats = firstSpeakerStats.get(firstMsg.senderId)!\n firstStats.count++\n firstStats.times.push(firstMsg.hour * 60 + firstMsg.minute)\n }\n\n const lastSpeakerRank: TimeRankItem[] = []\n for (const [memberId, stats] of lastSpeakerStats.entries()) {\n const info = memberInfo.get(memberId)!\n const avgMinutes = stats.times.reduce((a, b) => a + b, 0) / stats.times.length\n const maxMinutes = Math.max(...stats.times)\n lastSpeakerRank.push({\n memberId,\n platformId: info.platformId,\n name: info.name,\n count: stats.count,\n avgTime: formatMinutes(avgMinutes),\n extremeTime: formatMinutes(maxMinutes),\n percentage: totalDays > 0 ? Math.round((stats.count / totalDays) * 10000) / 100 : 0,\n })\n }\n lastSpeakerRank.sort((a, b) => b.count - a.count)\n\n const firstSpeakerRank: TimeRankItem[] = []\n for (const [memberId, stats] of firstSpeakerStats.entries()) {\n const info = memberInfo.get(memberId)!\n const avgMinutes = stats.times.reduce((a, b) => a + b, 0) / stats.times.length\n const minMinutes = Math.min(...stats.times)\n firstSpeakerRank.push({\n memberId,\n platformId: info.platformId,\n name: info.name,\n count: stats.count,\n avgTime: formatMinutes(avgMinutes),\n extremeTime: formatMinutes(minMinutes),\n percentage: totalDays > 0 ? Math.round((stats.count / totalDays) * 10000) / 100 : 0,\n })\n }\n firstSpeakerRank.sort((a, b) => b.count - a.count)\n\n const consecutiveRecords: ConsecutiveNightRecord[] = []\n for (const [memberId, nightDaysSet] of memberNightDays.entries()) {\n if (nightDaysSet.size === 0) continue\n const info = memberInfo.get(memberId)!\n const sortedNightDays = Array.from(nightDaysSet).sort()\n\n let maxStreak = 1\n let currentStreak = 1\n for (let i = 1; i < sortedNightDays.length; i++) {\n const prevDate = new Date(sortedNightDays[i - 1])\n const currDate = new Date(sortedNightDays[i])\n const diffDays = (currDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24)\n if (diffDays === 1) {\n currentStreak++\n maxStreak = Math.max(maxStreak, currentStreak)\n } else {\n currentStreak = 1\n }\n }\n\n const lastNightDay = sortedNightDays[sortedNightDays.length - 1]\n const today = new Date().toISOString().split('T')[0]\n const yesterday = new Date(Date.now() - 86400000).toISOString().split('T')[0]\n const isCurrentStreak = lastNightDay === today || lastNightDay === yesterday\n\n consecutiveRecords.push({\n memberId,\n platformId: info.platformId,\n name: info.name,\n maxConsecutiveDays: maxStreak,\n currentStreak: isCurrentStreak ? currentStreak : 0,\n })\n }\n consecutiveRecords.sort((a, b) => b.maxConsecutiveDays - a.maxConsecutiveDays)\n\n const championScores = new Map<number, { nightMessages: number; lastSpeakerCount: number; consecutiveDays: number }>()\n for (const item of nightOwlRank) {\n if (!championScores.has(item.memberId)) {\n championScores.set(item.memberId, { nightMessages: 0, lastSpeakerCount: 0, consecutiveDays: 0 })\n }\n championScores.get(item.memberId)!.nightMessages = item.totalNightMessages\n }\n for (const item of lastSpeakerRank) {\n if (!championScores.has(item.memberId)) {\n championScores.set(item.memberId, { nightMessages: 0, lastSpeakerCount: 0, consecutiveDays: 0 })\n }\n championScores.get(item.memberId)!.lastSpeakerCount = item.count\n }\n for (const item of consecutiveRecords) {\n if (!championScores.has(item.memberId)) {\n championScores.set(item.memberId, { nightMessages: 0, lastSpeakerCount: 0, consecutiveDays: 0 })\n }\n championScores.get(item.memberId)!.consecutiveDays = item.maxConsecutiveDays\n }\n\n const champions: NightOwlChampion[] = []\n for (const [memberId, scores] of championScores.entries()) {\n const info = memberInfo.get(memberId)!\n const score = scores.nightMessages * 1 + scores.lastSpeakerCount * 10 + scores.consecutiveDays * 20\n if (score > 0) {\n champions.push({\n memberId,\n platformId: info.platformId,\n name: info.name,\n score,\n nightMessages: scores.nightMessages,\n lastSpeakerCount: scores.lastSpeakerCount,\n consecutiveDays: scores.consecutiveDays,\n })\n }\n }\n champions.sort((a, b) => b.score - a.score)\n\n return { nightOwlRank, lastSpeakerRank, firstSpeakerRank, consecutiveRecords, champions, totalDays }\n}\n\nexport function getNightOwlAnalysis(db: DatabaseAdapter, filter?: TimeFilter): NightOwlAnalysis {\n const { conditions, params } = buildFilter(filter)\n\n const messages = db\n .prepare(\n `SELECT\n msg.id,\n msg.sender_id as senderId,\n msg.ts,\n m.platform_id as platformId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as name\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE 1=1 ${SYSTEM_FILTER} ${conditions}\n ORDER BY msg.ts ASC`\n )\n .all(...params) as Array<{ id: number; senderId: number; ts: number; platformId: string; name: string }>\n\n if (messages.length === 0) {\n return {\n nightOwlRank: [],\n lastSpeakerRank: [],\n firstSpeakerRank: [],\n consecutiveRecords: [],\n champions: [],\n totalDays: 0,\n }\n }\n\n return computeNightOwl(messages)\n}\n\n// ==================== 复读分析 ====================\n\nfunction computeRepeat(\n messages: Array<{ id: number; senderId: number; content: string; ts: number; platformId: string; name: string }>\n): RepeatAnalysis {\n const originatorCount = new Map<number, number>()\n const initiatorCount = new Map<number, number>()\n const breakerCount = new Map<number, number>()\n const memberMessageCount = new Map<number, number>()\n const memberInfo = new Map<number, { platformId: string; name: string }>()\n const chainLengthCount = new Map<number, number>()\n const contentStats = new Map<\n string,\n { count: number; maxChainLength: number; originatorId: number; lastTs: number; firstMessageId: number }\n >()\n\n let currentContent: string | null = null\n let repeatChain: Array<{ id: number; senderId: number; content: string; ts: number }> = []\n let totalRepeatChains = 0\n let totalChainLength = 0\n\n const fastestRepeaterStats = new Map<number, { totalDiff: number; count: number }>()\n\n const processRepeatChain = (\n chain: Array<{ id: number; senderId: number; content: string; ts: number }>,\n breakerId?: number\n ) => {\n if (chain.length < 3) return\n\n totalRepeatChains++\n const chainLength = chain.length\n totalChainLength += chainLength\n\n const oId = chain[0].senderId\n originatorCount.set(oId, (originatorCount.get(oId) || 0) + 1)\n\n const iId = chain[1].senderId\n initiatorCount.set(iId, (initiatorCount.get(iId) || 0) + 1)\n\n if (breakerId !== undefined) {\n breakerCount.set(breakerId, (breakerCount.get(breakerId) || 0) + 1)\n }\n\n chainLengthCount.set(chainLength, (chainLengthCount.get(chainLength) || 0) + 1)\n\n const content = chain[0].content\n const chainTs = chain[0].ts\n const firstMsgId = chain[0].id\n const existing = contentStats.get(content)\n if (existing) {\n existing.count++\n existing.lastTs = Math.max(existing.lastTs, chainTs)\n if (chainLength > existing.maxChainLength) {\n existing.maxChainLength = chainLength\n existing.originatorId = oId\n existing.firstMessageId = firstMsgId\n }\n } else {\n contentStats.set(content, {\n count: 1,\n maxChainLength: chainLength,\n originatorId: oId,\n lastTs: chainTs,\n firstMessageId: firstMsgId,\n })\n }\n\n for (let i = 1; i < chain.length; i++) {\n const currentMsg = chain[i]\n const prevMsg = chain[i - 1]\n const diff = (currentMsg.ts - prevMsg.ts) * 1000\n if (diff <= 20 * 1000) {\n if (!fastestRepeaterStats.has(currentMsg.senderId)) {\n fastestRepeaterStats.set(currentMsg.senderId, { totalDiff: 0, count: 0 })\n }\n const stats = fastestRepeaterStats.get(currentMsg.senderId)!\n stats.totalDiff += diff\n stats.count++\n }\n }\n }\n\n for (const msg of messages) {\n if (!memberInfo.has(msg.senderId)) {\n memberInfo.set(msg.senderId, { platformId: msg.platformId, name: msg.name })\n }\n memberMessageCount.set(msg.senderId, (memberMessageCount.get(msg.senderId) || 0) + 1)\n\n const content = msg.content.trim()\n if (content === currentContent) {\n const lastSender = repeatChain[repeatChain.length - 1]?.senderId\n if (lastSender !== msg.senderId) {\n repeatChain.push({ id: msg.id, senderId: msg.senderId, content, ts: msg.ts })\n }\n } else {\n processRepeatChain(repeatChain, msg.senderId)\n currentContent = content\n repeatChain = [{ id: msg.id, senderId: msg.senderId, content, ts: msg.ts }]\n }\n }\n processRepeatChain(repeatChain)\n\n const buildRankList = (countMap: Map<number, number>, total: number): RepeatStatItem[] => {\n const items: RepeatStatItem[] = []\n for (const [memberId, count] of countMap.entries()) {\n const info = memberInfo.get(memberId)\n if (info) {\n items.push({\n memberId,\n platformId: info.platformId,\n name: info.name,\n count,\n percentage: total > 0 ? Math.round((count / total) * 10000) / 100 : 0,\n })\n }\n }\n return items.sort((a, b) => b.count - a.count)\n }\n\n const buildRateList = (countMap: Map<number, number>): RepeatRateItem[] => {\n const items: RepeatRateItem[] = []\n for (const [memberId, count] of countMap.entries()) {\n const info = memberInfo.get(memberId)\n const totalMessages = memberMessageCount.get(memberId) || 0\n if (info && totalMessages > 0) {\n items.push({\n memberId,\n platformId: info.platformId,\n name: info.name,\n count,\n totalMessages,\n rate: Math.round((count / totalMessages) * 10000) / 100,\n })\n }\n }\n return items.sort((a, b) => b.rate - a.rate)\n }\n\n const buildFastestList = (): FastestRepeaterItem[] => {\n const items: FastestRepeaterItem[] = []\n for (const [memberId, stats] of fastestRepeaterStats.entries()) {\n if (stats.count < 5) continue\n const info = memberInfo.get(memberId)\n if (info) {\n items.push({\n memberId,\n platformId: info.platformId,\n name: info.name,\n count: stats.count,\n avgTimeDiff: Math.round(stats.totalDiff / stats.count),\n })\n }\n }\n return items.sort((a, b) => a.avgTimeDiff - b.avgTimeDiff)\n }\n\n const chainLengthDistribution: ChainLengthDistribution[] = []\n for (const [length, count] of chainLengthCount.entries()) {\n chainLengthDistribution.push({ length, count })\n }\n chainLengthDistribution.sort((a, b) => a.length - b.length)\n\n const hotContents: HotRepeatContent[] = []\n for (const [content, stats] of contentStats.entries()) {\n const originatorInfo = memberInfo.get(stats.originatorId)\n hotContents.push({\n content,\n count: stats.count,\n maxChainLength: stats.maxChainLength,\n originatorName: originatorInfo?.name || '未知',\n lastTs: stats.lastTs,\n firstMessageId: stats.firstMessageId,\n })\n }\n hotContents.sort((a, b) => b.maxChainLength - a.maxChainLength)\n const top50HotContents = hotContents.slice(0, 100)\n\n return {\n originators: buildRankList(originatorCount, totalRepeatChains),\n initiators: buildRankList(initiatorCount, totalRepeatChains),\n breakers: buildRankList(breakerCount, totalRepeatChains),\n fastestRepeaters: buildFastestList(),\n originatorRates: buildRateList(originatorCount),\n initiatorRates: buildRateList(initiatorCount),\n breakerRates: buildRateList(breakerCount),\n chainLengthDistribution,\n hotContents: top50HotContents,\n avgChainLength: totalRepeatChains > 0 ? Math.round((totalChainLength / totalRepeatChains) * 100) / 100 : 0,\n totalRepeatChains,\n }\n}\n\nexport function getRepeatAnalysis(db: DatabaseAdapter, filter?: TimeFilter): RepeatAnalysis {\n const { conditions, params } = buildFilter(filter)\n\n const messages = db\n .prepare(\n `SELECT\n msg.id,\n msg.sender_id as senderId,\n msg.content,\n msg.ts,\n m.platform_id as platformId,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as name\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE 1=1 ${SYSTEM_FILTER}\n AND msg.type = 0\n AND msg.content IS NOT NULL\n AND TRIM(msg.content) != ''\n ${conditions}\n ORDER BY msg.ts ASC, msg.id ASC`\n )\n .all(...params) as Array<{\n id: number\n senderId: number\n content: string\n ts: number\n platformId: string\n name: string\n }>\n\n if (messages.length === 0) {\n return {\n originators: [],\n initiators: [],\n breakers: [],\n fastestRepeaters: [],\n originatorRates: [],\n initiatorRates: [],\n breakerRates: [],\n chainLengthDistribution: [],\n hotContents: [],\n avgChainLength: 0,\n totalRepeatChains: 0,\n }\n }\n\n return computeRepeat(messages)\n}\n","/**\n * 内置工具目录(静态数据)\n *\n * 仅包含工具名称和分类,不含任何运行时依赖。\n * 用于前端展示工具列表和助手配置面板。\n */\n\nexport type ToolCategory = 'core' | 'analysis'\n\nexport interface BuiltinToolCatalogEntry {\n name: string\n category: ToolCategory\n}\n\nconst LEGACY_BUILTIN_TOOL_NAME_ALIASES: Record<string, string> = {\n get_session_messages: 'get_segment_messages',\n get_session_summaries: 'get_segment_summaries',\n}\n\nexport function normalizeBuiltinToolName(toolName: string): string {\n return LEGACY_BUILTIN_TOOL_NAME_ALIASES[toolName] ?? toolName\n}\n\nexport function normalizeBuiltinToolNames(toolNames: readonly string[]): string[] {\n return Array.from(new Set(toolNames.map(normalizeBuiltinToolName)))\n}\n\nexport const CHART_CAPABILITY_SKILL_ID = 'chart_runtime'\n\nexport const BUILTIN_TOOL_CATALOG: BuiltinToolCatalogEntry[] = [\n // Core 工具\n { name: 'get_chat_overview', category: 'core' },\n { name: 'search_messages', category: 'core' },\n { name: 'deep_search_messages', category: 'core' },\n { name: 'get_recent_messages', category: 'core' },\n { name: 'get_message_context', category: 'core' },\n { name: 'get_segment_messages', category: 'core' },\n { name: 'get_members', category: 'core' },\n { name: 'get_schema', category: 'core' },\n\n // Analysis 工具\n { name: 'get_member_stats', category: 'analysis' },\n { name: 'get_time_stats', category: 'analysis' },\n { name: 'get_member_name_history', category: 'analysis' },\n { name: 'get_conversation_between', category: 'analysis' },\n { name: 'get_segment_summaries', category: 'analysis' },\n { name: 'response_time_analysis', category: 'analysis' },\n { name: 'keyword_frequency', category: 'analysis' },\n { name: 'render_chart', category: 'analysis' },\n { name: 'execute_sql', category: 'analysis' },\n\n // SQL Analysis 工具\n { name: 'message_type_breakdown', category: 'analysis' },\n { name: 'peak_chat_hours_by_member', category: 'analysis' },\n { name: 'member_activity_trend', category: 'analysis' },\n { name: 'silent_members', category: 'analysis' },\n { name: 'reply_interaction_ranking', category: 'analysis' },\n { name: 'mutual_interaction_pairs', category: 'analysis' },\n { name: 'member_message_length_stats', category: 'analysis' },\n { name: 'daily_active_members', category: 'analysis' },\n { name: 'conversation_initiator_stats', category: 'analysis' },\n { name: 'activity_heatmap', category: 'analysis' },\n { name: 'unanswered_messages', category: 'analysis' },\n]\n","/**\n * Provider Registry — 内置 provider 目录\n */\n\nimport type { ProviderDefinition } from './model-types'\nexport type { ProviderDefinition }\n\n// ==================== 内置 Provider 目录 ====================\n\nconst OPENAI: ProviderDefinition = {\n id: 'openai',\n name: 'OpenAI',\n kind: 'official',\n website: 'https://openai.com',\n consoleUrl: 'https://platform.openai.com/api-keys',\n defaultBaseUrl: 'https://api.openai.com/v1',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: [\n 'gpt-5.4-pro',\n 'gpt-5.4',\n 'gpt-5.2-pro',\n 'gpt-5.2',\n 'gpt-5.1',\n 'gpt-5-pro',\n 'gpt-5',\n 'gpt-4.1',\n 'gpt-4.1-mini',\n 'gpt-4.1-nano',\n 'gpt-4o',\n 'gpt-4o-mini',\n 'o3',\n 'o4-mini',\n 'text-embedding-3-small',\n 'text-embedding-3-large',\n ],\n}\n\nconst ANTHROPIC: ProviderDefinition = {\n id: 'anthropic',\n name: 'Anthropic',\n kind: 'official',\n website: 'https://www.anthropic.com',\n consoleUrl: 'https://console.anthropic.com/settings/keys',\n defaultBaseUrl: 'https://api.anthropic.com',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: [\n 'claude-opus-4-6',\n 'claude-sonnet-4-6',\n 'claude-opus-4-5',\n 'claude-sonnet-4-5',\n 'claude-haiku-4-5',\n 'claude-sonnet-4-20250514',\n 'claude-opus-4-20250514',\n 'claude-haiku-3-5-20241022',\n ],\n}\n\nconst GEMINI: ProviderDefinition = {\n id: 'gemini',\n name: 'Gemini',\n kind: 'official',\n website: 'https://ai.google.dev',\n consoleUrl: 'https://aistudio.google.com/apikey',\n defaultBaseUrl: 'https://generativelanguage.googleapis.com/v1beta',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: [\n 'gemini-3.1-pro-preview',\n 'gemini-3-pro-preview',\n 'gemini-2.5-flash',\n 'gemini-2.5-pro',\n 'gemini-2.0-flash',\n 'text-embedding-004',\n ],\n}\n\nconst DEEPSEEK: ProviderDefinition = {\n id: 'deepseek',\n name: 'DeepSeek',\n kind: 'official',\n website: 'https://www.deepseek.com',\n consoleUrl: 'https://platform.deepseek.com/api_keys',\n defaultBaseUrl: 'https://api.deepseek.com/v1',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: ['deepseek-v4-pro', 'deepseek-v4-flash'],\n}\n\nconst QWEN: ProviderDefinition = {\n id: 'qwen',\n name: 'Qwen',\n kind: 'official',\n website: 'https://tongyi.aliyun.com',\n consoleUrl: 'https://dashscope.console.aliyun.com/apiKey',\n defaultBaseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: [\n 'qwen3.5-plus',\n 'qwen3.5-flash',\n 'qwen3-max',\n 'qwen-max',\n 'qwen-plus',\n 'qwen-turbo',\n 'qwen-long',\n 'text-embedding-v3',\n ],\n}\n\nconst GLM: ProviderDefinition = {\n id: 'glm',\n name: 'GLM',\n kind: 'official',\n website: 'https://www.zhipuai.cn',\n consoleUrl: 'https://open.bigmodel.cn/usercenter/apikeys',\n defaultBaseUrl: 'https://open.bigmodel.cn/api/paas/v4',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: [\n 'glm-5',\n 'glm-4.7',\n 'glm-4.6v',\n 'glm-4.6v-flash',\n 'glm-4.5',\n 'glm-4.5-flash',\n 'glm-4-plus',\n 'glm-4-flash',\n 'embedding-3',\n ],\n}\n\nconst KIMI: ProviderDefinition = {\n id: 'kimi',\n name: 'Kimi',\n kind: 'official',\n website: 'https://www.moonshot.cn',\n consoleUrl: 'https://platform.moonshot.cn/console/api-keys',\n defaultBaseUrl: 'https://api.moonshot.cn/v1',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: ['kimi-k2.5', 'kimi-k2-thinking', 'kimi-k2-thinking-turbo', 'kimi-k2-0905-Preview', 'moonshot-v1-auto'],\n}\n\nconst DOUBAO: ProviderDefinition = {\n id: 'doubao',\n name: 'Doubao',\n kind: 'official',\n website: 'https://www.volcengine.com/product/doubao',\n consoleUrl: 'https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey',\n defaultBaseUrl: 'https://ark.cn-beijing.volces.com/api/v3',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: [\n 'doubao-seed-2-0-pro-260215',\n 'doubao-seed-2-0-lite-260215',\n 'doubao-seed-2-0-mini-260215',\n 'doubao-seed-1-8-251228',\n 'doubao-seed-1-6-251015',\n 'doubao-seed-1-6-lite-251015',\n ],\n}\n\nconst SILICONFLOW: ProviderDefinition = {\n id: 'siliconflow',\n name: 'SiliconFlow',\n kind: 'official',\n website: 'https://siliconflow.cn',\n consoleUrl: 'https://cloud.siliconflow.cn/account/ak',\n defaultBaseUrl: 'https://api.siliconflow.cn/v1',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: ['deepseek-ai/DeepSeek-V3.2', 'deepseek-ai/DeepSeek-R1', 'Qwen/Qwen3-8B'],\n}\n\nconst GROQ: ProviderDefinition = {\n id: 'groq',\n name: 'Groq',\n kind: 'official',\n website: 'https://groq.com',\n consoleUrl: 'https://console.groq.com/keys',\n defaultBaseUrl: 'https://api.groq.com/openai/v1',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: ['llama3-70b-8192', 'llama3-8b-8192', 'mistral-saba-24b', 'gemma-9b-it'],\n}\n\nconst OPENROUTER: ProviderDefinition = {\n id: 'openrouter',\n name: 'OpenRouter',\n kind: 'aggregator',\n website: 'https://openrouter.ai',\n consoleUrl: 'https://openrouter.ai/keys',\n defaultBaseUrl: 'https://openrouter.ai/api/v1',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: ['deepseek/deepseek-v4-pro', 'google/gemini-2.5-flash-preview', 'mistralai/mistral-7b-instruct:free'],\n}\n\nconst XAI: ProviderDefinition = {\n id: 'xai',\n name: 'xAI',\n kind: 'official',\n website: 'https://x.ai',\n consoleUrl: 'https://console.x.ai',\n defaultBaseUrl: 'https://api.x.ai/v1',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: ['grok-4', 'grok-3', 'grok-3-fast', 'grok-3-mini', 'grok-3-mini-fast'],\n}\n\nconst MINIMAX: ProviderDefinition = {\n id: 'minimax',\n name: 'MiniMax',\n kind: 'official',\n website: 'https://www.minimaxi.com',\n consoleUrl: 'https://platform.minimaxi.com/user-center/basic-information/interface-key',\n defaultBaseUrl: 'https://api.minimaxi.com/v1',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: ['MiniMax-M2.7', 'MiniMax-M2.7-highspeed', 'MiniMax-M2.5', 'MiniMax-M2.5-highspeed'],\n}\n\nconst OPENAI_COMPATIBLE: ProviderDefinition = {\n id: 'openai-compatible',\n name: 'OpenAI Compatible',\n kind: 'openai-compatible',\n defaultBaseUrl: 'http://localhost:11434/v1',\n authMode: 'api-key',\n supportsCustomModels: true,\n builtin: true,\n enabledByDefault: true,\n modelIds: [],\n}\n\n// ==================== 内置目录导出 ====================\n\nexport const BUILTIN_PROVIDERS: ProviderDefinition[] = [\n OPENAI,\n ANTHROPIC,\n GEMINI,\n DEEPSEEK,\n QWEN,\n GLM,\n KIMI,\n DOUBAO,\n SILICONFLOW,\n GROQ,\n OPENROUTER,\n XAI,\n MINIMAX,\n OPENAI_COMPATIBLE,\n]\n\n/** 按 id 查找内置 provider */\nexport function getBuiltinProviderById(id: string): ProviderDefinition | null {\n return BUILTIN_PROVIDERS.find((p) => p.id === id) || null\n}\n","/**\n * Model Catalog — 内置模型目录\n */\n\nimport type { ModelDefinition } from './model-types'\nexport type { ModelDefinition }\n\n// ==================== Helper ====================\n\ntype ModelInput = Omit<ModelDefinition, 'builtin' | 'editable'>\n\nfunction builtin(m: ModelInput): ModelDefinition {\n return { ...m, builtin: true, editable: false }\n}\n\n// ==================== OpenAI ====================\n\nconst OPENAI_MODELS: ModelDefinition[] = [\n builtin({\n id: 'gpt-5.4-pro',\n providerId: 'openai',\n name: 'GPT-5.4 Pro',\n description: 'Latest flagship, best reasoning & coding',\n contextWindow: 1000000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'gpt-5.4',\n providerId: 'openai',\n name: 'GPT-5.4',\n description: 'GPT-5.4 standard',\n contextWindow: 1000000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'gpt-5.2-pro',\n providerId: 'openai',\n name: 'GPT-5.2 Pro',\n description: 'GPT-5.2 Pro',\n contextWindow: 1000000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'gpt-5.2',\n providerId: 'openai',\n name: 'GPT-5.2',\n description: 'GPT-5.2 standard',\n contextWindow: 1000000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'gpt-5.1',\n providerId: 'openai',\n name: 'GPT-5.1',\n description: 'GPT-5.1',\n contextWindow: 1000000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'gpt-5-pro',\n providerId: 'openai',\n name: 'GPT-5 Pro',\n description: 'GPT-5 Pro',\n contextWindow: 1000000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'gpt-5',\n providerId: 'openai',\n name: 'GPT-5',\n description: 'GPT-5 standard',\n contextWindow: 1000000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'gpt-4.1',\n providerId: 'openai',\n name: 'GPT-4.1',\n description: 'Best at coding & instruction following',\n contextWindow: 1047576,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'gpt-4.1-mini',\n providerId: 'openai',\n name: 'GPT-4.1 Mini',\n description: 'Cost-effective',\n contextWindow: 1047576,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'gpt-4.1-nano',\n providerId: 'openai',\n name: 'GPT-4.1 Nano',\n description: 'Fastest and most affordable',\n contextWindow: 1047576,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'gpt-4o',\n providerId: 'openai',\n name: 'GPT-4o',\n description: 'Multimodal flagship model',\n contextWindow: 128000,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'gpt-4o-mini',\n providerId: 'openai',\n name: 'GPT-4o Mini',\n description: 'Lightweight multimodal model',\n contextWindow: 128000,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'o3',\n providerId: 'openai',\n name: 'o3',\n description: 'Advanced reasoning model',\n contextWindow: 200000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'o4-mini',\n providerId: 'openai',\n name: 'o4-mini',\n description: 'Efficient reasoning model',\n contextWindow: 200000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'text-embedding-3-small',\n providerId: 'openai',\n name: 'Text Embedding 3 Small',\n description: 'Efficient embedding model',\n contextWindow: 8191,\n capabilities: ['embedding'],\n recommendedFor: ['embedding'],\n status: 'stable',\n }),\n builtin({\n id: 'text-embedding-3-large',\n providerId: 'openai',\n name: 'Text Embedding 3 Large',\n description: 'High-precision embedding model',\n contextWindow: 8191,\n capabilities: ['embedding'],\n recommendedFor: ['embedding'],\n status: 'stable',\n }),\n]\n\n// ==================== Anthropic ====================\n\nconst ANTHROPIC_MODELS: ModelDefinition[] = [\n builtin({\n id: 'claude-opus-4-6',\n providerId: 'anthropic',\n name: 'Claude Opus 4.6',\n description: 'Latest flagship, adaptive thinking',\n contextWindow: 200000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'claude-sonnet-4-6',\n providerId: 'anthropic',\n name: 'Claude Sonnet 4.6',\n description: 'Cost-effective, adaptive thinking',\n contextWindow: 200000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'claude-opus-4-5',\n providerId: 'anthropic',\n name: 'Claude Opus 4.5',\n description: 'Previous generation flagship',\n contextWindow: 200000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'claude-sonnet-4-5',\n providerId: 'anthropic',\n name: 'Claude Sonnet 4.5',\n description: 'Previous generation cost-effective',\n contextWindow: 200000,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'claude-haiku-4-5',\n providerId: 'anthropic',\n name: 'Claude Haiku 4.5',\n description: 'Fast and lightweight',\n contextWindow: 200000,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'claude-sonnet-4-20250514',\n providerId: 'anthropic',\n name: 'Claude Sonnet 4',\n description: 'Sonnet 4 snapshot',\n contextWindow: 200000,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'claude-opus-4-20250514',\n providerId: 'anthropic',\n name: 'Claude Opus 4',\n description: 'Opus 4 snapshot',\n contextWindow: 200000,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'claude-haiku-3-5-20241022',\n providerId: 'anthropic',\n name: 'Claude 3.5 Haiku',\n description: '3.5 Haiku snapshot',\n contextWindow: 200000,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n]\n\n// ==================== Gemini ====================\n\nconst GEMINI_MODELS: ModelDefinition[] = [\n builtin({\n id: 'gemini-3.1-pro-preview',\n providerId: 'gemini',\n name: 'Gemini 3.1 Pro',\n description: 'Latest Gemini 3.1 Pro preview',\n contextWindow: 1048576,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'preview',\n }),\n builtin({\n id: 'gemini-3-pro-preview',\n providerId: 'gemini',\n name: 'Gemini 3 Pro',\n description: 'Gemini 3 Pro preview',\n contextWindow: 1048576,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'preview',\n }),\n builtin({\n id: 'gemini-2.5-flash',\n providerId: 'gemini',\n name: 'Gemini 2.5 Flash',\n description: 'Cost-effective, low latency, adaptive thinking',\n contextWindow: 1048576,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'gemini-2.5-pro',\n providerId: 'gemini',\n name: 'Gemini 2.5 Pro',\n description: 'Deep reasoning and complex tasks',\n contextWindow: 1048576,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'gemini-2.0-flash',\n providerId: 'gemini',\n name: 'Gemini 2.0 Flash',\n description: 'Previous generation fast model',\n contextWindow: 1048576,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'text-embedding-004',\n providerId: 'gemini',\n name: 'Text Embedding 004',\n description: 'Google embedding model',\n contextWindow: 2048,\n capabilities: ['embedding'],\n recommendedFor: ['embedding'],\n status: 'stable',\n }),\n]\n\n// ==================== DeepSeek ====================\n\nconst DEEPSEEK_MODELS: ModelDefinition[] = [\n builtin({\n id: 'deepseek-v4-pro',\n providerId: 'deepseek',\n name: 'DeepSeek V4 Pro',\n description: 'Flagship model, 1M context, thinking & non-thinking',\n contextWindow: 1000000,\n capabilities: ['chat', 'reasoning', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'deepseek-v4-flash',\n providerId: 'deepseek',\n name: 'DeepSeek V4 Flash',\n description: 'Fast model, 1M context, thinking & non-thinking',\n contextWindow: 1000000,\n capabilities: ['chat', 'reasoning', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n]\n\n// ==================== 通义千问 ====================\n\nconst QWEN_MODELS: ModelDefinition[] = [\n builtin({\n id: 'qwen3.5-plus',\n providerId: 'qwen',\n name: 'Qwen3.5 Plus',\n description: 'Latest Qwen3.5 flagship',\n contextWindow: 262144,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'qwen3.5-flash',\n providerId: 'qwen',\n name: 'Qwen3.5 Flash',\n description: 'Qwen3.5 fast version',\n contextWindow: 262144,\n capabilities: ['chat', 'reasoning', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'qwen3-max',\n providerId: 'qwen',\n name: 'Qwen3 Max',\n description: 'Qwen3 massive-scale',\n contextWindow: 262144,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'qwen-max',\n providerId: 'qwen',\n name: 'Qwen Max',\n description: 'Massive-scale language model',\n contextWindow: 262144,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'qwen-plus',\n providerId: 'qwen',\n name: 'Qwen Plus',\n description: 'Good quality, suitable for most scenarios',\n contextWindow: 262144,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'qwen-turbo',\n providerId: 'qwen',\n name: 'Qwen Turbo',\n description: 'Fast and cost-effective',\n contextWindow: 1000000,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'qwen-long',\n providerId: 'qwen',\n name: 'Qwen Long',\n description: 'Ultra-long context model',\n contextWindow: 262144,\n capabilities: ['chat'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'text-embedding-v3',\n providerId: 'qwen',\n name: 'Text Embedding V3',\n description: 'Qwen embedding model',\n contextWindow: 8192,\n capabilities: ['embedding'],\n recommendedFor: ['embedding'],\n status: 'stable',\n }),\n]\n\n// ==================== 智谱 GLM ====================\n\nconst GLM_MODELS: ModelDefinition[] = [\n builtin({\n id: 'glm-5',\n providerId: 'glm',\n name: 'GLM-5',\n description: 'Latest flagship model',\n contextWindow: 200000,\n capabilities: ['chat', 'reasoning', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'glm-4.7',\n providerId: 'glm',\n name: 'GLM-4.7',\n description: 'GLM-4.7 high performance',\n contextWindow: 200000,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'glm-4.6v',\n providerId: 'glm',\n name: 'GLM-4.6V',\n description: '4.6 multimodal model',\n contextWindow: 200000,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'glm-4.6v-flash',\n providerId: 'glm',\n name: 'GLM-4.6V Flash',\n description: '4.6V free multimodal',\n contextWindow: 200000,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'glm-4.5',\n providerId: 'glm',\n name: 'GLM-4.5',\n description: 'GLM-4.5 standard',\n contextWindow: 200000,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'glm-4.5-flash',\n providerId: 'glm',\n name: 'GLM-4.5 Flash',\n description: '4.5 free model',\n contextWindow: 200000,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'glm-4-plus',\n providerId: 'glm',\n name: 'GLM-4 Plus',\n description: 'Previous generation flagship',\n contextWindow: 128000,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'glm-4-flash',\n providerId: 'glm',\n name: 'GLM-4 Flash',\n description: 'Fast model',\n contextWindow: 128000,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'embedding-3',\n providerId: 'glm',\n name: 'Embedding-3',\n description: 'Zhipu embedding model',\n contextWindow: 8192,\n capabilities: ['embedding'],\n recommendedFor: ['embedding'],\n status: 'stable',\n }),\n]\n\n// ==================== Kimi ====================\n\nconst KIMI_MODELS: ModelDefinition[] = [\n builtin({\n id: 'kimi-k2.5',\n providerId: 'kimi',\n name: 'Kimi K2.5',\n description: 'Latest flagship with vision support',\n contextWindow: 262144,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'kimi-k2-thinking',\n providerId: 'kimi',\n name: 'Kimi K2 Thinking',\n description: 'K2 deep reasoning',\n contextWindow: 131072,\n capabilities: ['chat', 'reasoning', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'kimi-k2-thinking-turbo',\n providerId: 'kimi',\n name: 'Kimi K2 Thinking Turbo',\n description: 'K2 fast reasoning',\n contextWindow: 131072,\n capabilities: ['chat', 'reasoning', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'kimi-k2-0905-Preview',\n providerId: 'kimi',\n name: 'Kimi K2 Preview',\n description: 'K2 preview',\n contextWindow: 131072,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: [],\n status: 'preview',\n }),\n builtin({\n id: 'moonshot-v1-auto',\n providerId: 'kimi',\n name: 'Moonshot V1 Auto',\n description: 'Auto-select context length',\n contextWindow: 131072,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n]\n\n// ==================== 豆包 ====================\n\nconst DOUBAO_MODELS: ModelDefinition[] = [\n builtin({\n id: 'doubao-seed-2-0-pro-260215',\n providerId: 'doubao',\n name: 'Doubao Seed 2.0 Pro',\n description: 'Latest flagship model',\n contextWindow: 256000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'doubao-seed-2-0-lite-260215',\n providerId: 'doubao',\n name: 'Doubao Seed 2.0 Lite',\n description: 'Seed 2.0 lightweight',\n contextWindow: 256000,\n capabilities: ['chat', 'reasoning', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'doubao-seed-2-0-mini-260215',\n providerId: 'doubao',\n name: 'Doubao Seed 2.0 Mini',\n description: 'Seed 2.0 ultra-fast',\n contextWindow: 256000,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'doubao-seed-1-8-251228',\n providerId: 'doubao',\n name: 'Doubao Seed 1.8',\n description: 'Previous generation flagship',\n contextWindow: 128000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'doubao-seed-1-6-251015',\n providerId: 'doubao',\n name: 'Doubao Seed 1.6',\n description: 'High-performance general model',\n contextWindow: 128000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'doubao-seed-1-6-lite-251015',\n providerId: 'doubao',\n name: 'Doubao Seed 1.6 Lite',\n description: 'Lightweight and cost-effective',\n contextWindow: 128000,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n]\n\n// ==================== 硅基流动 ====================\n\nconst SILICONFLOW_MODELS: ModelDefinition[] = [\n builtin({\n id: 'deepseek-ai/DeepSeek-V3.2',\n providerId: 'siliconflow',\n name: 'DeepSeek V3.2',\n description: 'DeepSeek V3.2 on SiliconFlow',\n contextWindow: 128000,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'deepseek-ai/DeepSeek-R1',\n providerId: 'siliconflow',\n name: 'DeepSeek R1',\n description: 'DeepSeek R1 reasoning model',\n contextWindow: 65536,\n capabilities: ['chat', 'reasoning'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'Qwen/Qwen3-8B',\n providerId: 'siliconflow',\n name: 'Qwen3 8B',\n description: 'Qwen3 8B lightweight model',\n contextWindow: 32768,\n capabilities: ['chat'],\n recommendedFor: [],\n status: 'stable',\n }),\n]\n\n// ==================== Groq ====================\n\nconst GROQ_MODELS: ModelDefinition[] = [\n builtin({\n id: 'llama3-70b-8192',\n providerId: 'groq',\n name: 'LLaMA3 70B',\n description: 'Meta LLaMA3 70B ultra-fast inference',\n contextWindow: 8192,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'llama3-8b-8192',\n providerId: 'groq',\n name: 'LLaMA3 8B',\n description: 'Meta LLaMA3 8B lightweight ultra-fast',\n contextWindow: 8192,\n capabilities: ['chat'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'mistral-saba-24b',\n providerId: 'groq',\n name: 'Mistral Saba 24B',\n description: 'Mistral Saba 24B',\n contextWindow: 32768,\n capabilities: ['chat'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'gemma-9b-it',\n providerId: 'groq',\n name: 'Gemma 9B',\n description: 'Google Gemma 9B',\n contextWindow: 8192,\n capabilities: ['chat'],\n recommendedFor: [],\n status: 'stable',\n }),\n]\n\n// ==================== OpenRouter ====================\n\nconst OPENROUTER_MODELS: ModelDefinition[] = [\n builtin({\n id: 'deepseek/deepseek-v4-pro',\n providerId: 'openrouter',\n name: 'DeepSeek V4 Pro',\n description: 'DeepSeek V4 Pro via OpenRouter',\n contextWindow: 1000000,\n capabilities: ['chat', 'reasoning', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'google/gemini-2.5-flash-preview',\n providerId: 'openrouter',\n name: 'Gemini 2.5 Flash',\n description: 'Google Gemini 2.5 Flash via OpenRouter',\n contextWindow: 1048576,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'mistralai/mistral-7b-instruct:free',\n providerId: 'openrouter',\n name: 'Mistral 7B (Free)',\n description: 'Mistral 7B free tier',\n contextWindow: 32768,\n capabilities: ['chat'],\n recommendedFor: [],\n status: 'stable',\n }),\n]\n\n// ==================== xAI ====================\n\nconst XAI_MODELS: ModelDefinition[] = [\n builtin({\n id: 'grok-4',\n providerId: 'xai',\n name: 'Grok 4',\n description: 'Latest flagship model',\n contextWindow: 256000,\n capabilities: ['chat', 'reasoning', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'grok-3',\n providerId: 'xai',\n name: 'Grok 3',\n description: 'Grok 3 standard',\n contextWindow: 131072,\n capabilities: ['chat', 'vision', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'grok-3-fast',\n providerId: 'xai',\n name: 'Grok 3 Fast',\n description: 'Grok 3 fast version',\n contextWindow: 131072,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'grok-3-mini',\n providerId: 'xai',\n name: 'Grok 3 Mini',\n description: 'Grok 3 small reasoning model',\n contextWindow: 131072,\n capabilities: ['chat', 'reasoning', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'grok-3-mini-fast',\n providerId: 'xai',\n name: 'Grok 3 Mini Fast',\n description: 'Grok 3 Mini fast version',\n contextWindow: 131072,\n capabilities: ['chat', 'reasoning', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n]\n\n// ==================== MiniMax ====================\n\nconst MINIMAX_MODELS: ModelDefinition[] = [\n builtin({\n id: 'MiniMax-M2.7',\n providerId: 'minimax',\n name: 'MiniMax M2.7',\n description: 'Latest flagship model',\n contextWindow: 200000,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'MiniMax-M2.7-highspeed',\n providerId: 'minimax',\n name: 'MiniMax M2.7 HS',\n description: 'M2.7 high-speed',\n contextWindow: 200000,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: ['chat'],\n status: 'stable',\n }),\n builtin({\n id: 'MiniMax-M2.5',\n providerId: 'minimax',\n name: 'MiniMax M2.5',\n description: 'M2.5 standard',\n contextWindow: 204800,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n builtin({\n id: 'MiniMax-M2.5-highspeed',\n providerId: 'minimax',\n name: 'MiniMax M2.5 HS',\n description: 'M2.5 high-speed',\n contextWindow: 204800,\n capabilities: ['chat', 'function_calling'],\n recommendedFor: [],\n status: 'stable',\n }),\n]\n\n// ==================== 汇总导出 ====================\n\nexport const BUILTIN_MODELS: ModelDefinition[] = [\n ...OPENAI_MODELS,\n ...ANTHROPIC_MODELS,\n ...GEMINI_MODELS,\n ...DEEPSEEK_MODELS,\n ...QWEN_MODELS,\n ...GLM_MODELS,\n ...KIMI_MODELS,\n ...DOUBAO_MODELS,\n ...SILICONFLOW_MODELS,\n ...GROQ_MODELS,\n ...OPENROUTER_MODELS,\n ...XAI_MODELS,\n ...MINIMAX_MODELS,\n]\n\n/** 按 provider 筛选内置模型 */\nexport function getBuiltinModelsByProvider(providerId: string): ModelDefinition[] {\n return BUILTIN_MODELS.filter((m) => m.providerId === providerId)\n}\n\n/** 按 id + providerId 查找内置模型 */\nexport function getBuiltinModelById(providerId: string, modelId: string): ModelDefinition | null {\n return BUILTIN_MODELS.find((m) => m.providerId === providerId && m.id === modelId) || null\n}\n","/**\n * Agent content parsing utilities.\n * Extracts thinking tags and strips tool-call markup from LLM output.\n */\n\nexport const THINK_TAGS = ['think', 'analysis', 'reasoning', 'reflection', 'thought', 'thinking']\n\nexport function extractThinkingContent(content: string): { thinking: string; cleanContent: string } {\n if (!content) {\n return { thinking: '', cleanContent: '' }\n }\n\n const tagPattern = THINK_TAGS.join('|')\n const thinkRegex = new RegExp(`<(${tagPattern})>([\\\\s\\\\S]*?)<\\\\/\\\\1>`, 'gi')\n const thinkingParts: string[] = []\n let cleanContent = content\n\n const matches = content.matchAll(thinkRegex)\n for (const match of matches) {\n const thinkText = match[2].trim()\n if (thinkText) {\n thinkingParts.push(thinkText)\n }\n cleanContent = cleanContent.replace(match[0], '')\n }\n\n return { thinking: thinkingParts.join('\\n').trim(), cleanContent: cleanContent.trim() }\n}\n\nexport function stripToolCallTags(content: string): string {\n return content.replace(/<tool_call>[\\s\\S]*?<\\/tool_call>/gi, '').trim()\n}\n\n/**\n * Recursively strip large avatar/senderAvatar base64 strings from objects.\n * Used when serializing tool results to avoid transmitting large image data.\n */\nexport function stripAvatarFields(obj: unknown): void {\n if (!obj || typeof obj !== 'object') return\n if (Array.isArray(obj)) {\n for (const item of obj) stripAvatarFields(item)\n return\n }\n const record = obj as Record<string, unknown>\n for (const key of Object.keys(record)) {\n if ((key === 'avatar' || key === 'senderAvatar') && typeof record[key] === 'string') {\n if ((record[key] as string).length > 200) {\n record[key] = '[stripped]'\n }\n } else if (typeof record[key] === 'object' && record[key] !== null) {\n stripAvatarFields(record[key])\n }\n }\n}\n","/**\n * Streaming think-tag parser.\n *\n * Detects <think>, <reasoning>, etc. tags embedded in streaming text deltas\n * and splits them into separate thinking/content events. Handles tags split\n * across multiple chunks via internal buffering.\n */\n\nimport { THINK_TAGS } from './content-parser'\n\nexport type StreamParserEvent =\n | { type: 'content'; content: string }\n | { type: 'thinking_start'; tag: string }\n | { type: 'thinking_delta'; content: string }\n | { type: 'thinking_end' }\n\ntype ParserState = 'content' | 'thinking'\n\nexport class StreamingThinkTagParser {\n private state: ParserState = 'content'\n private buffer = ''\n private activeTag = ''\n private emit: (event: StreamParserEvent) => void\n\n constructor(emit: (event: StreamParserEvent) => void) {\n this.emit = emit\n }\n\n feed(text: string): void {\n this.buffer += text\n this.processBuffer()\n }\n\n /** Flush any remaining buffer (call at stream end). */\n flush(): void {\n if (!this.buffer) return\n if (this.state === 'thinking') {\n this.emit({ type: 'thinking_delta', content: this.buffer })\n } else {\n this.emit({ type: 'content', content: this.buffer })\n }\n this.buffer = ''\n }\n\n private processBuffer(): void {\n while (this.buffer.length > 0) {\n const ltIdx = this.buffer.indexOf('<')\n\n if (ltIdx === -1) {\n this.emitBufferContent(this.buffer)\n this.buffer = ''\n return\n }\n\n if (ltIdx > 0) {\n this.emitBufferContent(this.buffer.slice(0, ltIdx))\n this.buffer = this.buffer.slice(ltIdx)\n }\n\n // Buffer starts with '<', try to match a tag\n const tagResult = this.tryMatchTag()\n if (tagResult === 'incomplete') {\n // Need more data; keep buffer for next feed()\n return\n }\n if (tagResult === 'matched') {\n // Tag was consumed and events emitted, continue processing\n continue\n }\n // 'no_match': the '<' is not a think tag, emit it and advance\n this.emitBufferContent('<')\n this.buffer = this.buffer.slice(1)\n }\n }\n\n /**\n * Try to match a think tag at the start of buffer.\n * Returns: 'matched' if a tag was consumed, 'incomplete' if more data\n * needed, 'no_match' if it's not a valid think tag.\n */\n private tryMatchTag(): 'matched' | 'incomplete' | 'no_match' {\n if (this.state === 'content') {\n return this.tryMatchOpenTag()\n }\n return this.tryMatchCloseTag()\n }\n\n private tryMatchOpenTag(): 'matched' | 'incomplete' | 'no_match' {\n // Look for <tagName> pattern\n for (const tag of THINK_TAGS) {\n const openTag = `<${tag}>`\n if (this.buffer.startsWith(openTag)) {\n this.buffer = this.buffer.slice(openTag.length)\n this.state = 'thinking'\n this.activeTag = tag\n this.emit({ type: 'thinking_start', tag })\n return 'matched'\n }\n // Could be a partial match — check if buffer is a prefix of the tag\n if (openTag.startsWith(this.buffer) && this.buffer.length < openTag.length) {\n return 'incomplete'\n }\n }\n return 'no_match'\n }\n\n private tryMatchCloseTag(): 'matched' | 'incomplete' | 'no_match' {\n const closeTag = `</${this.activeTag}>`\n if (this.buffer.startsWith(closeTag)) {\n this.buffer = this.buffer.slice(closeTag.length)\n this.state = 'content'\n this.emit({ type: 'thinking_end' })\n this.activeTag = ''\n return 'matched'\n }\n // Could be a partial match\n if (closeTag.startsWith(this.buffer) && this.buffer.length < closeTag.length) {\n return 'incomplete'\n }\n // Not a close tag — emit the '<' as thinking content and continue\n this.emit({ type: 'thinking_delta', content: '<' })\n this.buffer = this.buffer.slice(1)\n return 'matched'\n }\n\n private emitBufferContent(text: string): void {\n if (!text) return\n if (this.state === 'thinking') {\n this.emit({ type: 'thinking_delta', content: text })\n } else {\n this.emit({ type: 'content', content: text })\n }\n }\n}\n\n/**\n * Check if a model likely embeds think tags in content\n * (i.e. uses native <think> output rather than separate reasoning fields).\n */\nexport function needsStreamingThinkParsing(provider: string, _modelId: string): boolean {\n return provider === 'minimax'\n}\n","/**\n * Thinking / Reasoning level configuration for AI models.\n *\n * Inspired by cherry-studio's MODEL_SUPPORTED_REASONING_EFFORT table.\n * This module is the single source of truth for:\n * - which models support thinking and at what granularity\n * - what `compat` fields pi-ai needs to actually inject the right request params\n *\n * Scope: openai-completions API path only.\n * anthropic-messages / google-generative-ai are out of scope for this version.\n */\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\n/**\n * ThinkingLevel values the UI selector can display.\n * - 'default': no reasoning params sent — rely on model's native default behavior\n * - 'off': explicitly disable reasoning\n * - 'auto': let the model flexibly decide reasoning intensity\n * - others: specific intensity levels\n */\nexport type ThinkingLevel = 'default' | 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'auto'\n\n/** Used internally to categorise a model into a thinking behaviour group. */\ntype ThinkingType =\n | 'none' // model is not a reasoning model\n | 'qwen' // Qwen/GLM-family: boolean enable_thinking (off/on)\n | 'deepseek_v4' // DeepSeek V4+: thinkingFormat:'deepseek', off/high/xhigh\n | 'deepseek_hybrid' // DeepSeek V3.x hybrid inference: supports auto\n | 'o_series' // OpenAI o1/o3: can't disable thinking; low/medium/high only\n | 'gpt5' // OpenAI gpt-5 base: can't disable; minimal/low/medium/high\n | 'gpt5_1' // OpenAI gpt-5.1: off→none supported; off/low/medium/high\n | 'gpt5_2plus' // OpenAI gpt-5.2+: off→none + xhigh; full range\n | 'grok' // xAI grok: can't disable; low/high only\n | 'kimi' // Kimi thinking: off/auto/low/medium/high\n | 'doubao' // Doubao seed reasoning: off/auto/high\n | 'hunyuan' // Hunyuan: enable_thinking boolean, similar to qwen\n | 'gemini' // Gemini 2.5+: thinking level via Google API (off/low/medium/high)\n | 'default' // generic reasoning: off / low / medium / high\n\n// ── Internal: per-type level tables ──────────────────────────────────────────\n\n/** UI options for each type — what the selector will show. */\nconst TYPE_LEVELS: Record<ThinkingType, ThinkingLevel[]> = {\n none: [],\n qwen: ['default', 'off', 'high'],\n deepseek_v4: ['default', 'off', 'high', 'xhigh'],\n deepseek_hybrid: ['default', 'off', 'auto'],\n o_series: ['default', 'low', 'medium', 'high'],\n gpt5: ['default', 'minimal', 'low', 'medium', 'high'],\n gpt5_1: ['default', 'off', 'low', 'medium', 'high'],\n gpt5_2plus: ['default', 'off', 'minimal', 'low', 'medium', 'high', 'xhigh'],\n grok: ['default', 'low', 'high'],\n kimi: ['default', 'off', 'auto', 'low', 'medium', 'high'],\n doubao: ['default', 'off', 'auto', 'high'],\n hunyuan: ['default', 'off', 'high'],\n gemini: ['default', 'off', 'low', 'medium', 'high'],\n default: ['default', 'off', 'low', 'medium', 'high'],\n}\n\n/**\n * thinkingLevelMap entries for models that use `reasoning_effort`.\n * `null` means that level is explicitly not supported.\n * `undefined` (key absent) means pass the level string verbatim.\n * Matches pi-ai's convention in models.generated.js.\n */\nconst TYPE_LEVEL_MAP: Partial<Record<ThinkingType, Partial<Record<ThinkingLevel, string | null>>>> = {\n gpt5: {\n minimal: 'minimal',\n low: 'low',\n medium: 'medium',\n high: 'high',\n },\n gpt5_1: {\n off: 'none',\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: null,\n },\n gpt5_2plus: {\n off: 'none',\n minimal: 'minimal',\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: 'xhigh',\n },\n grok: {\n minimal: null,\n low: 'low',\n medium: null,\n high: 'high',\n xhigh: null,\n },\n kimi: {\n off: 'none',\n auto: 'auto',\n minimal: null,\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: null,\n },\n doubao: {\n off: null,\n auto: 'auto',\n high: 'high',\n },\n deepseek_hybrid: {\n off: null,\n auto: 'auto',\n high: 'high',\n },\n}\n\n// ── Internal: model → ThinkingType classification ─────────────────────────────\n\n/**\n * Broad regex matching common reasoning model naming patterns.\n * Used as a fallback when no specific provider/model rule matches.\n * Excludes explicit '-non-reasoning' variants.\n */\nconst REASONING_FALLBACK_REGEX =\n /^(?!.*-non-reasoning\\b)(o\\d+(?:-[\\w-]+)?|.*\\b(?:reasoning|reasoner|thinking|think)\\b.*|.*-[rR]\\d+.*|.*\\bqwq(?:-[\\w-]+)?\\b.*|.*\\bhunyuan-t1(?:-[\\w-]+)?\\b.*|.*\\bgrok-(?:3-mini|4|4-fast|build)(?:-[\\w-]+)?\\b.*)$/i\n\n/**\n * Classify a model into a ThinkingType based on provider + model id.\n */\nfunction classifyThinkingType(provider: string, modelId: string): ThinkingType {\n const id = modelId.toLowerCase()\n const prov = provider.toLowerCase()\n\n // ── Explicit non-reasoning variants ──\n if (id.includes('-non-reasoning')) return 'none'\n\n // ── Anthropic uses different API format (out of scope) ──\n if (prov === 'anthropic') return 'none'\n\n // ── Gemini 2.5+ reasoning models (via Google Generative AI API) ──\n if (prov === 'gemini') {\n if (/gemini-(?:2\\.5|3(?:\\.\\d+)?-(?:flash|pro)|flash-latest|pro-latest)/i.test(id)) return 'gemini'\n return 'none'\n }\n\n // ── Qwen (official DashScope + self-hosted models containing qwen/qwq) ──\n if (prov === 'qwen' || /\\bqwen|qwq/i.test(id)) return 'qwen'\n\n // ── DeepSeek V4+ ──\n if (/deepseek[_-]v([4-9]|\\d{2,})/i.test(id) || /deepseek-ai\\/deepseek-r1/i.test(id)) return 'deepseek_v4'\n\n // ── DeepSeek V3.x hybrid inference (deepseek-chat, deepseek-v3.x) ──\n if (/deepseek[_-]chat|deepseek[_-]v3/i.test(id)) return 'deepseek_hybrid'\n\n // ── OpenAI gpt-5.x family ──\n if (prov === 'openai' || prov === 'openai-compatible') {\n if (/gpt-5\\.[2-9]|gpt-5\\.[1-9]\\d/.test(id)) return 'gpt5_2plus'\n if (/gpt-5\\.1/.test(id)) return 'gpt5_1'\n if (/gpt-5/.test(id)) return 'gpt5'\n if (/^o\\d/.test(id)) return 'o_series'\n }\n // o-series on other providers (e.g., Azure, OpenRouter)\n if (/^o\\d/.test(id)) return 'o_series'\n\n // ── xAI Grok reasoning models (grok-3-mini, grok-4, grok-4-fast, grok-build) ──\n if (/\\bgrok-(?:3-mini|4|4-fast|build)\\b/i.test(id)) return 'grok'\n\n // ── Kimi thinking models (k2-thinking, k2.5+, k3+) ──\n if (/\\bkimi-k(?:2-thinking|2\\.[5-9]|[3-9])/i.test(id)) return 'kimi'\n if (prov === 'kimi' && /thinking/i.test(id)) return 'kimi'\n\n // ── Doubao seed reasoning ──\n if (/\\bdoubao-.*seed/i.test(id) || (prov === 'doubao' && /seed/i.test(id))) return 'doubao'\n\n // ── Hunyuan reasoning models ──\n if (/\\bhunyuan-(?:t1|a13b)/i.test(id)) return 'hunyuan'\n\n // ── GLM (ZAI) — glm-5, glm-4.5/4.6/4.7, glm-z1, glm-zero-preview ──\n if (prov === 'glm' || /\\bglm-?(?:5|4\\.[5-7]|z1|zero)/i.test(id)) return 'qwen'\n\n // ── SiliconFlow hosted reasoning models (DeepSeek R1 family) ──\n if (prov === 'siliconflow' && /r[1-9]/i.test(id)) return 'deepseek_v4'\n\n // ── OpenRouter: classify by inner model id ──\n if (prov === 'openrouter') {\n if (/deepseek.*(v[4-9]|r\\d)/i.test(id)) return 'deepseek_v4'\n if (/deepseek[_-]?(chat|v3)/i.test(id)) return 'deepseek_hybrid'\n if (/\\bgrok-(?:3-mini|4|4-fast|build)\\b/i.test(id)) return 'grok'\n if (/\\bkimi-k(?:2-thinking|2\\.[5-9]|[3-9])/i.test(id)) return 'kimi'\n if (REASONING_FALLBACK_REGEX.test(id)) return 'default'\n return 'none'\n }\n\n // ── Models using thinking:{type:'disabled'/'enabled'} format (same as DeepSeek) ──\n if (/\\bmimo-v2/i.test(id)) return 'deepseek_hybrid'\n if (/\\bminimax-m[123]/i.test(id)) return 'deepseek_hybrid'\n if (/\\bseed-oss/i.test(id)) return 'deepseek_hybrid'\n\n // ── Known reasoning model families → 'default' bucket (supportsReasoningEffort) ──\n if (/\\bbaichuan-m[23]/i.test(id)) return 'default'\n if (/\\bmagistral/i.test(id)) return 'default'\n if (/\\bmistral-small-2603/i.test(id)) return 'default'\n if (/\\bstep-(?:3|r1-v-mini)/i.test(id)) return 'default'\n if (/\\bgemma-?4/i.test(id)) return 'default'\n if (/\\bpangu-pro-moe/i.test(id)) return 'default'\n if (/\\bsonar-deep-research/i.test(id)) return 'default'\n if (/\\bring-(?:1t|mini|flash)/i.test(id)) return 'default'\n\n // ── Self-hosted / unknown — heuristic from model name ──\n if (/\\bqwen|qwq/i.test(id)) return 'qwen'\n if (/deepseek[_-]?r\\d|deepseek[_-]?v[4-9]/i.test(id)) return 'deepseek_v4'\n if (/^o\\d/i.test(id)) return 'o_series'\n\n // Broad fallback: match common reasoning naming patterns\n if (REASONING_FALLBACK_REGEX.test(id)) return 'default'\n\n return 'none'\n}\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\n/**\n * Returns the list of ThinkingLevel values the selector should show for a model.\n * An empty array means the model has no thinking support — hide the selector.\n *\n * Used by: UI (ChatStatusBar) to render the level picker.\n */\nexport function getSupportedThinkingLevels(provider: string, modelId: string): ThinkingLevel[] {\n const type = classifyThinkingType(provider, modelId)\n return TYPE_LEVELS[type]\n}\n\n/**\n * Returns whether the model/provider combo is a reasoning-capable model\n * on the openai-completions path. (anthropic/gemini always return false here.)\n *\n * Used by: buildPiModel to set `PiModel.reasoning`.\n */\nexport function isReasoningModel(provider: string, modelId: string): boolean {\n return classifyThinkingType(provider, modelId) !== 'none'\n}\n\n/**\n * PiModel compat fragment that makes the chosen thinkingLevel actually reach\n * the request body. Must be spread into the PiModel returned by buildPiModel.\n *\n * Used by: buildPiModel (openai-completions branch).\n */\nexport interface ThinkingCompat {\n thinkingFormat?: 'qwen' | 'deepseek'\n supportsReasoningEffort?: true\n thinkingLevelMap?: Partial<Record<ThinkingLevel, string | null>>\n}\n\nexport function getThinkingCompat(provider: string, modelId: string): ThinkingCompat {\n const type = classifyThinkingType(provider, modelId)\n\n if (type === 'none') return {}\n\n // Gemini uses pi-ai's native Google provider; no compat fields needed.\n if (type === 'gemini') return {}\n\n if (type === 'qwen' || type === 'hunyuan') {\n return { thinkingFormat: 'qwen' }\n }\n\n if (type === 'deepseek_v4') {\n return {\n thinkingFormat: 'deepseek',\n thinkingLevelMap: { high: 'high', xhigh: 'max', minimal: null, low: null, medium: null },\n }\n }\n\n if (type === 'deepseek_hybrid') {\n return {\n thinkingFormat: 'deepseek',\n thinkingLevelMap: { auto: 'auto', high: 'high', minimal: null, low: null, medium: null, xhigh: null },\n }\n }\n\n // All remaining types use OpenAI-style reasoning_effort.\n const compat: ThinkingCompat = { supportsReasoningEffort: true }\n const levelMap = TYPE_LEVEL_MAP[type]\n if (levelMap) {\n compat.thinkingLevelMap = levelMap as Partial<Record<ThinkingLevel, string | null>>\n }\n return compat\n}\n","/**\n * Tool result text extraction and truncation.\n *\n * Tool results are persisted (truncated) into assistant message content blocks\n * so later turns can replay them as real toolCall/toolResult message pairs,\n * keeping the model grounded in what it actually retrieved.\n */\n\n/**\n * Max characters of a single tool result persisted into a content block.\n * Bounds both DB row size and the per-result token cost when history is replayed.\n */\nexport const MAX_PERSISTED_TOOL_RESULT_CHARS = 4000\n\nconst TRUNCATION_MARKER = '\\n…[truncated]'\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Extract the text the model saw from an AgentToolResult-shaped value\n * (`{ content: [{ type: 'text', text }], details? }`). Non-text parts are skipped.\n */\nexport function extractToolResultText(toolResult: unknown): string {\n if (!isRecord(toolResult)) return ''\n if (!Array.isArray(toolResult.content)) return ''\n return toolResult.content\n .map((part) => (isRecord(part) && part.type === 'text' && typeof part.text === 'string' ? part.text : ''))\n .filter((text) => text.length > 0)\n .join('\\n')\n}\n\nexport function truncateToolResultText(text: string, maxChars: number = MAX_PERSISTED_TOOL_RESULT_CHARS): string {\n if (text.length <= maxChars) return text\n return text.slice(0, maxChars) + TRUNCATION_MARKER\n}\n","export interface ParsedStableVersion {\n major: number\n minor: number\n patch: number\n}\n\ninterface ParsedComparableVersion extends ParsedStableVersion {\n prerelease: boolean\n}\n\nfunction parseStableVersion(version: string): ParsedStableVersion | null {\n const normalized = version.trim().replace(/^v/i, '')\n const match = /^(\\d+)\\.(\\d+)\\.(\\d+)$/.exec(normalized)\n if (!match) return null\n\n return buildParsedVersion(match)\n}\n\nfunction parseComparableCurrentVersion(version: string): ParsedComparableVersion | null {\n const normalized = version.trim().replace(/^v/i, '')\n const match = /^(\\d+)\\.(\\d+)\\.(\\d+)(-[0-9A-Za-z.-]+)?$/.exec(normalized)\n if (!match) return null\n\n return {\n ...buildParsedVersion(match),\n prerelease: Boolean(match[4]),\n }\n}\n\nfunction buildParsedVersion(match: RegExpExecArray): ParsedStableVersion {\n return {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n }\n}\n\nexport function isStableVersion(version: string): boolean {\n return parseStableVersion(version) !== null\n}\n\nexport function isNewerStableVersion(latest: string, current: string): boolean {\n const latestVersion = parseStableVersion(latest)\n const currentVersion = parseComparableCurrentVersion(current)\n if (!latestVersion || !currentVersion) return false\n\n if (latestVersion.major !== currentVersion.major) return latestVersion.major > currentVersion.major\n if (latestVersion.minor !== currentVersion.minor) return latestVersion.minor > currentVersion.minor\n if (latestVersion.patch !== currentVersion.patch) return latestVersion.patch > currentVersion.patch\n return currentVersion.prerelease\n}\n","export type ChartType = 'bar' | 'line' | 'pie' | 'heatmap'\n\nexport type ChartFieldType = 'string' | 'number' | 'integer' | 'boolean' | 'date' | 'datetime' | 'category'\n\nexport interface ChartField {\n name: string\n type: ChartFieldType\n label?: string\n unit?: string\n}\n\nexport interface ChartEncoding {\n x?: string\n y?: string\n value?: string\n label?: string\n series?: string\n color?: string\n}\n\nexport interface ChartSpec {\n version: 1\n type: ChartType\n title: string\n subtitle?: string\n description?: string\n encoding: ChartEncoding\n fields?: ChartField[]\n unit?: string\n filters?: {\n timeRange?: { startTs?: number; endTs?: number; label?: string }\n members?: Array<{ id?: number; name: string }>\n keywords?: string[]\n messageTypes?: number[]\n custom?: Record<string, unknown>\n }\n display?: {\n horizontal?: boolean\n stacked?: boolean\n showLegend?: boolean\n showDataZoom?: boolean\n height?: number\n }\n}\n\nexport interface ChartDataset {\n columns: ChartField[]\n rows: Record<string, unknown>[]\n}\n\nexport interface BarChartRenderData {\n labels: string[]\n values: number[]\n}\n\nexport interface LineChartRenderData {\n labels: string[]\n values: number[]\n series?: Array<{ name: string; values: number[] }>\n}\n\nexport interface PieChartRenderData {\n labels: string[]\n values: number[]\n}\n\nexport interface HeatmapChartRenderData {\n xLabels: string[]\n yLabels: string[]\n data: Array<[number, number, number]>\n}\n\nexport type ChartRenderData = BarChartRenderData | LineChartRenderData | PieChartRenderData | HeatmapChartRenderData\n\nexport interface ChartPayload {\n version: 1\n spec: ChartSpec\n dataset: ChartDataset\n data: ChartRenderData\n rowCount: number\n truncated?: boolean\n}\n\nexport class ChartValidationError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'ChartValidationError'\n }\n}\n\nconst CHART_TYPES = new Set<ChartType>(['bar', 'line', 'pie', 'heatmap'])\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction asFieldName(value: unknown, label: string): string {\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new ChartValidationError(`${label} must be a non-empty field name`)\n }\n return value\n}\n\nfunction assertFieldExists(rows: Record<string, unknown>[], field: string): void {\n if (rows.length === 0) return\n if (!Object.prototype.hasOwnProperty.call(rows[0], field)) {\n throw new ChartValidationError(`Field \"${field}\" does not exist in SQL result`)\n }\n}\n\nfunction toLabel(value: unknown): string {\n if (value === null || value === undefined || value === '') return '(empty)'\n return String(value)\n}\n\nfunction toNumber(value: unknown, field: string): number {\n const num = typeof value === 'number' ? value : Number(value)\n if (!Number.isFinite(num)) {\n throw new ChartValidationError(`Field \"${field}\" contains non-numeric value \"${String(value)}\"`)\n }\n return num\n}\n\nfunction inferFieldType(values: unknown[]): ChartFieldType {\n const present = values.filter((v) => v !== null && v !== undefined)\n if (present.length === 0) return 'string'\n if (present.every((v) => typeof v === 'number' && Number.isInteger(v))) return 'integer'\n if (\n present.every(\n (v) => typeof v === 'number' || (typeof v === 'string' && v.trim() !== '' && !Number.isNaN(Number(v)))\n )\n ) {\n return 'number'\n }\n if (present.every((v) => typeof v === 'boolean')) return 'boolean'\n return 'category'\n}\n\nexport function normalizeChartSpec(raw: unknown): ChartSpec {\n if (!isRecord(raw)) throw new ChartValidationError('chartSpec must be an object')\n if (raw.version !== 1) throw new ChartValidationError('chartSpec.version must be 1')\n if (typeof raw.type !== 'string' || !CHART_TYPES.has(raw.type as ChartType)) {\n throw new ChartValidationError('chartSpec.type must be one of: bar, line, pie, heatmap')\n }\n if (typeof raw.title !== 'string' || raw.title.trim().length === 0) {\n throw new ChartValidationError('chartSpec.title must be a non-empty string')\n }\n if (!isRecord(raw.encoding)) throw new ChartValidationError('chartSpec.encoding must be an object')\n\n const spec = raw as unknown as ChartSpec\n switch (spec.type) {\n case 'bar':\n case 'line':\n asFieldName(spec.encoding.x, 'encoding.x')\n asFieldName(spec.encoding.y, 'encoding.y')\n break\n case 'pie':\n asFieldName(spec.encoding.label, 'encoding.label')\n asFieldName(spec.encoding.value, 'encoding.value')\n break\n case 'heatmap':\n asFieldName(spec.encoding.x, 'encoding.x')\n asFieldName(spec.encoding.y, 'encoding.y')\n asFieldName(spec.encoding.value, 'encoding.value')\n break\n }\n\n return {\n ...spec,\n title: spec.title.trim(),\n }\n}\n\nexport function inferChartDataset(rows: Record<string, unknown>[], declaredFields?: ChartField[]): ChartDataset {\n const declared = new Map((declaredFields ?? []).map((field) => [field.name, field]))\n const columnNames = rows.length > 0 ? Object.keys(rows[0]) : [...declared.keys()]\n const columns = columnNames.map((name) => {\n const declaredField = declared.get(name)\n if (declaredField) return declaredField\n return {\n name,\n type: inferFieldType(rows.map((row) => row[name])),\n }\n })\n\n return { columns, rows }\n}\n\nfunction aggregateByLabel(\n rows: Record<string, unknown>[],\n labelField: string,\n valueField: string\n): {\n labels: string[]\n values: number[]\n} {\n const labels: string[] = []\n const indexByLabel = new Map<string, number>()\n const values: number[] = []\n\n for (const row of rows) {\n const label = toLabel(row[labelField])\n let index = indexByLabel.get(label)\n if (index === undefined) {\n index = labels.length\n indexByLabel.set(label, index)\n labels.push(label)\n values.push(0)\n }\n values[index] += toNumber(row[valueField], valueField)\n }\n\n return { labels, values }\n}\n\nfunction buildLineData(rows: Record<string, unknown>[], spec: ChartSpec): LineChartRenderData {\n const xField = spec.encoding.x!\n const yField = spec.encoding.y!\n const seriesField = spec.encoding.series\n\n if (!seriesField) return aggregateByLabel(rows, xField, yField)\n\n assertFieldExists(rows, seriesField)\n const labels: string[] = []\n const labelIndex = new Map<string, number>()\n const seriesNames: string[] = []\n const seriesIndex = new Map<string, number>()\n const matrix: number[][] = []\n\n for (const row of rows) {\n const label = toLabel(row[xField])\n const seriesName = toLabel(row[seriesField])\n let xIndex = labelIndex.get(label)\n if (xIndex === undefined) {\n xIndex = labels.length\n labelIndex.set(label, xIndex)\n labels.push(label)\n for (const values of matrix) values.push(0)\n }\n\n let sIndex = seriesIndex.get(seriesName)\n if (sIndex === undefined) {\n sIndex = seriesNames.length\n seriesIndex.set(seriesName, sIndex)\n seriesNames.push(seriesName)\n matrix.push(Array(labels.length).fill(0))\n }\n\n matrix[sIndex][xIndex] += toNumber(row[yField], yField)\n }\n\n const series = seriesNames.map((name, index) => ({ name, values: matrix[index] }))\n return {\n labels,\n values: series[0]?.values ?? [],\n series,\n }\n}\n\nfunction buildHeatmapData(rows: Record<string, unknown>[], spec: ChartSpec): HeatmapChartRenderData {\n const xField = spec.encoding.x!\n const yField = spec.encoding.y!\n const valueField = spec.encoding.value!\n const xLabels: string[] = []\n const yLabels: string[] = []\n const xIndex = new Map<string, number>()\n const yIndex = new Map<string, number>()\n const values = new Map<string, number>()\n\n for (const row of rows) {\n const xLabel = toLabel(row[xField])\n const yLabel = toLabel(row[yField])\n let xi = xIndex.get(xLabel)\n if (xi === undefined) {\n xi = xLabels.length\n xIndex.set(xLabel, xi)\n xLabels.push(xLabel)\n }\n let yi = yIndex.get(yLabel)\n if (yi === undefined) {\n yi = yLabels.length\n yIndex.set(yLabel, yi)\n yLabels.push(yLabel)\n }\n const key = `${xi}:${yi}`\n values.set(key, (values.get(key) ?? 0) + toNumber(row[valueField], valueField))\n }\n\n const data: Array<[number, number, number]> = []\n for (const [key, value] of values.entries()) {\n const [x, y] = key.split(':').map(Number)\n data.push([x, y, value])\n }\n\n return { xLabels, yLabels, data }\n}\n\nexport function buildChartPayload(\n rows: Record<string, unknown>[],\n rawSpec: unknown,\n options?: { truncated?: boolean }\n): ChartPayload {\n const spec = normalizeChartSpec(rawSpec)\n const dataset = inferChartDataset(rows, spec.fields)\n\n const requiredFields = new Set<string>()\n for (const field of Object.values(spec.encoding)) {\n if (typeof field === 'string' && field) requiredFields.add(field)\n }\n for (const field of requiredFields) assertFieldExists(rows, field)\n\n let data: ChartRenderData\n switch (spec.type) {\n case 'bar':\n data = aggregateByLabel(rows, spec.encoding.x!, spec.encoding.y!)\n break\n case 'line':\n data = buildLineData(rows, spec)\n break\n case 'pie':\n data = aggregateByLabel(rows, spec.encoding.label!, spec.encoding.value!)\n break\n case 'heatmap':\n data = buildHeatmapData(rows, spec)\n break\n }\n\n return {\n version: 1,\n spec,\n dataset,\n data,\n rowCount: rows.length,\n truncated: options?.truncated,\n }\n}\n","/**\n * 词性标签定义(静态数据,平台无关)\n */\n\nimport type { PosTagInfo } from './types'\n\nexport const POS_TAG_DEFINITIONS: PosTagInfo[] = [\n { tag: 'n', name: '名词', description: '普通名词', meaningful: true },\n { tag: 'nr', name: '人名', description: '人名', meaningful: true },\n { tag: 'ns', name: '地名', description: '地名', meaningful: true },\n { tag: 'nt', name: '机构名', description: '机构团体名', meaningful: true },\n { tag: 'nz', name: '其他专名', description: '其他专有名词', meaningful: true },\n { tag: 'nw', name: '作品名', description: '作品名', meaningful: true },\n { tag: 'v', name: '动词', description: '普通动词', meaningful: false },\n { tag: 'vn', name: '动名词', description: '动名词', meaningful: true },\n { tag: 'vd', name: '副动词', description: '副动词', meaningful: false },\n { tag: 'vg', name: '动语素', description: '动词性语素', meaningful: false },\n { tag: 'a', name: '形容词', description: '普通形容词', meaningful: true },\n { tag: 'an', name: '名形词', description: '名形词', meaningful: true },\n { tag: 'ad', name: '副形词', description: '副形词', meaningful: true },\n { tag: 'ag', name: '形语素', description: '形容词性语素', meaningful: true },\n { tag: 'i', name: '成语', description: '成语', meaningful: true },\n { tag: 'l', name: '习用语', description: '习用语', meaningful: true },\n { tag: 'j', name: '简称', description: '简称略语', meaningful: true },\n { tag: 'd', name: '副词', description: '副词', meaningful: false },\n { tag: 'p', name: '介词', description: '介词', meaningful: false },\n { tag: 'c', name: '连词', description: '连词', meaningful: false },\n { tag: 'u', name: '助词', description: '助词', meaningful: false },\n { tag: 'r', name: '代词', description: '代词', meaningful: false },\n { tag: 'm', name: '数词', description: '数词', meaningful: false },\n { tag: 'q', name: '量词', description: '量词', meaningful: false },\n { tag: 'f', name: '方位词', description: '方位词', meaningful: false },\n { tag: 't', name: '时间词', description: '时间词', meaningful: false },\n { tag: 'e', name: '叹词', description: '叹词', meaningful: false },\n { tag: 'y', name: '语气词', description: '语气词', meaningful: false },\n { tag: 'o', name: '拟声词', description: '拟声词', meaningful: false },\n { tag: 'x', name: '非语素字', description: '非语素字', meaningful: false },\n { tag: 'w', name: '标点符号', description: '标点符号', meaningful: false },\n]\n\nexport const MEANINGFUL_POS_TAGS = new Set(POS_TAG_DEFINITIONS.filter((t) => t.meaningful).map((t) => t.tag))\n","import type { SupportedLocale } from './types'\n\n/**\n * 停用词表(平台无关的静态数据)\n */\n\n/**\n * 停用词表\n * 用于过滤无意义的高频词\n */\n\n/** 中文停用词 */\nexport const CHINESE_STOPWORDS = new Set([\n // 代词\n '我',\n '你',\n '他',\n '她',\n '它',\n '我们',\n '你们',\n '他们',\n '她们',\n '它们',\n '自己',\n '别人',\n '大家',\n '谁',\n '什么',\n '哪',\n '哪里',\n '哪儿',\n '这',\n '那',\n '这个',\n '那个',\n '这些',\n '那些',\n '这里',\n '那里',\n '这儿',\n '那儿',\n '这样',\n '那样',\n // 助词\n '的',\n '地',\n '得',\n '了',\n '着',\n '过',\n '吗',\n '呢',\n '吧',\n '啊',\n '呀',\n '哇',\n '哦',\n '嗯',\n '噢',\n '喔',\n '呃',\n '唉',\n '哎',\n '嘛',\n // 介词\n '在',\n '从',\n '到',\n '向',\n '往',\n '把',\n '被',\n '给',\n '跟',\n '和',\n '与',\n '对',\n '比',\n '为',\n '因',\n '由',\n '以',\n '按',\n '用',\n '让',\n // 连词\n '和',\n '与',\n '或',\n '或者',\n '而',\n '并',\n '并且',\n '但',\n '但是',\n '可是',\n '然而',\n '不过',\n '只是',\n '如果',\n '要是',\n '假如',\n '虽然',\n '尽管',\n '即使',\n '所以',\n '因此',\n '于是',\n '那么',\n '因为',\n '由于',\n '既然',\n '为了',\n '以便',\n // 副词\n '不',\n '没',\n '没有',\n '很',\n '太',\n '最',\n '更',\n '也',\n '都',\n '就',\n '才',\n '又',\n '再',\n '还',\n '却',\n '只',\n '只是',\n '已',\n '已经',\n '曾',\n '曾经',\n '正',\n '正在',\n '将',\n '将要',\n '会',\n '能',\n '可以',\n '可能',\n '应该',\n '必须',\n '一定',\n '大概',\n '也许',\n '或许',\n '其实',\n '确实',\n '真的',\n '当然',\n '一直',\n '总是',\n '经常',\n '常常',\n '往往',\n '偶尔',\n '几乎',\n '差不多',\n '简直',\n '反正',\n '终于',\n // 量词\n '个',\n '只',\n '条',\n '件',\n '位',\n '种',\n '些',\n '点',\n '下',\n '次',\n // 数词\n '一',\n '二',\n '三',\n '四',\n '五',\n '六',\n '七',\n '八',\n '九',\n '十',\n '百',\n '千',\n '万',\n '亿',\n '两',\n '几',\n '多',\n '少',\n '第',\n '每',\n // 动词(常见无实意动词)\n '是',\n '有',\n '在',\n '做',\n '去',\n '来',\n '说',\n '看',\n '想',\n '要',\n '能',\n '会',\n '让',\n '给',\n '叫',\n '用',\n '打',\n '把',\n '被',\n '到',\n // 其他常见词\n '上',\n '下',\n '前',\n '后',\n '里',\n '外',\n '中',\n '内',\n '左',\n '右',\n '东',\n '南',\n '西',\n '北',\n '时',\n '时候',\n '现在',\n '今天',\n '明天',\n '昨天',\n '年',\n '月',\n '日',\n '号',\n '点',\n '分',\n '秒',\n '周',\n '星期',\n // 网络聊天常见无意义词\n '好',\n '好的',\n '行',\n '可以',\n '嗯嗯',\n '哈',\n '呵',\n '额',\n '恩',\n '昂',\n 'ok',\n 'OK',\n '好吧',\n '知道',\n '知道了',\n '谢谢',\n '感谢',\n '抱歉',\n '不好意思',\n // 语气词和程度词(虽然词性是名词/动词,但在聊天中无实际意义)\n '感觉',\n '有点',\n '可能',\n '应该',\n '好像',\n '觉得',\n '认为',\n '看看',\n '看到',\n '说',\n '问',\n '找',\n '弄',\n '搞',\n '搞定',\n '整',\n '干',\n '做',\n '来',\n '去',\n '有',\n '没有',\n '没',\n '是不是',\n '有没有',\n '能不能',\n '会不会',\n '要不要',\n '怎样',\n '如何',\n '为何',\n '为什么',\n '怎么',\n '怎么样',\n '怎么办',\n '东西',\n '事情',\n '事',\n '问题',\n '时候',\n '地方',\n '情况',\n '样子',\n '意思',\n '一下',\n '一点',\n '一些',\n '一样',\n '一起',\n '一直',\n '一般',\n '一定',\n '差不多',\n])\n\n/** 英文停用词 */\nexport const ENGLISH_STOPWORDS = new Set([\n // Articles\n 'a',\n 'an',\n 'the',\n // Pronouns\n 'i',\n 'me',\n 'my',\n 'myself',\n 'we',\n 'our',\n 'ours',\n 'ourselves',\n 'you',\n 'your',\n 'yours',\n 'yourself',\n 'yourselves',\n 'he',\n 'him',\n 'his',\n 'himself',\n 'she',\n 'her',\n 'hers',\n 'herself',\n 'it',\n 'its',\n 'itself',\n 'they',\n 'them',\n 'their',\n 'theirs',\n 'themselves',\n 'what',\n 'which',\n 'who',\n 'whom',\n 'this',\n 'that',\n 'these',\n 'those',\n // Prepositions\n 'in',\n 'on',\n 'at',\n 'by',\n 'for',\n 'with',\n 'about',\n 'against',\n 'between',\n 'into',\n 'through',\n 'during',\n 'before',\n 'after',\n 'above',\n 'below',\n 'to',\n 'from',\n 'up',\n 'down',\n 'out',\n 'off',\n 'over',\n 'under',\n 'again',\n 'further',\n // Conjunctions\n 'and',\n 'but',\n 'or',\n 'nor',\n 'so',\n 'yet',\n 'both',\n 'either',\n 'neither',\n 'not',\n 'only',\n 'own',\n 'same',\n 'than',\n 'too',\n 'very',\n 'just',\n // Be verbs\n 'am',\n 'is',\n 'are',\n 'was',\n 'were',\n 'be',\n 'been',\n 'being',\n // Have verbs\n 'have',\n 'has',\n 'had',\n 'having',\n // Do verbs\n 'do',\n 'does',\n 'did',\n 'doing',\n // Modal verbs\n 'will',\n 'would',\n 'shall',\n 'should',\n 'can',\n 'could',\n 'may',\n 'might',\n 'must',\n // Other common words\n 'if',\n 'then',\n 'else',\n 'when',\n 'where',\n 'why',\n 'how',\n 'all',\n 'each',\n 'every',\n 'both',\n 'few',\n 'more',\n 'most',\n 'other',\n 'some',\n 'such',\n 'no',\n 'any',\n 'now',\n 'here',\n 'there',\n 'of',\n 'as',\n // Contractions (without apostrophe)\n 'dont',\n 'doesnt',\n 'didnt',\n 'wont',\n 'wouldnt',\n 'cant',\n 'couldnt',\n 'shouldnt',\n 'isnt',\n 'arent',\n 'wasnt',\n 'werent',\n 'havent',\n 'hasnt',\n 'hadnt',\n // Chat common words\n 'ok',\n 'okay',\n 'yes',\n 'no',\n 'yeah',\n 'yep',\n 'nope',\n 'sure',\n 'thanks',\n 'thank',\n 'please',\n 'sorry',\n 'hi',\n 'hello',\n 'hey',\n 'bye',\n 'goodbye',\n 'well',\n 'like',\n 'know',\n 'think',\n 'want',\n 'need',\n 'get',\n 'got',\n 'go',\n 'going',\n 'come',\n 'coming',\n 'make',\n 'made',\n 'take',\n 'took',\n 'see',\n 'saw',\n 'look',\n 'looking',\n 'say',\n 'said',\n 'tell',\n 'told',\n 'ask',\n 'asked',\n 'let',\n 'put',\n 'keep',\n 'give',\n 'gave',\n 'find',\n 'found',\n 'try',\n 'tried',\n // Time words\n 'today',\n 'tomorrow',\n 'yesterday',\n 'now',\n 'then',\n 'always',\n 'never',\n 'sometimes',\n 'often',\n 'usually',\n 'still',\n 'already',\n 'soon',\n 'later',\n])\n\n/** 日语停用词 */\nexport const JAPANESE_STOPWORDS = new Set([\n // 助詞\n 'の',\n 'に',\n 'は',\n 'を',\n 'た',\n 'が',\n 'で',\n 'て',\n 'と',\n 'し',\n 'れ',\n 'さ',\n 'ある',\n 'いる',\n 'も',\n 'する',\n 'から',\n 'な',\n 'こと',\n 'として',\n 'い',\n 'や',\n 'れる',\n 'など',\n 'なっ',\n 'ない',\n 'この',\n 'ため',\n 'その',\n 'あっ',\n 'よう',\n 'また',\n 'もの',\n 'という',\n 'あり',\n 'まで',\n 'られ',\n 'なる',\n 'へ',\n 'か',\n 'だ',\n 'これ',\n 'によって',\n 'により',\n 'おり',\n 'より',\n 'による',\n 'ず',\n 'なり',\n 'られる',\n 'において',\n 'ば',\n 'なかっ',\n 'なく',\n 'しかし',\n 'について',\n 'せ',\n 'だっ',\n 'その後',\n 'できる',\n 'それ',\n 'う',\n 'ので',\n 'なお',\n 'のみ',\n 'でき',\n 'き',\n 'つ',\n 'における',\n 'および',\n 'いう',\n 'さらに',\n 'でも',\n 'ら',\n 'たり',\n 'その他',\n 'に関する',\n 'たち',\n 'ます',\n 'ん',\n 'なら',\n 'に対して',\n // 代名詞\n '私',\n '僕',\n '俺',\n '自分',\n 'あなた',\n '彼',\n '彼女',\n 'それ',\n 'これ',\n 'あれ',\n 'ここ',\n 'そこ',\n 'あそこ',\n 'どこ',\n 'みんな',\n '皆',\n // 接続詞\n 'そして',\n 'しかし',\n 'でも',\n 'だから',\n 'それで',\n 'だけど',\n 'けど',\n 'ところで',\n 'さて',\n 'つまり',\n 'すなわち',\n 'ただし',\n 'もし',\n 'また',\n // 副詞\n 'とても',\n 'すごく',\n 'もう',\n 'まだ',\n 'よく',\n 'ちょっと',\n 'ちょと',\n 'もっと',\n 'やっぱり',\n 'やはり',\n 'たぶん',\n 'きっと',\n 'ぜんぜん',\n 'ほんとに',\n 'ほんと',\n 'かなり',\n 'だいたい',\n 'ほとんど',\n 'まあ',\n 'なんか',\n 'なんとなく',\n // 感動詞・フィラー\n 'あ',\n 'ああ',\n 'えー',\n 'うん',\n 'えっ',\n 'おお',\n 'へー',\n 'ふーん',\n 'はい',\n 'いいえ',\n 'うーん',\n 'まあ',\n 'ねえ',\n 'ほら',\n 'あのね',\n 'えっと',\n 'その',\n // 動詞(高頻度)\n 'いる',\n 'ある',\n 'する',\n 'なる',\n 'できる',\n 'いく',\n 'くる',\n 'みる',\n 'おもう',\n 'いう',\n 'やる',\n 'くれる',\n 'もらう',\n 'あげる',\n 'しまう',\n 'おく',\n // 形容詞(高頻度)\n 'いい',\n 'ない',\n 'よい',\n 'すごい',\n 'おおきい',\n 'ちいさい',\n // 助動詞\n 'です',\n 'ます',\n 'でした',\n 'ました',\n 'ません',\n 'だった',\n 'でしょう',\n // チャットでの高頻語\n 'www',\n 'ww',\n 'lol',\n 'ok',\n 'おけ',\n 'りょ',\n 'おつ',\n 'わら',\n '笑',\n 'それな',\n 'たしかに',\n 'マジ',\n 'まじ',\n 'ガチ',\n 'がち',\n])\n\n/**\n * 获取停用词集合\n * @param locale 语言\n * @returns 停用词集合\n */\nexport function getStopwords(locale: string): Set<string> {\n const normalizedLocale = normalizeStopwordLocale(locale)\n\n if (normalizedLocale.startsWith('zh')) {\n return CHINESE_STOPWORDS\n }\n if (normalizedLocale === 'ja-JP') {\n return JAPANESE_STOPWORDS\n }\n return ENGLISH_STOPWORDS\n}\n\n/**\n * 判断是否为停用词\n * @param word 词语\n * @param locale 语言\n * @returns 是否为停用词\n */\nexport function isStopword(word: string, locale: string): boolean {\n const normalizedLocale = normalizeStopwordLocale(locale)\n const stopwords = getStopwords(normalizedLocale)\n const normalizedWord = normalizedLocale === 'en-US' ? word.toLowerCase() : word\n return stopwords.has(normalizedWord)\n}\n\n/**\n * 规范化停用词处理使用的 locale。\n * 这里额外兜底一次,避免上游误传数字或其他异常值时直接导致运行时崩溃。\n */\nfunction normalizeStopwordLocale(locale: string): SupportedLocale {\n if (typeof locale !== 'string') {\n return 'en-US'\n }\n\n if (locale.startsWith('zh')) {\n return 'zh-CN'\n }\n\n if (locale === 'ja-JP') {\n return 'ja-JP'\n }\n\n return 'en-US'\n}\n","/**\n * 文本处理工具(纯函数,平台无关)\n */\n\nconst EMOJI_REGEX = /[\\u{1F300}-\\u{1FAFF}\\u{1F1E0}-\\u{1F1FF}\\u{2600}-\\u{26FF}\\u{2700}-\\u{27BF}]/gu\nconst EMOJI_VARIATION_SELECTOR_REGEX = /\\u{FE0F}/gu\nconst PUNCTUATION_REGEX = /[!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{|}~,。!?、;:\"\"''()【】《》…—~·\\s]/g\nconst URL_REGEX = /https?:\\/\\/[^\\s]+/g\nconst MENTION_REGEX = /@[^\\s@]+/g\nconst PURE_NUMBER_REGEX = /^\\d+$/\nconst SYSTEM_PLACEHOLDER_REGEX =\n /\\[(?:图片|视频|语音|文件|动画表情|表情|链接|位置|名片|红包|转账|音乐|Image|Video|Voice|File|Sticker|Link)\\]/gi\nconst BRACKET_EMOJI_PLACEHOLDER_REGEX =\n /(?:\\[|【)([\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Letter}\\p{Number}_-]{1,16})(?:\\]|】)/gu\nconst CJK_TEXT_REGEX = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}]/u\n\nconst BRACKET_EMOJI_MAP: Record<string, string> = {\n 破涕为笑: '😂',\n 微笑: '🙂',\n 呲牙: '😁',\n 大笑: '😄',\n 笑哭: '😂',\n 流泪: '😢',\n 捂脸: '🤦',\n 发呆: '😳',\n 害羞: '😊',\n 调皮: '😜',\n 色: '😍',\n 惊讶: '😮',\n 撇嘴: '😒',\n 难过: '😞',\n 酷: '😎',\n 抓狂: '😫',\n 吐: '🤮',\n 偷笑: '🤭',\n 可爱: '😊',\n 白眼: '🙄',\n 傲慢: '😤',\n 饥饿: '😋',\n 困: '😴',\n 惊恐: '😱',\n 流汗: '😅',\n 憨笑: '😄',\n 悠闲: '😌',\n 奋斗: '💪',\n 咒骂: '😡',\n 疑问: '❓',\n 嘘: '🤫',\n 晕: '😵',\n 衰: '😞',\n 敲打: '👊',\n 再见: '👋',\n 擦汗: '😅',\n 抠鼻: '👃',\n 鼓掌: '👏',\n 坏笑: '😏',\n 左哼哼: '😤',\n 右哼哼: '😤',\n 哈欠: '🥱',\n 鄙视: '😒',\n 委屈: '🥺',\n 快哭了: '😢',\n 阴险: '😏',\n 亲亲: '😘',\n 吓: '😱',\n 可怜: '🥺',\n 菜刀: '🔪',\n 西瓜: '🍉',\n 啤酒: '🍺',\n 篮球: '🏀',\n 乒乓: '🏓',\n 咖啡: '☕',\n 饭: '🍚',\n 猪头: '🐷',\n 玫瑰: '🌹',\n 凋谢: '🥀',\n 爱心: '❤️',\n 心碎: '💔',\n 蛋糕: '🎂',\n 闪电: '⚡',\n 炸弹: '💣',\n 刀: '🔪',\n 足球: '⚽',\n 便便: '💩',\n 月亮: '🌙',\n 太阳: '☀️',\n 礼物: '🎁',\n 拥抱: '🤗',\n 强: '👍',\n 弱: '👎',\n 握手: '🤝',\n 胜利: '✌️',\n 抱拳: '🙏',\n 勾引: '☝️',\n 拳头: '✊',\n 差劲: '👎',\n 爱你: '🤟',\n NO: '🙅',\n OK: '👌',\n}\n\n/**\n * 清理文本:移除 URL、@提及、表情、标点等\n */\nexport function cleanText(text: string): string {\n return text\n .replace(URL_REGEX, ' ')\n .replace(MENTION_REGEX, ' ')\n .replace(SYSTEM_PLACEHOLDER_REGEX, ' ')\n .replace(BRACKET_EMOJI_PLACEHOLDER_REGEX, (match, name: string) => {\n if (BRACKET_EMOJI_MAP[name]) return BRACKET_EMOJI_MAP[name]\n return CJK_TEXT_REGEX.test(name) ? ' ' : match\n })\n .replace(EMOJI_REGEX, ' ')\n .replace(EMOJI_VARIATION_SELECTOR_REGEX, ' ')\n .replace(PUNCTUATION_REGEX, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/**\n * 判断是否为有效词语\n */\nexport function isValidWord(\n word: string,\n locale: string,\n minLength: number,\n enableStopwords: boolean,\n isStopwordFn: (word: string, locale: string) => boolean\n): boolean {\n if (!word || word.trim().length === 0) return false\n if (PURE_NUMBER_REGEX.test(word)) return false\n if (word.length < minLength) return false\n if (enableStopwords && isStopwordFn(word, locale)) return false\n return true\n}\n","/**\n * Owner profile matching — platform-independent \"who am I\" resolution.\n *\n * Given a platform-level owner profile (stored in preferences) and the member\n * list of a session, decide which member is the owner. Matching is strictly\n * deterministic: exact platformId first, then normalized-name equality for an\n * allowlisted set of platforms whose exports lack stable native IDs.\n * No fuzzy matching, no heuristics, no LLM inference.\n */\n\nimport type { OwnerProfile } from '@openchatlab/shared-types'\n\n/**\n * Platforms whose text exports use display names as platformId, so a\n * normalized-name fallback is safe and necessary. All other platforms\n * (including 'unknown') match by exact platformId only.\n */\nexport const NAME_MATCH_PLATFORMS: ReadonlySet<string> = new Set(['whatsapp', 'line', 'instagram'])\n\nexport function isNameMatchPlatform(platform: string): boolean {\n return NAME_MATCH_PLATFORMS.has(platform)\n}\n\n/** Minimal member shape needed for owner matching. */\nexport interface OwnerMatchCandidate {\n platformId: string\n accountName?: string | null\n groupNickname?: string | null\n aliases?: string[] | null\n /** Computed display name, if the caller already derived one. */\n displayName?: string | null\n}\n\nexport type OwnerMatchResult =\n | { type: 'exact'; platformId: string }\n | { type: 'name'; platformId: string }\n | { type: 'none' }\n | { type: 'ambiguous'; platformIds: string[] }\n\n// Invisible direction/control characters that chat exports commonly embed\n// around names (LRM/RLM, directional embeddings/isolates, zero-width chars, BOM).\nconst INVISIBLE_CHARS_RE = /[\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF]/g\n\n/**\n * Deterministic name normalization for matching:\n * strip invisible chars → NFKC → collapse whitespace → trim → lowercase.\n * Stored names keep their original form; normalize only at compare time.\n */\nexport function normalizeOwnerName(name: string): string {\n return name.replace(INVISIBLE_CHARS_RE, '').normalize('NFKC').replace(/\\s+/g, ' ').trim().toLowerCase()\n}\n\n/** Collect all candidate name strings of a member (original, non-normalized, deduped, non-empty). */\nexport function collectCandidateNames(member: OwnerMatchCandidate): string[] {\n const names = [\n member.platformId,\n member.accountName ?? '',\n member.groupNickname ?? '',\n ...(member.aliases ?? []),\n member.displayName ?? '',\n ]\n const result: string[] = []\n const seen = new Set<string>()\n for (const name of names) {\n if (!name || !name.trim()) continue\n if (seen.has(name)) continue\n seen.add(name)\n result.push(name)\n }\n return result\n}\n\n/**\n * Merge a member's candidate names into an existing confirmedNames list.\n * Keeps original strings; dedupes by exact string equality, preserving order.\n */\nexport function mergeConfirmedNames(existing: string[], member: OwnerMatchCandidate): string[] {\n const result: string[] = []\n const seen = new Set<string>()\n for (const name of [...existing, ...collectCandidateNames(member)]) {\n if (!name || !name.trim()) continue\n if (seen.has(name)) continue\n seen.add(name)\n result.push(name)\n }\n return result\n}\n\n/**\n * Match a platform owner profile against a session's members.\n *\n * 1. Exact platformId match wins immediately.\n * 2. Name fallback only for NAME_MATCH_PLATFORMS: a member matches when any\n * of its normalized candidate names equals any normalized confirmed name.\n * 3. Exactly one member must match; zero → none, multiple → ambiguous.\n */\nexport function matchOwnerProfile(\n platform: string,\n profile: OwnerProfile,\n members: OwnerMatchCandidate[]\n): OwnerMatchResult {\n const exact = members.find((m) => m.platformId === profile.platformId)\n if (exact) {\n return { type: 'exact', platformId: exact.platformId }\n }\n\n if (!isNameMatchPlatform(platform)) {\n return { type: 'none' }\n }\n\n const confirmed = new Set(profile.confirmedNames.map(normalizeOwnerName).filter((n) => n.length > 0))\n if (confirmed.size === 0) {\n return { type: 'none' }\n }\n\n const matched = members.filter((m) =>\n collectCandidateNames(m).some((name) => confirmed.has(normalizeOwnerName(name)))\n )\n\n if (matched.length === 1) {\n return { type: 'name', platformId: matched[0].platformId }\n }\n if (matched.length > 1) {\n return { type: 'ambiguous', platformIds: matched.map((m) => m.platformId) }\n }\n return { type: 'none' }\n}\n","/**\n * Canonical message deduplication key generator.\n *\n * Used by both Electron (worker import, merger) and Server (importer)\n * to produce deterministic content-hash keys for messages that lack\n * a platform_message_id.\n *\n * Uses a pure-JS FNV-1a 64-bit hash (browser-safe, no Node.js deps).\n * Segments are NUL-separated; content is preceded by a type discriminator\n * so that null content and the literal string \"null\" hash differently.\n *\n * Empty-string content is normalized to null before hashing to match the\n * storage-layer behavior where '' is folded to NULL in SQLite.\n */\n\n/**\n * FNV-1a 64-bit hash implemented with two 32-bit halves.\n * Returns a 16-char hex string (~11 chars base36).\n */\nfunction fnv1a64(input: string): string {\n // FNV offset basis for 64-bit: 0xcbf29ce484222325\n let h0 = 0x811c9dc5 // low 32\n let h1 = 0xcbf29ce4 // high 32\n\n for (let i = 0; i < input.length; i++) {\n const c = input.charCodeAt(i)\n h0 ^= c\n // FNV prime for 64-bit: 0x00000100000001B3\n // Multiply (h1:h0) * 0x01000193 (32-bit FNV prime for low half mixing)\n // and cross-mix to approximate 64-bit FNV\n const t0 = Math.imul(h0, 0x01000193)\n const t1 = Math.imul(h1, 0x01000193) + Math.imul(h0, 0x01)\n h0 = t0 >>> 0\n h1 = t1 >>> 0\n }\n\n const hi = h1.toString(16).padStart(8, '0')\n const lo = h0.toString(16).padStart(8, '0')\n return hi + lo\n}\n\nexport function generateMessageKey(timestamp: number, senderPlatformId: string, content: string | null): string {\n const normalizedContent = content || null\n const parts = [\n String(timestamp),\n senderPlatformId,\n normalizedContent === null ? 'null' : 'text',\n normalizedContent ?? '',\n ]\n return fnv1a64(parts.join('\\0'))\n}\n","/**\n * Merger pure algorithms — platform ID collision detection,\n * normalization, conflict detection, and dedup-sort merge.\n *\n * All functions are pure (no I/O, no Node.js deps).\n */\n\nimport { generateMessageKey } from '../import/dedup'\n\n// ==================== Types ====================\n\nexport interface MergerMember {\n platformId: string\n accountName?: string\n groupNickname?: string\n avatar?: string\n}\n\nexport interface MergerMessage {\n senderPlatformId: string\n senderAccountName?: string\n senderGroupNickname?: string\n timestamp: number\n type: number\n content?: string | null\n}\n\nexport interface MergeConflict {\n id: string\n timestamp: number\n sender: string\n contentLength1: number\n contentLength2: number\n content1: string\n content2: string\n}\n\nexport interface ConflictCheckResult {\n conflicts: MergeConflict[]\n totalMessages: number\n}\n\nexport interface MergedMember {\n platformId: string\n accountName?: string\n groupNickname?: string\n avatar?: string\n}\n\nexport interface MergedMessage {\n sender: string\n accountName?: string\n groupNickname?: string\n timestamp: number\n type: number\n content?: string | null\n}\n\n// ==================== Platform ID collision ====================\n\nexport function getCollidingPlatformIds(\n sources: Array<{ platform: string; members: Array<{ platformId: string }> }>\n): Set<string> {\n const map = new Map<string, Set<string>>()\n for (const source of sources) {\n for (const member of source.members) {\n if (!map.has(member.platformId)) map.set(member.platformId, new Set())\n map.get(member.platformId)!.add(source.platform || 'unknown')\n }\n }\n const result = new Set<string>()\n for (const [id, platforms] of map) {\n if (platforms.size > 1) result.add(id)\n }\n return result\n}\n\nexport function getCollidingPlatformIdsFromMessages(\n allMessages: Array<{ msg: MergerMessage; platform: string }>\n): Set<string> {\n const map = new Map<string, Set<string>>()\n for (const item of allMessages) {\n const pid = item.msg.senderPlatformId\n if (!map.has(pid)) map.set(pid, new Set())\n map.get(pid)!.add(item.platform || 'unknown')\n }\n const result = new Set<string>()\n for (const [id, platforms] of map) {\n if (platforms.size > 1) result.add(id)\n }\n return result\n}\n\nexport function normalizePlatformId(platformId: string, platform: string, collidingIds: Set<string>): string {\n if (!collidingIds.has(platformId)) return platformId\n const np = encodeURIComponent(platform || 'unknown')\n const ni = encodeURIComponent(platformId)\n return `__chatlab_platform__${np}__${ni}`\n}\n\n// ==================== Helpers ====================\n\nfunction getMessageKey(msg: MergerMessage, senderOverride?: string): string {\n return generateMessageKey(msg.timestamp, senderOverride || msg.senderPlatformId, msg.content ?? null)\n}\n\nfunction getDisplayName(msg: MergerMessage): string {\n return msg.senderGroupNickname || msg.senderAccountName || msg.senderPlatformId\n}\n\nfunction isImageOnlyMessage(content: string | undefined | null): boolean {\n if (!content) return false\n return /^\\[图片:\\s*.+\\]$/.test(content.trim())\n}\n\n// ==================== Conflict detection ====================\n\nexport function detectConflictsInMessages(\n allMessages: Array<{ msg: MergerMessage; source: string; platform: string }>\n): ConflictCheckResult {\n const collidingIds = getCollidingPlatformIdsFromMessages(allMessages)\n const conflicts: MergeConflict[] = []\n\n const timeGroups = new Map<number, Array<{ msg: MergerMessage; source: string; platform: string }>>()\n for (const item of allMessages) {\n const ts = item.msg.timestamp\n if (!timeGroups.has(ts)) timeGroups.set(ts, [])\n timeGroups.get(ts)!.push(item)\n }\n\n for (const [ts, items] of timeGroups) {\n if (items.length < 2) continue\n\n const senderGroups = new Map<string, typeof items>()\n for (const item of items) {\n const sender = normalizePlatformId(item.msg.senderPlatformId, item.platform || 'unknown', collidingIds)\n if (!senderGroups.has(sender)) senderGroups.set(sender, [])\n senderGroups.get(sender)!.push(item)\n }\n\n for (const [sender, senderItems] of senderGroups) {\n if (senderItems.length < 2) continue\n const sources = new Set(senderItems.map((it) => it.source))\n if (sources.size < 2) continue\n\n const contentGroups = new Map<string, typeof senderItems>()\n for (const item of senderItems) {\n const c = item.msg.content || ''\n if (!contentGroups.has(c)) contentGroups.set(c, [])\n contentGroups.get(c)!.push(item)\n }\n\n if (contentGroups.size > 1) {\n const entries = Array.from(contentGroups.entries())\n for (let i = 0; i < entries.length - 1; i++) {\n for (let j = i + 1; j < entries.length; j++) {\n const [c1, items1] = entries[i]\n const [c2, items2] = entries[j]\n const item1 = items1[0]\n const item2 = items2.find((it) => it.source !== item1.source)\n if (!item2) continue\n if (isImageOnlyMessage(c1) && isImageOnlyMessage(c2)) continue\n\n conflicts.push({\n id: `conflict_${ts}_${sender}_${conflicts.length}`,\n timestamp: ts,\n sender: getDisplayName(item1.msg) || sender,\n contentLength1: c1.length,\n contentLength2: c2.length,\n content1: c1,\n content2: c2,\n })\n }\n }\n }\n }\n }\n\n const uniqueKeys = new Set<string>()\n for (const item of allMessages) {\n const nid = normalizePlatformId(item.msg.senderPlatformId, item.platform || 'unknown', collidingIds)\n uniqueKeys.add(getMessageKey(item.msg, nid))\n }\n\n return { conflicts, totalMessages: uniqueKeys.size }\n}\n\n// ==================== Member merge ====================\n\nexport function mergeMembers(\n sources: Array<{ platform: string; members: MergerMember[] }>,\n collidingIds: Set<string>\n): Map<string, MergedMember> {\n const map = new Map<string, MergedMember>()\n for (const { platform, members } of sources) {\n const p = platform || 'unknown'\n for (const m of members) {\n const nid = normalizePlatformId(m.platformId, p, collidingIds)\n const existing = map.get(nid)\n if (existing) {\n if (m.accountName) existing.accountName = m.accountName\n if (m.groupNickname) existing.groupNickname = m.groupNickname\n if (m.avatar) existing.avatar = m.avatar\n } else {\n map.set(nid, {\n platformId: nid,\n accountName: m.accountName,\n groupNickname: m.groupNickname,\n avatar: m.avatar,\n })\n }\n }\n }\n return map\n}\n\n// ==================== Message dedup + sort ====================\n\nexport function deduplicateAndSortMessages(\n sources: Array<{ platform: string; messages: MergerMessage[] }>,\n collidingIds: Set<string>\n): MergedMessage[] {\n const seen = new Set<string>()\n const merged: MergedMessage[] = []\n\n for (const { platform, messages } of sources) {\n const p = platform || 'unknown'\n for (const msg of messages) {\n const nid = normalizePlatformId(msg.senderPlatformId, p, collidingIds)\n const key = getMessageKey(msg, nid)\n if (seen.has(key)) continue\n seen.add(key)\n merged.push({\n sender: nid,\n accountName: msg.senderAccountName,\n groupNickname: msg.senderGroupNickname,\n timestamp: msg.timestamp,\n type: msg.type,\n content: msg.content,\n })\n }\n }\n\n merged.sort((a, b) => a.timestamp - b.timestamp)\n return merged\n}\n","/**\n * ChatLab 聊天会话数据库 Schema 定义\n *\n * 所有 CREATE TABLE / INDEX 语句的单一事实来源。\n * 新建数据库时使用完整 Schema,现有数据库通过迁移脚本演进。\n *\n * 当前 Schema 版本:7\n */\n\n/** 当前 Schema 版本(最新迁移的版本号) */\nexport const CURRENT_SCHEMA_VERSION = 8\n\n/**\n * Table DDL only (no indexes). Used by bulk-import workflows that defer\n * index creation until after data is loaded for better write performance.\n */\nexport const CHAT_DB_TABLES = `\n CREATE TABLE IF NOT EXISTS meta (\n name TEXT NOT NULL,\n platform TEXT NOT NULL,\n type TEXT NOT NULL,\n imported_at INTEGER NOT NULL,\n group_id TEXT,\n group_avatar TEXT,\n owner_id TEXT,\n schema_version INTEGER DEFAULT ${CURRENT_SCHEMA_VERSION},\n session_gap_threshold INTEGER\n );\n\n CREATE TABLE IF NOT EXISTS member (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n platform_id TEXT NOT NULL UNIQUE,\n account_name TEXT,\n group_nickname TEXT,\n aliases TEXT DEFAULT '[]',\n avatar TEXT,\n roles TEXT DEFAULT '[]'\n );\n\n CREATE TABLE IF NOT EXISTS member_name_history (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n member_id INTEGER NOT NULL,\n name_type TEXT NOT NULL,\n name TEXT NOT NULL,\n start_ts INTEGER NOT NULL,\n end_ts INTEGER,\n FOREIGN KEY(member_id) REFERENCES member(id)\n );\n\n CREATE TABLE IF NOT EXISTS message (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n sender_id INTEGER NOT NULL,\n sender_account_name TEXT,\n sender_group_nickname TEXT,\n ts INTEGER NOT NULL,\n type INTEGER NOT NULL,\n content TEXT,\n reply_to_message_id TEXT DEFAULT NULL,\n platform_message_id TEXT DEFAULT NULL,\n FOREIGN KEY(sender_id) REFERENCES member(id)\n );\n\n CREATE TABLE IF NOT EXISTS segment (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n start_ts INTEGER NOT NULL,\n end_ts INTEGER NOT NULL,\n message_count INTEGER DEFAULT 0,\n is_manual INTEGER DEFAULT 0,\n summary TEXT\n );\n\n CREATE TABLE IF NOT EXISTS message_context (\n message_id INTEGER PRIMARY KEY,\n segment_id INTEGER NOT NULL,\n topic_id INTEGER\n );\n`\n\n/**\n * Index DDL only. Applied after bulk import or as part of full schema init.\n */\nexport const CHAT_DB_INDEXES = `\n CREATE INDEX IF NOT EXISTS idx_message_ts ON message(ts);\n CREATE INDEX IF NOT EXISTS idx_message_sender ON message(sender_id);\n CREATE INDEX IF NOT EXISTS idx_message_sender_ts ON message(sender_id, ts);\n CREATE INDEX IF NOT EXISTS idx_message_type_ts ON message(type, ts);\n CREATE INDEX IF NOT EXISTS idx_message_reply_to ON message(reply_to_message_id);\n CREATE INDEX IF NOT EXISTS idx_message_platform_id ON message(platform_message_id);\n CREATE INDEX IF NOT EXISTS idx_member_name_history_member_id ON member_name_history(member_id);\n CREATE INDEX IF NOT EXISTS idx_segment_time ON segment(start_ts, end_ts);\n CREATE INDEX IF NOT EXISTS idx_context_segment ON message_context(segment_id);\n`\n\n/**\n * Combined tables + indexes DDL (Schema Version 4).\n * For new database creation where deferred indexing is not needed.\n */\nexport const CHAT_DB_SCHEMA = CHAT_DB_TABLES + CHAT_DB_INDEXES\n\n/**\n * FTS5 全文搜索虚拟表 DDL\n *\n * content='' 表示使用外部内容表模式(不存储原始内容,只存储索引)。\n * 填充需要在导入或迁移时手动执行。\n */\nexport const FTS_TABLE_SCHEMA = `\n CREATE VIRTUAL TABLE IF NOT EXISTS message_fts USING fts5(\n content,\n content='',\n content_rowid=id\n );\n`\n","/**\n * 数据库迁移框架(平台无关)\n *\n * 提供 Schema 版本检测和通用迁移执行逻辑。\n * 具体的迁移脚本由各平台运行时注册(因为某些迁移依赖 NLP 分词器等平台特性)。\n */\n\nimport type { DatabaseAdapter } from '../interfaces'\n\n/** 迁移脚本接口 */\nexport interface Migration {\n version: number\n description: string\n up: (db: DatabaseAdapter) => void\n}\n\n/**\n * 读取数据库的 schema 版本\n */\nexport function getSchemaVersion(db: DatabaseAdapter): number {\n try {\n const tableInfo = db.pragma('table_info(meta)') as Array<{ name: string }>\n const hasVersionColumn = tableInfo.some((col) => col.name === 'schema_version')\n if (!hasVersionColumn) return 0\n\n const result = db.prepare('SELECT schema_version FROM meta LIMIT 1').get() as\n | { schema_version: number | null }\n | undefined\n return result?.schema_version ?? 0\n } catch {\n return 0\n }\n}\n\n/**\n * 设置数据库的 schema 版本\n */\nexport function setSchemaVersion(db: DatabaseAdapter, version: number): void {\n const tableInfo = db.pragma('table_info(meta)') as Array<{ name: string }>\n const hasVersionColumn = tableInfo.some((col) => col.name === 'schema_version')\n\n if (!hasVersionColumn) {\n db.exec('ALTER TABLE meta ADD COLUMN schema_version INTEGER DEFAULT 0')\n }\n\n db.prepare('UPDATE meta SET schema_version = ?').run(version)\n}\n\n/**\n * 检查数据库是否需要迁移\n */\nexport function needsMigration(db: DatabaseAdapter, targetVersion: number): boolean {\n return getSchemaVersion(db) < targetVersion\n}\n\n/**\n * 执行数据库迁移\n *\n * @param db 数据库适配器\n * @param migrationsList 迁移脚本列表(由调用方提供,可包含平台特定逻辑)\n * @param forceRepair 是否强制修复\n * @returns 是否执行了迁移\n */\nexport function runMigrations(db: DatabaseAdapter, migrationsList: Migration[], forceRepair = false): boolean {\n const currentVersion = getSchemaVersion(db)\n\n if (!forceRepair && currentVersion >= (migrationsList.at(-1)?.version ?? 0)) {\n return false\n }\n\n const pending = forceRepair ? migrationsList : migrationsList.filter((m) => m.version > currentVersion)\n\n if (pending.length === 0) return false\n\n db.transaction(() => {\n for (const migration of pending) {\n migration.up(db)\n setSchemaVersion(db, migration.version)\n }\n })\n\n return true\n}\n","/**\n * 分词引擎(Node.js 实现,依赖 @node-rs/jieba)\n *\n * 中文使用 jieba,英文/日语使用 Intl.Segmenter。\n * 类型、停用词、文本处理工具来自 @openchatlab/core。\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { createRequire } from 'module'\nimport type {\n SupportedLocale,\n PosFilterMode,\n DictType,\n SegmentOptions,\n BatchSegmentOptions,\n BatchSegmentResult,\n} from '@openchatlab/core'\nimport { MEANINGFUL_POS_TAGS, isStopword, cleanText, isValidWord } from '@openchatlab/core'\n\ninterface JiebaInstance {\n cut: (text: string, hmm?: boolean) => string[]\n tag: (text: string) => Array<{ tag: string; word: string }>\n}\n\nlet _nlpDir: string | null = null\nconst jiebaInstances = new Map<DictType, JiebaInstance>()\nconst require = createRequire(import.meta.url)\n\n/**\n * 设置自定义词库目录路径(由应用初始化时调用)\n */\nexport function initNlpDir(nlpDir: string): void {\n _nlpDir = nlpDir\n}\n\nexport function getNlpDir(): string | null {\n return _nlpDir\n}\n\nfunction existsDictOnDisk(dictId: string): boolean {\n if (!_nlpDir) return false\n return fs.existsSync(path.join(_nlpDir, `${dictId}.dict`))\n}\n\nfunction tryLoadDictFromDisk(dictId: string): Buffer | null {\n if (!_nlpDir) return null\n const dictPath = path.join(_nlpDir, `${dictId}.dict`)\n if (!fs.existsSync(dictPath)) return null\n try {\n return fs.readFileSync(dictPath)\n } catch {\n return null\n }\n}\n\n/**\n * 获取 Jieba 实例(支持多词库)\n */\nexport function getJieba(dictType: DictType = 'default'): JiebaInstance {\n const effectiveType = dictType === 'default' ? 'zh-CN' : dictType\n const cached = jiebaInstances.get(effectiveType)\n if (cached) {\n if (existsDictOnDisk(effectiveType)) return cached\n jiebaInstances.delete(effectiveType)\n console.log(`[NLP] jieba cache invalidated (dict missing): ${effectiveType}`)\n }\n\n try {\n const { Jieba } = require('@node-rs/jieba')\n let instance: JiebaInstance\n const diskDict = tryLoadDictFromDisk(effectiveType)\n if (diskDict) {\n instance = Jieba.withDict(diskDict)\n console.log(`[NLP] jieba dict loaded: ${effectiveType} (${diskDict.length} bytes)`)\n } else {\n instance = new Jieba()\n console.warn(`[NLP] jieba dict missing: ${effectiveType}, fallback to built-in tokenizer`)\n }\n jiebaInstances.set(effectiveType, instance)\n return instance\n } catch (error) {\n console.error(`[NLP] Failed to load jieba module (dict=${effectiveType}):`, error)\n throw new Error(`jieba 模块加载失败 (${effectiveType})`)\n }\n}\n\n/**\n * 清除指定词库的缓存实例(词库更新后调用)\n */\nexport function clearJiebaInstance(dictType: DictType): void {\n jiebaInstances.delete(dictType)\n console.log(`[NLP] jieba instance cleared: ${dictType}`)\n}\n\nfunction segmentChinese(\n text: string,\n options: { posFilterMode?: PosFilterMode; customPosTags?: string[]; dictType?: DictType } = {}\n): string[] {\n const { posFilterMode = 'meaningful', customPosTags, dictType = 'default' } = options\n const cleaned = cleanText(text)\n if (!cleaned) return []\n\n try {\n const jieba = getJieba(dictType)\n if (posFilterMode === 'all') return jieba.cut(cleaned, false)\n const tagged = jieba.tag(cleaned)\n const allowedTags = posFilterMode === 'custom' && customPosTags ? new Set(customPosTags) : MEANINGFUL_POS_TAGS\n return tagged.filter((item) => allowedTags.has(item.tag)).map((item) => item.word)\n } catch (error) {\n console.error('[NLP] Chinese segmentation failed:', error)\n try {\n return getJieba('default').cut(cleanText(text), false)\n } catch {\n return cleaned.split('')\n }\n }\n}\n\nfunction segmentEnglish(text: string): string[] {\n const cleaned = cleanText(text)\n if (!cleaned) return []\n try {\n const segmenter = new Intl.Segmenter('en', { granularity: 'word' })\n return [...segmenter.segment(cleaned)].filter((s) => s.isWordLike).map((s) => s.segment.toLowerCase())\n } catch {\n return cleaned\n .toLowerCase()\n .split(/\\s+/)\n .filter((w) => w.length > 0)\n }\n}\n\nfunction segmentJapanese(text: string): string[] {\n const cleaned = cleanText(text)\n if (!cleaned) return []\n try {\n const segmenter = new Intl.Segmenter('ja', { granularity: 'word' })\n return [...segmenter.segment(cleaned)].filter((s) => s.isWordLike).map((s) => s.segment)\n } catch {\n return cleaned.split('').filter((ch) => ch.trim().length > 0)\n }\n}\n\n/**\n * 通用分词入口\n */\nexport function segment(text: string, locale: SupportedLocale, options: SegmentOptions = {}): string[] {\n const {\n minLength,\n posFilterMode = 'meaningful',\n customPosTags,\n enableStopwords = true,\n dictType = 'default',\n } = options\n const isChinese = locale.startsWith('zh')\n const isJapanese = locale === 'ja-JP'\n const defaultMinLength = isChinese || isJapanese ? 2 : 3\n const effectiveMinLength = minLength ?? defaultMinLength\n\n let words: string[]\n if (isChinese) {\n words = segmentChinese(text, { posFilterMode, customPosTags, dictType })\n } else if (isJapanese) {\n words = segmentJapanese(text)\n } else {\n words = segmentEnglish(text)\n }\n\n return words.filter((word) => isValidWord(word, locale, effectiveMinLength, enableStopwords, isStopword))\n}\n\n/**\n * 批量分词并统计词频\n */\nexport function batchSegmentWithFrequency(\n texts: string[],\n locale: SupportedLocale,\n options: BatchSegmentOptions = {}\n): BatchSegmentResult {\n const {\n minLength,\n minCount = 2,\n topN = 100,\n posFilterMode,\n customPosTags,\n enableStopwords,\n dictType,\n excludeWords,\n } = options\n const wordFrequency = new Map<string, number>()\n const excludeSet = excludeWords?.length ? new Set(excludeWords.map((w) => w.toLowerCase())) : null\n\n for (const text of texts) {\n const words = segment(text, locale, { minLength, posFilterMode, customPosTags, enableStopwords, dictType })\n for (const word of words) {\n if (excludeSet && excludeSet.has(word.toLowerCase())) continue\n wordFrequency.set(word, (wordFrequency.get(word) || 0) + 1)\n }\n }\n\n const filtered = new Map<string, number>()\n let totalWords = 0\n for (const [word, count] of wordFrequency) {\n if (count >= minCount) {\n filtered.set(word, count)\n totalWords += count\n }\n }\n\n const sorted = [...filtered.entries()].sort((a, b) => b[1] - a[1]).slice(0, topN)\n return { words: new Map(sorted), uniqueWords: filtered.size, totalWords }\n}\n\n/**\n * 中文单遍分词:一次 jieba.tag 同时产出词频与词性统计。\n *\n * 仅适用于中文 meaningful/custom 模式(all 模式走 jieba.cut,无词性)。\n * 输出与 `batchSegmentWithFrequency` + `collectPosTagStats` 组合完全一致,\n * 用于消除对同一批文本重复分词的开销。\n */\nexport function batchSegmentChineseWithStats(\n texts: string[],\n locale: SupportedLocale,\n options: BatchSegmentOptions = {}\n): BatchSegmentResult & { posTagStats: Map<string, number> } {\n const {\n minLength,\n minCount = 2,\n topN = 100,\n posFilterMode = 'meaningful',\n customPosTags,\n enableStopwords = true,\n dictType = 'default',\n excludeWords,\n } = options\n\n const effectiveMinLength = minLength ?? 2\n const allowedTags = posFilterMode === 'custom' && customPosTags ? new Set(customPosTags) : MEANINGFUL_POS_TAGS\n const excludeSet = excludeWords?.length ? new Set(excludeWords.map((w) => w.toLowerCase())) : null\n\n const wordFrequency = new Map<string, number>()\n const posTagStats = new Map<string, number>()\n\n try {\n const jieba = getJieba(dictType)\n for (const text of texts) {\n const cleaned = cleanText(text)\n if (!cleaned) continue\n for (const { tag, word } of jieba.tag(cleaned)) {\n if (!isValidWord(word, locale, effectiveMinLength, enableStopwords, isStopword)) continue\n // 词性统计覆盖全部有效词(与 collectPosTagStats 一致)\n posTagStats.set(tag, (posTagStats.get(tag) || 0) + 1)\n // 词频仅统计命中允许词性的词(与 batchSegmentWithFrequency 一致)\n if (!allowedTags.has(tag)) continue\n if (excludeSet && excludeSet.has(word.toLowerCase())) continue\n wordFrequency.set(word, (wordFrequency.get(word) || 0) + 1)\n }\n }\n } catch (error) {\n console.error('[NLP] Chinese single-pass segmentation failed:', error)\n }\n\n const filtered = new Map<string, number>()\n let totalWords = 0\n for (const [word, count] of wordFrequency) {\n if (count >= minCount) {\n filtered.set(word, count)\n totalWords += count\n }\n }\n\n const sorted = [...filtered.entries()].sort((a, b) => b[1] - a[1]).slice(0, topN)\n return { words: new Map(sorted), uniqueWords: filtered.size, totalWords, posTagStats }\n}\n\n/**\n * 收集文本的词性统计\n */\nexport function collectPosTagStats(\n texts: string[],\n minWordLength: number = 2,\n enableStopwords: boolean = true,\n dictType: DictType = 'default'\n): Map<string, number> {\n const posStats = new Map<string, number>()\n try {\n const jieba = getJieba(dictType)\n for (const text of texts) {\n const cleaned = cleanText(text)\n if (!cleaned) continue\n const tagged = jieba.tag(cleaned)\n for (const item of tagged) {\n if (!isValidWord(item.word, 'zh-CN', minWordLength, enableStopwords, isStopword)) continue\n posStats.set(item.tag, (posStats.get(item.tag) || 0) + 1)\n }\n }\n } catch (error) {\n console.error('[NLP] Failed to collect POS stats:', error)\n }\n return posStats\n}\n\nimport { POS_TAG_DEFINITIONS } from '@openchatlab/core'\nimport type { PosTagInfo } from '@openchatlab/core'\n\n/**\n * 获取所有词性标签信息\n */\nexport function getPosTagDefinitions(): PosTagInfo[] {\n return POS_TAG_DEFINITIONS\n}\n","/**\n * FTS5 tokenizer — shared implementation.\n *\n * Unlike NLP word-frequency analysis, FTS tokenization keeps all tokens\n * (no POS filtering, no stopwords) to maximize recall in search scenarios.\n *\n * Uses jieba for Chinese (naturally handles mixed CJK/Latin text),\n * falls back to whitespace split when jieba is unavailable.\n */\n\nimport { getJieba } from './segmenter'\n\nexport function tokenizeForFts(text: string | null | undefined): string {\n if (!text || text.trim().length === 0) return ''\n\n try {\n const jieba = getJieba()\n const tokens = jieba.cut(text, false)\n return tokens\n .map((t) => t.trim().toLowerCase())\n .filter((t) => t.length > 0)\n .join(' ')\n } catch {\n return fallbackTokenize(text)\n }\n}\n\nfunction fallbackTokenize(text: string): string {\n return text\n .toLowerCase()\n .split(/\\s+/)\n .filter((t) => t.length > 0)\n .join(' ')\n}\n\nfunction escapeToken(token: string): string {\n return `\"${token.replace(/\"/g, '\"\"')}\"`\n}\n\n/**\n * Convert a list of user search keywords into an FTS5 MATCH expression.\n *\n * - Single keyword: tokens joined with AND (all must appear)\n * - Multiple keywords: groups joined with OR (any match)\n */\nexport function tokenizeQueryForFts(keywords: string[]): string {\n if (keywords.length === 0) return ''\n\n const groups = keywords\n .map((kw) => {\n const trimmed = kw.trim()\n if (!trimmed) return ''\n\n try {\n const jieba = getJieba()\n const tokens = jieba\n .cut(trimmed, false)\n .map((t) => t.trim().toLowerCase())\n .filter((t) => t.length > 0)\n\n if (tokens.length === 0) return ''\n if (tokens.length === 1) return escapeToken(tokens[0])\n return tokens.map(escapeToken).join(' ')\n } catch {\n const simple = trimmed.toLowerCase().trim()\n return simple ? escapeToken(simple) : ''\n }\n })\n .filter((g) => g.length > 0)\n\n if (groups.length === 0) return ''\n if (groups.length === 1) return groups[0]\n\n return groups.map((g) => (g.includes(' ') ? `(${g})` : g)).join(' OR ')\n}\n","/**\n * AI 日志模块(平台无关)\n *\n * 将 AI 相关操作日志写入本地文件。Electron 和 Server/CLI 共用。\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\n\ntype LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR'\n\nexport class AiLogger {\n private debugMode = false\n private logDir: string\n private logFile: string | null = null\n private logStream: fs.WriteStream | null = null\n\n constructor(logsDir: string) {\n this.logDir = path.join(logsDir, 'ai')\n }\n\n setDebugMode(enabled: boolean): void {\n this.debugMode = enabled\n }\n\n isDebugMode(): boolean {\n return this.debugMode\n }\n\n debug(category: string, message: string, data?: unknown): void {\n this.writeLog('DEBUG', category, message, data)\n }\n\n info(category: string, message: string, data?: unknown): void {\n this.writeLog('INFO', category, message, data)\n }\n\n warn(category: string, message: string, data?: unknown): void {\n this.writeLog('WARN', category, message, data)\n }\n\n error(category: string, message: string, data?: unknown): void {\n this.writeLog('ERROR', category, message, data)\n }\n\n close(): void {\n if (this.logStream) {\n this.logStream.end()\n this.logStream = null\n }\n }\n\n getLogPath(): string {\n return this.getLogFilePath()\n }\n\n getExistingLogPath(): string | null {\n if (this.logFile && fs.existsSync(this.logFile)) {\n return this.logFile\n }\n return null\n }\n\n private ensureLogDir(): void {\n if (!fs.existsSync(this.logDir)) {\n fs.mkdirSync(this.logDir, { recursive: true })\n }\n }\n\n private getLogFilePath(): string {\n if (this.logFile) return this.logFile\n\n this.ensureLogDir()\n const now = new Date()\n const date = now.toISOString().split('T')[0]\n const hours = String(now.getHours()).padStart(2, '0')\n const minutes = String(now.getMinutes()).padStart(2, '0')\n this.logFile = path.join(this.logDir, `ai_${date}_${hours}-${minutes}.log`)\n\n return this.logFile\n }\n\n private getLogStream(): fs.WriteStream {\n if (this.logStream) return this.logStream\n\n const filePath = this.getLogFilePath()\n const isNewOrEmptyFile = !fs.existsSync(filePath) || fs.statSync(filePath).size === 0\n this.logStream = fs.createWriteStream(filePath, { flags: 'a', encoding: 'utf-8' })\n if (isNewOrEmptyFile) {\n this.logStream.write(`Local Path: ${filePath.replace(/ /g, '\\\\ ')}\\n\\n`)\n }\n\n return this.logStream\n }\n\n private writeLog(level: LogLevel, category: string, message: string, data?: unknown): void {\n const timestamp = new Date().toISOString()\n let logLine = `[${timestamp}] [${level}] [${category}] ${message}`\n\n if (data !== undefined) {\n try {\n const dataStr = typeof data === 'string' ? data : JSON.stringify(data, null, 2)\n if (!this.debugMode && dataStr.length > 2000) {\n logLine += `\\n${dataStr.slice(0, 2000)}...[truncated, ${dataStr.length} chars total]`\n } else {\n logLine += `\\n${dataStr}`\n }\n } catch {\n logLine += `\\n[unserializable data]`\n }\n }\n\n logLine += '\\n'\n\n try {\n const stream = this.getLogStream()\n stream.write(logLine)\n } catch (error) {\n console.error('[AiLogger] Failed to write log:', error)\n }\n\n if (level === 'WARN' || level === 'ERROR') {\n console.log(`[AI] ${message}`)\n }\n }\n}\n\nexport function extractErrorInfo(error: unknown): Record<string, unknown> {\n if (error instanceof Error) {\n const info: Record<string, unknown> = {\n name: error.name,\n message: error.message,\n }\n if ('cause' in error && error.cause) {\n info.cause = extractErrorInfo(error.cause)\n }\n return info\n }\n if (typeof error === 'object' && error !== null) {\n return { raw: JSON.stringify(error) }\n }\n return { message: String(error) }\n}\n\nexport function extractErrorStack(error: unknown, stackLines: number = 5): string | null {\n if (error instanceof Error && error.stack) {\n const lines = error.stack.split('\\n')\n return lines.slice(1, stackLines + 1).join('\\n')\n }\n return null\n}\n","/**\n * Unified application logger (platform-agnostic, Node side).\n *\n * Shared by Electron main process, CLI and CLI Web runtime. Writes general\n * application/diagnostic logs to a single rolling file `<logsDir>/app.log`.\n *\n * Scope of this logger: general app + key-path + crash logs. AI logs (AiLogger)\n * and import perf logs keep their own per-scenario files.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { extractErrorInfo, extractErrorStack } from '../ai/ai-logger'\n\ntype LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR'\n\nconst LEVEL_ORDER: Record<LogLevel, number> = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3 }\n\n// Rotate when app.log reaches this size; old content moves to app.old.log.\nconst MAX_SIZE_BYTES = 10 * 1024 * 1024 // 10MB\n\nclass AppLogger {\n private logFile: string\n private oldLogFile: string\n private threshold: LogLevel\n\n constructor(logsDir: string) {\n this.logFile = path.join(logsDir, 'app.log')\n this.oldLogFile = path.join(logsDir, 'app.old.log')\n this.threshold = resolveThreshold()\n }\n\n debug(scope: string, message: string, data?: unknown): void {\n this.write('DEBUG', scope, message, data)\n }\n\n info(scope: string, message: string, data?: unknown): void {\n this.write('INFO', scope, message, data)\n }\n\n warn(scope: string, message: string, data?: unknown): void {\n this.write('WARN', scope, message, data)\n }\n\n /** `data` may be an Error; its name/message/stack are extracted automatically. */\n error(scope: string, message: string, data?: unknown): void {\n this.write('ERROR', scope, message, data)\n }\n\n getLogPath(): string {\n return this.logFile\n }\n\n private write(level: LogLevel, scope: string, message: string, data?: unknown): void {\n if (LEVEL_ORDER[level] < LEVEL_ORDER[this.threshold]) return\n\n let line = `[${new Date().toISOString()}] [${level}] [${scope}] ${message}`\n\n try {\n const tail = formatData(data)\n if (tail) line += `\\n${tail}`\n line += '\\n'\n ensureDir(path.dirname(this.logFile))\n this.rotateIfNeeded()\n fs.appendFileSync(this.logFile, line, 'utf-8')\n } catch (err) {\n // Logging must never break the app; surface to console only.\n console.error('[AppLogger] Failed to write log:', err)\n }\n }\n\n // ponytail: rename-based rotation, atomic, no rewrite, ~20MB total ceiling\n private rotateIfNeeded(): void {\n let size = 0\n try {\n size = fs.statSync(this.logFile).size\n } catch {\n return // file doesn't exist yet\n }\n if (size < MAX_SIZE_BYTES) return\n fs.renameSync(this.logFile, this.oldLogFile) // overwrites previous .old, atomic\n }\n}\n\nfunction resolveThreshold(): LogLevel {\n const raw = (process.env.CHATLAB_LOG_LEVEL || '').toUpperCase()\n if (raw === 'DEBUG' || raw === 'INFO' || raw === 'WARN' || raw === 'ERROR') return raw\n return 'INFO'\n}\n\nfunction formatData(data: unknown): string {\n if (data === undefined) return ''\n if (data instanceof Error) {\n const info = extractErrorInfo(data)\n const stack = extractErrorStack(data)\n return JSON.stringify(info) + (stack ? `\\n${stack}` : '')\n }\n if (typeof data === 'string') return data\n try {\n return JSON.stringify(data)\n } catch {\n return '[unserializable data]'\n }\n}\n\nfunction ensureDir(dir: string): void {\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })\n}\n\nlet instance: AppLogger | null = null\n\n/** Initialize the singleton app logger. Call once per runtime at startup. */\nexport function initAppLogger(logsDir: string): void {\n instance = new AppLogger(logsDir)\n}\n\n/**\n * The shared logger. Safe to call before init: logs are dropped silently until\n * `initAppLogger` runs (avoids ordering hazards at very early startup).\n */\nexport const appLogger = {\n debug: (scope: string, message: string, data?: unknown) => instance?.debug(scope, message, data),\n info: (scope: string, message: string, data?: unknown) => instance?.info(scope, message, data),\n warn: (scope: string, message: string, data?: unknown) => instance?.warn(scope, message, data),\n error: (scope: string, message: string, data?: unknown) => instance?.error(scope, message, data),\n getLogPath: () => instance?.getLogPath() ?? null,\n}\n","/**\n * Electron legacy data detection and migration for CLI/Node.js runtime.\n *\n * When a user who has been using the Electron desktop app installs the CLI,\n * this module detects existing Electron data and repoints config.toml\n * so that CLI sees the same databases and configuration.\n *\n * Migration behavior:\n * 1. Detect Electron legacy data directory (platform-specific)\n * 2. If databases exist there, write user_data_dir to config.toml pointing to old path\n * 3. Copy system data (ai, settings, cache, logs, temp, nlp) from old path to ~/.chatlab/\n */\n\nimport * as fs from 'fs'\nimport * as os from 'os'\nimport * as path from 'path'\nimport { writeConfigField } from '@openchatlab/config'\n\nconst SYSTEM_SUBDIRS = ['ai', 'settings', 'cache', 'logs', 'temp', 'nlp']\nconst STORAGE_CONFIG_FILE = 'storage.json'\n\ninterface StorageConfig {\n dataDir?: string\n}\n\n/**\n * Get the Electron userData directory path (platform-specific, no Electron dependency).\n *\n * - macOS: ~/Library/Application Support/ChatLab/\n * - Windows: %APPDATA%/ChatLab/\n * - Linux: ~/.config/ChatLab/\n */\nfunction getElectronUserDataDir(): string {\n const home = os.homedir()\n switch (process.platform) {\n case 'darwin':\n return path.join(home, 'Library', 'Application Support', 'ChatLab')\n case 'win32': {\n const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming')\n return path.join(appData, 'ChatLab')\n }\n default:\n return path.join(home, '.config', 'ChatLab')\n }\n}\n\nfunction readElectronStorageConfig(electronUserData: string): StorageConfig {\n const configPath = path.join(electronUserData, STORAGE_CONFIG_FILE)\n if (!fs.existsSync(configPath)) return {}\n try {\n return JSON.parse(fs.readFileSync(configPath, 'utf-8')) as StorageConfig\n } catch {\n return {}\n }\n}\n\n/**\n * Resolve the old Electron data directory, considering storage.json custom paths.\n */\nfunction resolveElectronDataDir(): string {\n const electronUserData = getElectronUserDataDir()\n const storageConfig = readElectronStorageConfig(electronUserData)\n if (storageConfig.dataDir && path.isAbsolute(storageConfig.dataDir)) {\n return storageConfig.dataDir\n }\n return path.join(electronUserData, 'data')\n}\n\nfunction hasDatabases(dir: string): boolean {\n const dbDir = path.join(dir, 'databases')\n if (!fs.existsSync(dbDir)) return false\n try {\n return fs.readdirSync(dbDir).some((f) => f.endsWith('.db'))\n } catch {\n return false\n }\n}\n\nfunction ensureDir(dirPath: string): void {\n if (!fs.existsSync(dirPath)) {\n fs.mkdirSync(dirPath, { recursive: true })\n }\n}\n\nfunction copyDirMerge(src: string, dest: string): { copied: number; skipped: number } {\n const stats = { copied: 0, skipped: 0 }\n if (!fs.existsSync(src)) return stats\n\n ensureDir(dest)\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(src, { withFileTypes: true })\n } catch {\n return stats\n }\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name)\n const destPath = path.join(dest, entry.name)\n try {\n if (entry.isDirectory()) {\n const sub = copyDirMerge(srcPath, destPath)\n stats.copied += sub.copied\n stats.skipped += sub.skipped\n } else if (!fs.existsSync(destPath)) {\n fs.copyFileSync(srcPath, destPath)\n stats.copied++\n } else {\n stats.skipped++\n }\n } catch {\n // Skip files that fail to copy\n }\n }\n return stats\n}\n\nfunction writeMigrationLog(logDir: string, message: string): void {\n try {\n ensureDir(logDir)\n const logPath = path.join(logDir, 'app.log')\n const ts = new Date().toISOString().replace('T', ' ').slice(0, 19)\n fs.appendFileSync(logPath, `[${ts}] [MIGRATION] ${message}\\n`, 'utf-8')\n } catch {\n // Silent on log failure\n }\n}\n\nexport interface ElectronMigrationResult {\n migrated: boolean\n userDataDir?: string\n systemDirsCopied?: number\n /** true if Electron userData directory was found (indicating desktop app was used) */\n electronDetected: boolean\n error?: string\n}\n\n/**\n * Check if the Electron desktop app was previously used on this machine.\n * Examines the platform-specific Electron userData directory for signs of active use.\n */\nexport function wasElectronUsed(): boolean {\n const electronUserData = getElectronUserDataDir()\n if (!fs.existsSync(electronUserData)) return false\n\n // Electron creates these on active use (not just installation)\n return (\n fs.existsSync(path.join(electronUserData, 'Preferences')) ||\n fs.existsSync(path.join(electronUserData, 'Local Storage')) ||\n fs.existsSync(path.join(electronUserData, 'Session Storage')) ||\n fs.existsSync(path.join(electronUserData, 'data'))\n )\n}\n\n/**\n * Verify that the resolved database directory actually contains databases.\n * If the directory is empty but Electron was previously used, this indicates\n * a misconfigured data path — the user's databases are somewhere else.\n *\n * @param databaseDir - The databases/ directory path (from PathProvider.getDatabaseDir())\n *\n * Call this AFTER NodePathProvider is initialized, regardless of how\n * the data directory was resolved (config.toml, env var, migration, or default).\n */\nexport function verifyCliDataPath(databaseDir: string): boolean {\n if (fs.existsSync(databaseDir)) {\n try {\n if (fs.readdirSync(databaseDir).some((f) => f.endsWith('.db'))) return true\n } catch {\n // fall through\n }\n }\n\n // No databases found — only block if Electron was previously used\n return !wasElectronUsed()\n}\n\n/**\n * Detect and migrate Electron legacy data for CLI first-run.\n *\n * Should be called when config.toml has no user_data_dir set (i.e. first run).\n *\n * Possible outcomes:\n * - migrated=true: found Electron databases, config.toml updated\n * - migrated=false, electronDetected=false: no Electron installation found\n * - migrated=false, electronDetected=true: Electron was used but databases not found\n * (user likely has a custom data directory that we can't locate)\n */\nexport function migrateFromElectronIfNeeded(systemDir: string): ElectronMigrationResult {\n const electronDataDir = resolveElectronDataDir()\n\n if (!hasDatabases(electronDataDir)) {\n return { migrated: false, electronDetected: wasElectronUsed() }\n }\n\n console.log(`[Migration] Detected Electron data at: ${electronDataDir}`)\n\n try {\n // Step 1: Point config.toml to the Electron data directory (databases stay in place)\n writeConfigField('data', 'user_data_dir', electronDataDir)\n\n // Step 2: Copy system data from Electron dir to ~/.chatlab/ (merge, don't overwrite)\n let totalCopied = 0\n let totalSkipped = 0\n const movedDirs: string[] = []\n\n for (const subDir of SYSTEM_SUBDIRS) {\n const srcDir = path.join(electronDataDir, subDir)\n const destDir = path.join(systemDir, subDir)\n if (!fs.existsSync(srcDir)) continue\n\n const { copied, skipped } = copyDirMerge(srcDir, destDir)\n if (copied > 0 || skipped > 0) {\n movedDirs.push(subDir)\n totalCopied += copied\n totalSkipped += skipped\n console.log(`[Migration] ${subDir}: copied ${copied}, skipped ${skipped}`)\n }\n }\n\n // Step 3: Remove copied system dirs from old data path (databases stay)\n for (const subDir of movedDirs) {\n const srcDir = path.join(electronDataDir, subDir)\n try {\n fs.rmSync(srcDir, { recursive: true, force: true })\n } catch {\n // Non-critical: old dir remains but won't cause issues\n }\n }\n\n const logsDir = path.join(systemDir, 'logs')\n const summary =\n `CLI first-run: detected Electron data at ${electronDataDir}, ` +\n `pointed user_data_dir there, copied ${totalCopied} system files (${movedDirs.join(', ') || 'none'})`\n writeMigrationLog(logsDir, summary)\n console.log(`[Migration] ${summary}`)\n\n return {\n migrated: true,\n electronDetected: true,\n userDataDir: electronDataDir,\n systemDirsCopied: totalCopied,\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error)\n console.error('[Migration] Electron data migration failed:', errorMsg)\n try {\n writeMigrationLog(path.join(systemDir, 'logs'), `Electron migration failed: ${errorMsg}`)\n } catch {\n // Ignore log failure\n }\n return { migrated: false, electronDetected: true, error: errorMsg }\n }\n}\n","/**\n * Session-level JSON cache module (platform-agnostic).\n *\n * Extracted from electron/main/database/sessionCache.ts.\n * Each session has a {sessionId}.cache.json file partitioned by key.\n * Decoupled from DB schema — reads auto-rebuild on failure, no versioning needed.\n *\n * File path: {cacheDir}/{sessionId}.cache.json\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport type { DatabaseAdapter } from '@openchatlab/core'\n\n// ==================== Generic cache infrastructure ====================\n\ninterface CacheEntry<T = unknown> {\n data: T\n ts: number\n}\n\ntype CacheFile = Record<string, CacheEntry>\n\nexport function getCachePath(sessionId: string, cacheDir: string): string {\n return path.join(cacheDir, `${sessionId}.cache.json`)\n}\n\nfunction readCacheFile(cachePath: string): CacheFile | null {\n try {\n if (!fs.existsSync(cachePath)) return null\n return JSON.parse(fs.readFileSync(cachePath, 'utf-8')) as CacheFile\n } catch {\n return null\n }\n}\n\nfunction writeCacheFile(cachePath: string, content: CacheFile): void {\n try {\n const dir = path.dirname(cachePath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n fs.writeFileSync(cachePath, JSON.stringify(content), 'utf-8')\n } catch {\n // Write failure is non-fatal\n }\n}\n\nexport function getCache<T>(sessionId: string, key: string, cacheDir: string): T | null {\n const cachePath = getCachePath(sessionId, cacheDir)\n const file = readCacheFile(cachePath)\n if (!file || !file[key]) return null\n return file[key].data as T\n}\n\nexport function setCache<T>(sessionId: string, key: string, data: T, cacheDir: string): void {\n const cachePath = getCachePath(sessionId, cacheDir)\n const file = readCacheFile(cachePath) ?? {}\n file[key] = { data, ts: Math.floor(Date.now() / 1000) }\n writeCacheFile(cachePath, file)\n}\n\nexport function invalidateCache(sessionId: string, cacheDir: string, key?: string): void {\n const cachePath = getCachePath(sessionId, cacheDir)\n try {\n if (!key) {\n if (fs.existsSync(cachePath)) fs.unlinkSync(cachePath)\n } else {\n const file = readCacheFile(cachePath)\n if (file && file[key]) {\n delete file[key]\n writeCacheFile(cachePath, file)\n }\n }\n } catch {\n // Ignore\n }\n}\n\nexport function deleteSessionCache(sessionId: string, cacheDir: string): void {\n const cachePath = getCachePath(sessionId, cacheDir)\n try {\n if (fs.existsSync(cachePath)) fs.unlinkSync(cachePath)\n } catch {\n // Ignore\n }\n}\n\n// ==================== Overview cache (aggregate stats) ====================\n\nexport const CACHE_KEY_OVERVIEW = 'overview'\n\nexport interface OverviewCache {\n totalMessages: number\n totalMembers: number\n firstMessageTs: number | null\n lastMessageTs: number | null\n /**\n * MAX(message.id) at the time the cache was written.\n * Used as a cheap O(1) freshness fingerprint: if current MAX(id) differs\n * the cache is stale and must be recomputed. Absent in legacy cache files\n * written before this field was added; absence is treated as stale.\n */\n maxMessageId?: number\n}\n\n/**\n * Return the current MAX(message.id) for freshness checking.\n * This is O(1) via the AUTOINCREMENT rowid B-tree.\n * Returns 0 for an empty table.\n */\nfunction getMaxMessageId(db: DatabaseAdapter): number {\n const row = db.prepare('SELECT MAX(id) AS m FROM message').get() as { m: number | null }\n return row.m ?? 0\n}\n\nexport function computeAndSetOverviewCache(db: DatabaseAdapter, sessionId: string, cacheDir: string): OverviewCache {\n const msgStats = db.prepare('SELECT MIN(ts) as first_ts, MAX(ts) as last_ts FROM message').get() as {\n first_ts: number | null\n last_ts: number | null\n }\n\n const totalMessages = (\n db\n .prepare(\n `SELECT COUNT(*) as count FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE COALESCE(m.account_name, '') != '系统消息'`\n )\n .get() as { count: number }\n ).count\n\n const totalMembers = (\n db.prepare(`SELECT COUNT(*) as count FROM member WHERE COALESCE(account_name, '') != '系统消息'`).get() as {\n count: number\n }\n ).count\n\n const data: OverviewCache = {\n totalMessages,\n totalMembers,\n firstMessageTs: msgStats.first_ts,\n lastMessageTs: msgStats.last_ts,\n maxMessageId: getMaxMessageId(db),\n }\n\n setCache(sessionId, CACHE_KEY_OVERVIEW, data, cacheDir)\n computeAndSetMembersCache(db, sessionId, cacheDir)\n\n return data\n}\n\n/**\n * Cache-first overview read with fingerprint validation.\n *\n * Checks cached `maxMessageId` against the live `MAX(message.id)`. If they\n * match the cache is fresh and returned as-is (one extra O(1) query).\n * On any mismatch — new inserts, legacy cache lacking the field, or a cold\n * miss — the cache is recomputed and written before returning.\n *\n * Use this instead of bare `getCache` whenever you need a guaranteed-fresh\n * overview (e.g. AI system prompt data-snapshot construction).\n */\nexport function getValidatedOverviewCache(db: DatabaseAdapter, sessionId: string, cacheDir: string): OverviewCache {\n const cached = getCache<OverviewCache>(sessionId, CACHE_KEY_OVERVIEW, cacheDir)\n if (cached && cached.maxMessageId !== undefined && cached.maxMessageId === getMaxMessageId(db)) {\n return cached\n }\n return computeAndSetOverviewCache(db, sessionId, cacheDir)\n}\n\n// ==================== Members cache (per-member stats) ====================\n\nexport const CACHE_KEY_MEMBERS = 'members'\n\nexport interface MemberStat {\n name: string\n count: number\n}\n\nexport interface MembersCache {\n members: Record<number, MemberStat>\n}\n\nexport function computeAndSetMembersCache(db: DatabaseAdapter, sessionId: string, cacheDir: string): MembersCache {\n const rows = db\n .prepare(\n `SELECT msg.sender_id, COUNT(*) as count,\n COALESCE(m.group_nickname, m.account_name, m.platform_id) as name\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n GROUP BY msg.sender_id`\n )\n .all() as Array<{ sender_id: number; count: number; name: string }>\n\n const members: Record<number, MemberStat> = {}\n for (const row of rows) {\n members[row.sender_id] = { name: row.name, count: row.count }\n }\n\n const data: MembersCache = { members }\n setCache(sessionId, CACHE_KEY_MEMBERS, data, cacheDir)\n return data\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { PathProvider } from '@openchatlab/core'\n\nexport type RuntimeKind = 'cli' | 'desktop' | 'mcp' | 'unknown'\n\nexport interface RuntimeIdentity {\n version: string\n kind: RuntimeKind\n}\n\nexport interface DataDirCompatibilityMeta {\n formatVersion: 1\n minRuntimeVersion: string\n dataCompatibilityVersion: number\n reasons: string[]\n updatedBy: {\n runtime: RuntimeKind\n module: string\n version: string\n }\n updatedAt: number\n}\n\nexport interface RaiseDataDirCompatibilityInput {\n minRuntimeVersion: string\n dataCompatibilityVersion: number\n reason: string\n runtime: RuntimeIdentity\n module: string\n now?: () => number\n}\n\nexport interface AssertDataDirCompatibilityOptions {\n warn?: (message: string) => void\n env?: Pick<NodeJS.ProcessEnv, 'CHATLAB_ALLOW_INCOMPATIBLE_DATA_DIR'>\n}\n\ntype DataDirCompatibilityErrorCode = 'DATA_DIR_REQUIRES_NEWER_RUNTIME' | 'DATA_DIR_COMPATIBILITY_META_INVALID'\n\nexport class DataDirCompatibilityError extends Error {\n readonly code: DataDirCompatibilityErrorCode\n readonly userDataDir: string\n readonly metaPath: string\n readonly currentVersion?: string\n readonly minRuntimeVersion?: string\n readonly statusCode = 409\n\n constructor(\n code: DataDirCompatibilityErrorCode,\n message: string,\n options: {\n userDataDir: string\n metaPath: string\n currentVersion?: string\n minRuntimeVersion?: string\n cause?: unknown\n }\n ) {\n super(message, { cause: options.cause })\n this.name = 'DataDirCompatibilityError'\n this.code = code\n this.userDataDir = options.userDataDir\n this.metaPath = options.metaPath\n this.currentVersion = options.currentVersion\n this.minRuntimeVersion = options.minRuntimeVersion\n }\n}\n\nexport function readDataDirCompatibilityMeta(userDataDir: string): DataDirCompatibilityMeta | null {\n const metaPath = getMetaPath(userDataDir)\n if (!fs.existsSync(metaPath)) return null\n\n let parsed: unknown\n try {\n parsed = JSON.parse(fs.readFileSync(metaPath, 'utf-8'))\n } catch (error) {\n throw createInvalidMetaError(userDataDir, metaPath, 'Invalid data directory compatibility meta JSON.', error)\n }\n\n return validateDataDirCompatibilityMeta(parsed, userDataDir, metaPath)\n}\n\nexport function assertDataDirCompatible(\n pathProvider: PathProvider,\n runtime: RuntimeIdentity,\n options: AssertDataDirCompatibilityOptions = {}\n): void {\n const userDataDir = pathProvider.getUserDataDir()\n const meta = readDataDirCompatibilityMeta(userDataDir)\n if (!meta) return\n\n const runtimeStableVersion = normalizeRuntimeStableVersion(runtime.version)\n if (!runtimeStableVersion) {\n throw new DataDirCompatibilityError(\n 'DATA_DIR_REQUIRES_NEWER_RUNTIME',\n `ChatLab data directory compatibility check requires a valid runtime semver; current version is ${runtime.version}.`,\n {\n userDataDir,\n metaPath: getMetaPath(userDataDir),\n currentVersion: runtime.version,\n minRuntimeVersion: meta.minRuntimeVersion,\n }\n )\n }\n\n if (compareStableSemver(runtimeStableVersion, meta.minRuntimeVersion) < 0) {\n const env = options.env ?? process.env\n if (env.CHATLAB_ALLOW_INCOMPATIBLE_DATA_DIR === '1') {\n const warn = options.warn ?? console.warn\n warn(\n [\n 'CHATLAB_ALLOW_INCOMPATIBLE_DATA_DIR=1 is set. ChatLab will bypass the data directory runtime version gate.',\n `Current runtime ${runtime.kind}@${runtime.version} is below required version ${meta.minRuntimeVersion}.`,\n `Data directory: ${userDataDir}. Continuing may risk data corruption.`,\n ].join(' ')\n )\n return\n }\n\n throw new DataDirCompatibilityError(\n 'DATA_DIR_REQUIRES_NEWER_RUNTIME',\n `ChatLab data directory requires runtime version ${meta.minRuntimeVersion} or newer; current version is ${runtime.version}.`,\n {\n userDataDir,\n metaPath: getMetaPath(userDataDir),\n currentVersion: runtime.version,\n minRuntimeVersion: meta.minRuntimeVersion,\n }\n )\n }\n}\n\nexport function raiseDataDirMinRuntimeVersion(\n pathProvider: PathProvider,\n input: RaiseDataDirCompatibilityInput\n): DataDirCompatibilityMeta {\n const userDataDir = pathProvider.getUserDataDir()\n const metaPath = getMetaPath(userDataDir)\n\n assertStableVersion(input.minRuntimeVersion, userDataDir, metaPath, 'Invalid minRuntimeVersion.')\n const runtimeStableVersion = assertRuntimeComparableVersion(input.runtime.version, userDataDir, metaPath)\n if (!isRuntimeKind(input.runtime.kind)) {\n throw createInvalidMetaError(userDataDir, metaPath, 'Invalid runtime kind.')\n }\n if (!isNonEmptyString(input.module) || !isNonEmptyString(input.reason)) {\n throw createInvalidMetaError(userDataDir, metaPath, 'Invalid compatibility raise metadata.')\n }\n if (!Number.isInteger(input.dataCompatibilityVersion) || input.dataCompatibilityVersion < 0) {\n throw createInvalidMetaError(userDataDir, metaPath, 'Invalid dataCompatibilityVersion.')\n }\n\n const existing = readDataDirCompatibilityMeta(userDataDir)\n const minRuntimeVersion =\n existing && compareStableSemver(existing.minRuntimeVersion, input.minRuntimeVersion) > 0\n ? existing.minRuntimeVersion\n : input.minRuntimeVersion\n const dataCompatibilityVersion = Math.max(existing?.dataCompatibilityVersion ?? 0, input.dataCompatibilityVersion)\n const reasons = mergeReasons(existing?.reasons ?? [], input.reason)\n const now = input.now ?? (() => Math.floor(Date.now() / 1000))\n\n const nextMeta: DataDirCompatibilityMeta = {\n formatVersion: 1,\n minRuntimeVersion,\n dataCompatibilityVersion,\n reasons,\n updatedBy: {\n runtime: input.runtime.kind,\n module: input.module,\n version: runtimeStableVersion,\n },\n updatedAt: now(),\n }\n\n writeMetaAtomic(userDataDir, metaPath, nextMeta)\n return nextMeta\n}\n\nfunction getMetaPath(userDataDir: string): string {\n return path.join(userDataDir, '.chatlab-meta.json')\n}\n\nfunction validateDataDirCompatibilityMeta(\n value: unknown,\n userDataDir: string,\n metaPath: string\n): DataDirCompatibilityMeta {\n if (!isRecord(value)) {\n throw createInvalidMetaError(userDataDir, metaPath, 'Data directory compatibility meta must be an object.')\n }\n\n if (value.formatVersion !== 1) {\n throw createInvalidMetaError(userDataDir, metaPath, 'Unsupported data directory compatibility meta format.')\n }\n\n const minRuntimeVersion = value.minRuntimeVersion\n const dataCompatibilityVersion = value.dataCompatibilityVersion\n const reasons = value.reasons\n const updatedBy = value.updatedBy\n const updatedAt = value.updatedAt\n\n if (\n !isStableSemver(minRuntimeVersion) ||\n typeof dataCompatibilityVersion !== 'number' ||\n !Number.isInteger(dataCompatibilityVersion) ||\n dataCompatibilityVersion < 0 ||\n !Array.isArray(reasons) ||\n !reasons.every(isNonEmptyString) ||\n !isRecord(updatedBy) ||\n !isRuntimeKind(updatedBy.runtime) ||\n !isNonEmptyString(updatedBy.module) ||\n !isStableSemver(updatedBy.version) ||\n typeof updatedAt !== 'number' ||\n !Number.isInteger(updatedAt) ||\n updatedAt < 0\n ) {\n throw createInvalidMetaError(userDataDir, metaPath, 'Invalid data directory compatibility meta fields.')\n }\n\n return {\n formatVersion: 1,\n minRuntimeVersion,\n dataCompatibilityVersion,\n reasons: [...reasons],\n updatedBy: {\n runtime: updatedBy.runtime,\n module: updatedBy.module,\n version: updatedBy.version,\n },\n updatedAt,\n }\n}\n\nfunction createInvalidMetaError(\n userDataDir: string,\n metaPath: string,\n message: string,\n cause?: unknown\n): DataDirCompatibilityError {\n return new DataDirCompatibilityError('DATA_DIR_COMPATIBILITY_META_INVALID', message, {\n userDataDir,\n metaPath,\n cause,\n })\n}\n\nfunction assertStableVersion(version: string, userDataDir: string, metaPath: string, message: string): void {\n if (!isStableSemver(version)) {\n throw createInvalidMetaError(userDataDir, metaPath, message)\n }\n}\n\nfunction isStableSemver(version: unknown): version is string {\n return typeof version === 'string' && /^\\d+\\.\\d+\\.\\d+$/.test(version)\n}\n\nfunction assertRuntimeComparableVersion(version: string, userDataDir: string, metaPath: string): string {\n const normalized = normalizeRuntimeStableVersion(version)\n if (!normalized) {\n throw createInvalidMetaError(userDataDir, metaPath, 'Invalid runtime version.')\n }\n return normalized\n}\n\n// 当前运行时允许使用 beta/rc 等 prerelease;数据目录门禁比较和 meta 写入只使用稳定 core 版本。\nfunction normalizeRuntimeStableVersion(version: string): string | null {\n const match = /^v?(\\d+\\.\\d+\\.\\d+)(-[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?$/.exec(version.trim())\n if (!match) return null\n if (match[1] === '0.0.0' && match[2]) return null\n return match[1]\n}\n\nfunction compareStableSemver(left: string, right: string): number {\n const leftParts = left.split('.').map(Number)\n const rightParts = right.split('.').map(Number)\n\n for (let i = 0; i < 3; i += 1) {\n if (leftParts[i] > rightParts[i]) return 1\n if (leftParts[i] < rightParts[i]) return -1\n }\n\n return 0\n}\n\nfunction mergeReasons(existing: string[], next: string): string[] {\n return [...new Set([...existing, next])]\n}\n\nfunction writeMetaAtomic(userDataDir: string, metaPath: string, meta: DataDirCompatibilityMeta): void {\n fs.mkdirSync(userDataDir, { recursive: true })\n const tempPath = path.join(userDataDir, `.chatlab-meta.json.${process.pid}.${Date.now()}.tmp`)\n try {\n fs.writeFileSync(tempPath, `${JSON.stringify(meta, null, 2)}\\n`, 'utf-8')\n fs.renameSync(tempPath, metaPath)\n } catch (error) {\n try {\n if (fs.existsSync(tempPath)) fs.unlinkSync(tempPath)\n } catch {\n // Ignore cleanup failures so callers see the original write/rename error.\n }\n throw error\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0\n}\n\nfunction isRuntimeKind(value: unknown): value is RuntimeKind {\n return value === 'cli' || value === 'desktop' || value === 'mcp' || value === 'unknown'\n}\n","/**\n * Chat session DB migration definitions (platform-agnostic).\n *\n * Extracted from electron/main/database/migrations.ts.\n * Migration scripts use only DatabaseAdapter — no Electron or Node-specific APIs.\n * Version 4 (FTS backfill) requires a tokenizer injected via MigrationDeps.\n */\n\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport type { PathProvider } from '@openchatlab/core'\nimport type { Migration as CoreMigration } from '@openchatlab/core'\nimport { raiseDataDirMinRuntimeVersion, type RuntimeIdentity } from '../data-dir-compat'\n\nexport interface MigrationDeps {\n /** FTS tokenizer — needed by v4 migration for backfilling the FTS index */\n tokenizeForFts?: (content: string) => string | null\n}\n\nexport interface ChatDbCompatibilityRaise {\n migrationVersion: number\n minRuntimeVersion: string\n dataCompatibilityVersion: number\n reason: string\n module: string\n}\n\nexport const CHAT_DB_COMPATIBILITY_RAISES: ChatDbCompatibilityRaise[] = [\n {\n migrationVersion: 6,\n minRuntimeVersion: '0.25.1',\n dataCompatibilityVersion: 1,\n reason: 'segment-schema',\n module: 'chat-db-migration',\n },\n]\n\nexport function raiseChatDbCompatibilityGate(pathProvider: PathProvider, runtime: RuntimeIdentity): void {\n for (const compatibilityRaise of CHAT_DB_COMPATIBILITY_RAISES) {\n raiseDataDirMinRuntimeVersion(pathProvider, {\n minRuntimeVersion: compatibilityRaise.minRuntimeVersion,\n dataCompatibilityVersion: compatibilityRaise.dataCompatibilityVersion,\n reason: compatibilityRaise.reason,\n runtime,\n module: compatibilityRaise.module,\n })\n }\n}\n\n/**\n * Build the chat DB migration list.\n *\n * @param deps Optional dependencies (tokenizer for FTS backfill)\n * @returns Array of migrations compatible with core `runMigrations`\n */\nexport function getChatDbMigrations(deps?: MigrationDeps): CoreMigration[] {\n const hasColumn = (db: DatabaseAdapter, tableName: string, columnName: string): boolean => {\n const tableInfo = db.pragma(`table_info(${tableName})`) as Array<{ name: string }>\n return tableInfo.some((col) => col.name === columnName)\n }\n\n const addColumnIfMissing = (db: DatabaseAdapter, tableName: string, columnName: string, definition: string): void => {\n if (!hasColumn(db, tableName, columnName)) {\n db.exec(`ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${definition}`)\n }\n }\n\n return [\n {\n version: 1,\n description: 'Add owner_id column to meta',\n up: (db: DatabaseAdapter) => {\n const tableInfo = db.pragma('table_info(meta)') as Array<{ name: string }>\n if (!tableInfo.some((col) => col.name === 'owner_id')) {\n db.exec('ALTER TABLE meta ADD COLUMN owner_id TEXT')\n }\n },\n },\n {\n version: 2,\n description: 'Add roles, reply_to_message_id, platform_message_id columns',\n up: (db: DatabaseAdapter) => {\n const memberTableInfo = db.pragma('table_info(member)') as Array<{ name: string }>\n if (!memberTableInfo.some((col) => col.name === 'roles')) {\n db.exec(\"ALTER TABLE member ADD COLUMN roles TEXT DEFAULT '[]'\")\n }\n\n const messageTableInfo = db.pragma('table_info(message)') as Array<{ name: string }>\n\n if (!messageTableInfo.some((col) => col.name === 'reply_to_message_id')) {\n db.exec('ALTER TABLE message ADD COLUMN reply_to_message_id TEXT DEFAULT NULL')\n }\n\n if (!messageTableInfo.some((col) => col.name === 'platform_message_id')) {\n db.exec('ALTER TABLE message ADD COLUMN platform_message_id TEXT DEFAULT NULL')\n }\n\n try {\n db.exec('CREATE INDEX IF NOT EXISTS idx_message_platform_id ON message(platform_message_id)')\n } catch {\n // Index may already exist\n }\n },\n },\n {\n version: 3,\n description: 'Add chat_session and message_context tables',\n up: (db: DatabaseAdapter) => {\n db.exec(`\n CREATE TABLE IF NOT EXISTS chat_session (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n start_ts INTEGER NOT NULL,\n end_ts INTEGER NOT NULL,\n message_count INTEGER DEFAULT 0,\n is_manual INTEGER DEFAULT 0,\n summary TEXT\n )\n `)\n\n try {\n db.exec('CREATE INDEX IF NOT EXISTS idx_session_time ON chat_session(start_ts, end_ts)')\n } catch {\n // Index may already exist\n }\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS message_context (\n message_id INTEGER PRIMARY KEY,\n session_id INTEGER NOT NULL,\n topic_id INTEGER\n )\n `)\n\n try {\n db.exec('CREATE INDEX IF NOT EXISTS idx_context_session ON message_context(session_id)')\n } catch {\n // Index may already exist\n }\n\n const tableInfo = db.pragma('table_info(meta)') as Array<{ name: string }>\n if (!tableInfo.some((col) => col.name === 'session_gap_threshold')) {\n db.exec('ALTER TABLE meta ADD COLUMN session_gap_threshold INTEGER')\n }\n },\n },\n {\n version: 4,\n description: 'Add FTS5 full-text search index',\n up: (db: DatabaseAdapter) => {\n const hasTable = db.prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name='message_fts'\").get()\n if (hasTable) return\n\n db.exec(`\n CREATE VIRTUAL TABLE IF NOT EXISTS message_fts USING fts5(\n content,\n content='',\n content_rowid=id\n )\n `)\n\n const tokenize = deps?.tokenizeForFts\n if (!tokenize) return\n\n const BATCH_SIZE = 5000\n const insertFts = db.prepare('INSERT INTO message_fts(rowid, content) VALUES (?, ?)')\n\n const countRow = db\n .prepare(\"SELECT COUNT(*) as total FROM message WHERE type = 0 AND content IS NOT NULL AND content != ''\")\n .get() as { total: number } | undefined\n\n const total = countRow?.total ?? 0\n let offset = 0\n while (offset < total) {\n const rows = db\n .prepare(\n `SELECT id, content FROM message\n WHERE type = 0 AND content IS NOT NULL AND content != ''\n ORDER BY id ASC LIMIT ? OFFSET ?`\n )\n .all(BATCH_SIZE, offset) as Array<{ id: number; content: string }>\n\n if (rows.length === 0) break\n\n for (const row of rows) {\n const tokens = tokenize(row.content)\n if (tokens) {\n insertFts.run(row.id, tokens)\n }\n }\n\n offset += BATCH_SIZE\n }\n },\n },\n {\n version: 5,\n description: 'Repair legacy member/message columns',\n up: (db: DatabaseAdapter) => {\n addColumnIfMissing(db, 'meta', 'group_id', 'TEXT')\n addColumnIfMissing(db, 'meta', 'group_avatar', 'TEXT')\n addColumnIfMissing(db, 'meta', 'owner_id', 'TEXT')\n addColumnIfMissing(db, 'meta', 'session_gap_threshold', 'INTEGER')\n\n const memberHadName = hasColumn(db, 'member', 'name')\n const memberHadNickname = hasColumn(db, 'member', 'nickname')\n addColumnIfMissing(db, 'member', 'account_name', 'TEXT')\n addColumnIfMissing(db, 'member', 'group_nickname', 'TEXT')\n addColumnIfMissing(db, 'member', 'aliases', \"TEXT DEFAULT '[]'\")\n addColumnIfMissing(db, 'member', 'avatar', 'TEXT')\n addColumnIfMissing(db, 'member', 'roles', \"TEXT DEFAULT '[]'\")\n\n if (memberHadName) {\n db.exec(\"UPDATE member SET account_name = COALESCE(NULLIF(account_name, ''), name)\")\n }\n if (memberHadNickname) {\n db.exec(\"UPDATE member SET group_nickname = COALESCE(NULLIF(group_nickname, ''), nickname)\")\n }\n db.exec(\"UPDATE member SET aliases = COALESCE(aliases, '[]'), roles = COALESCE(roles, '[]')\")\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS member_name_history (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n member_id INTEGER NOT NULL,\n name_type TEXT DEFAULT 'account_name',\n name TEXT NOT NULL,\n start_ts INTEGER NOT NULL,\n end_ts INTEGER,\n FOREIGN KEY(member_id) REFERENCES member(id)\n )\n `)\n addColumnIfMissing(db, 'member_name_history', 'name_type', \"TEXT DEFAULT 'account_name'\")\n addColumnIfMissing(db, 'message', 'sender_account_name', 'TEXT')\n addColumnIfMissing(db, 'message', 'sender_group_nickname', 'TEXT')\n addColumnIfMissing(db, 'message', 'reply_to_message_id', 'TEXT DEFAULT NULL')\n addColumnIfMissing(db, 'message', 'platform_message_id', 'TEXT DEFAULT NULL')\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS chat_session (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n start_ts INTEGER NOT NULL,\n end_ts INTEGER NOT NULL,\n message_count INTEGER DEFAULT 0,\n is_manual INTEGER DEFAULT 0,\n summary TEXT\n )\n `)\n db.exec(`\n CREATE TABLE IF NOT EXISTS message_context (\n message_id INTEGER PRIMARY KEY,\n session_id INTEGER NOT NULL,\n topic_id INTEGER\n )\n `)\n\n db.exec('CREATE INDEX IF NOT EXISTS idx_message_platform_id ON message(platform_message_id)')\n db.exec('CREATE INDEX IF NOT EXISTS idx_member_name_history_member_id ON member_name_history(member_id)')\n db.exec('CREATE INDEX IF NOT EXISTS idx_session_time ON chat_session(start_ts, end_ts)')\n db.exec('CREATE INDEX IF NOT EXISTS idx_context_session ON message_context(session_id)')\n },\n },\n {\n version: 6,\n description: 'Rename chat_session index tables to segment terminology',\n up: (db: DatabaseAdapter) => {\n const hasTable = (tableName: string): boolean => {\n const row = db.prepare(\"SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?\").get(tableName) as\n | Record<string, unknown>\n | undefined\n return !!row\n }\n\n db.exec(`\n DROP INDEX IF EXISTS idx_session_time;\n DROP INDEX IF EXISTS idx_context_session;\n `)\n\n const hasLegacyChatSession = hasTable('chat_session')\n const hasSegment = hasTable('segment')\n\n if (hasLegacyChatSession && !hasSegment) {\n db.exec('ALTER TABLE chat_session RENAME TO segment')\n } else if (hasLegacyChatSession && hasSegment) {\n db.exec(`\n INSERT OR IGNORE INTO segment (id, start_ts, end_ts, message_count, is_manual, summary)\n SELECT id, start_ts, end_ts, message_count, is_manual, summary\n FROM chat_session;\n DROP TABLE chat_session;\n `)\n }\n\n if (hasTable('message_context') && hasColumn(db, 'message_context', 'session_id')) {\n db.exec('ALTER TABLE message_context RENAME COLUMN session_id TO segment_id')\n }\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS segment (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n start_ts INTEGER NOT NULL,\n end_ts INTEGER NOT NULL,\n message_count INTEGER DEFAULT 0,\n is_manual INTEGER DEFAULT 0,\n summary TEXT\n );\n\n CREATE TABLE IF NOT EXISTS message_context (\n message_id INTEGER PRIMARY KEY,\n segment_id INTEGER NOT NULL,\n topic_id INTEGER\n );\n\n CREATE INDEX IF NOT EXISTS idx_segment_time ON segment(start_ts, end_ts);\n CREATE INDEX IF NOT EXISTS idx_context_segment ON message_context(segment_id);\n `)\n },\n },\n {\n version: 7,\n description: 'Repair fully missing segment message contexts',\n up: (db: DatabaseAdapter) => {\n const counts = db\n .prepare(\n `SELECT\n (SELECT COUNT(*) FROM message) AS messageCount,\n (SELECT COUNT(*) FROM segment) AS segmentCount,\n (SELECT COUNT(*) FROM message_context) AS contextCount,\n (SELECT COALESCE(SUM(message_count), 0) FROM segment) AS declaredMessageCount`\n )\n .get() as\n | {\n messageCount: number\n segmentCount: number\n contextCount: number\n declaredMessageCount: number\n }\n | undefined\n\n if (!counts || counts.messageCount === 0 || counts.segmentCount === 0 || counts.contextCount > 0) {\n return\n }\n\n if (counts.declaredMessageCount !== counts.messageCount) {\n throw new Error(\n 'Cannot safely repair missing message_context rows: segment message counts do not match message table'\n )\n }\n\n const invalidRange = db\n .prepare(\n `WITH ordered_segments AS (\n SELECT\n id,\n start_ts,\n end_ts,\n LAG(end_ts) OVER (ORDER BY start_ts, id) AS previousEndTs\n FROM segment\n )\n SELECT COUNT(*) AS count\n FROM ordered_segments\n WHERE start_ts > end_ts OR (previousEndTs IS NOT NULL AND start_ts <= previousEndTs)`\n )\n .get() as { count: number } | undefined\n if ((invalidRange?.count ?? 0) > 0) {\n throw new Error(\n 'Cannot safely repair missing message_context rows: segment time ranges are invalid or overlapping'\n )\n }\n\n const mismatchedSegments = db\n .prepare(\n `SELECT COUNT(*) AS count\n FROM (\n SELECT s.id\n FROM segment s\n LEFT JOIN message m ON m.ts >= s.start_ts AND m.ts <= s.end_ts\n GROUP BY s.id\n HAVING COUNT(m.id) != s.message_count\n )`\n )\n .get() as { count: number } | undefined\n if ((mismatchedSegments?.count ?? 0) > 0) {\n throw new Error(\n 'Cannot safely repair missing message_context rows: messages do not match segment time ranges'\n )\n }\n\n const result = db\n .prepare(\n `INSERT INTO message_context (message_id, segment_id, topic_id)\n SELECT m.id, s.id, NULL\n FROM segment s\n JOIN message m ON m.ts >= s.start_ts AND m.ts <= s.end_ts`\n )\n .run()\n if (result.changes !== counts.messageCount) {\n throw new Error('Cannot safely repair missing message_context rows: rebuilt row count is inconsistent')\n }\n },\n },\n {\n version: 8,\n description: 'Add analysis tool performance indexes',\n up: (db: DatabaseAdapter) => {\n addColumnIfMissing(db, 'message', 'reply_to_message_id', 'TEXT DEFAULT NULL')\n\n db.exec(`\n CREATE INDEX IF NOT EXISTS idx_message_sender_ts ON message(sender_id, ts);\n CREATE INDEX IF NOT EXISTS idx_message_type_ts ON message(type, ts);\n CREATE INDEX IF NOT EXISTS idx_message_reply_to ON message(reply_to_message_id);\n `)\n },\n },\n ]\n}\n","import path from 'node:path'\n\nexport const CONTACTS_DIR_NAME = 'contacts'\nexport const CONTACTS_FACTS_DIR_NAME = 'facts'\n\nexport function getContactsDir(userDataDir: string): string {\n return path.join(userDataDir, CONTACTS_DIR_NAME)\n}\n\nexport function getContactsFactsCacheDir(userDataDir: string): string {\n return path.join(getContactsDir(userDataDir), CONTACTS_FACTS_DIR_NAME)\n}\n","import path from 'node:path'\n\nexport const PEOPLE_DIR_NAME = 'people'\nexport const RELATIONSHIPS_DIR_NAME = 'relationships'\nexport const RELATIONSHIPS_FACTS_DIR_NAME = 'facts'\n\nexport function getPeopleRelationshipsDir(userDataDir: string): string {\n return path.join(userDataDir, PEOPLE_DIR_NAME, RELATIONSHIPS_DIR_NAME)\n}\n\nexport function getPeopleRelationshipsFactsCacheDir(userDataDir: string): string {\n return path.join(getPeopleRelationshipsDir(userDataDir), RELATIONSHIPS_FACTS_DIR_NAME)\n}\n","/**\n * 数据库连接管理器\n *\n * 管理 ChatLab 会话数据库的打开、缓存与关闭。\n * 等效于 electron/main/worker/core/dbCore.ts 中的 dbCache 机制,\n * 但基于 DatabaseAdapter 接口而非直接使用 better-sqlite3。\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport type { DatabaseAdapter, PathProvider } from '@openchatlab/core'\nimport {\n CHAT_DB_TABLES,\n isChatSessionDb,\n runMigrations as coreRunMigrations,\n needsMigration as coreNeedsMigration,\n CURRENT_SCHEMA_VERSION,\n getSchemaVersion,\n} from '@openchatlab/core'\nimport { openBetterSqliteDatabase } from './better-sqlite3-adapter'\nimport { deleteSessionCache } from './cache/session-cache'\nimport { assertDataDirCompatible, type RuntimeIdentity } from './data-dir-compat'\nimport {\n CHAT_DB_COMPATIBILITY_RAISES,\n getChatDbMigrations,\n raiseChatDbCompatibilityGate,\n type MigrationDeps,\n} from './migrations'\nimport { tokenizeForFts } from './nlp/fts-tokenizer'\nimport { getContactsFactsCacheDir } from './services/contacts/paths'\nimport { getPeopleRelationshipsFactsCacheDir } from './services/people/relationships/paths'\n\nfunction createMigrationDeps(overrides?: MigrationDeps): MigrationDeps {\n return {\n tokenizeForFts,\n ...overrides,\n }\n}\n\ninterface DatabaseManagerOptions {\n nativeBinding?: string\n migrationDeps?: MigrationDeps\n runtime?: RuntimeIdentity\n allowMissingRuntimeForTests?: boolean\n}\n\ninterface OpenRawSessionDatabaseOptions {\n readonly?: boolean\n create?: boolean\n initializeChatTables?: boolean\n}\n\nexport class DatabaseManager {\n private cache = new Map<string, DatabaseAdapter>()\n private nativeBinding?: string\n private migrationDeps?: MigrationDeps\n private runtime: RuntimeIdentity | null\n\n constructor(\n private pathProvider: PathProvider,\n options: DatabaseManagerOptions = {}\n ) {\n if (!options.runtime && !options.allowMissingRuntimeForTests) {\n throw new Error('DatabaseManager runtime identity is required for data directory compatibility checks.')\n }\n\n this.nativeBinding = options?.nativeBinding\n this.migrationDeps = createMigrationDeps(options?.migrationDeps)\n this.runtime = options?.runtime ?? null\n }\n\n /**\n * Open a session DB (read-only by default, cached).\n */\n open(sessionId: string, options?: { readonly?: boolean }): DatabaseAdapter | null {\n this.assertCompatible()\n\n if (this.cache.has(sessionId)) {\n return this.cache.get(sessionId)!\n }\n\n const dbPath = this.getDbPath(sessionId)\n if (!fs.existsSync(dbPath)) return null\n\n this.migrateIfNeeded(dbPath)\n this.assertCompatible()\n\n const adapter = openBetterSqliteDatabase(dbPath, {\n readonly: options?.readonly ?? true,\n nativeBinding: this.nativeBinding,\n })\n this.cache.set(sessionId, adapter)\n return adapter\n }\n\n private migrateIfNeeded(dbPath: string): void {\n const readonlyAdapter = openBetterSqliteDatabase(dbPath, {\n readonly: true,\n nativeBinding: this.nativeBinding,\n })\n\n try {\n if (!isChatSessionDb(readonlyAdapter)) return\n this.raiseCompatibilityGateIfNeeded(getSchemaVersion(readonlyAdapter))\n if (!coreNeedsMigration(readonlyAdapter, CURRENT_SCHEMA_VERSION)) return\n } finally {\n readonlyAdapter.close()\n }\n\n const adapter = openBetterSqliteDatabase(dbPath, {\n readonly: false,\n nativeBinding: this.nativeBinding,\n })\n\n try {\n const migrations = getChatDbMigrations(this.migrationDeps)\n this.runMigrations(adapter, migrations)\n } finally {\n adapter.close()\n }\n }\n\n /**\n * Open a session DB in read-write mode and auto-run pending migrations.\n * Falls back to `open(id, { readonly: false })` if migration is unnecessary.\n */\n openWritable(sessionId: string): DatabaseAdapter | null {\n this.assertCompatible()\n\n const existing = this.cache.get(sessionId)\n if (existing && !existing.readonly) return existing\n\n if (existing) {\n existing.close()\n this.cache.delete(sessionId)\n }\n\n const dbPath = this.getDbPath(sessionId)\n if (!fs.existsSync(dbPath)) return null\n\n const adapter = openBetterSqliteDatabase(dbPath, {\n readonly: false,\n nativeBinding: this.nativeBinding,\n })\n\n if (coreNeedsMigration(adapter, CURRENT_SCHEMA_VERSION)) {\n const migrations = getChatDbMigrations(this.migrationDeps)\n this.runMigrations(adapter, migrations)\n this.assertCompatible()\n } else {\n this.raiseCompatibilityGateIfNeeded(getSchemaVersion(adapter))\n }\n\n this.cache.set(sessionId, adapter)\n return adapter\n }\n\n /**\n * 关闭指定会话的数据库连接\n */\n close(sessionId: string): void {\n const adapter = this.cache.get(sessionId)\n if (adapter) {\n adapter.close()\n this.cache.delete(sessionId)\n }\n }\n\n /**\n * 关闭所有数据库连接\n */\n closeAll(): void {\n for (const [id, adapter] of this.cache) {\n adapter.close()\n this.cache.delete(id)\n }\n }\n\n /**\n * 列举数据库目录下的所有聊天会话 ID\n */\n listSessionIds(): string[] {\n this.assertCompatible()\n\n const dbDir = this.pathProvider.getDatabaseDir()\n if (!fs.existsSync(dbDir)) return []\n\n return fs\n .readdirSync(dbDir)\n .filter((f) => f.endsWith('.db'))\n .map((f) => f.replace('.db', ''))\n .filter((id) => {\n const db = this.open(id)\n if (!db) return false\n return isChatSessionDb(db)\n })\n }\n\n /**\n * 获取数据库文件路径\n */\n getDbPath(sessionId: string): string {\n return path.join(this.pathProvider.getDatabaseDir(), `${sessionId}.db`)\n }\n\n openRawSessionDatabase(sessionId: string, options: OpenRawSessionDatabaseOptions = {}): DatabaseAdapter {\n this.assertCompatible()\n\n const dbPath = this.getDbPath(sessionId)\n if (!options.create && !fs.existsSync(dbPath)) {\n throw new Error(`Session database not found: ${sessionId}`)\n }\n\n if (!options.readonly) {\n this.close(sessionId)\n fs.mkdirSync(path.dirname(dbPath), { recursive: true })\n }\n\n const adapter = openBetterSqliteDatabase(dbPath, {\n readonly: options.readonly ?? false,\n nativeBinding: this.nativeBinding,\n })\n\n if (options.initializeChatTables) {\n adapter.exec(CHAT_DB_TABLES)\n }\n\n return adapter\n }\n\n /**\n * 删除一个会话在本地留下的完整文件集合。\n * SQLite WAL 模式会产生 sidecar 文件,前端查询还会写 JSON 缓存;只删主库会让列表和后续同步读到脏状态。\n */\n deleteSessionDatabaseFiles(sessionId: string): boolean {\n this.close(sessionId)\n\n const dbPath = this.getDbPath(sessionId)\n const existed = ['', '-wal', '-shm'].some((suffix) => fs.existsSync(dbPath + suffix))\n for (const suffix of ['', '-wal', '-shm']) {\n try {\n const filePath = dbPath + suffix\n if (fs.existsSync(filePath)) fs.unlinkSync(filePath)\n } catch {\n /* ignore cleanup failures */\n }\n }\n const cacheDir = this.pathProvider.getCacheDir()\n deleteSessionCache(sessionId, cacheDir)\n deleteSessionCache(sessionId, path.join(cacheDir, 'query'))\n deleteSessionCache(sessionId, getContactsFactsCacheDir(this.pathProvider.getUserDataDir()))\n deleteSessionCache(sessionId, getPeopleRelationshipsFactsCacheDir(this.pathProvider.getUserDataDir()))\n return existed\n }\n\n raiseCurrentChatDbCompatibilityGate(): void {\n if (!this.runtime) return\n raiseChatDbCompatibilityGate(this.pathProvider, this.runtime)\n }\n\n private assertCompatible(): void {\n if (!this.runtime) return\n assertDataDirCompatible(this.pathProvider, this.runtime)\n }\n\n private runMigrations(adapter: DatabaseAdapter, migrations: ReturnType<typeof getChatDbMigrations>): void {\n const beforeVersion = getSchemaVersion(adapter)\n const migrated = coreRunMigrations(adapter, migrations)\n if (!migrated || !this.runtime) return\n\n const afterVersion = getSchemaVersion(adapter)\n if (shouldRaiseCompatibilityGate(beforeVersion, afterVersion)) {\n this.raiseCurrentChatDbCompatibilityGate()\n }\n }\n\n private raiseCompatibilityGateIfNeeded(schemaVersion: number): void {\n if (!this.runtime) return\n if (shouldRepairCompatibilityGate(schemaVersion)) {\n this.raiseCurrentChatDbCompatibilityGate()\n }\n }\n}\n\nfunction shouldRaiseCompatibilityGate(beforeVersion: number, afterVersion: number): boolean {\n return CHAT_DB_COMPATIBILITY_RAISES.some(\n (compatibilityRaise) =>\n beforeVersion < compatibilityRaise.migrationVersion && afterVersion >= compatibilityRaise.migrationVersion\n )\n}\n\nfunction shouldRepairCompatibilityGate(schemaVersion: number): boolean {\n return CHAT_DB_COMPATIBILITY_RAISES.some((compatibilityRaise) => schemaVersion >= compatibilityRaise.migrationVersion)\n}\n","/**\n * Analytics result cache (platform-agnostic).\n *\n * Caches expensive analytics / NLP computations (word frequency, catchphrase,\n * activity stats, ...) as JSON under {queryCacheDir}/{sessionId}.cache.json,\n * reusing the generic session-cache infrastructure.\n *\n * Each entry carries a `v` fingerprint derived from the session DB file state.\n * Any write to the chat DB (import, incremental import, member merge/rename,\n * owner change, summary generation) changes the file and thus the fingerprint,\n * forcing a recompute. This makes the cache self-validating even across\n * processes — e.g. when `chatlab start` is serving while a separate\n * `chatlab import` mutates the same database file.\n */\n\nimport * as fs from 'fs'\nimport { getCache, setCache } from './session-cache'\n\ninterface VersionedEntry<T> {\n v: string\n data: T\n}\n\n/**\n * Derive a cheap version fingerprint from the session DB file state.\n *\n * Combines mtime + size of the main DB file and its `-wal` sidecar so that\n * WAL-only writes (before a checkpoint) are detected too. Missing files\n * contribute a placeholder; a fully missing DB yields a stable placeholder\n * version, which is acceptable because the caller only computes once the DB\n * actually exists.\n */\nexport function getDbFileVersion(dbPath: string): string {\n const parts: string[] = []\n for (const p of [dbPath, `${dbPath}-wal`]) {\n try {\n const st = fs.statSync(p)\n parts.push(`${Math.floor(st.mtimeMs)}:${st.size}`)\n } catch {\n parts.push('-')\n }\n }\n return parts.join('|')\n}\n\n/**\n * Cache-first analytics read with version validation.\n *\n * Returns the cached value when the stored fingerprint matches `version`;\n * otherwise runs `compute()`, persists the result tagged with the current\n * version (overwriting any stale entry under the same key), and returns it.\n */\nexport function getOrComputeAnalysisCache<T>(\n sessionId: string,\n key: string,\n queryCacheDir: string,\n version: string,\n compute: () => T\n): T {\n const cached = getCache<VersionedEntry<T>>(sessionId, key, queryCacheDir)\n if (cached && cached.v === version) {\n return cached.data\n }\n const data = compute()\n setCache<VersionedEntry<T>>(sessionId, key, { v: version, data }, queryCacheDir)\n return data\n}\n","import type { PathProvider } from '@openchatlab/core'\n\nexport interface StaticPathProviderSnapshot {\n systemDir: string\n userDataDir: string\n databaseDir: string\n vectorDir: string\n aiDataDir: string\n settingsDir: string\n cacheDir: string\n tempDir: string\n logsDir: string\n downloadsDir: string\n}\n\nexport function snapshotPathProvider(pathProvider: PathProvider): StaticPathProviderSnapshot {\n return {\n systemDir: pathProvider.getSystemDir(),\n userDataDir: pathProvider.getUserDataDir(),\n databaseDir: pathProvider.getDatabaseDir(),\n vectorDir: pathProvider.getVectorDir(),\n aiDataDir: pathProvider.getAiDataDir(),\n settingsDir: pathProvider.getSettingsDir(),\n cacheDir: pathProvider.getCacheDir(),\n tempDir: pathProvider.getTempDir(),\n logsDir: pathProvider.getLogsDir(),\n downloadsDir: pathProvider.getDownloadsDir(),\n }\n}\n\nexport class StaticPathProvider implements PathProvider {\n constructor(private readonly paths: StaticPathProviderSnapshot) {}\n\n getSystemDir(): string {\n return this.paths.systemDir\n }\n\n getUserDataDir(): string {\n return this.paths.userDataDir\n }\n\n getDatabaseDir(): string {\n return this.paths.databaseDir\n }\n\n getVectorDir(): string {\n return this.paths.vectorDir\n }\n\n getAiDataDir(): string {\n return this.paths.aiDataDir\n }\n\n getSettingsDir(): string {\n return this.paths.settingsDir\n }\n\n getCacheDir(): string {\n return this.paths.cacheDir\n }\n\n getTempDir(): string {\n return this.paths.tempDir\n }\n\n getLogsDir(): string {\n return this.paths.logsDir\n }\n\n getDownloadsDir(): string {\n return this.paths.downloadsDir\n }\n}\n","/**\n * DatabaseManager adapter for the shared service layer.\n *\n * Bridges DatabaseManager's connection lifecycle to the SessionRuntimeAdapter\n * interface expected by session/member services.\n */\n\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport type { DatabaseManager } from '../../database-manager'\nimport type { SessionRuntimeAdapter } from './types'\n\nexport function createDatabaseManagerAdapter(dbManager: DatabaseManager): SessionRuntimeAdapter {\n return {\n listSessionIds: () => dbManager.listSessionIds(),\n openReadonly: (sessionId) => dbManager.open(sessionId),\n openWritable: (sessionId) => dbManager.openWritable(sessionId),\n closeSession: (sessionId) => dbManager.close(sessionId),\n getDbPath: (sessionId) => dbManager.getDbPath(sessionId),\n\n deleteSessionFile(sessionId: string): boolean {\n return dbManager.deleteSessionDatabaseFiles(sessionId)\n },\n\n ensureReadonly(sessionId: string): DatabaseAdapter {\n const db = dbManager.open(sessionId)\n if (!db) {\n throw Object.assign(new Error(`Session not found: ${sessionId}`), { statusCode: 404 })\n }\n return db\n },\n\n ensureWritable(sessionId: string): DatabaseAdapter {\n const db = dbManager.openWritable(sessionId)\n if (!db) {\n throw Object.assign(new Error(`Session not found: ${sessionId}`), { statusCode: 404 })\n }\n return db\n },\n }\n}\n","/**\n * @openchatlab/shared-types\n * 平台无关的共享类型定义,三端(Electron / Node 服务 / Web)统一使用\n */\n\n// ==================== 时间筛选 ====================\n\nexport interface TimeFilter {\n startTs?: number\n endTs?: number\n memberId?: number | null\n}\n\n// ==================== 枚举与平台 ====================\n\n/**\n * 消息类型枚举\n *\n * 分类说明:\n * - 基础消息 (0-19): 常见的内容类型\n * - 交互消息 (20-39): 涉及互动的消息类型\n * - 系统消息 (80-89): 系统相关消息\n * - 其他 (99): 未知或无法分类的消息\n */\nexport enum MessageType {\n // ========== 基础消息类型 (0-19) ==========\n TEXT = 0,\n IMAGE = 1,\n VOICE = 2,\n VIDEO = 3,\n FILE = 4,\n EMOJI = 5,\n LINK = 7,\n LOCATION = 8,\n\n // ========== 交互消息类型 (20-39) ==========\n RED_PACKET = 20,\n TRANSFER = 21,\n POKE = 22,\n CALL = 23,\n SHARE = 24,\n REPLY = 25,\n FORWARD = 26,\n CONTACT = 27,\n\n // ========== 系统消息类型 (80-89) ==========\n SYSTEM = 80,\n RECALL = 81,\n\n // ========== 其他 (99) ==========\n OTHER = 99,\n}\n\n/**\n * 聊天平台类型(字符串,允许任意值)\n * 常见平台示例:qq, weixin, discord, whatsapp 等\n * 合并多平台记录时使用 'mixed'\n */\nexport type ChatPlatform = string\n\nexport const KNOWN_PLATFORMS = {\n QQ: 'qq',\n WECHAT: 'weixin',\n DISCORD: 'discord',\n WHATSAPP: 'whatsapp',\n TELEGRAM: 'telegram',\n INSTAGRAM: 'instagram',\n GOOGLE_CHAT: 'google-chat',\n LINE: 'line',\n UNKNOWN: 'unknown',\n} as const\n\n/**\n * 聊天类型枚举\n */\nexport enum ChatType {\n GROUP = 'group',\n PRIVATE = 'private',\n}\n\n// ==================== 成员角色 ====================\n\nexport interface MemberRole {\n id: string\n name?: string\n}\n\nexport const STANDARD_ROLE_IDS = {\n OWNER: 'owner',\n ADMIN: 'admin',\n} as const\n\n// ==================== 标准协议(Parser 输出) ====================\n\nexport interface ParsedMember {\n platformId: string\n accountName: string\n groupNickname?: string\n avatar?: string\n roles?: MemberRole[]\n}\n\nexport interface ParsedMessage {\n platformMessageId?: string\n senderPlatformId: string\n senderAccountName: string\n senderGroupNickname?: string\n timestamp: number\n type: MessageType\n content: string | null\n replyToMessageId?: string\n}\n\n// ==================== Preferences (跨端偏好设置) ====================\n\nexport interface WordFilterScheme {\n id: string\n name: string\n words: string[]\n createdAt: number\n}\n\nexport interface ContextCompressionSettings {\n enabled: boolean\n tokenThresholdPercent: number\n bufferSizePercent: number\n maxToolResultPercent: number\n}\n\nexport type ChartAutoMode = 'explicit' | 'suggest' | 'aggressive'\n\nexport interface AIGlobalSettings {\n maxMessagesPerRequest: number\n exportFormat: 'markdown' | 'txt'\n sqlExportFormat: 'csv' | 'json'\n enableAutoSkill: boolean\n chartAutoMode: ChartAutoMode\n searchContextBefore: number\n searchContextAfter: number\n contextCompression: ContextCompressionSettings\n}\n\nexport interface KeywordTemplate {\n id: string\n name: string\n keywords: string[]\n [key: string]: unknown\n}\n\nexport interface DesensitizeRule {\n id: string\n label: string\n pattern: string\n replacement: string\n enabled: boolean\n builtin: boolean\n locales: string[]\n group?: string\n}\n\nexport interface AIPreprocessConfig {\n dataCleaning: boolean\n mergeConsecutive: boolean\n mergeWindowSeconds: number\n blacklistKeywords: string[]\n denoise: boolean\n desensitize: boolean\n desensitizeRulesSchemaVersion?: number\n desensitizeBuiltinRuleOverrides?: Record<string, boolean>\n desensitizeRules: DesensitizeRule[]\n anonymizeNames: boolean\n}\n\nexport interface FilterHistoryItem {\n id: string\n sessionId: string\n createdAt: number\n name: string\n mode: 'condition' | 'session'\n conditionFilter?: {\n keywords: string[]\n timeRange: { start: number; end: number } | null\n senderIds: number[]\n contextSize: number\n }\n selectedSessionIds?: number[]\n}\n\nexport type OwnerMatchMode = 'platform_id' | 'name'\n\n/**\n * Platform-level owner identity (\"who am I\" on this chat platform).\n * Stored in preferences.json and shared across sessions of the same platform.\n */\nexport interface OwnerProfile {\n platformId: string\n displayName: string\n /** Original (non-normalized) names confirmed by the user; normalization happens at match time. */\n confirmedNames: string[]\n matchMode: OwnerMatchMode\n updatedAt: number\n}\n\nexport type ApplyOwnerProfileReason = 'no_profile' | 'no_match' | 'ambiguous' | 'already_set' | 'missing_session'\n\nexport interface ApplyOwnerProfileResult {\n applied: boolean\n ownerId?: string\n reason?: ApplyOwnerProfileReason\n /** Whether the user chose \"do not remind me\" for this session (UI hint only). */\n dismissed: boolean\n}\n\nexport interface SetOwnerAndApplyProfileResult {\n sessionId: string\n platform: string\n ownerId: string\n /** Other same-platform sessions auto-filled by the updated profile. */\n updatedSessionIds: string[]\n /**\n * The actual owner_id written to each updated session.\n * On name-match platforms the matched member's platformId can differ from\n * ownerId (the source session's platformId), so callers must use this map\n * rather than ownerId when caching the result for updated sessions.\n */\n updatedSessionOwnerIds: Record<string, string>\n}\n\n// ==================== Contacts (cross-session relationship view) ====================\n\nexport type ContactPool = 'friend' | 'non_friend'\n\nexport type ContactFriendSource = 'private' | 'manual'\n\nexport type ContactsCacheStatus = 'fresh' | 'stale' | 'missing'\n\nexport type ContactsTaskStatus = 'idle' | 'running' | 'succeeded' | 'failed' | 'superseded'\n\nexport const CONTACTS_TIME_RANGE_PRESETS = ['1y', '2y', '3y', '5y', 'all'] as const\n\nexport type ContactsTimeRangePreset = (typeof CONTACTS_TIME_RANGE_PRESETS)[number]\n\nexport interface ContactsTimeRangeState {\n preset: ContactsTimeRangePreset\n anchorTs: number | null\n startTs: number | null\n}\n\nexport interface ContactScoreBreakdown {\n privateMessageScore?: number\n privateRegularityScore?: number\n commonGroupScore?: number\n coOccurrenceScore?: number\n replyInteractionScore?: number\n privateMessageCount?: number\n activePrivateMonths?: number\n commonGroupCount?: number\n coOccurrenceCount?: number\n coOccurrenceRawScore?: number\n replyInteractionCount?: number\n repliesFromOwnerToContact?: number\n repliesFromContactToOwner?: number\n}\n\nexport interface ContactSourceSession {\n id: string\n name: string\n platform: ChatPlatform\n type: ChatType\n messageCount?: number\n privateMessageCount?: number\n coOccurrenceCount?: number\n coOccurrenceRawScore?: number\n replyInteractionCount?: number\n repliesFromOwnerToContact?: number\n repliesFromContactToOwner?: number\n lastMessageTs?: number | null\n lastInteractionTs?: number | null\n}\n\nexport interface ContactItem {\n key: string\n platform: ChatPlatform\n platformId: string\n sessionScoped: boolean\n sessionId?: string\n displayName: string\n aliases: string[]\n avatar: string | null\n isFriend: boolean\n pool: ContactPool\n friendSource?: ContactFriendSource\n score: number\n scoreBreakdown: ContactScoreBreakdown\n sourceSessions: ContactSourceSession[]\n searchText: string\n lastInteractionTs: number | null\n}\n\nexport type ContactListItem = Omit<ContactItem, 'sourceSessions' | 'searchText'>\n\nexport interface ContactsDiagnostics {\n privateSessionCount: number\n activePrivateSessionCount: number\n contactsEnabled: boolean\n skippedMissingOwnerSessions: number\n skippedUnresolvedOwnerSessions: number\n skippedAmbiguousPrivateSessions: number\n skippedInvalidPlatformIdMembers: number\n skippedFailedSessions: number\n warnings: string[]\n}\n\nexport interface ContactsCacheState {\n status: ContactsCacheStatus\n computedAt: number | null\n signature?: string\n staleReason?: string\n}\n\nexport interface ContactsTaskState {\n id: string | null\n status: ContactsTaskStatus\n startedAt: number | null\n finishedAt: number | null\n processedSessions: number\n totalSessions: number\n timeRangePreset?: ContactsTimeRangePreset\n currentSessionId?: string\n lastError?: string\n}\n\nexport interface ContactsPagination {\n page: number\n pageSize: number\n total: number\n hasMore: boolean\n}\n\nexport interface ContactsStats {\n friendsTotal: number\n nonFriendsTotal: number\n}\n\nexport interface ContactsListResponse {\n contacts: ContactListItem[]\n diagnostics: ContactsDiagnostics\n cache: ContactsCacheState\n timeRange: ContactsTimeRangeState\n algorithmVersion: string\n pagination: ContactsPagination\n stats: ContactsStats\n task?: ContactsTaskState\n}\n\nexport interface ContactDetailResponse {\n contact: ContactItem | null\n cache: ContactsCacheState\n timeRange: ContactsTimeRangeState\n algorithmVersion: string\n task?: ContactsTaskState\n}\n\nexport type ContactsResponse = ContactsListResponse\n\n// ==================== People Relationships (galaxy graph) ====================\n\nexport type PeopleRelationshipsCacheStatus = ContactsCacheStatus\nexport type PeopleRelationshipsTaskStatus = ContactsTaskStatus\nexport type PeopleRelationshipsGraphScope = 'panorama' | 'close' | 'friends'\n\nexport interface PeopleRelationshipGraphNode {\n key: string\n kind?: 'contact' | 'owner'\n platform: ChatPlatform\n platformId: string\n sessionScoped: boolean\n sessionId?: string\n displayName: string\n aliases: string[]\n avatar: string | null\n pool: ContactPool\n friendSource?: ContactFriendSource\n score: number\n rank: number\n communityId: string\n x: number\n y: number\n size: number\n color: string\n labelVisibility: 0 | 1 | 2\n lastInteractionTs: number | null\n privateMessageCount: number\n groupMessageCount: number\n commonGroupCount: number\n searchText: string\n}\n\nexport interface PeopleRelationshipGraphEdge {\n id: string\n sourceKey: string\n targetKey: string\n weight: number\n coOccurrenceCount: number\n coOccurrenceRawScore: number\n replyInteractionCount: number\n repliesFromSourceToTarget: number\n repliesFromTargetToSource: number\n sourceGroupCount: number\n sourceSessionIds: string[]\n lastInteractionTs: number | null\n visibility: 0 | 1 | 2\n}\n\nexport interface PeopleRelationshipCommunity {\n id: string\n label: string\n size: number\n x: number\n y: number\n color: string\n}\n\nexport interface PeopleRelationshipsGraphData {\n nodes: PeopleRelationshipGraphNode[]\n edges: PeopleRelationshipGraphEdge[]\n communities: PeopleRelationshipCommunity[]\n}\n\nexport interface PeopleRelationshipsDiagnostics {\n processedPrivateSessions: number\n processedGroupSessions: number\n skippedMissingOwnerSessions: number\n skippedUnresolvedOwnerSessions: number\n skippedAmbiguousPrivateSessions: number\n skippedFailedSessions: number\n totalNodes: number\n totalEdges: number\n panoramaIncludedGroupSessions: number\n panoramaExcludedLowValueGroupSessions: number\n panoramaIncludedGroupMembers: number\n panoramaExcludedGroupMembers: number\n panoramaCandidateNodes: number\n panoramaGroupInclusionReasons: Record<string, number>\n coreNodeCount: number\n coreEdgeCount: number\n warnings: string[]\n}\n\nexport interface PeopleRelationshipsCacheState {\n status: PeopleRelationshipsCacheStatus\n computedAt: number | null\n signature?: string\n staleReason?: string\n}\n\nexport interface PeopleRelationshipsTaskState {\n id: string | null\n status: PeopleRelationshipsTaskStatus\n startedAt: number | null\n finishedAt: number | null\n processedSessions: number\n totalSessions: number\n timeRangePreset?: ContactsTimeRangePreset\n currentSessionId?: string\n lastError?: string\n}\n\nexport interface PeopleRelationshipsSearchResult {\n key: string\n kind?: 'contact' | 'owner'\n displayName: string\n platform: ChatPlatform\n platformId: string\n avatar: string | null\n pool: ContactPool\n friendSource?: ContactFriendSource\n score: number\n rank: number\n communityId: string\n inCoreGraph: boolean\n}\n\nexport interface PeopleRelationshipsGraphResponse {\n graph: PeopleRelationshipsGraphData\n searchResults: PeopleRelationshipsSearchResult[]\n diagnostics: PeopleRelationshipsDiagnostics\n cache: PeopleRelationshipsCacheState\n timeRange: ContactsTimeRangeState\n algorithmVersion: string\n task?: PeopleRelationshipsTaskState\n}\n\nexport interface PeopleRelationshipsNeighborhoodResponse {\n contact: PeopleRelationshipGraphNode | null\n graph: PeopleRelationshipsGraphData\n diagnostics: PeopleRelationshipsDiagnostics\n cache: PeopleRelationshipsCacheState\n timeRange: ContactsTimeRangeState\n algorithmVersion: string\n task?: PeopleRelationshipsTaskState\n}\n\nexport interface Preferences {\n pinnedSessionIds: string[]\n aiPreprocessConfig: AIPreprocessConfig\n aiGlobalSettings: AIGlobalSettings\n customKeywordTemplates: KeywordTemplate[]\n deletedPresetTemplateIds: string[]\n wordFilter: {\n schemes: WordFilterScheme[]\n defaultSchemeId: string | null\n sessionSchemeOverrides: Record<string, string | null>\n }\n filterHistory: FilterHistoryItem[]\n /** Per-model thinking level, keyed by `${configId}:${modelId}`. */\n thinkingLevels: Record<string, string>\n /** Platform-level owner identity, keyed by platform (e.g. 'whatsapp'). */\n ownerProfilesByPlatform: Record<string, OwnerProfile>\n /** Sessions where the user chose \"do not remind me\"; suppresses the owner prompt UI only. */\n ownerPromptDismissedSessionIds: string[]\n}\n\nexport interface UiConfig {\n default_session_tab: 'overview' | 'ai-chat'\n session_gap_threshold: number\n summary_strategy?: 'brief' | 'standard'\n}\n"],"mappings":"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AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,IAAM,oBAAoB,oBAAI,IAAI;AAAA;AAAA,EAEvC;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,IAAM,qBAAqB,oBAAI,IAAI;AAAA;AAAA,EAExC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AA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","names":["fs","path","fs","path","fs","path","fs","path","fs","path","fs","path","fs","os","path","members","mergeMembers","fs","path","require","instance","fs","path","fs","path","fs","os","path","ensureDir","fs","path","fs","path","isRecord","hasColumn","hasTable","path","path","fs","path","fs"]}