chatlab-cli 0.37.0 → 0.37.2

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 (156) hide show
  1. package/dist/{chat-command-WBAJBKIE.mjs → chat-command-G2EX7VHR.mjs} +24 -12
  2. package/dist/chat-command-G2EX7VHR.mjs.map +1 -0
  3. package/dist/{chunk-N2WGG3MS.mjs → chunk-E6LCCPIB.mjs} +6 -3
  4. package/dist/chunk-E6LCCPIB.mjs.map +1 -0
  5. package/dist/{chunk-EJBBVLUN.mjs → chunk-EFX66A2O.mjs} +2 -2
  6. package/dist/{chunk-DKIYHA3O.mjs → chunk-EK5QIJLZ.mjs} +20 -23
  7. package/dist/chunk-EK5QIJLZ.mjs.map +1 -0
  8. package/dist/{chunk-5YOHLJFB.mjs → chunk-F4HVCFZL.mjs} +48 -7
  9. package/dist/chunk-F4HVCFZL.mjs.map +1 -0
  10. package/dist/{chunk-SQWN4GTQ.mjs → chunk-FCUUAGGC.mjs} +2 -2
  11. package/dist/{chunk-DQR2YS3I.mjs → chunk-H46CLFWK.mjs} +8 -6
  12. package/dist/{chunk-DQR2YS3I.mjs.map → chunk-H46CLFWK.mjs.map} +1 -1
  13. package/dist/{chunk-4J4MA3RC.mjs → chunk-HBAQPF6X.mjs} +2180 -1941
  14. package/dist/chunk-HBAQPF6X.mjs.map +1 -0
  15. package/dist/{chunk-RXMSZIYD.mjs → chunk-HMVM7VCA.mjs} +3 -3
  16. package/dist/{chunk-FM3GXNEO.mjs → chunk-L6RJIVLS.mjs} +2 -2
  17. package/dist/{chunk-MSMDLDPB.mjs → chunk-T5H56AZV.mjs} +16 -8
  18. package/dist/chunk-T5H56AZV.mjs.map +1 -0
  19. package/dist/{chunk-2TV7M4CC.mjs → chunk-XQ6Z2KNN.mjs} +2 -2
  20. package/dist/{chunk-CR2MU7LN.mjs → chunk-YI3EQYEG.mjs} +12 -12
  21. package/dist/cli.mjs +9 -9
  22. package/dist/contacts-worker.mjs +2 -2
  23. package/dist/global-insight-worker.mjs +1 -1
  24. package/dist/{http-X6OEOOSH.mjs → http-5BRVWAJR.mjs} +12 -12
  25. package/dist/{import-WBJ6YIQ7.mjs → import-IAECQDKC.mjs} +7 -7
  26. package/dist/index.mjs +12 -12
  27. package/dist/{mcp-FR6OKWKL.mjs → mcp-XXSQJBGU.mjs} +8 -8
  28. package/dist/people-relationships-worker.mjs +2 -2
  29. package/dist/semantic-index-worker.mjs +1 -1
  30. package/dist/{src-WE5HNN2O.mjs → src-4HLKMQ7S.mjs} +6 -6
  31. package/dist/{stream-import-YYQVZDGQ.mjs → stream-import-PUG42I36.mjs} +7 -7
  32. package/dist/{worker-runtime-YGB5ZDE4.mjs → worker-runtime-X5563TVT.mjs} +5 -5
  33. package/dist-cli-web/assets/{AISettingsTab-Co2sM1iY.js → AISettingsTab-m_r2fFEj.js} +1 -1
  34. package/dist-cli-web/assets/{AboutTab-DQ8r6mYE.js → AboutTab-J65UqIuk.js} +1 -1
  35. package/dist-cli-web/assets/{Alert-DU1tLAAg.js → Alert-qBIGn_CW.js} +1 -1
  36. package/dist-cli-web/assets/{ApiSettingsTab-D5g5b79L.js → ApiSettingsTab-CxWRJI9j.js} +1 -1
  37. package/dist-cli-web/assets/{BatchManageTab-BFUyVaGU.js → BatchManageTab-BINNVwm1.js} +1 -1
  38. package/dist-cli-web/assets/{BufferResource-C-RfYwQW.js → BufferResource-C-jLPrHv.js} +1 -1
  39. package/dist-cli-web/assets/{CanvasRenderer-Dz7JAMrR.js → CanvasRenderer-kDLW8GSt.js} +1 -1
  40. package/dist-cli-web/assets/{CardDecoration-D28DI1G9.js → CardDecoration-CnYxleQM.js} +1 -1
  41. package/dist-cli-web/assets/{ChatRecordDrawer-Cph819yY.js → ChatRecordDrawer-CMg3FEWH.js} +1 -1
  42. package/dist-cli-web/assets/{ChatRecordWorkspace-BAfERdPc.js → ChatRecordWorkspace-AfFIMUbZ.js} +1 -1
  43. package/dist-cli-web/assets/{Checkbox-B8uqt13j.js → Checkbox-D-hAv15R.js} +1 -1
  44. package/dist-cli-web/assets/{DataDirCleanupNotice-3sGBE5vU.js → DataDirCleanupNotice-P80h2p4l.js} +1 -1
  45. package/dist-cli-web/assets/{DatePicker.vue_vue_type_script_setup_true_lang-CQuub8-f.js → DatePicker.vue_vue_type_script_setup_true_lang-CSp691Uv.js} +1 -1
  46. package/dist-cli-web/assets/{DebugToolsPanel-Cwkr0BOY.js → DebugToolsPanel-BK2VbKB0.js} +1 -1
  47. package/dist-cli-web/assets/{EChart.vue_vue_type_script_setup_true_lang-BuLujQCF.js → EChart.vue_vue_type_script_setup_true_lang-BcE0mE_N.js} +1 -1
  48. package/dist-cli-web/assets/{EChartBar.vue_vue_type_script_setup_true_lang-DuA5xbcC.js → EChartBar.vue_vue_type_script_setup_true_lang-BypaV5wc.js} +1 -1
  49. package/dist-cli-web/assets/{EChartLine.vue_vue_type_script_setup_true_lang-CvmMEfYq.js → EChartLine.vue_vue_type_script_setup_true_lang-D8SyOfWC.js} +1 -1
  50. package/dist-cli-web/assets/{EChartWordcloud-CHVtyPR7.js → EChartWordcloud-0rihhg03.js} +1 -1
  51. package/dist-cli-web/assets/{FileDropZone.vue_vue_type_script_setup_true_lang-BKPy9Qv-.js → FileDropZone.vue_vue_type_script_setup_true_lang-Dyuzh7Ex.js} +1 -1
  52. package/dist-cli-web/assets/{Filter-CMfRHIoK.js → Filter-rD_jWpbO.js} +1 -1
  53. package/dist-cli-web/assets/{GlobalTaskBar-Jz6MASCu.js → GlobalTaskBar-sXQuGvF-.js} +1 -1
  54. package/dist-cli-web/assets/{GroupRelationshipGalaxyView-Ch4obfEq.js → GroupRelationshipGalaxyView-BN1L9DFf.js} +1 -1
  55. package/dist-cli-web/assets/{Label-CuryuymO.js → Label-Ce4wl_Ph.js} +1 -1
  56. package/dist-cli-web/assets/{LoadingState.vue_vue_type_script_setup_true_lang-CoeQsaiL.js → LoadingState.vue_vue_type_script_setup_true_lang-DmVfwvnD.js} +1 -1
  57. package/dist-cli-web/assets/{MessageExportModal-JqGZ-iLr.js → MessageExportModal-C2IUNPhg.js} +1 -1
  58. package/dist-cli-web/assets/{PageHeader-D4up77TB.js → PageHeader-D5BcYKBs.js} +1 -1
  59. package/dist-cli-web/assets/{PageTabs.vue_vue_type_script_setup_true_lang-DYQRFk9s.js → PageTabs.vue_vue_type_script_setup_true_lang-DHly2-fe.js} +1 -1
  60. package/dist-cli-web/assets/{RelationshipGalaxyCanvas-Cn7Eta5U.js → RelationshipGalaxyCanvas-CHPqtqQz.js} +3 -3
  61. package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-LlaX57z6.js → RelationshipGalaxyThreeCanvas-qR4S-9n7.js} +1 -1
  62. package/dist-cli-web/assets/{RenderTargetSystem-JhA2PUFi.js → RenderTargetSystem-BaIFulUj.js} +1 -1
  63. package/dist-cli-web/assets/{RovingFocusItem-DHaiTb4j.js → RovingFocusItem-Bqgmzo42.js} +1 -1
  64. package/dist-cli-web/assets/{ScreenCaptureModal-Bxhyt5g1.js → ScreenCaptureModal-sJqB_hrK.js} +1 -1
  65. package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-BPt98e6z.js +1 -0
  66. package/dist-cli-web/assets/{Select-Beeqv0wU.js → Select-BAnN4HMs.js} +1 -1
  67. package/dist-cli-web/assets/SettingsModal-C4ANMlJJ.js +2 -0
  68. package/dist-cli-web/assets/{StorageTab-B7GHadia.js → StorageTab-BgNQliz4.js} +1 -1
  69. package/dist-cli-web/assets/{Switch-u4zlvUMa.js → Switch-DddEvDJa.js} +1 -1
  70. package/dist-cli-web/assets/{Tabs-B7-Koq3x.js → Tabs-DwjL-ueH.js} +1 -1
  71. package/dist-cli-web/assets/{Tabs-RWKHwtfM.js → Tabs-xVtuBuO0.js} +1 -1
  72. package/dist-cli-web/assets/{ThemeCard.vue_vue_type_script_setup_true_lang-ndsa3-aT.js → ThemeCard.vue_vue_type_script_setup_true_lang-CNCVa86b.js} +1 -1
  73. package/dist-cli-web/assets/{TimeSelect.vue_vue_type_script_setup_true_lang-DKa1flT-.js → TimeSelect.vue_vue_type_script_setup_true_lang-BehUIWki.js} +1 -1
  74. package/dist-cli-web/assets/{VisuallyHiddenInput-DGDqcvUy.js → VisuallyHiddenInput-ST6JNfSG.js} +1 -1
  75. package/dist-cli-web/assets/{WebGLRenderer-ifvgA1fe.js → WebGLRenderer-DRraqD4Y.js} +1 -1
  76. package/dist-cli-web/assets/{WebGPURenderer-DwzmuWF0.js → WebGPURenderer-CnzaYARg.js} +1 -1
  77. package/dist-cli-web/assets/aiChat-CeJy-gG-.js +8 -0
  78. package/dist-cli-web/assets/{base-BWDX-164.js → base-BHbtUviQ.js} +1 -1
  79. package/dist-cli-web/assets/{browserAll-CSIIETeE.js → browserAll-DYxkitnx.js} +1 -1
  80. package/dist-cli-web/assets/{chatMessages--r0obAVw.css → chatMessages-BGRbSlvD.css} +1 -1
  81. package/dist-cli-web/assets/{chatMessages-Zywjea1n.js → chatMessages-Dj1u-tFB.js} +20 -20
  82. package/dist-cli-web/assets/{cli-web-BRY49NWe.js → cli-web-BtH8nfjA.js} +1 -1
  83. package/dist-cli-web/assets/{fetch-DNSDHSqQ.js → fetch-BJ82mWvv.js} +1 -1
  84. package/dist-cli-web/assets/{fetch-D3nLtFBi.js → fetch-C1Tm66i9.js} +1 -1
  85. package/dist-cli-web/assets/{fetch-jx1VdM9j.js → fetch-CAEyoWQA.js} +1 -1
  86. package/dist-cli-web/assets/{fetch-qBaUXqFt.js → fetch-CGnWNSsG.js} +1 -1
  87. package/dist-cli-web/assets/{fetch-mYSeL4Ki.js → fetch-D7hfFyoG.js} +1 -1
  88. package/dist-cli-web/assets/{fetch-BiXab0IF.js → fetch-DNmRaRxX.js} +1 -1
  89. package/dist-cli-web/assets/{fetch-wzv71PYy.js → fetch-DogRrvT6.js} +1 -1
  90. package/dist-cli-web/assets/fetch-JEEBqR_7.js +1 -0
  91. package/dist-cli-web/assets/{fetch-r6mhEGrP.js → fetch-T5Gi1TUZ.js} +1 -1
  92. package/dist-cli-web/assets/{fetch-DeHqF6yL.js → fetch-U9QyidNZ.js} +1 -1
  93. package/dist-cli-web/assets/{fetch-Co2vLmiE.js → fetch-ejEMWPM2.js} +1 -1
  94. package/dist-cli-web/assets/{fetch-Bf4cqeM3.js → fetch-emZXHmoE.js} +1 -1
  95. package/dist-cli-web/assets/{host-1emQf9_4.js → host-BUvwCvZR.js} +1 -1
  96. package/dist-cli-web/assets/{host-jXJHyyOX.js → host-VTWF81F5.js} +1 -1
  97. package/dist-cli-web/assets/{index-CYc_VLgK.js → index-B3MCGDwx.js} +1 -1
  98. package/dist-cli-web/assets/index-BI2Rb_L3.js +2 -0
  99. package/dist-cli-web/assets/{index-BOcFyPCa.js → index-BLYSew5C.js} +1 -1
  100. package/dist-cli-web/assets/index-Bs7gk1gf.css +1 -0
  101. package/dist-cli-web/assets/{index-BgelKQTW.css → index-C75W1B5J.css} +1 -1
  102. package/dist-cli-web/assets/{index-DOqbUvhA.js → index-CVRBBnQw.js} +1 -1
  103. package/dist-cli-web/assets/index-Cza8BzZE.js +137 -0
  104. package/dist-cli-web/assets/{index-DnpnIyle.js → index-D9C3dyV8.js} +33 -33
  105. package/dist-cli-web/assets/{index-LV9LfFNu.js → index-DDt7Tfda.js} +1 -1
  106. package/dist-cli-web/assets/{index-B4mJkX_d.js → index-DQFcmWSa.js} +1 -1
  107. package/dist-cli-web/assets/{index-slDUxib2.js → index-DUBl6aBF.js} +1 -1
  108. package/dist-cli-web/assets/{index-B7biznha.js → index-DZK9Njvy.js} +1 -1
  109. package/dist-cli-web/assets/{index-Cw0IhKZ6.js → index-HjrJ3DXO.js} +1 -1
  110. package/dist-cli-web/assets/index-KpnLSzzX.js +64 -0
  111. package/dist-cli-web/assets/{index-BQ463TS7.js → index-e6fOnhNS.js} +1 -1
  112. package/dist-cli-web/assets/{index-ClHMs0-a.js → index-iEQnKg2R.js} +2 -2
  113. package/dist-cli-web/assets/{people-page-header-Cx5r9Bk2.js → people-page-header-DWXwFJ8M.js} +1 -1
  114. package/dist-cli-web/assets/{prompt-BggEDxxi.js → prompt-Cepfb-5-.js} +1 -1
  115. package/dist-cli-web/assets/{semantic-index-config-builder-1sO0aEc1.js → semantic-index-config-builder-hTY6QyCe.js} +1 -1
  116. package/dist-cli-web/assets/service-BjE0YJaM.js +1 -0
  117. package/dist-cli-web/assets/service-DQxttlJp.js +1 -0
  118. package/dist-cli-web/assets/{service-Bvq1fRkC.js → service-DwCmXFtI.js} +1 -1
  119. package/dist-cli-web/assets/useFormControl-CJyJY8AK.js +1 -0
  120. package/dist-cli-web/assets/useInsightTabAnalytics-CplGbsbe.js +75 -0
  121. package/dist-cli-web/assets/{ActionToolsPanel-CmYpw8Mt.css → useInsightTabAnalytics-DDNXXH8v.css} +1 -1
  122. package/dist-cli-web/assets/{useTable-DC7oxmBl.js → useTable-Dmpbk7fZ.js} +1 -1
  123. package/dist-cli-web/assets/{useToast-CCPBE-Sy.js → useToast-Bmgpd3sl.js} +1 -1
  124. package/dist-cli-web/assets/{webworkerAll-FCJk6reG.js → webworkerAll-DkPWEyVL.js} +1 -1
  125. package/dist-cli-web/index.html +1 -1
  126. package/package.json +3 -3
  127. package/dist/chat-command-WBAJBKIE.mjs.map +0 -1
  128. package/dist/chunk-4J4MA3RC.mjs.map +0 -1
  129. package/dist/chunk-5YOHLJFB.mjs.map +0 -1
  130. package/dist/chunk-DKIYHA3O.mjs.map +0 -1
  131. package/dist/chunk-MSMDLDPB.mjs.map +0 -1
  132. package/dist/chunk-N2WGG3MS.mjs.map +0 -1
  133. package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang--RPlmfIu.js +0 -75
  134. package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-BlAje3Tf.js +0 -1
  135. package/dist-cli-web/assets/SettingsModal-DV8yDBJs.js +0 -2
  136. package/dist-cli-web/assets/aiChat-BY4C3UQe.js +0 -8
  137. package/dist-cli-web/assets/fetch-C8CcoTWY.js +0 -1
  138. package/dist-cli-web/assets/index-2dJCozDq.js +0 -64
  139. package/dist-cli-web/assets/index-6ejRbH3h.js +0 -2
  140. package/dist-cli-web/assets/index-CIIyg_o9.css +0 -1
  141. package/dist-cli-web/assets/index-kePiJ3ca.js +0 -137
  142. package/dist-cli-web/assets/service-DkPheDt8.js +0 -1
  143. package/dist-cli-web/assets/service-DvI8O_7K.js +0 -1
  144. package/dist-cli-web/assets/useFormControl-B0aShtLp.js +0 -1
  145. /package/dist/{chunk-EJBBVLUN.mjs.map → chunk-EFX66A2O.mjs.map} +0 -0
  146. /package/dist/{chunk-SQWN4GTQ.mjs.map → chunk-FCUUAGGC.mjs.map} +0 -0
  147. /package/dist/{chunk-RXMSZIYD.mjs.map → chunk-HMVM7VCA.mjs.map} +0 -0
  148. /package/dist/{chunk-FM3GXNEO.mjs.map → chunk-L6RJIVLS.mjs.map} +0 -0
  149. /package/dist/{chunk-2TV7M4CC.mjs.map → chunk-XQ6Z2KNN.mjs.map} +0 -0
  150. /package/dist/{chunk-CR2MU7LN.mjs.map → chunk-YI3EQYEG.mjs.map} +0 -0
  151. /package/dist/{http-X6OEOOSH.mjs.map → http-5BRVWAJR.mjs.map} +0 -0
  152. /package/dist/{import-WBJ6YIQ7.mjs.map → import-IAECQDKC.mjs.map} +0 -0
  153. /package/dist/{mcp-FR6OKWKL.mjs.map → mcp-XXSQJBGU.mjs.map} +0 -0
  154. /package/dist/{src-WE5HNN2O.mjs.map → src-4HLKMQ7S.mjs.map} +0 -0
  155. /package/dist/{stream-import-YYQVZDGQ.mjs.map → stream-import-PUG42I36.mjs.map} +0 -0
  156. /package/dist/{worker-runtime-YGB5ZDE4.mjs.map → worker-runtime-X5563TVT.mjs.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/ai/logger.ts","../src/ai/manager-factory.ts","../src/ai/tool-adapter.ts","../src/ai/assistant-loader.ts","../src/ai/agent.ts","../src/ai/cross-chat-tool-adapter.ts","../src/ai/agent-stream-runner.ts"],"sourcesContent":["/**\n * Server 端 AI 日志\n *\n * 复用 @openchatlab/node-runtime 的 AiLogger,\n * 在 startHttpServer() 中通过 initServerAiLogger() 初始化。\n */\n\nimport { AiLogger } from '@openchatlab/node-runtime'\n\nlet logger: AiLogger | null = null\n\nexport function initServerAiLogger(logsDir: string): AiLogger {\n if (!logger) {\n logger = new AiLogger(logsDir)\n }\n return logger\n}\n\nexport function getServerAiLogger(): AiLogger | null {\n return logger\n}\n\nexport function closeServerAiLogger(): void {\n if (logger) {\n logger.close()\n logger = null\n }\n}\n","/**\n * Factory functions for creating AssistantManager and SkillManagerCore instances.\n *\n * Server-side equivalent of the Electron adapter in electron/main/ai/assistant/manager.ts.\n * Uses Node.js fs directly (no Electron dependency).\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { createHash, randomUUID } from 'crypto'\nimport {\n appLogger,\n AssistantManager,\n DEFAULT_GENERAL_ASSISTANT_RAW_CONFIGS,\n LEGACY_GENERAL_ASSISTANT_DIGESTS,\n SkillManagerCore,\n type AssistantManagerFs,\n type SkillManagerFs,\n} from '@openchatlab/node-runtime'\n\nconst nodeFs: AssistantManagerFs & SkillManagerFs = {\n ensureDir(dir: string) {\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })\n },\n listFiles(dir: string, ext: string) {\n if (!fs.existsSync(dir)) return []\n return fs.readdirSync(dir).filter((f) => f.endsWith(ext))\n },\n readFile(filePath: string) {\n return fs.readFileSync(filePath, 'utf-8')\n },\n writeFile(filePath: string, content: string) {\n fs.writeFileSync(filePath, content, 'utf-8')\n },\n deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) fs.unlinkSync(filePath)\n },\n fileExists(filePath: string) {\n return fs.existsSync(filePath)\n },\n joinPath(...parts: string[]) {\n return path.join(...parts)\n },\n}\n\nconst managerCache = new Map<string, { assistant: AssistantManager; skill: SkillManagerCore }>()\n\nexport function getAssistantManager(aiDataDir: string): AssistantManager {\n let cached = managerCache.get(aiDataDir)\n if (!cached) {\n cached = {\n assistant: new AssistantManager({\n fs: nodeFs,\n assistantsDir: path.join(aiDataDir, 'assistants'),\n builtinRawConfigs: DEFAULT_GENERAL_ASSISTANT_RAW_CONFIGS,\n contentHash: (content: string) => createHash('sha256').update(content).digest('hex'),\n legacyBuiltinDigests: LEGACY_GENERAL_ASSISTANT_DIGESTS,\n generateId: () => `custom_${randomUUID().replace(/-/g, '').slice(0, 12)}`,\n logger: appLogger,\n }),\n skill: new SkillManagerCore({\n fs: nodeFs,\n skillsDir: path.join(aiDataDir, 'skills'),\n builtinRawSkills: [],\n contentHash: (content: string) => createHash('md5').update(content).digest('hex'),\n }),\n }\n managerCache.set(aiDataDir, cached)\n }\n return cached.assistant\n}\n\nexport function getSkillManagerCore(aiDataDir: string): SkillManagerCore {\n let cached = managerCache.get(aiDataDir)\n if (!cached) {\n getAssistantManager(aiDataDir)\n cached = managerCache.get(aiDataDir)!\n }\n return cached.skill\n}\n","/**\n * 工具适配层\n *\n * 将 @openchatlab/tools 的 ToolDefinition 适配为 @earendil-works/pi-agent-core 的 AgentTool 格式。\n * 消息类工具返回 rawMessages 时自动执行预处理管道(清洗、去噪、脱敏、截断、格式化)。\n */\n\nimport type {\n ToolDefinition,\n ToolExecutionContext,\n SemanticSearchToolService,\n RawMessage,\n ToolTimeRange,\n} from '@openchatlab/tools'\nimport { CoreDataProvider, executeToolForAgent, toAgentToolParameters } from '@openchatlab/tools'\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport {\n applyPreprocessingPipeline,\n batchSegmentWithFrequency,\n preprocessMessages,\n type AgentTool,\n type PreprocessableMessage,\n type PreprocessConfig,\n type TruncationStrategy,\n createChartSchemaGateState,\n wrapWithChartSchemaGate,\n} from '@openchatlab/node-runtime'\nimport { getServerAiLogger } from './logger'\n\nconst DEFAULT_MAX_TOOL_RESULT_TOKENS = 8000\n\nconst TOOL_TRUNCATION_STRATEGY: Record<string, TruncationStrategy> = {\n search_messages: 'keep_first',\n deep_search_messages: 'keep_first',\n get_recent_messages: 'keep_last',\n get_message_context: 'keep_last',\n get_segment_messages: 'keep_last',\n get_conversation_between: 'keep_last',\n}\n\nexport interface ServerToolContext {\n db: DatabaseAdapter\n sessionId: string\n locale?: string\n /** 语义检索窄接口(仅当前会话可检索时由 runner 注入) */\n semanticIndexService?: SemanticSearchToolService\n /** 预处理配置(脱敏/匿名化/清洗) */\n preprocessConfig?: Record<string, unknown>\n /** 当前用户平台 id(昵称匿名化 owner 识别) */\n ownerPlatformId?: string\n /** 会话时间范围筛选(来自请求参数,供证据类工具继承) */\n timeFilter?: ToolTimeRange\n /** 关键词搜索消息条数上限 */\n maxMessagesLimit?: number\n}\n\nexport interface AdaptToolsOptions {\n maxToolResultTokens?: number\n}\n\nexport function adaptToolsForAgent(\n tools: ToolDefinition[],\n getContext: () => ServerToolContext,\n options?: AdaptToolsOptions\n): AgentTool<any, any>[] {\n const tokenBudget = options?.maxToolResultTokens ?? DEFAULT_MAX_TOOL_RESULT_TOKENS\n const chartSchemaGateState = createChartSchemaGateState()\n\n return tools.map((tool) =>\n wrapWithChartSchemaGate(\n {\n name: tool.name,\n label: tool.name,\n description: tool.description,\n parameters: toAgentToolParameters(tool.inputSchema) as any,\n executionMode: tool.executionMode,\n async execute(_toolCallId: string, params: unknown, signal, onUpdate) {\n const ctx = getContext()\n const execCtx: ToolExecutionContext = {\n db: ctx.db,\n dataProvider: new CoreDataProvider(ctx.db),\n sessionId: ctx.sessionId,\n locale: ctx.locale,\n abortSignal: signal,\n reportProgress: (progress) => onUpdate?.({ content: [], details: { progress } }),\n semanticIndexService: ctx.semanticIndexService,\n preprocessConfig: ctx.preprocessConfig,\n ownerPlatformId: ctx.ownerPlatformId,\n timeFilter: ctx.timeFilter,\n maxMessagesLimit: ctx.maxMessagesLimit,\n maxToolResultTokens: tokenBudget,\n segmentText: (texts, locale, options) => batchSegmentWithFrequency(texts, locale as any, options as any),\n desensitizeMessages: (messages: RawMessage[]): RawMessage[] =>\n preprocessMessages(\n messages as PreprocessableMessage[],\n ctx.preprocessConfig as PreprocessConfig | undefined\n ) as RawMessage[],\n }\n const result = await executeToolForAgent(tool, params, execCtx)\n const details = result.details as Record<string, unknown> | null\n if (!Array.isArray(details?.rawMessages)) return result\n\n const { rawMessages, chart, charts, ...extraDetails } = details\n const preprocessCfg = ctx.preprocessConfig as PreprocessConfig | undefined\n const pipelineResult = applyPreprocessingPipeline({\n rawMessages: rawMessages as PreprocessableMessage[],\n preprocessConfig: preprocessCfg,\n anonymizeNames: preprocessCfg?.anonymizeNames ?? false,\n ownerPlatformId: ctx.ownerPlatformId,\n locale: ctx.locale,\n maxToolResultTokens: tokenBudget,\n truncationStrategy: TOOL_TRUNCATION_STRATEGY[tool.name] ?? 'keep_last',\n extraDetails,\n logger: getServerAiLogger() ?? undefined,\n })\n const chartDetails = { ...(chart ? { chart } : {}), ...(charts ? { charts } : {}) }\n return {\n content: [{ type: 'text', text: pipelineResult.text }],\n details: Object.keys(chartDetails).length > 0 ? chartDetails : null,\n }\n },\n },\n chartSchemaGateState\n )\n )\n}\n","/**\n * 助手配置加载器\n *\n * 通过共享 AssistantManager 加载配置,确保 standalone CLI 在首次使用时\n * 也会创建默认助手,而不是绕过初始化直接读取磁盘。\n */\n\nimport type { AssistantConfig } from '@openchatlab/node-runtime'\nimport { getAssistantManager } from './manager-factory'\n\nexport function loadAssistantConfig(aiDataDir: string, assistantId: string): AssistantConfig | null {\n return getAssistantManager(aiDataDir).getAssistantConfig(assistantId)\n}\n","/**\n * 服务端 Agent\n *\n * 使用 @openchatlab/node-runtime 的 runAgentCore 编排对话流程,\n * 通过 AgentEventHandler 输出与 Electron 端一致的流式事件。\n */\n\nimport {\n DEFAULT_MAX_TOOL_ROUNDS,\n DEFAULT_CONTEXT_COMPRESSION_CONFIG,\n buildPiModel,\n buildPlanGuidance,\n createAnalysisPlanner,\n createLlmRouteDecider,\n createPlanContentBlock,\n decideRequestRoute,\n runAgentCore,\n checkAndCompress,\n buildSystemPrompt,\n createAiTranslate,\n createCompressionLlmAdapter,\n AgentEventHandler,\n formatAIError,\n shouldUseChartCapabilityForMessage,\n getChartPlannerCapabilityForMessage,\n initTokenizer,\n type AgentStreamChunk,\n type PiMessage,\n type SimpleHistoryMessage,\n type AIChatManager,\n type AgentTool,\n type DataSnapshot,\n type AIServiceConfig,\n type OwnerInfo,\n type MentionedMember,\n} from '@openchatlab/node-runtime'\nimport type { ChartAutoMode } from '@openchatlab/shared-types'\n\nimport { getServerAiLogger } from './logger'\n\nexport type { AgentStreamChunk }\n\nexport interface RunAgentOptions {\n userMessage: string\n aiChatId: string\n historyLeafMessageId?: string | null\n chatType?: 'group' | 'private'\n locale?: string\n assistantSystemPrompt?: string\n skillMenu?: string | null\n skillDef?: { name: string; prompt: string }\n tools?: AgentTool[]\n llmConfig: AIServiceConfig | null\n aiChatManager: AIChatManager\n onEvent: (event: AgentStreamChunk) => void\n abortSignal?: AbortSignal\n ownerInfo?: OwnerInfo\n mentionedMembers?: MentionedMember[]\n dataSnapshot?: DataSnapshot\n thinkingLevel?: string\n chartAutoMode?: ChartAutoMode\n}\n\nexport async function runServerAgent(options: RunAgentOptions): Promise<void> {\n const {\n userMessage,\n aiChatId,\n historyLeafMessageId,\n chatType = 'group',\n locale = 'zh-CN',\n assistantSystemPrompt,\n skillMenu,\n skillDef,\n tools = [],\n llmConfig,\n aiChatManager,\n onEvent,\n abortSignal,\n ownerInfo,\n mentionedMembers,\n dataSnapshot,\n thinkingLevel,\n chartAutoMode = 'suggest',\n } = options\n\n const aiLogger = getServerAiLogger()\n\n // 确保 tokenizer rank 表已加载(compression + agent 路径均依赖)\n await initTokenizer()\n\n if (!llmConfig) {\n onEvent({ type: 'error', error: { name: 'ConfigError', message: 'LLM service not configured' } })\n onEvent({ type: 'done', isFinished: true })\n return\n }\n\n const piModel = buildPiModel(llmConfig)\n const t = createAiTranslate(locale)\n\n let skillCtx: { skillDef?: { name: string; prompt: string }; skillMenu?: string } | undefined\n if (skillDef) {\n skillCtx = { skillDef }\n } else if (skillMenu) {\n skillCtx = { skillMenu }\n }\n\n const systemPrompt = buildSystemPrompt({\n t,\n chatType,\n assistantSystemPrompt,\n ownerInfo,\n locale,\n skillCtx,\n mentionedMembers,\n dataSnapshot,\n })\n\n const handler = new AgentEventHandler({\n onChunk: onEvent,\n context: {},\n systemPrompt,\n })\n\n if (historyLeafMessageId === undefined) {\n const llmAdapter = createCompressionLlmAdapter({\n piModel,\n apiKey: llmConfig.apiKey,\n onCompressing: () => handler.emitStatus('compressing', []),\n })\n const compressionResult = await checkAndCompress(\n aiChatId,\n DEFAULT_CONTEXT_COMPRESSION_CONFIG,\n systemPrompt,\n llmAdapter,\n aiChatManager,\n aiLogger ?? undefined\n )\n if (compressionResult.compressed) {\n onEvent({\n type: 'compression_done',\n compressionResult: {\n summaryContent: compressionResult.summaryContent ?? '',\n tokensBefore: compressionResult.tokensBefore ?? 0,\n tokensAfter: compressionResult.tokensAfter ?? 0,\n timestamp: Date.now(),\n },\n })\n }\n } else {\n aiLogger?.info?.('Compression', 'Skipping compression for edited branch request', {\n aiChatId,\n historyLeafMessageId,\n })\n }\n\n if (abortSignal?.aborted) {\n handler.emitStatus('aborted', [], { force: true })\n onEvent({ type: 'done', isFinished: true, usage: handler.cloneUsage() })\n return\n }\n\n let history: SimpleHistoryMessage[] = []\n try {\n history = aiChatManager.getHistoryForAgent(aiChatId, undefined, historyLeafMessageId)\n } catch {\n // empty history on failure\n }\n\n handler.emitStatus('preparing', [], { pendingUserMessage: userMessage, force: true })\n\n const steerMessage = t('ai.agent.answerWithoutTools')\n let cachedMessages: PiMessage[] = []\n const effectiveTools =\n chartAutoMode === 'explicit' && !shouldUseChartCapabilityForMessage(userMessage)\n ? tools.filter((tool) => tool.name !== 'render_chart')\n : tools\n\n try {\n const routeInput = {\n userMessage,\n chatType,\n locale,\n dataSnapshot,\n availableTools: effectiveTools.map((tool) => tool.name),\n availableCapabilities: [\n getChartPlannerCapabilityForMessage({\n userMessage,\n locale,\n availableTools: effectiveTools.map((tool) => tool.name),\n chartAutoMode,\n }),\n ].filter((capability) => capability !== null),\n skillSummary: skillDef?.name ?? (skillMenu ? 'auto_skill_menu' : undefined),\n }\n const routeStartedAt = Date.now()\n const routeDecision = await decideRequestRoute(routeInput, {\n llmRouter: createLlmRouteDecider({\n piModel,\n apiKey: llmConfig.apiKey,\n abortSignal,\n }),\n })\n aiLogger?.info('Router', 'Shadow route decision', {\n ...routeDecision,\n elapsedMs: Date.now() - routeStartedAt,\n availableToolCount: tools.length,\n shadowOnly: true,\n })\n onEvent({ type: 'route', routeDecision })\n\n let effectiveSystemPrompt = systemPrompt\n if (routeDecision.route === 'planned_execution') {\n const planStartedAt = Date.now()\n const planner = createAnalysisPlanner({\n piModel,\n apiKey: llmConfig.apiKey,\n onPlanDelta: (delta) => onEvent({ type: 'plan_delta', planDelta: delta }),\n onThinkingDelta: (delta) => onEvent({ type: 'think', content: delta, thinkTag: 'thinking' }),\n onThinkingEnd: (durationMs) =>\n onEvent({ type: 'think', content: '', thinkTag: 'thinking', thinkDurationMs: durationMs }),\n onValidationDelta: (delta) => onEvent({ type: 'think', content: delta, thinkTag: 'plan_validation' }),\n onValidationEnd: (durationMs) =>\n onEvent({ type: 'think', content: '', thinkTag: 'plan_validation', thinkDurationMs: durationMs }),\n })\n const plan = await planner(routeInput, abortSignal)\n if (plan) {\n const planBlock = createPlanContentBlock(plan)\n onEvent({ type: 'plan', plan: planBlock })\n effectiveSystemPrompt = `${systemPrompt}\\n\\n${buildPlanGuidance(plan)}`\n aiLogger?.info('Planner', 'Plan generated', {\n title: plan.title,\n steps: plan.steps.length,\n successCriteria: plan.successCriteria.length,\n elapsedMs: Date.now() - planStartedAt,\n })\n } else {\n aiLogger?.warn('Planner', 'Plan generation skipped or failed', {\n elapsedMs: Date.now() - planStartedAt,\n route: routeDecision.route,\n })\n onEvent({ type: 'plan_skipped' })\n }\n }\n\n const result = await runAgentCore({\n piModel,\n apiKey: llmConfig.apiKey,\n systemPrompt: effectiveSystemPrompt,\n tools: effectiveTools,\n history,\n userMessage,\n maxToolRounds: DEFAULT_MAX_TOOL_ROUNDS,\n abortSignal,\n providerSessionId: aiChatId,\n steerMessage,\n thinkingLevel: thinkingLevel as import('@openchatlab/core').ThinkingLevel | undefined,\n onConvertToLlm: (filteredMessages) => {\n cachedMessages = filteredMessages as PiMessage[]\n },\n onEvent: (coreEvent) => handler.handleCoreEvent(coreEvent, cachedMessages),\n onDebugContext: (messages) => {\n try {\n aiChatManager.setPendingDebugContext(aiChatId, JSON.stringify(messages, null, 2))\n } catch {\n // silent\n }\n },\n })\n\n if (result.error) {\n const friendlyMessage = formatAIError(result.error)\n onEvent({ type: 'error', error: { name: 'AgentError', message: friendlyMessage } })\n }\n\n handler.emitStatus('completed', cachedMessages, { force: true })\n onEvent({ type: 'done', isFinished: true, usage: result.usage })\n } catch (error) {\n const friendlyMessage = formatAIError(error)\n aiLogger?.error('ServerAgent', 'Agent execution error', { error: String(error) })\n handler.emitStatus('error', cachedMessages, { force: true })\n onEvent({ type: 'error', error: { name: 'AgentError', message: friendlyMessage } })\n onEvent({ type: 'done', isFinished: true, usage: handler.cloneUsage() })\n }\n}\n","import type {\n AgentTool,\n AIMemoryService,\n CrossChatAnalysisService,\n PreprocessConfig,\n SessionRuntimeAdapter,\n} from '@openchatlab/node-runtime'\nimport type { AIEntityRef } from '@openchatlab/shared-types'\nimport {\n countTokens,\n preprocessCrossChatLabel,\n preprocessCrossChatMessages,\n preprocessCrossChatSummaries,\n} from '@openchatlab/node-runtime'\nimport { createCrossChatAgentToolAdapters, type CrossChatToolExecutionContext } from '@openchatlab/tools'\n\nexport function createCliCrossChatTools(options: {\n analysisService: CrossChatAnalysisService\n sessionAdapter: SessionRuntimeAdapter\n locale?: string\n entityRefs?: AIEntityRef[]\n preprocessConfig?: Record<string, unknown>\n maxToolResultTokens: number\n memoryService: AIMemoryService\n aiChatId: string\n reportMemoryChange?: (memoryId: string) => void\n}): AgentTool<any, any>[] {\n const context: Omit<CrossChatToolExecutionContext, 'abortSignal' | 'reportProgress'> = {\n locale: options.locale,\n entityRefs: options.entityRefs,\n analysisService: options.analysisService,\n memoryService: options.memoryService,\n aiChatId: options.aiChatId,\n reportMemoryChange: options.reportMemoryChange,\n maxToolResultTokens: options.maxToolResultTokens,\n countTokens,\n preprocessMessagesBySession: (sessionId, messages) =>\n preprocessCrossChatMessages(\n options.sessionAdapter,\n sessionId,\n messages,\n options.preprocessConfig as PreprocessConfig | undefined\n ),\n preprocessSummariesBySession: (sessionId, summaries) =>\n preprocessCrossChatSummaries(\n options.sessionAdapter,\n sessionId,\n summaries,\n options.preprocessConfig as PreprocessConfig | undefined\n ),\n preprocessModelLabel: (value, pseudonym) =>\n preprocessCrossChatLabel(value, pseudonym, options.preprocessConfig as PreprocessConfig | undefined),\n }\n\n return createCrossChatAgentToolAdapters(context) as AgentTool<any, any>[]\n}\n","/**\n * CLI Agent stream runner — provides runAgentStream implementation\n * for the shared HTTP route context.\n */\n\nimport type {\n DatabaseManager,\n AIChatManager,\n AgentStreamChunk,\n LLMConfigStore,\n SemanticIndexRuntime,\n CrossChatAnalysisService,\n SessionRuntimeAdapter,\n AIMemoryService,\n} from '@openchatlab/node-runtime'\nimport {\n CHART_CAPABILITY_CORE_TOOLS,\n DEFAULT_CONTEXT_COMPRESSION_CONFIG,\n SkillManager,\n buildPiModel,\n buildSkillMenuWithBuiltinChart,\n createLlmRuntimeStores,\n createActivateSkillTool,\n createDataSnapshotFromOverview,\n getAllowedBuiltinToolsForChartAutoSkill,\n getChartCapabilitySkill,\n getSkillConfigWithBuiltinChart,\n resolveChartRuntimeForRequest,\n resolveCrossChatToolResultTokenBudget,\n runCrossChatAgent,\n} from '@openchatlab/node-runtime'\nimport { getChatOverview, normalizeBuiltinToolNames } from '@openchatlab/core'\nimport type { ChartAutoMode } from '@openchatlab/shared-types'\nimport type { DataSnapshot } from '@openchatlab/node-runtime'\nimport type { AgentStreamRequest } from '@openchatlab/http-routes'\nimport { AGENT_TOOL_REGISTRY, SEMANTIC_SEARCH_TOOL_NAME } from '@openchatlab/tools'\nimport { buildSemanticSearchGuidance } from '@openchatlab/node-runtime'\nimport { adaptToolsForAgent } from './tool-adapter'\nimport { loadAssistantConfig } from './assistant-loader'\nimport { runServerAgent } from './agent'\nimport { createCliCrossChatTools } from './cross-chat-tool-adapter'\nimport { getServerAiLogger } from './logger'\n\nfunction getAiDir(dbManager: DatabaseManager): string {\n const pathProvider = (dbManager as any)['pathProvider']\n if (!pathProvider) {\n throw Object.assign(new Error('PathProvider not available'), { statusCode: 500 })\n }\n return pathProvider.getAiDataDir()\n}\n\nconst RAW_SQL_TOOL_NAMES = new Set(['execute_sql'])\n\nexport function getAvailableToolDefs(isChartCapability: boolean, allowedToolSet: Set<string> | null) {\n if (isChartCapability) {\n const chartCoreTools = new Set<string>(CHART_CAPABILITY_CORE_TOOLS)\n return AGENT_TOOL_REGISTRY.filter(\n (tool) =>\n chartCoreTools.has(tool.name) ||\n (tool.category === 'analysis' && allowedToolSet?.has(tool.name) && !RAW_SQL_TOOL_NAMES.has(tool.name))\n )\n }\n\n return allowedToolSet\n ? AGENT_TOOL_REGISTRY.filter((tool) => tool.category !== 'analysis' || allowedToolSet.has(tool.name))\n : AGENT_TOOL_REGISTRY\n}\n\n// 区分“未配置白名单”和“配置为空白名单”:前者无限制,后者禁用 analysis 工具。\nexport function getAllowedToolSet(\n isChartCapability: boolean,\n allowedBuiltinTools?: readonly string[]\n): Set<string> | null {\n if (isChartCapability) {\n return new Set(normalizeBuiltinToolNames(allowedBuiltinTools ?? []))\n }\n return allowedBuiltinTools === undefined ? null : new Set(normalizeBuiltinToolNames(allowedBuiltinTools))\n}\n\nexport function createCliRunAgentStream(\n dbManager: DatabaseManager,\n aiChatManager: AIChatManager,\n options: {\n llmConfigStore?: LLMConfigStore\n semanticIndexService?: SemanticIndexRuntime\n crossChatAnalysisService?: CrossChatAnalysisService\n sessionAdapter?: SessionRuntimeAdapter\n memoryService?: AIMemoryService\n } = {}\n): (params: AgentStreamRequest, onEvent: (chunk: AgentStreamChunk) => void, abortSignal: AbortSignal) => Promise<void> {\n const aiDataDir = getAiDir(dbManager)\n const llmConfigStore = options.llmConfigStore ?? createLlmRuntimeStores(aiDataDir).llmConfigStore\n const semanticIndexService = options.semanticIndexService\n\n return async (params, onEvent, abortSignal) => {\n const {\n userMessage,\n aiChatId,\n historyLeafMessageId,\n chatKind = 'session',\n sessionId,\n entityRefs,\n chatType,\n locale,\n assistantId,\n skillId,\n enableAutoSkill,\n chartAutoMode,\n ownerInfo,\n mentionedMembers,\n thinkingLevel,\n } = params\n\n const conversation = aiChatManager.getAIChat(aiChatId)\n if (\n !conversation ||\n conversation.kind !== chatKind ||\n (chatKind === 'session' && conversation.sessionId !== sessionId)\n ) {\n onEvent({\n type: 'error',\n error: { name: 'ValidationError', message: 'AI conversation does not match the requested target' },\n })\n onEvent({ type: 'done', isFinished: true })\n return\n }\n\n let assistantSystemPrompt: string | undefined\n let assistantAllowedTools: string[] | undefined\n if (assistantId) {\n const assistantConfig = loadAssistantConfig(aiDataDir, assistantId)\n if (assistantConfig?.systemPrompt) {\n assistantSystemPrompt = assistantConfig.systemPrompt\n }\n assistantAllowedTools = assistantConfig?.allowedBuiltinTools\n }\n\n const llmConfig = llmConfigStore.getDefaultAssistantConfig()\n const maxToolResultPercent = DEFAULT_CONTEXT_COMPRESSION_CONFIG.maxToolResultPercent ?? 50\n const contextWindow = llmConfig ? (buildPiModel(llmConfig).contextWindow ?? 128000) : 128000\n const maxToolResultTokens = Math.floor(contextWindow * (maxToolResultPercent / 100))\n\n if (chatKind === 'global') {\n if (!llmConfig) {\n onEvent({ type: 'error', error: { name: 'ConfigError', message: 'LLM service not configured' } })\n onEvent({ type: 'done', isFinished: true })\n return\n }\n if (!options.crossChatAnalysisService || !options.sessionAdapter || !options.memoryService) {\n onEvent({ type: 'error', error: { name: 'RuntimeError', message: 'Cross-chat analysis is unavailable' } })\n onEvent({ type: 'done', isFinished: true })\n return\n }\n const tools = createCliCrossChatTools({\n analysisService: options.crossChatAnalysisService,\n sessionAdapter: options.sessionAdapter,\n locale,\n entityRefs,\n preprocessConfig: params.preprocessConfig,\n maxToolResultTokens: resolveCrossChatToolResultTokenBudget(contextWindow),\n memoryService: options.memoryService,\n aiChatId,\n reportMemoryChange: (memoryId) => onEvent({ type: 'memory_change', memoryId }),\n })\n await runCrossChatAgent({\n userMessage,\n entityRefs,\n aiChatId,\n historyLeafMessageId,\n locale,\n piModel: buildPiModel(llmConfig),\n apiKey: llmConfig.apiKey,\n tools,\n aiChatManager,\n memoryService: options.memoryService,\n onEvent,\n abortSignal,\n thinkingLevel: thinkingLevel as import('@openchatlab/core').ThinkingLevel | undefined,\n logger: getServerAiLogger() ?? undefined,\n })\n return\n }\n\n if (!sessionId) {\n onEvent({ type: 'error', error: { name: 'ValidationError', message: 'Session ID is required' } })\n onEvent({ type: 'done', isFinished: true })\n return\n }\n\n const db = (dbManager as any).open?.(sessionId)\n const resolvedChartAutoMode: ChartAutoMode = chartAutoMode ?? 'suggest'\n const chartRuntime = resolveChartRuntimeForRequest({\n skillId,\n userMessage,\n locale,\n assistantAllowedTools,\n enableAutoDetection: enableAutoSkill === true,\n chartAutoMode: resolvedChartAutoMode,\n })\n const isChartCapability = chartRuntime.isChartCapability\n const autoSkillAllowedTools =\n !skillId && enableAutoSkill && resolvedChartAutoMode !== 'explicit'\n ? getAllowedBuiltinToolsForChartAutoSkill(assistantAllowedTools)\n : assistantAllowedTools\n const allowedToolSet = getAllowedToolSet(\n isChartCapability,\n isChartCapability ? chartRuntime.allowedBuiltinTools : autoSkillAllowedTools\n )\n const availableToolDefs = getAvailableToolDefs(isChartCapability, allowedToolSet)\n\n // 语义检索按需暴露:仅当前会话可检索时保留工具,否则从工具集中过滤掉以减少 schema token 与无效调用。\n const canSemanticSearch = !!db && !!semanticIndexService && (await semanticIndexService.canSearch(sessionId))\n const filteredToolDefs = canSemanticSearch\n ? availableToolDefs\n : availableToolDefs.filter((tool) => tool.name !== SEMANTIC_SEARCH_TOOL_NAME)\n\n const agentTools = db\n ? adaptToolsForAgent(\n filteredToolDefs,\n () => ({\n db,\n sessionId,\n locale,\n semanticIndexService,\n preprocessConfig: params.preprocessConfig,\n ownerPlatformId: ownerInfo?.platformId,\n timeFilter: params.timeFilter,\n maxMessagesLimit: params.maxMessagesLimit,\n }),\n { maxToolResultTokens }\n )\n : []\n\n // 工具可用时向 system prompt 加简短引导,提示何时调用语义检索。\n if (canSemanticSearch) {\n assistantSystemPrompt = [assistantSystemPrompt, buildSemanticSearchGuidance(locale)].filter(Boolean).join('\\n\\n')\n }\n\n const skillMgr = new SkillManager(aiDataDir)\n skillMgr.init()\n const toolNames = agentTools.map((t: { name: string }) => t.name)\n\n let resolvedSkillDef: { name: string; prompt: string } | undefined\n let resolvedSkillMenu: string | undefined\n if (isChartCapability) {\n const def = chartRuntime.skillDef ?? getChartCapabilitySkill(locale ?? 'zh-CN')\n resolvedSkillDef = { name: def.name, prompt: def.prompt }\n } else if (skillId) {\n const def = skillMgr.getSkillConfig(skillId)\n if (def) resolvedSkillDef = { name: def.name, prompt: def.prompt }\n } else if (enableAutoSkill) {\n const baseMenu = skillMgr.getSkillMenu(chatType ?? 'group', toolNames)\n const menu =\n resolvedChartAutoMode === 'aggressive' ? buildSkillMenuWithBuiltinChart(baseMenu, locale, toolNames) : baseMenu\n if (menu) resolvedSkillMenu = menu\n }\n\n if (resolvedSkillMenu) {\n const activateSkillTool = createActivateSkillTool({\n chatType: chatType ?? 'group',\n allowedTools: toolNames,\n coreToolNames: new Set<string>(CHART_CAPABILITY_CORE_TOOLS),\n locale,\n getSkillConfig: (id) =>\n resolvedChartAutoMode === 'aggressive'\n ? getSkillConfigWithBuiltinChart(id, locale, (skillConfigId) => skillMgr.getSkillConfig(skillConfigId))\n : skillMgr.getSkillConfig(id),\n })\n agentTools.push(activateSkillTool as any)\n }\n\n let dataSnapshot: DataSnapshot | undefined\n if (db) {\n try {\n dataSnapshot = createDataSnapshotFromOverview(getChatOverview(db, 10))\n } catch {\n // non-fatal\n }\n }\n\n await runServerAgent({\n userMessage,\n aiChatId,\n historyLeafMessageId,\n chatType,\n locale,\n assistantSystemPrompt,\n skillMenu: resolvedSkillMenu,\n skillDef: resolvedSkillDef,\n tools: agentTools,\n llmConfig,\n aiChatManager,\n onEvent,\n abortSignal,\n ownerInfo,\n mentionedMembers,\n dataSnapshot,\n thinkingLevel,\n chartAutoMode: resolvedChartAutoMode,\n })\n }\n}\n"],"mappings":"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","names":["options"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../packages/node-runtime/src/services/people/relationships/facts-cache.ts","../../../packages/node-runtime/src/services/people/relationships/time-range.ts","../../../packages/node-runtime/src/services/people/relationships/compute.ts"],"sourcesContent":["import { ChatType, type ChatPlatform, type ContactsTimeRangeState } from '@openchatlab/shared-types'\nimport type {\n ContactMemberRef,\n GroupContactFacts,\n RelationshipGraphEdgeFact,\n RelationshipGraphMemberFact,\n} from '@openchatlab/core'\nimport { deleteSessionCache, getCache, setCache } from '../../../cache/session-cache'\n\nexport const PEOPLE_RELATIONSHIPS_FACTS_FORMAT_VERSION = 3\n\nexport interface PeopleRelationshipsFactsCacheStats {\n latestHits: number\n latestMisses: number\n factsHits: number\n factsMisses: number\n writes: number\n}\n\nexport interface PeopleRelationshipsSessionMetaFacts {\n name: string\n platform: ChatPlatform\n type: ChatType.PRIVATE | ChatType.GROUP\n ownerId: string | null\n owner?: ContactMemberRef\n}\n\nexport interface PeopleRelationshipsCachedPrivateFacts {\n contact: ContactMemberRef\n privateMessageCount: number\n activeMonths: string[]\n lastMessageTs: number | null\n}\n\nexport interface PeopleRelationshipsCachedGroupFacts {\n members: RelationshipGraphMemberFact[]\n edges: RelationshipGraphEdgeFact[]\n ownerEdges: GroupContactFacts[]\n ownerMessageCount: number\n}\n\nexport type PeopleRelationshipsSessionFacts =\n | { kind: 'not_chat_db'; latestMessageTs: null }\n | { kind: 'missing_meta'; latestMessageTs: number | null }\n | { kind: 'unsupported_type'; latestMessageTs: number | null }\n | { kind: 'missing_owner'; meta: PeopleRelationshipsSessionMetaFacts; latestMessageTs: number | null }\n | { kind: 'unresolved_owner'; meta: PeopleRelationshipsSessionMetaFacts; latestMessageTs: number | null }\n | { kind: 'private_missing'; meta: PeopleRelationshipsSessionMetaFacts; latestMessageTs: number | null }\n | { kind: 'private_ambiguous'; meta: PeopleRelationshipsSessionMetaFacts; latestMessageTs: number | null }\n | {\n kind: 'private'\n meta: PeopleRelationshipsSessionMetaFacts\n latestMessageTs: number | null\n facts: PeopleRelationshipsCachedPrivateFacts\n }\n | {\n kind: 'group'\n meta: PeopleRelationshipsSessionMetaFacts\n latestMessageTs: number | null\n facts: PeopleRelationshipsCachedGroupFacts\n }\n\nexport interface PeopleRelationshipsSessionLatestFacts {\n latestMessageTs: number | null\n}\n\nexport type PeopleRelationshipsCacheReadResult<T> = { hit: true; data: T } | { hit: false }\n\ninterface VersionedPeopleRelationshipsCacheEntry<T> {\n v: string\n data: T\n}\n\nexport function createEmptyPeopleRelationshipsFactsCacheStats(): PeopleRelationshipsFactsCacheStats {\n return {\n latestHits: 0,\n latestMisses: 0,\n factsHits: 0,\n factsMisses: 0,\n writes: 0,\n }\n}\n\nexport function buildPeopleRelationshipsSessionLatestCacheKey(algorithmVersion: string): string {\n return `people-relationships:latest:v${PEOPLE_RELATIONSHIPS_FACTS_FORMAT_VERSION}:${algorithmVersion}`\n}\n\nexport function buildPeopleRelationshipsSessionFactsCacheKey(\n algorithmVersion: string,\n timeRange: ContactsTimeRangeState\n): string {\n return [\n 'people-relationships:facts',\n `v${PEOPLE_RELATIONSHIPS_FACTS_FORMAT_VERSION}`,\n algorithmVersion,\n `preset:${timeRange.preset}`,\n `start:${timeRange.startTs ?? 'all'}`,\n ].join(':')\n}\n\nexport function readCachedPeopleRelationshipsSessionLatest(\n sessionId: string,\n cacheDir: string,\n key: string,\n dbVersion: string\n): PeopleRelationshipsCacheReadResult<PeopleRelationshipsSessionLatestFacts> {\n const cached = getCache<VersionedPeopleRelationshipsCacheEntry<PeopleRelationshipsSessionLatestFacts>>(\n sessionId,\n key,\n cacheDir\n )\n if (!cached || cached.v !== dbVersion || !isPeopleRelationshipsSessionLatestFacts(cached.data)) return { hit: false }\n return { hit: true, data: cached.data }\n}\n\nexport function writeCachedPeopleRelationshipsSessionLatest(\n sessionId: string,\n cacheDir: string,\n key: string,\n dbVersion: string,\n data: PeopleRelationshipsSessionLatestFacts\n): void {\n setCache<VersionedPeopleRelationshipsCacheEntry<PeopleRelationshipsSessionLatestFacts>>(\n sessionId,\n key,\n { v: dbVersion, data },\n cacheDir\n )\n}\n\nexport function readCachedPeopleRelationshipsSessionFacts(\n sessionId: string,\n cacheDir: string,\n key: string,\n dbVersion: string\n): PeopleRelationshipsCacheReadResult<PeopleRelationshipsSessionFacts> {\n const cached = getCache<VersionedPeopleRelationshipsCacheEntry<PeopleRelationshipsSessionFacts>>(\n sessionId,\n key,\n cacheDir\n )\n if (!cached || cached.v !== dbVersion || !isPeopleRelationshipsSessionFacts(cached.data)) return { hit: false }\n return { hit: true, data: cached.data }\n}\n\nexport function writeCachedPeopleRelationshipsSessionFacts(\n sessionId: string,\n cacheDir: string,\n key: string,\n dbVersion: string,\n data: PeopleRelationshipsSessionFacts\n): void {\n setCache<VersionedPeopleRelationshipsCacheEntry<PeopleRelationshipsSessionFacts>>(\n sessionId,\n key,\n { v: dbVersion, data },\n cacheDir\n )\n}\n\nexport function deletePeopleRelationshipsSessionFactsCache(sessionId: string, cacheDir: string): void {\n deleteSessionCache(sessionId, cacheDir)\n}\n\nfunction isPeopleRelationshipsSessionLatestFacts(value: unknown): value is PeopleRelationshipsSessionLatestFacts {\n return isObject(value) && isNullableNumber(value.latestMessageTs)\n}\n\nfunction isPeopleRelationshipsSessionFacts(value: unknown): value is PeopleRelationshipsSessionFacts {\n if (!isObject(value) || typeof value.kind !== 'string' || !isNullableNumber(value.latestMessageTs)) return false\n switch (value.kind) {\n case 'not_chat_db':\n case 'missing_meta':\n case 'unsupported_type':\n return true\n case 'missing_owner':\n case 'unresolved_owner':\n case 'private_missing':\n case 'private_ambiguous':\n return isPeopleRelationshipsSessionMetaFacts(value.meta)\n case 'private':\n return isPeopleRelationshipsSessionMetaFacts(value.meta) && isPeopleRelationshipsCachedPrivateFacts(value.facts)\n case 'group':\n return isPeopleRelationshipsSessionMetaFacts(value.meta) && isPeopleRelationshipsCachedGroupFacts(value.facts)\n default:\n return false\n }\n}\n\nfunction isPeopleRelationshipsSessionMetaFacts(value: unknown): value is PeopleRelationshipsSessionMetaFacts {\n return (\n isObject(value) &&\n typeof value.name === 'string' &&\n typeof value.platform === 'string' &&\n (value.type === ChatType.PRIVATE || value.type === ChatType.GROUP) &&\n (typeof value.ownerId === 'string' || value.ownerId === null) &&\n (value.owner === undefined || isContactMemberRef(value.owner))\n )\n}\n\nfunction isPeopleRelationshipsCachedPrivateFacts(value: unknown): value is PeopleRelationshipsCachedPrivateFacts {\n return (\n isObject(value) &&\n isContactMemberRef(value.contact) &&\n isFiniteNumber(value.privateMessageCount) &&\n Array.isArray(value.activeMonths) &&\n value.activeMonths.every((month) => typeof month === 'string') &&\n isNullableNumber(value.lastMessageTs)\n )\n}\n\nfunction isPeopleRelationshipsCachedGroupFacts(value: unknown): value is PeopleRelationshipsCachedGroupFacts {\n return (\n isObject(value) &&\n Array.isArray(value.members) &&\n value.members.every(isRelationshipGraphMemberFact) &&\n Array.isArray(value.edges) &&\n value.edges.every(isRelationshipGraphEdgeFact) &&\n Array.isArray(value.ownerEdges) &&\n value.ownerEdges.every(isGroupContactFacts) &&\n isFiniteNumber(value.ownerMessageCount)\n )\n}\n\nfunction isGroupContactFacts(value: unknown): value is GroupContactFacts {\n return (\n isObject(value) &&\n isContactMemberRef(value.contact) &&\n isFiniteNumber(value.messageCount) &&\n isFiniteNumber(value.coOccurrenceCount) &&\n isFiniteNumber(value.coOccurrenceRawScore) &&\n isFiniteNumber(value.replyInteractionCount) &&\n isFiniteNumber(value.repliesFromOwnerToContact) &&\n isFiniteNumber(value.repliesFromContactToOwner) &&\n isNullableNumber(value.lastInteractionTs)\n )\n}\n\nfunction isRelationshipGraphMemberFact(value: unknown): value is RelationshipGraphMemberFact {\n return (\n isObject(value) &&\n isContactMemberRef(value.contact) &&\n isFiniteNumber(value.messageCount) &&\n isNullableNumber(value.lastMessageTs)\n )\n}\n\nfunction isRelationshipGraphEdgeFact(value: unknown): value is RelationshipGraphEdgeFact {\n return (\n isObject(value) &&\n isContactMemberRef(value.source) &&\n isContactMemberRef(value.target) &&\n isFiniteNumber(value.coOccurrenceCount) &&\n isFiniteNumber(value.coOccurrenceRawScore) &&\n isFiniteNumber(value.replyInteractionCount) &&\n isFiniteNumber(value.repliesFromSourceToTarget) &&\n isFiniteNumber(value.repliesFromTargetToSource) &&\n isNullableNumber(value.lastInteractionTs)\n )\n}\n\nfunction isContactMemberRef(value: unknown): value is ContactMemberRef {\n return (\n isObject(value) &&\n isFiniteNumber(value.id) &&\n typeof value.platformId === 'string' &&\n typeof value.name === 'string' &&\n Array.isArray(value.aliases) &&\n value.aliases.every((alias) => typeof alias === 'string') &&\n (typeof value.avatar === 'string' || value.avatar === null)\n )\n}\n\nfunction isNullableNumber(value: unknown): value is number | null {\n return value === null || isFiniteNumber(value)\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value)\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n","import {\n CONTACTS_TIME_RANGE_PRESETS,\n type ContactsTimeRangePreset,\n type ContactsTimeRangeState,\n} from '@openchatlab/shared-types'\n\nconst SECONDS_PER_YEAR = 365 * 24 * 60 * 60\nconst DEFAULT_PEOPLE_RELATIONSHIPS_TIME_RANGE_PRESET: ContactsTimeRangePreset = '1y'\n\nconst YEARS_BY_PRESET: Partial<Record<ContactsTimeRangePreset, number>> = {\n '1y': 1,\n '2y': 2,\n '3y': 3,\n '5y': 5,\n}\n\nexport function normalizePeopleRelationshipsTimeRangePreset(value: unknown): ContactsTimeRangePreset {\n return CONTACTS_TIME_RANGE_PRESETS.includes(value as ContactsTimeRangePreset)\n ? (value as ContactsTimeRangePreset)\n : DEFAULT_PEOPLE_RELATIONSHIPS_TIME_RANGE_PRESET\n}\n\nexport function resolvePeopleRelationshipsTimeRange(\n presetInput: unknown,\n anchorTs: number | null | undefined\n): ContactsTimeRangeState {\n const preset = normalizePeopleRelationshipsTimeRangePreset(presetInput)\n const normalizedAnchor = typeof anchorTs === 'number' ? anchorTs : null\n const years = YEARS_BY_PRESET[preset]\n\n return {\n preset,\n anchorTs: normalizedAnchor,\n startTs: normalizedAnchor !== null && years ? normalizedAnchor - years * SECONDS_PER_YEAR : null,\n }\n}\n","import {\n ChatType,\n type ChatPlatform,\n type ContactsTimeRangePreset,\n type ContactsTimeRangeState,\n type PeopleRelationshipCommunity,\n type PeopleRelationshipsGraphData,\n type PeopleRelationshipGraphEdge,\n type PeopleRelationshipGraphNode,\n type PeopleRelationshipsDiagnostics,\n} from '@openchatlab/shared-types'\nimport {\n buildContactKey,\n computeFriendScores,\n computeNonFriendScores,\n getGroupContactFacts,\n getGroupRelationshipGraphFacts,\n getLatestContactMessageTs,\n getPrivateContactFacts,\n getSessionMeta,\n isChatSessionDb,\n resolveOwnerMember,\n shouldScopeContactToSession,\n} from '@openchatlab/core'\nimport type { ContactMemberRef, SessionMeta } from '@openchatlab/core'\nimport { getDbFileVersion } from '../../../cache/analytics-cache'\nimport { appLogger } from '../../../logging/app-logger'\nimport type { SessionRuntimeAdapter } from '../../adapters'\nimport {\n buildPeopleRelationshipsSessionFactsCacheKey,\n buildPeopleRelationshipsSessionLatestCacheKey,\n createEmptyPeopleRelationshipsFactsCacheStats,\n readCachedPeopleRelationshipsSessionFacts,\n readCachedPeopleRelationshipsSessionLatest,\n writeCachedPeopleRelationshipsSessionFacts,\n writeCachedPeopleRelationshipsSessionLatest,\n type PeopleRelationshipsCachedGroupFacts,\n type PeopleRelationshipsCachedPrivateFacts,\n type PeopleRelationshipsFactsCacheStats,\n type PeopleRelationshipsSessionFacts,\n type PeopleRelationshipsSessionLatestFacts,\n type PeopleRelationshipsSessionMetaFacts,\n} from './facts-cache'\nimport { resolvePeopleRelationshipsTimeRange } from './time-range'\n\nexport const PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION = 'people-relationships-v2'\n\nconst PRIVATE_COMMUNITY_ID = 'private'\nconst OWNER_KEY_PREFIX = 'owner'\nconst REPLY_WEIGHT = 3\nconst CO_OCCURRENCE_COUNT_WEIGHT = 0.05\nconst EDGE_RECENCY_HALF_LIFE_SECONDS = 120 * 24 * 60 * 60\nconst EDGE_RECENCY_FLOOR = 0.1\nconst MIN_GROUP_FRIENDS_FOR_PANORAMA = 3\nconst MIN_GROUP_FRIEND_RATIO_FOR_PANORAMA = 0.2\nconst MIN_OWNER_CONNECTED_MEMBERS_FOR_PANORAMA = 3\nconst MIN_OWNER_GROUP_MESSAGES_FOR_PANORAMA = 20\nconst MIN_OWNER_GROUP_MESSAGE_RATIO_FOR_PANORAMA = 0.05\nconst MIN_OWNER_GROUP_MESSAGES_FOR_RATIO = 8\nconst SMALL_GROUP_MEMBER_LIMIT_FOR_FRIEND_EDGE = 12\nconst MAX_OWNER_CONNECTED_NON_FRIENDS_PER_GROUP = 30\nconst MAX_FRIEND_CONNECTED_NON_FRIENDS_PER_GROUP = 30\nconst MAX_HIGH_SIGNAL_NON_FRIENDS_PER_GROUP = 10\nconst MAX_MEMBERS_PER_GROUP_FOR_PANORAMA = 80\n\nconst COMMUNITY_COLORS = [\n '#7dd3fc',\n '#f0abfc',\n '#facc15',\n '#34d399',\n '#fb7185',\n '#a78bfa',\n '#2dd4bf',\n '#f97316',\n '#60a5fa',\n '#e879f9',\n]\n\nexport interface PeopleRelationshipsWorkerStats {\n durationMs: number\n totalSessions: number\n processedSessions: number\n skippedFailedSessions: number\n}\n\nexport interface PeopleRelationshipsComputeLimits {\n coreNodeLimit?: number\n coreEdgeLimit?: number\n perNodeEdgeLimit?: number\n neighborhoodNodeLimit?: number\n neighborhoodEdgeLimit?: number\n searchResultLimit?: number\n}\n\nexport interface PeopleRelationshipsSnapshot {\n nodes: PeopleRelationshipGraphNode[]\n edges: PeopleRelationshipGraphEdge[]\n communities: PeopleRelationshipCommunity[]\n graph: PeopleRelationshipsGraphData\n diagnostics: PeopleRelationshipsDiagnostics\n algorithmVersion: string\n signature: string\n timeRange: ContactsTimeRangeState\n computedAt: number\n workerStats: PeopleRelationshipsWorkerStats\n limits: Required<PeopleRelationshipsComputeLimits>\n}\n\nexport interface PeopleRelationshipsComputeProgress {\n processedSessions: number\n totalSessions: number\n currentSessionId?: string\n}\n\nexport interface ComputePeopleRelationshipsSnapshotOptions {\n adapter: SessionRuntimeAdapter\n signature: string\n timeRangePreset?: ContactsTimeRangePreset\n factsCacheDir?: string\n limits?: PeopleRelationshipsComputeLimits\n now?: () => number\n onProgress?: (progress: PeopleRelationshipsComputeProgress) => void\n}\n\ninterface NodeAccumulator {\n key: string\n kind: 'contact' | 'owner'\n platform: ChatPlatform\n platformId: string\n sessionScoped: boolean\n sessionId?: string\n displayName: string\n aliases: Set<string>\n avatar: string | null\n isFriend: boolean\n friendSource?: 'private'\n privateMessageCount: number\n activePrivateMonths: Set<string>\n groupMessageCount: number\n commonGroupSessionIds: Set<string>\n ownerCoOccurrenceCount: number\n ownerCoOccurrenceRawScore: number\n ownerReplyInteractionCount: number\n ownerRepliesFromOwnerToContact: number\n ownerRepliesFromContactToOwner: number\n communityWeights: Map<string, number>\n edgeWeight: number\n panoramaCandidate: boolean\n lastInteractionTs: number | null\n}\n\ninterface EdgeAccumulator {\n sourceKey: string\n targetKey: string\n coOccurrenceCount: number\n coOccurrenceRawScore: number\n replyInteractionCount: number\n repliesFromSourceToTarget: number\n repliesFromTargetToSource: number\n sourceSessionIds: Set<string>\n panoramaEligible: boolean\n lastInteractionTs: number | null\n}\n\ninterface GroupPanoramaContribution {\n sessionId: string\n memberKeys: Set<string>\n memberMessageCounts: Map<string, number>\n ownerConnectedKeys: Set<string>\n ownerEdgeScores: Map<string, number>\n relationshipEdgeIds: Set<string>\n relationshipEdgeEndpoints: Map<string, [string, string]>\n relationshipEdgeScores: Map<string, number>\n friendConnectedScores: Map<string, number>\n ownerMessageCount: number\n}\n\ninterface BuildGraphResult {\n nodes: PeopleRelationshipGraphNode[]\n edges: PeopleRelationshipGraphEdge[]\n communities: PeopleRelationshipCommunity[]\n graph: PeopleRelationshipsGraphData\n diagnostics: PeopleRelationshipsDiagnostics\n}\n\ninterface PeopleRelationshipsFactsCacheContext {\n dir: string\n latestKey: string\n stats: PeopleRelationshipsFactsCacheStats\n}\n\nexport function computePeopleRelationshipsSnapshot(\n options: ComputePeopleRelationshipsSnapshotOptions\n): PeopleRelationshipsSnapshot {\n const startedAt = options.now?.() ?? Date.now()\n const sessionIds = options.adapter.listSessionIds()\n const limits = normalizeLimits(options.limits)\n const factsCache = options.factsCacheDir ? createFactsCacheContext(options.factsCacheDir) : null\n const timeRange = resolvePeopleRelationshipsTimeRange(\n options.timeRangePreset,\n findGlobalLatestMessageTs(options.adapter, sessionIds, factsCache)\n )\n const result = computePeopleRelationships({\n adapter: options.adapter,\n sessionIds,\n timeRange,\n factsCache,\n limits,\n onProgress: options.onProgress,\n })\n const finishedAt = options.now?.() ?? Date.now()\n if (factsCache) {\n appLogger.info('people-relationships', 'people relationships session facts cache summary', factsCache.stats)\n }\n appLogger.info('people-relationships', 'people relationships panorama filter summary', {\n includedGroupSessions: result.diagnostics.panoramaIncludedGroupSessions,\n excludedLowValueGroupSessions: result.diagnostics.panoramaExcludedLowValueGroupSessions,\n includedGroupMembers: result.diagnostics.panoramaIncludedGroupMembers,\n excludedGroupMembers: result.diagnostics.panoramaExcludedGroupMembers,\n candidateNodes: result.diagnostics.panoramaCandidateNodes,\n reasons: result.diagnostics.panoramaGroupInclusionReasons,\n })\n return {\n ...result,\n algorithmVersion: PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION,\n signature: options.signature,\n timeRange,\n computedAt: finishedAt,\n limits,\n workerStats: {\n durationMs: Math.max(0, finishedAt - startedAt),\n totalSessions: sessionIds.length,\n processedSessions: sessionIds.length,\n skippedFailedSessions: result.diagnostics.skippedFailedSessions,\n },\n }\n}\n\nexport function buildPeopleRelationshipsNeighborhoodGraph(\n snapshot: PeopleRelationshipsSnapshot,\n contactKey: string,\n limits: PeopleRelationshipsComputeLimits = {}\n): PeopleRelationshipsGraphData {\n const normalizedLimits = normalizeLimits({ ...snapshot.limits, ...limits })\n const center = snapshot.nodes.find((node) => node.key === contactKey)\n if (!center) return { nodes: [], edges: [], communities: [] }\n\n const relatedEdges = sortEdgesForDisplay(\n snapshot.edges.filter((edge) => edge.sourceKey === contactKey || edge.targetKey === contactKey)\n )\n const { directEdges, nodeKeys } = selectNeighborhoodDirectEdges(relatedEdges, contactKey, normalizedLimits)\n\n const directEdgeIds = new Set(relatedEdges.map((edge) => edge.id))\n const secondaryEdgeLimit = Math.max(0, normalizedLimits.neighborhoodEdgeLimit - directEdges.length)\n const secondaryEdges = sortEdgesForDisplay(\n snapshot.edges.filter(\n (edge) => !directEdgeIds.has(edge.id) && nodeKeys.has(edge.sourceKey) && nodeKeys.has(edge.targetKey)\n )\n ).slice(0, secondaryEdgeLimit)\n const edges = sortEdgesForDisplay([...directEdges, ...secondaryEdges])\n const nodes = layoutNeighborhoodNodes(\n snapshot.nodes.filter((node) => nodeKeys.has(node.key)),\n directEdges,\n contactKey\n ).sort(compareNodes)\n return {\n nodes,\n edges,\n communities: filterNeighborhoodCommunities(snapshot.communities, directEdges),\n }\n}\n\nfunction selectNeighborhoodDirectEdges(\n relatedEdges: PeopleRelationshipGraphEdge[],\n contactKey: string,\n limits: Required<PeopleRelationshipsComputeLimits>\n): { directEdges: PeopleRelationshipGraphEdge[]; nodeKeys: Set<string> } {\n // 邻域图先按直接关系排序选择节点,避免高连接节点绕过节点/边上限。\n const nodeKeys = new Set<string>([contactKey])\n const directEdges: PeopleRelationshipGraphEdge[] = []\n\n for (const edge of relatedEdges) {\n if (directEdges.length >= limits.neighborhoodEdgeLimit) break\n const neighborKey = getNeighborhoodNeighborKey(edge, contactKey)\n if (!neighborKey) continue\n if (!nodeKeys.has(neighborKey)) {\n if (nodeKeys.size >= limits.neighborhoodNodeLimit) continue\n nodeKeys.add(neighborKey)\n }\n directEdges.push(edge)\n }\n\n return { directEdges, nodeKeys }\n}\n\nfunction getNeighborhoodNeighborKey(edge: PeopleRelationshipGraphEdge, contactKey: string): string | null {\n if (edge.sourceKey === contactKey) return edge.targetKey\n if (edge.targetKey === contactKey) return edge.sourceKey\n return null\n}\n\nfunction layoutNeighborhoodNodes(\n nodes: PeopleRelationshipGraphNode[],\n relatedEdges: PeopleRelationshipGraphEdge[],\n contactKey: string\n): PeopleRelationshipGraphNode[] {\n const center = nodes.find((node) => node.key === contactKey)\n if (!center) return nodes.map((node) => ({ ...node }))\n\n const directEdgeByNodeKey = new Map<string, PeopleRelationshipGraphEdge>()\n for (const edge of relatedEdges) {\n const neighborKey =\n edge.sourceKey === contactKey ? edge.targetKey : edge.targetKey === contactKey ? edge.sourceKey : null\n if (neighborKey) directEdgeByNodeKey.set(neighborKey, edge)\n }\n const maxDirectWeight = Math.max(...[...directEdgeByNodeKey.values()].map((edge) => edge.weight), 1)\n const communityGroups = new Map<string, PeopleRelationshipGraphNode[]>()\n for (const node of nodes) {\n if (node.key === contactKey) continue\n const group = communityGroups.get(node.communityId) ?? []\n group.push(node)\n communityGroups.set(node.communityId, group)\n }\n\n const sortedCommunityGroups = [...communityGroups.entries()].sort((a, b) => {\n if (a[0] === center.communityId) return -1\n if (b[0] === center.communityId) return 1\n const weightA = sumNeighborhoodCommunityWeight(a[1], directEdgeByNodeKey)\n const weightB = sumNeighborhoodCommunityWeight(b[1], directEdgeByNodeKey)\n return weightB - weightA || a[0].localeCompare(b[0])\n })\n\n const localNodes: PeopleRelationshipGraphNode[] = [{ ...center, x: 0, y: 0 }]\n for (const [communityIndex, [communityId, communityNodes]] of sortedCommunityGroups.entries()) {\n const baseAngle = neighborhoodCommunityAngle(communityIndex, communityId, center.communityId)\n communityNodes.sort((a, b) => {\n const edgeA = directEdgeByNodeKey.get(a.key)?.weight ?? 0\n const edgeB = directEdgeByNodeKey.get(b.key)?.weight ?? 0\n return edgeB - edgeA || compareNodes(a, b)\n })\n\n for (const [index, node] of communityNodes.entries()) {\n const angle = neighborhoodNodeAngle(baseAngle, communityId, node.key, index, communityNodes.length)\n const radius = neighborhoodNodeRadius(\n node,\n center,\n directEdgeByNodeKey.get(node.key),\n maxDirectWeight,\n communityNodes.length\n )\n localNodes.push({\n ...node,\n x: roundNum(Math.cos(angle) * radius, 2),\n y: roundNum(Math.sin(angle) * radius, 2),\n })\n }\n }\n return localNodes\n}\n\nfunction sumNeighborhoodCommunityWeight(\n nodes: PeopleRelationshipGraphNode[],\n directEdgeByNodeKey: Map<string, PeopleRelationshipGraphEdge>\n): number {\n return nodes.reduce((sum, node) => sum + (directEdgeByNodeKey.get(node.key)?.weight ?? 0), 0)\n}\n\nfunction neighborhoodCommunityAngle(index: number, communityId: string, centerCommunityId: string): number {\n if (communityId === centerCommunityId) return -Math.PI / 2\n return index * 2.399963229728653\n}\n\nfunction neighborhoodNodeAngle(\n baseAngle: number,\n communityId: string,\n nodeKey: string,\n index: number,\n communitySize: number\n): number {\n const seedOffset = ((stableHash(`${communityId}:${nodeKey}:neighborhood-angle`) % 1000) / 1000 - 0.5) * 0.12\n const centeredIndex = index - (communitySize - 1) / 2\n const spacing = communitySize <= 4 ? 0.42 : communitySize <= 10 ? 0.24 : 0.14\n const maxSpread = communitySize <= 4 ? 0.72 : communitySize <= 20 ? 1.08 : 1.32\n return baseAngle + clamp(centeredIndex * spacing + seedOffset, -maxSpread, maxSpread)\n}\n\nfunction neighborhoodNodeRadius(\n node: PeopleRelationshipGraphNode,\n center: PeopleRelationshipGraphNode,\n directEdge: PeopleRelationshipGraphEdge | undefined,\n maxDirectWeight: number,\n communitySize: number\n): number {\n const directStrength = directEdge ? Math.min(1, directEdge.weight / maxDirectWeight) : 0\n const sameCommunityBonus = node.communityId === center.communityId ? 110 : 0\n const smallCommunityBonus = node.communityId === center.communityId && communitySize <= 6 ? 80 : 0\n const friendBonus = node.pool === 'friend' ? 70 : 0\n const scoreBonus = Math.min(90, Math.sqrt(Math.max(0, Math.min(1, node.score))) * 90)\n const rankPenalty = Math.sqrt(Math.max(0, node.rank - center.rank)) * 8\n return roundNum(\n clamp(\n 260 +\n (1 - directStrength) * 560 +\n rankPenalty -\n sameCommunityBonus -\n smallCommunityBonus -\n friendBonus -\n scoreBonus,\n 120,\n 1100\n ),\n 2\n )\n}\n\nfunction computePeopleRelationships(options: {\n adapter: SessionRuntimeAdapter\n sessionIds: string[]\n timeRange: ContactsTimeRangeState\n factsCache: PeopleRelationshipsFactsCacheContext | null\n limits: Required<PeopleRelationshipsComputeLimits>\n onProgress?: (progress: PeopleRelationshipsComputeProgress) => void\n}): BuildGraphResult {\n const diagnostics = createEmptyDiagnostics()\n const nodes = new Map<string, NodeAccumulator>()\n const edges = new Map<string, EdgeAccumulator>()\n const groupContributions: GroupPanoramaContribution[] = []\n const communityLabels = new Map<string, string>([[PRIVATE_COMMUNITY_ID, 'Private contacts']])\n let processedSessions = 0\n\n for (const sessionId of options.sessionIds) {\n options.onProgress?.({ processedSessions, totalSessions: options.sessionIds.length, currentSessionId: sessionId })\n try {\n const facts = getSessionFacts(options.adapter, sessionId, options.timeRange, options.factsCache)\n applySessionFacts(nodes, edges, groupContributions, communityLabels, diagnostics, sessionId, facts)\n } catch (error) {\n diagnostics.skippedFailedSessions++\n appLogger.error('people-relationships', `failed to process people relationship session: ${sessionId}`, error)\n } finally {\n processedSessions++\n options.onProgress?.({ processedSessions, totalSessions: options.sessionIds.length, currentSessionId: sessionId })\n }\n }\n\n applyPanoramaContributions([...nodes.values()], edges, groupContributions, diagnostics)\n return buildGraph(\n [...nodes.values()],\n [...edges.values()],\n communityLabels,\n groupContributions,\n options.limits,\n diagnostics\n )\n}\n\nfunction findGlobalLatestMessageTs(\n adapter: SessionRuntimeAdapter,\n sessionIds: string[],\n factsCache: PeopleRelationshipsFactsCacheContext | null\n): number | null {\n let latest: number | null = null\n for (const sessionId of sessionIds) {\n const dbVersion = getSessionDbVersion(adapter, sessionId)\n const cached = readLatestFacts(sessionId, factsCache, dbVersion)\n if (cached) {\n if (cached.latestMessageTs !== null) latest = Math.max(latest ?? 0, cached.latestMessageTs)\n continue\n }\n try {\n const db = adapter.openReadonly(sessionId)\n const ts = db && isChatSessionDb(db) ? getLatestContactMessageTs(db) : null\n writeLatestFacts(adapter, sessionId, factsCache, { latestMessageTs: ts }, dbVersion)\n if (ts !== null) latest = Math.max(latest ?? 0, ts)\n } catch (error) {\n appLogger.error(\n 'people-relationships',\n `failed to inspect people relationship session range: ${sessionId}`,\n error\n )\n }\n }\n return latest\n}\n\nfunction getSessionFacts(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n timeRange: ContactsTimeRangeState,\n factsCache: PeopleRelationshipsFactsCacheContext | null\n): PeopleRelationshipsSessionFacts {\n const dbVersion = getSessionDbVersion(adapter, sessionId)\n const cached = readSessionFacts(sessionId, timeRange, factsCache, dbVersion)\n if (cached) return cached\n\n const facts = computeSessionFacts(adapter, sessionId, timeRange)\n writeSessionFacts(adapter, sessionId, timeRange, factsCache, facts, dbVersion)\n return facts\n}\n\nfunction computeSessionFacts(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n timeRange: ContactsTimeRangeState\n): PeopleRelationshipsSessionFacts {\n const db = adapter.openReadonly(sessionId)\n if (!db || !isChatSessionDb(db)) return { kind: 'not_chat_db', latestMessageTs: null }\n\n const latestMessageTs = getLatestContactMessageTs(db)\n const meta = getSessionMeta(db)\n if (!meta) return { kind: 'missing_meta', latestMessageTs }\n if (meta.type !== ChatType.PRIVATE && meta.type !== ChatType.GROUP)\n return { kind: 'unsupported_type', latestMessageTs }\n\n const cachedMeta = toPeopleRelationshipsSessionMetaFacts(meta)\n if (!meta.ownerId?.trim()) return { kind: 'missing_owner', meta: cachedMeta, latestMessageTs }\n\n const owner = resolveOwnerMember(db)\n if (!owner) return { kind: 'unresolved_owner', meta: cachedMeta, latestMessageTs }\n const cachedMetaWithOwner = toPeopleRelationshipsSessionMetaFacts(meta, owner)\n\n if (meta.type === ChatType.PRIVATE) {\n const facts = getPrivateContactFacts(db, owner.id, { startTs: timeRange.startTs })\n if (facts.type === 'missing') return { kind: 'private_missing', meta: cachedMetaWithOwner, latestMessageTs }\n if (facts.type === 'ambiguous') return { kind: 'private_ambiguous', meta: cachedMetaWithOwner, latestMessageTs }\n return { kind: 'private', meta: cachedMetaWithOwner, latestMessageTs, facts }\n }\n\n return {\n kind: 'group',\n meta: cachedMetaWithOwner,\n latestMessageTs,\n facts: {\n ...getGroupRelationshipGraphFacts(db, owner.id, { startTs: timeRange.startTs }),\n ownerEdges: getGroupContactFacts(db, owner.id, { startTs: timeRange.startTs }),\n },\n }\n}\n\nfunction applySessionFacts(\n nodes: Map<string, NodeAccumulator>,\n edges: Map<string, EdgeAccumulator>,\n groupContributions: GroupPanoramaContribution[],\n communityLabels: Map<string, string>,\n diagnostics: PeopleRelationshipsDiagnostics,\n sessionId: string,\n sessionFacts: PeopleRelationshipsSessionFacts\n): void {\n switch (sessionFacts.kind) {\n case 'missing_owner':\n diagnostics.skippedMissingOwnerSessions++\n return\n case 'unresolved_owner':\n diagnostics.skippedUnresolvedOwnerSessions++\n return\n case 'private_ambiguous':\n diagnostics.skippedAmbiguousPrivateSessions++\n return\n case 'private':\n diagnostics.processedPrivateSessions++\n applyPrivateFacts(nodes, edges, sessionId, sessionFacts.meta, sessionFacts.facts)\n return\n case 'group':\n diagnostics.processedGroupSessions++\n applyGroupFacts(\n nodes,\n edges,\n groupContributions,\n communityLabels,\n sessionId,\n sessionFacts.meta,\n sessionFacts.facts\n )\n return\n default:\n return\n }\n}\n\nfunction applyPrivateFacts(\n nodes: Map<string, NodeAccumulator>,\n edges: Map<string, EdgeAccumulator>,\n sessionId: string,\n meta: PeopleRelationshipsSessionMetaFacts,\n facts: PeopleRelationshipsCachedPrivateFacts\n): void {\n if (facts.privateMessageCount <= 0) return\n const ownerNode = getOrCreateOwnerNode(nodes, meta)\n const node = getOrCreateNode(nodes, sessionId, meta, facts.contact)\n node.isFriend = true\n node.friendSource = 'private'\n node.panoramaCandidate = true\n node.privateMessageCount += facts.privateMessageCount\n for (const month of facts.activeMonths) node.activePrivateMonths.add(month)\n node.communityWeights.set(PRIVATE_COMMUNITY_ID, (node.communityWeights.get(PRIVATE_COMMUNITY_ID) ?? 0) + 1)\n updateLastInteraction(node, facts.lastMessageTs)\n\n if (!ownerNode) return\n ownerNode.isFriend = true\n ownerNode.panoramaCandidate = true\n ownerNode.privateMessageCount += facts.privateMessageCount\n for (const month of facts.activeMonths) ownerNode.activePrivateMonths.add(month)\n ownerNode.communityWeights.set(PRIVATE_COMMUNITY_ID, (ownerNode.communityWeights.get(PRIVATE_COMMUNITY_ID) ?? 0) + 1)\n updateLastInteraction(ownerNode, facts.lastMessageTs)\n applyOwnerContactEdge(edges, ownerNode, node, {\n coOccurrenceCount: facts.privateMessageCount,\n coOccurrenceRawScore: facts.privateMessageCount * 1.8,\n replyInteractionCount: 0,\n repliesFromOwnerToContact: 0,\n repliesFromContactToOwner: 0,\n lastInteractionTs: facts.lastMessageTs,\n sessionId,\n panoramaEligible: true,\n })\n}\n\nfunction applyGroupFacts(\n nodes: Map<string, NodeAccumulator>,\n edges: Map<string, EdgeAccumulator>,\n groupContributions: GroupPanoramaContribution[],\n communityLabels: Map<string, string>,\n sessionId: string,\n meta: PeopleRelationshipsSessionMetaFacts,\n facts: PeopleRelationshipsCachedGroupFacts\n): void {\n const communityId = `group:${sessionId}`\n communityLabels.set(communityId, meta.name)\n const ownerNode = getOrCreateOwnerNode(nodes, meta)\n const contribution: GroupPanoramaContribution = {\n sessionId,\n memberKeys: new Set(),\n memberMessageCounts: new Map(),\n ownerConnectedKeys: new Set(),\n ownerEdgeScores: new Map(),\n relationshipEdgeIds: new Set(),\n relationshipEdgeEndpoints: new Map(),\n relationshipEdgeScores: new Map(),\n friendConnectedScores: new Map(),\n ownerMessageCount: facts.ownerMessageCount,\n }\n for (const member of facts.members) {\n const node = getOrCreateNode(nodes, sessionId, meta, member.contact)\n contribution.memberKeys.add(node.key)\n contribution.memberMessageCounts.set(node.key, member.messageCount)\n node.groupMessageCount += member.messageCount\n node.commonGroupSessionIds.add(sessionId)\n node.communityWeights.set(\n communityId,\n (node.communityWeights.get(communityId) ?? 0) + Math.max(1, member.messageCount)\n )\n updateLastInteraction(node, member.lastMessageTs)\n }\n\n if (ownerNode) {\n ownerNode.groupMessageCount += facts.ownerMessageCount\n for (const fact of facts.ownerEdges) {\n if (fact.coOccurrenceCount <= 0 && fact.replyInteractionCount <= 0) continue\n const contactNode = getOrCreateNode(nodes, sessionId, meta, fact.contact)\n const ownerEdgeScore = computeRawEdgeWeight({\n coOccurrenceCount: fact.coOccurrenceCount,\n coOccurrenceRawScore: fact.coOccurrenceRawScore,\n replyInteractionCount: fact.replyInteractionCount,\n })\n contribution.memberKeys.add(contactNode.key)\n contribution.ownerConnectedKeys.add(contactNode.key)\n contribution.ownerEdgeScores.set(\n contactNode.key,\n (contribution.ownerEdgeScores.get(contactNode.key) ?? 0) + ownerEdgeScore\n )\n ownerNode.commonGroupSessionIds.add(sessionId)\n ownerNode.communityWeights.set(communityId, (ownerNode.communityWeights.get(communityId) ?? 0) + 1)\n updateLastInteraction(ownerNode, fact.lastInteractionTs)\n applyOwnerContactEdge(edges, ownerNode, contactNode, {\n coOccurrenceCount: fact.coOccurrenceCount,\n coOccurrenceRawScore: fact.coOccurrenceRawScore,\n replyInteractionCount: fact.replyInteractionCount,\n repliesFromOwnerToContact: fact.repliesFromOwnerToContact,\n repliesFromContactToOwner: fact.repliesFromContactToOwner,\n lastInteractionTs: fact.lastInteractionTs,\n sessionId,\n })\n }\n }\n\n for (const fact of facts.edges) {\n const sourceNode = getOrCreateNode(nodes, sessionId, meta, fact.source)\n const targetNode = getOrCreateNode(nodes, sessionId, meta, fact.target)\n const sourceKey = sourceNode.key < targetNode.key ? sourceNode.key : targetNode.key\n const targetKey = sourceNode.key < targetNode.key ? targetNode.key : sourceNode.key\n const edge = getOrCreateEdge(edges, sourceKey, targetKey)\n const currentEdgeId = edgeId(sourceKey, targetKey)\n const relationshipEdgeScore = computeRawEdgeWeight(fact)\n contribution.memberKeys.add(sourceNode.key)\n contribution.memberKeys.add(targetNode.key)\n contribution.relationshipEdgeIds.add(currentEdgeId)\n contribution.relationshipEdgeEndpoints.set(currentEdgeId, [sourceKey, targetKey])\n contribution.relationshipEdgeScores.set(\n currentEdgeId,\n (contribution.relationshipEdgeScores.get(currentEdgeId) ?? 0) + relationshipEdgeScore\n )\n edge.coOccurrenceCount += fact.coOccurrenceCount\n edge.coOccurrenceRawScore += fact.coOccurrenceRawScore\n edge.replyInteractionCount += fact.replyInteractionCount\n if (sourceNode.key === sourceKey) {\n edge.repliesFromSourceToTarget += fact.repliesFromSourceToTarget\n edge.repliesFromTargetToSource += fact.repliesFromTargetToSource\n } else {\n edge.repliesFromSourceToTarget += fact.repliesFromTargetToSource\n edge.repliesFromTargetToSource += fact.repliesFromSourceToTarget\n }\n edge.sourceSessionIds.add(sessionId)\n edge.lastInteractionTs = maxNullableTs(edge.lastInteractionTs, fact.lastInteractionTs)\n sourceNode.commonGroupSessionIds.add(sessionId)\n targetNode.commonGroupSessionIds.add(sessionId)\n sourceNode.communityWeights.set(communityId, (sourceNode.communityWeights.get(communityId) ?? 0) + 1)\n targetNode.communityWeights.set(communityId, (targetNode.communityWeights.get(communityId) ?? 0) + 1)\n updateLastInteraction(sourceNode, fact.lastInteractionTs)\n updateLastInteraction(targetNode, fact.lastInteractionTs)\n }\n groupContributions.push(contribution)\n}\n\nfunction buildGraph(\n nodeAccumulators: NodeAccumulator[],\n edgeAccumulators: EdgeAccumulator[],\n communityLabels: Map<string, string>,\n groupContributions: GroupPanoramaContribution[],\n limits: Required<PeopleRelationshipsComputeLimits>,\n baseDiagnostics: PeopleRelationshipsDiagnostics\n): BuildGraphResult {\n const edgeWeights = new Map<string, number>()\n for (const edge of edgeAccumulators) {\n const weight = computeEdgeWeight(edge)\n edgeWeights.set(edgeId(edge.sourceKey, edge.targetKey), weight)\n }\n\n for (const edge of edgeAccumulators) {\n const weight = edgeWeights.get(edgeId(edge.sourceKey, edge.targetKey)) ?? 0\n const source = nodeAccumulators.find((node) => node.key === edge.sourceKey)\n const target = nodeAccumulators.find((node) => node.key === edge.targetKey)\n if (source) source.edgeWeight += weight\n if (target) target.edgeWeight += weight\n }\n\n const nodeScores = computeContactPriorityScores(nodeAccumulators)\n const rankedNodes = nodeAccumulators\n .map((node) => ({\n node,\n score: nodeScores.get(node) ?? 0,\n relationshipActivityScore: computeRelationshipActivityScore(node),\n }))\n .sort(compareRankedNodes)\n const panoramaCandidateKeys = new Set(\n rankedNodes.filter((item) => item.node.panoramaCandidate).map((item) => item.node.key)\n )\n const panoramaEligibleEdgeIds = new Set(\n edgeAccumulators.filter((edge) => edge.panoramaEligible).map((edge) => edgeId(edge.sourceKey, edge.targetKey))\n )\n const maxScore = Math.max(...rankedNodes.map((item) => item.score), 1)\n const nodeByKey = new Map<string, PeopleRelationshipGraphNode>()\n\n rankedNodes.forEach((item, index) => {\n const communityId = pickCommunityId(item.node)\n const color = colorForCommunity(communityId)\n nodeByKey.set(item.node.key, {\n key: item.node.key,\n kind: item.node.kind,\n platform: item.node.platform,\n platformId: item.node.platformId,\n sessionScoped: item.node.sessionScoped,\n sessionId: item.node.sessionId,\n displayName: item.node.displayName,\n aliases: [...item.node.aliases].filter((alias) => alias !== item.node.displayName),\n avatar: item.node.avatar,\n pool: item.node.isFriend ? 'friend' : 'non_friend',\n friendSource: item.node.friendSource,\n score: roundNum(item.score),\n rank: index + 1,\n communityId,\n x: 0,\n y: 0,\n size: roundNum(4 + Math.sqrt(item.score / maxScore) * 18, 2),\n color,\n labelVisibility: item.node.kind === 'owner' || index < 30 ? 2 : index < 160 ? 1 : 0,\n lastInteractionTs: item.node.lastInteractionTs,\n privateMessageCount: item.node.privateMessageCount,\n groupMessageCount: item.node.groupMessageCount,\n commonGroupCount: item.node.commonGroupSessionIds.size,\n searchText: [\n item.node.displayName,\n item.node.platformId,\n ...item.node.aliases,\n ...(item.node.kind === 'owner' ? ['我', 'me', 'myself', 'owner'] : []),\n ]\n .join(' ')\n .toLowerCase(),\n })\n })\n\n const allNodes = layoutNodes([...nodeByKey.values()], communityLabels)\n const allEdges = edgeAccumulators\n .map((edge) => toGraphEdge(edge, edgeWeights.get(edgeId(edge.sourceKey, edge.targetKey)) ?? 0))\n .filter((edge) => nodeByKey.has(edge.sourceKey) && nodeByKey.has(edge.targetKey))\n .sort(compareEdges)\n const communities = buildCommunities(allNodes, communityLabels, groupContributions)\n const coreNodeKeys = new Set(\n allNodes\n .filter((node) => panoramaCandidateKeys.has(node.key))\n .slice(0, limits.coreNodeLimit)\n .map((node) => node.key)\n )\n const coreEdges = cropEdges(\n allEdges.filter(\n (edge) =>\n coreNodeKeys.has(edge.sourceKey) && coreNodeKeys.has(edge.targetKey) && panoramaEligibleEdgeIds.has(edge.id)\n ),\n limits\n )\n const graphNodes = allNodes.filter((node) => coreNodeKeys.has(node.key))\n const graph: PeopleRelationshipsGraphData = {\n nodes: graphNodes,\n edges: coreEdges,\n communities: filterCommunities(communities, graphNodes),\n }\n const diagnostics: PeopleRelationshipsDiagnostics = {\n ...baseDiagnostics,\n totalNodes: allNodes.length,\n totalEdges: allEdges.length,\n panoramaCandidateNodes: panoramaCandidateKeys.size,\n coreNodeCount: graph.nodes.length,\n coreEdgeCount: graph.edges.length,\n }\n return { nodes: allNodes, edges: allEdges, communities, graph, diagnostics }\n}\n\nfunction cropEdges(\n edges: PeopleRelationshipGraphEdge[],\n limits: Required<PeopleRelationshipsComputeLimits>\n): PeopleRelationshipGraphEdge[] {\n const counts = new Map<string, number>()\n const kept: PeopleRelationshipGraphEdge[] = []\n for (const edge of sortEdgesForDisplay(edges)) {\n if (kept.length >= limits.coreEdgeLimit) break\n const sourceCount = counts.get(edge.sourceKey) ?? 0\n const targetCount = counts.get(edge.targetKey) ?? 0\n if (sourceCount >= limits.perNodeEdgeLimit || targetCount >= limits.perNodeEdgeLimit) continue\n kept.push(edge)\n counts.set(edge.sourceKey, sourceCount + 1)\n counts.set(edge.targetKey, targetCount + 1)\n }\n return kept\n}\n\nfunction layoutNodes(\n nodes: PeopleRelationshipGraphNode[],\n communityLabels: Map<string, string>\n): PeopleRelationshipGraphNode[] {\n const byCommunity = new Map<string, PeopleRelationshipGraphNode[]>()\n for (const node of nodes) {\n const group = byCommunity.get(node.communityId) ?? []\n group.push(node)\n byCommunity.set(node.communityId, group)\n }\n\n const sortedCommunities = [...byCommunity.entries()].sort((a, b) => {\n if (a[0] === PRIVATE_COMMUNITY_ID) return -1\n if (b[0] === PRIVATE_COMMUNITY_ID) return 1\n const scoreA = a[1].reduce((sum, node) => sum + node.score, 0)\n const scoreB = b[1].reduce((sum, node) => sum + node.score, 0)\n return scoreB - scoreA || (communityLabels.get(a[0]) ?? a[0]).localeCompare(communityLabels.get(b[0]) ?? b[0])\n })\n\n for (const [communityIndex, [communityId, communityNodes]] of sortedCommunities.entries()) {\n const baseAngle = communityAngle(communityIndex, communityId)\n communityNodes.sort(compareNodes)\n for (const [index, node] of communityNodes.entries()) {\n if (node.kind === 'owner') {\n node.x = 0\n node.y = 0\n continue\n }\n\n const angle = egoNodeAngle(baseAngle, communityId, node.key, index, communityNodes.length)\n const radius = egoNodeRadius(node, communityId, communityNodes.length)\n node.x = roundNum(Math.cos(angle) * radius, 2)\n node.y = roundNum(Math.sin(angle) * radius, 2)\n }\n }\n return nodes.sort(compareNodes)\n}\n\nfunction communityAngle(index: number, communityId: string): number {\n if (communityId === PRIVATE_COMMUNITY_ID) return -Math.PI / 2\n return index * 2.399963229728653\n}\n\nfunction egoNodeAngle(\n baseAngle: number,\n communityId: string,\n nodeKey: string,\n index: number,\n communitySize: number\n): number {\n const seedOffset = ((stableHash(`${communityId}:${nodeKey}:angle`) % 1000) / 1000 - 0.5) * 0.16\n const centeredIndex = index - (communitySize - 1) / 2\n const spacing = communitySize <= 4 ? 0.38 : communitySize <= 12 ? 0.24 : 0.12\n const maxSpread = communitySize <= 4 ? 0.75 : communitySize <= 20 ? 1.1 : 1.35\n return baseAngle + clamp(centeredIndex * spacing + seedOffset, -maxSpread, maxSpread)\n}\n\nfunction egoNodeRadius(node: PeopleRelationshipGraphNode, communityId: string, communitySize: number): number {\n const rankPenalty = Math.sqrt(Math.max(0, node.rank - 1)) * 26\n const scoreBonus = Math.min(260, Math.sqrt(Math.max(0, node.score)) * 22)\n const privateBonus = node.privateMessageCount > 0 ? Math.min(220, Math.log1p(node.privateMessageCount) * 48) : 0\n const friendBonus = node.pool === 'friend' ? 280 : 0\n const commonGroupBonus = Math.min(180, node.commonGroupCount * 18)\n // 大群更可能是同事群、兴趣群或通知群;这里只把它作为布局噪声惩罚,不改变关系评分。\n const groupSizePenalty = communityId.startsWith('group:')\n ? Math.min(1400, Math.max(0, communitySize - 6) * 22 + Math.sqrt(communitySize) * 24)\n : 0\n const baseRadius = node.pool === 'friend' ? 430 : 760\n const minRadius = node.pool === 'friend' ? 160 : 340\n return roundNum(\n clamp(\n baseRadius + rankPenalty + groupSizePenalty - scoreBonus - privateBonus - friendBonus - commonGroupBonus,\n minRadius,\n 2600\n ),\n 2\n )\n}\n\nfunction buildCommunities(\n nodes: PeopleRelationshipGraphNode[],\n communityLabels: Map<string, string>,\n groupContributions: GroupPanoramaContribution[]\n): PeopleRelationshipCommunity[] {\n const groups = new Map<string, PeopleRelationshipGraphNode[]>()\n for (const node of nodes) {\n const group = groups.get(node.communityId) ?? []\n group.push(node)\n groups.set(node.communityId, group)\n }\n const groupMemberCounts = new Map(groupContributions.map((item) => [`group:${item.sessionId}`, item.memberKeys.size]))\n const communitiesById = new Map<string, PeopleRelationshipCommunity>()\n\n for (const [id, group] of groups) {\n const x = group.reduce((sum, node) => sum + node.x, 0) / group.length\n const y = group.reduce((sum, node) => sum + node.y, 0) / group.length\n communitiesById.set(id, {\n id,\n label: communityLabels.get(id) ?? id,\n size: groupMemberCounts.get(id) ?? group.length,\n x: roundNum(x, 2),\n y: roundNum(y, 2),\n color: colorForCommunity(id),\n })\n }\n\n for (const [id, label] of communityLabels) {\n if (communitiesById.has(id) || !id.startsWith('group:')) continue\n communitiesById.set(id, {\n id,\n label,\n size: groupMemberCounts.get(id) ?? 0,\n x: 0,\n y: 0,\n color: colorForCommunity(id),\n })\n }\n\n return [...communitiesById.values()].sort((a, b) => b.size - a.size || a.label.localeCompare(b.label))\n}\n\nfunction filterNeighborhoodCommunities(\n communities: PeopleRelationshipCommunity[],\n relatedEdges: PeopleRelationshipGraphEdge[]\n): PeopleRelationshipCommunity[] {\n const communityById = new Map(communities.map((community) => [community.id, community]))\n const sourceCommunityIds = new Set<string>()\n for (const edge of relatedEdges) {\n for (const sessionId of edge.sourceSessionIds) sourceCommunityIds.add(`group:${sessionId}`)\n }\n return [...sourceCommunityIds]\n .map((id) => communityById.get(id))\n .filter((community): community is PeopleRelationshipCommunity => Boolean(community))\n .sort((a, b) => b.size - a.size || a.label.localeCompare(b.label))\n}\n\nfunction filterCommunities(\n communities: PeopleRelationshipCommunity[],\n nodes: PeopleRelationshipGraphNode[]\n): PeopleRelationshipCommunity[] {\n const ids = new Set(nodes.map((node) => node.communityId))\n return communities.filter((community) => ids.has(community.id))\n}\n\nfunction applyPanoramaContributions(\n nodes: NodeAccumulator[],\n edges: Map<string, EdgeAccumulator>,\n groupContributions: GroupPanoramaContribution[],\n diagnostics: PeopleRelationshipsDiagnostics\n): void {\n const nodeByKey = new Map(nodes.map((node) => [node.key, node]))\n for (const contribution of groupContributions) {\n const decision = evaluateGroupPanoramaContribution(contribution, nodeByKey)\n if (!decision.included) {\n diagnostics.panoramaExcludedLowValueGroupSessions++\n diagnostics.panoramaExcludedGroupMembers += contribution.memberKeys.size\n continue\n }\n\n diagnostics.panoramaIncludedGroupSessions++\n incrementPanoramaReason(diagnostics, decision.reason)\n\n for (const key of decision.selectedMemberKeys) {\n const node = nodeByKey.get(key)\n if (node) node.panoramaCandidate = true\n }\n for (const edgeId of contribution.relationshipEdgeIds) {\n const endpoints = contribution.relationshipEdgeEndpoints.get(edgeId)\n if (!endpoints) continue\n if (!decision.selectedMemberKeys.has(endpoints[0]) || !decision.selectedMemberKeys.has(endpoints[1])) continue\n const edge = edges.get(edgeId)\n if (edge) edge.panoramaEligible = true\n }\n for (const key of contribution.ownerConnectedKeys) {\n if (!decision.selectedMemberKeys.has(key)) continue\n const ownerKey = buildOwnerKeyForContribution(nodeByKey, key)\n if (!ownerKey) continue\n const ownerEdgeId = sortedEdgeId(ownerKey, key)\n const edge = edges.get(ownerEdgeId)\n if (edge) edge.panoramaEligible = true\n }\n\n diagnostics.panoramaIncludedGroupMembers += decision.selectedMemberKeys.size\n diagnostics.panoramaExcludedGroupMembers += Math.max(\n 0,\n contribution.memberKeys.size - decision.selectedMemberKeys.size\n )\n }\n}\n\nfunction evaluateGroupPanoramaContribution(\n contribution: GroupPanoramaContribution,\n nodeByKey: Map<string, NodeAccumulator>\n): { included: boolean; reason: string; selectedMemberKeys: Set<string> } {\n const friendKeys = [...contribution.memberKeys].filter((key) => nodeByKey.get(key)?.isFriend === true)\n const memberCount = contribution.memberKeys.size\n const friendCount = friendKeys.length\n const friendRatio = memberCount > 0 ? friendCount / memberCount : 0\n const ownerConnectedCount = contribution.ownerConnectedKeys.size\n const totalGroupMessages =\n contribution.ownerMessageCount +\n [...contribution.memberMessageCounts.values()].reduce((sum, count) => sum + count, 0)\n const ownerMessageRatio = totalGroupMessages > 0 ? contribution.ownerMessageCount / totalGroupMessages : 0\n const friendConnectedScores = computeFriendConnectedScores(contribution, new Set(friendKeys))\n contribution.friendConnectedScores.clear()\n for (const [key, score] of friendConnectedScores) contribution.friendConnectedScores.set(key, score)\n\n let reason: string | null = null\n if (friendCount === 0) {\n reason = null\n } else if (friendCount >= MIN_GROUP_FRIENDS_FOR_PANORAMA) {\n reason = 'friend_count'\n } else if (friendCount >= 2 && friendRatio >= MIN_GROUP_FRIEND_RATIO_FOR_PANORAMA) {\n reason = 'friend_ratio'\n } else if (ownerConnectedCount >= MIN_OWNER_CONNECTED_MEMBERS_FOR_PANORAMA) {\n reason = 'owner_connected'\n } else if (\n contribution.ownerMessageCount >= MIN_OWNER_GROUP_MESSAGES_FOR_PANORAMA ||\n (contribution.ownerMessageCount >= MIN_OWNER_GROUP_MESSAGES_FOR_RATIO &&\n ownerMessageRatio >= MIN_OWNER_GROUP_MESSAGE_RATIO_FOR_PANORAMA)\n ) {\n reason = 'owner_activity'\n } else if (memberCount <= SMALL_GROUP_MEMBER_LIMIT_FOR_FRIEND_EDGE && friendConnectedScores.size > 0) {\n reason = 'friend_connected'\n }\n\n if (!reason) return { included: false, reason: 'low_value', selectedMemberKeys: new Set() }\n return {\n included: true,\n reason,\n selectedMemberKeys: selectPanoramaMembers(contribution, nodeByKey, new Set(friendKeys)),\n }\n}\n\nfunction computeFriendConnectedScores(\n contribution: GroupPanoramaContribution,\n friendKeys: Set<string>\n): Map<string, number> {\n const scores = new Map<string, number>()\n for (const [edgeId, endpoints] of contribution.relationshipEdgeEndpoints) {\n const score = contribution.relationshipEdgeScores.get(edgeId) ?? 0\n const [sourceKey, targetKey] = endpoints\n if (friendKeys.has(sourceKey) && !friendKeys.has(targetKey)) {\n scores.set(targetKey, (scores.get(targetKey) ?? 0) + score)\n } else if (friendKeys.has(targetKey) && !friendKeys.has(sourceKey)) {\n scores.set(sourceKey, (scores.get(sourceKey) ?? 0) + score)\n }\n }\n return scores\n}\n\nfunction selectPanoramaMembers(\n contribution: GroupPanoramaContribution,\n nodeByKey: Map<string, NodeAccumulator>,\n friendKeys: Set<string>\n): Set<string> {\n const selected = new Set(friendKeys)\n const remainingLimit = () => Math.max(0, MAX_MEMBERS_PER_GROUP_FOR_PANORAMA - selected.size)\n\n addTopScoredKeys(selected, contribution.ownerEdgeScores, MAX_OWNER_CONNECTED_NON_FRIENDS_PER_GROUP, remainingLimit())\n addTopScoredKeys(\n selected,\n contribution.friendConnectedScores,\n MAX_FRIEND_CONNECTED_NON_FRIENDS_PER_GROUP,\n remainingLimit()\n )\n addTopScoredKeys(\n selected,\n computeHighSignalNonFriendScores(contribution, nodeByKey, friendKeys),\n MAX_HIGH_SIGNAL_NON_FRIENDS_PER_GROUP,\n remainingLimit()\n )\n return selected\n}\n\nfunction computeHighSignalNonFriendScores(\n contribution: GroupPanoramaContribution,\n nodeByKey: Map<string, NodeAccumulator>,\n friendKeys: Set<string>\n): Map<string, number> {\n const scores = new Map<string, number>()\n for (const key of contribution.memberKeys) {\n const node = nodeByKey.get(key)\n if (!node || node.kind === 'owner' || friendKeys.has(key)) continue\n const messageScore = Math.log1p(contribution.memberMessageCounts.get(key) ?? 0)\n const ownerScore = contribution.ownerEdgeScores.get(key) ?? 0\n const friendScore = contribution.friendConnectedScores.get(key) ?? 0\n const relationshipScore = sumRelationshipScoresForKey(contribution, key)\n const score = ownerScore * 2 + friendScore * 1.5 + relationshipScore + messageScore\n if (score > 0) scores.set(key, score)\n }\n return scores\n}\n\nfunction sumRelationshipScoresForKey(contribution: GroupPanoramaContribution, key: string): number {\n let total = 0\n for (const [edgeId, endpoints] of contribution.relationshipEdgeEndpoints) {\n if (endpoints[0] === key || endpoints[1] === key) total += contribution.relationshipEdgeScores.get(edgeId) ?? 0\n }\n return total\n}\n\nfunction addTopScoredKeys(\n selected: Set<string>,\n scores: Map<string, number>,\n limit: number,\n remainingLimit: number\n): void {\n if (limit <= 0 || remainingLimit <= 0) return\n const items = [...scores.entries()]\n .filter(([key, score]) => !selected.has(key) && score > 0)\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n for (const [key] of items.slice(0, Math.min(limit, remainingLimit))) {\n selected.add(key)\n }\n}\n\nfunction incrementPanoramaReason(diagnostics: PeopleRelationshipsDiagnostics, reason: string): void {\n diagnostics.panoramaGroupInclusionReasons[reason] = (diagnostics.panoramaGroupInclusionReasons[reason] ?? 0) + 1\n}\n\nfunction buildOwnerKeyForContribution(nodeByKey: Map<string, NodeAccumulator>, contactKey: string): string {\n const contact = nodeByKey.get(contactKey)\n return contact ? buildOwnerKey() : ''\n}\n\nfunction toGraphEdge(edge: EdgeAccumulator, weight: number): PeopleRelationshipGraphEdge {\n return {\n id: edgeId(edge.sourceKey, edge.targetKey),\n sourceKey: edge.sourceKey,\n targetKey: edge.targetKey,\n weight: roundNum(weight),\n coOccurrenceCount: edge.coOccurrenceCount,\n coOccurrenceRawScore: roundNum(edge.coOccurrenceRawScore),\n replyInteractionCount: edge.replyInteractionCount,\n repliesFromSourceToTarget: edge.repliesFromSourceToTarget,\n repliesFromTargetToSource: edge.repliesFromTargetToSource,\n sourceGroupCount: edge.sourceSessionIds.size,\n sourceSessionIds: [...edge.sourceSessionIds].sort(),\n lastInteractionTs: edge.lastInteractionTs,\n visibility: weight >= 8 ? 2 : 1,\n }\n}\n\nfunction createFactsCacheContext(dir: string): PeopleRelationshipsFactsCacheContext {\n return {\n dir,\n latestKey: buildPeopleRelationshipsSessionLatestCacheKey(PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION),\n stats: createEmptyPeopleRelationshipsFactsCacheStats(),\n }\n}\n\nfunction readLatestFacts(\n sessionId: string,\n factsCache: PeopleRelationshipsFactsCacheContext | null,\n dbVersion: string\n): PeopleRelationshipsSessionLatestFacts | null {\n if (!factsCache) return null\n const cached = readCachedPeopleRelationshipsSessionLatest(sessionId, factsCache.dir, factsCache.latestKey, dbVersion)\n if (!cached.hit) {\n factsCache.stats.latestMisses++\n return null\n }\n factsCache.stats.latestHits++\n return cached.data\n}\n\nfunction writeLatestFacts(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n factsCache: PeopleRelationshipsFactsCacheContext | null,\n data: PeopleRelationshipsSessionLatestFacts,\n expectedDbVersion: string\n): void {\n if (!factsCache) return\n const dbVersion = getSessionDbVersion(adapter, sessionId)\n if (dbVersion !== expectedDbVersion) {\n appLogger.debug('people-relationships', 'skipped latest facts cache write because db version changed', {\n sessionId,\n })\n return\n }\n writeCachedPeopleRelationshipsSessionLatest(sessionId, factsCache.dir, factsCache.latestKey, dbVersion, data)\n factsCache.stats.writes++\n}\n\nfunction readSessionFacts(\n sessionId: string,\n timeRange: ContactsTimeRangeState,\n factsCache: PeopleRelationshipsFactsCacheContext | null,\n dbVersion: string\n): PeopleRelationshipsSessionFacts | null {\n if (!factsCache) return null\n const cached = readCachedPeopleRelationshipsSessionFacts(\n sessionId,\n factsCache.dir,\n buildPeopleRelationshipsSessionFactsCacheKey(PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION, timeRange),\n dbVersion\n )\n if (!cached.hit) {\n factsCache.stats.factsMisses++\n return null\n }\n factsCache.stats.factsHits++\n return cached.data\n}\n\nfunction writeSessionFacts(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n timeRange: ContactsTimeRangeState,\n factsCache: PeopleRelationshipsFactsCacheContext | null,\n facts: PeopleRelationshipsSessionFacts,\n expectedDbVersion: string\n): void {\n if (!factsCache) return\n const dbVersion = getSessionDbVersion(adapter, sessionId)\n if (dbVersion !== expectedDbVersion) {\n appLogger.debug('people-relationships', 'skipped session facts cache write because db version changed', {\n sessionId,\n })\n return\n }\n writeCachedPeopleRelationshipsSessionFacts(\n sessionId,\n factsCache.dir,\n buildPeopleRelationshipsSessionFactsCacheKey(PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION, timeRange),\n dbVersion,\n facts\n )\n writeCachedPeopleRelationshipsSessionLatest(sessionId, factsCache.dir, factsCache.latestKey, dbVersion, {\n latestMessageTs: facts.latestMessageTs,\n })\n factsCache.stats.writes += 2\n}\n\nfunction getSessionDbVersion(adapter: SessionRuntimeAdapter, sessionId: string): string {\n return getDbFileVersion(adapter.getDbPath(sessionId))\n}\n\nfunction getOrCreateNode(\n nodes: Map<string, NodeAccumulator>,\n sessionId: string,\n meta: PeopleRelationshipsSessionMetaFacts,\n contact: ContactMemberRef\n): NodeAccumulator {\n const sessionScoped = shouldScopeContactToSession(meta.platform, contact)\n const key = buildContactKey(meta.platform, contact.platformId, sessionScoped ? sessionId : undefined)\n const existing = nodes.get(key)\n if (existing) {\n mergeContactIdentity(existing, contact)\n return existing\n }\n\n const created: NodeAccumulator = {\n key,\n kind: 'contact',\n platform: meta.platform,\n platformId: contact.platformId,\n sessionScoped,\n sessionId: sessionScoped ? sessionId : undefined,\n displayName: contact.name || contact.platformId,\n aliases: new Set([contact.platformId, contact.name, ...contact.aliases].filter(Boolean)),\n avatar: contact.avatar,\n isFriend: false,\n privateMessageCount: 0,\n activePrivateMonths: new Set(),\n groupMessageCount: 0,\n commonGroupSessionIds: new Set(),\n ownerCoOccurrenceCount: 0,\n ownerCoOccurrenceRawScore: 0,\n ownerReplyInteractionCount: 0,\n ownerRepliesFromOwnerToContact: 0,\n ownerRepliesFromContactToOwner: 0,\n communityWeights: new Map(),\n edgeWeight: 0,\n panoramaCandidate: false,\n lastInteractionTs: null,\n }\n nodes.set(key, created)\n return created\n}\n\nfunction getOrCreateOwnerNode(\n nodes: Map<string, NodeAccumulator>,\n meta: PeopleRelationshipsSessionMetaFacts\n): NodeAccumulator | null {\n if (!meta.owner) return null\n const key = buildOwnerKey()\n const existing = nodes.get(key)\n if (existing) {\n mergeContactIdentity(existing, meta.owner)\n return existing\n }\n\n const created: NodeAccumulator = {\n key,\n kind: 'owner',\n platform: meta.platform,\n platformId: meta.owner.platformId || meta.ownerId || OWNER_KEY_PREFIX,\n sessionScoped: false,\n displayName: meta.owner.name || meta.owner.platformId || 'Me',\n aliases: new Set(\n [meta.owner.platformId, meta.owner.name, ...meta.owner.aliases, '我', 'me', 'owner'].filter(Boolean)\n ),\n avatar: meta.owner.avatar,\n isFriend: true,\n privateMessageCount: 0,\n activePrivateMonths: new Set(),\n groupMessageCount: 0,\n commonGroupSessionIds: new Set(),\n ownerCoOccurrenceCount: 0,\n ownerCoOccurrenceRawScore: 0,\n ownerReplyInteractionCount: 0,\n ownerRepliesFromOwnerToContact: 0,\n ownerRepliesFromContactToOwner: 0,\n communityWeights: new Map(),\n edgeWeight: 0,\n panoramaCandidate: true,\n lastInteractionTs: null,\n }\n nodes.set(key, created)\n return created\n}\n\nfunction applyOwnerContactEdge(\n edges: Map<string, EdgeAccumulator>,\n ownerNode: NodeAccumulator,\n contactNode: NodeAccumulator,\n facts: {\n coOccurrenceCount: number\n coOccurrenceRawScore: number\n replyInteractionCount: number\n repliesFromOwnerToContact: number\n repliesFromContactToOwner: number\n lastInteractionTs: number | null\n sessionId: string\n panoramaEligible?: boolean\n }\n): void {\n const sourceKey = ownerNode.key < contactNode.key ? ownerNode.key : contactNode.key\n const targetKey = ownerNode.key < contactNode.key ? contactNode.key : ownerNode.key\n const edge = getOrCreateEdge(edges, sourceKey, targetKey)\n edge.coOccurrenceCount += facts.coOccurrenceCount\n edge.coOccurrenceRawScore += facts.coOccurrenceRawScore\n edge.replyInteractionCount += facts.replyInteractionCount\n if (sourceKey === ownerNode.key) {\n edge.repliesFromSourceToTarget += facts.repliesFromOwnerToContact\n edge.repliesFromTargetToSource += facts.repliesFromContactToOwner\n } else {\n edge.repliesFromSourceToTarget += facts.repliesFromContactToOwner\n edge.repliesFromTargetToSource += facts.repliesFromOwnerToContact\n }\n edge.sourceSessionIds.add(facts.sessionId)\n if (facts.panoramaEligible) edge.panoramaEligible = true\n edge.lastInteractionTs = maxNullableTs(edge.lastInteractionTs, facts.lastInteractionTs)\n contactNode.ownerCoOccurrenceCount += facts.coOccurrenceCount\n contactNode.ownerCoOccurrenceRawScore += facts.coOccurrenceRawScore\n contactNode.ownerReplyInteractionCount += facts.replyInteractionCount\n contactNode.ownerRepliesFromOwnerToContact += facts.repliesFromOwnerToContact\n contactNode.ownerRepliesFromContactToOwner += facts.repliesFromContactToOwner\n updateLastInteraction(contactNode, facts.lastInteractionTs)\n}\n\nfunction getOrCreateEdge(edges: Map<string, EdgeAccumulator>, sourceKey: string, targetKey: string): EdgeAccumulator {\n const id = edgeId(sourceKey, targetKey)\n const existing = edges.get(id)\n if (existing) return existing\n const created: EdgeAccumulator = {\n sourceKey,\n targetKey,\n coOccurrenceCount: 0,\n coOccurrenceRawScore: 0,\n replyInteractionCount: 0,\n repliesFromSourceToTarget: 0,\n repliesFromTargetToSource: 0,\n sourceSessionIds: new Set(),\n panoramaEligible: false,\n lastInteractionTs: null,\n }\n edges.set(id, created)\n return created\n}\n\nfunction toPeopleRelationshipsSessionMetaFacts(\n meta: SessionMeta,\n owner?: ContactMemberRef\n): PeopleRelationshipsSessionMetaFacts {\n return {\n name: meta.name,\n platform: meta.platform,\n type: meta.type as ChatType.PRIVATE | ChatType.GROUP,\n ownerId: meta.ownerId,\n owner,\n }\n}\n\nfunction buildOwnerKey(): string {\n return OWNER_KEY_PREFIX\n}\n\nfunction mergeContactIdentity(acc: NodeAccumulator, contact: ContactMemberRef): void {\n if (contact.name) acc.aliases.add(contact.name)\n acc.aliases.add(contact.platformId)\n for (const alias of contact.aliases) acc.aliases.add(alias)\n if ((!acc.displayName || acc.displayName === acc.platformId) && contact.name) acc.displayName = contact.name\n if (!acc.avatar && contact.avatar) acc.avatar = contact.avatar\n}\n\nfunction pickCommunityId(node: NodeAccumulator): string {\n let bestId = node.isFriend ? PRIVATE_COMMUNITY_ID : ''\n let bestWeight = node.isFriend ? 1 : 0\n for (const [id, weight] of node.communityWeights) {\n if (weight > bestWeight || (weight === bestWeight && id < bestId)) {\n bestId = id\n bestWeight = weight\n }\n }\n return bestId || PRIVATE_COMMUNITY_ID\n}\n\nfunction computeEdgeWeight(edge: EdgeAccumulator): number {\n return computeRawEdgeWeight(edge)\n}\n\nfunction computeRawEdgeWeight(edge: {\n coOccurrenceRawScore: number\n replyInteractionCount: number\n coOccurrenceCount: number\n}): number {\n return (\n edge.coOccurrenceRawScore +\n edge.replyInteractionCount * REPLY_WEIGHT +\n edge.coOccurrenceCount * CO_OCCURRENCE_COUNT_WEIGHT\n )\n}\n\n// 默认全景优先级复用联系人页评分;关系边权只作为同分时的补充,避免大群活跃成员挤掉真正联系人。\nfunction computeContactPriorityScores(nodes: NodeAccumulator[]): Map<NodeAccumulator, number> {\n const result = new Map<NodeAccumulator, number>()\n for (const node of nodes) {\n if (node.kind === 'owner') result.set(node, 1)\n }\n\n const friendInputs = nodes\n .filter((node) => node.kind !== 'owner' && node.isFriend)\n .map((node) => ({\n node,\n privateMessageCount: node.privateMessageCount,\n activeMonths: [...node.activePrivateMonths],\n commonGroupCount: node.commonGroupSessionIds.size,\n }))\n const nonFriendInputs = nodes\n .filter((node) => node.kind !== 'owner' && !node.isFriend)\n .map((node) => ({\n node,\n coOccurrenceRawScore: node.ownerCoOccurrenceRawScore,\n commonGroupCount: node.commonGroupSessionIds.size,\n replyInteractionCount: node.ownerReplyInteractionCount,\n coOccurrenceCount: node.ownerCoOccurrenceCount,\n repliesFromOwnerToContact: node.ownerRepliesFromOwnerToContact,\n repliesFromContactToOwner: node.ownerRepliesFromContactToOwner,\n }))\n\n const friendScores = computeFriendScores(friendInputs)\n const nonFriendScores = computeNonFriendScores(nonFriendInputs)\n for (const input of friendInputs) {\n result.set(input.node, friendScores.get(input)?.score ?? 0)\n }\n for (const input of nonFriendInputs) {\n result.set(input.node, nonFriendScores.get(input)?.score ?? 0)\n }\n return result\n}\n\nfunction compareRankedNodes(\n a: { node: NodeAccumulator; score: number; relationshipActivityScore: number },\n b: { node: NodeAccumulator; score: number; relationshipActivityScore: number }\n): number {\n const poolPriorityDiff = getNodePoolPriority(a.node) - getNodePoolPriority(b.node)\n if (poolPriorityDiff !== 0) return poolPriorityDiff\n return (\n b.score - a.score ||\n b.relationshipActivityScore - a.relationshipActivityScore ||\n a.node.displayName.localeCompare(b.node.displayName)\n )\n}\n\nfunction getNodePoolPriority(node: NodeAccumulator): number {\n if (node.kind === 'owner') return 0\n return node.isFriend ? 1 : 2\n}\n\nfunction computeRelationshipActivityScore(node: NodeAccumulator): number {\n return (\n (node.isFriend ? 60 : 0) +\n node.privateMessageCount * 1.8 +\n node.groupMessageCount * 0.35 +\n node.commonGroupSessionIds.size * 8 +\n node.edgeWeight * 6\n )\n}\n\nfunction normalizeLimits(limits: PeopleRelationshipsComputeLimits = {}): Required<PeopleRelationshipsComputeLimits> {\n return {\n coreNodeLimit: normalizePositiveLimit(limits.coreNodeLimit, 1500),\n coreEdgeLimit: normalizePositiveLimit(limits.coreEdgeLimit, 6000),\n perNodeEdgeLimit: normalizePositiveLimit(limits.perNodeEdgeLimit, 12),\n neighborhoodNodeLimit: normalizePositiveLimit(limits.neighborhoodNodeLimit, 80),\n neighborhoodEdgeLimit: normalizePositiveLimit(limits.neighborhoodEdgeLimit, 240),\n searchResultLimit: normalizePositiveLimit(limits.searchResultLimit, 20),\n }\n}\n\nfunction normalizePositiveLimit(value: number | undefined, fallback: number): number {\n if (!Number.isFinite(value)) return fallback\n return Math.max(1, Math.trunc(value!))\n}\n\nfunction createEmptyDiagnostics(): PeopleRelationshipsDiagnostics {\n return {\n processedPrivateSessions: 0,\n processedGroupSessions: 0,\n skippedMissingOwnerSessions: 0,\n skippedUnresolvedOwnerSessions: 0,\n skippedAmbiguousPrivateSessions: 0,\n skippedFailedSessions: 0,\n totalNodes: 0,\n totalEdges: 0,\n panoramaIncludedGroupSessions: 0,\n panoramaExcludedLowValueGroupSessions: 0,\n panoramaIncludedGroupMembers: 0,\n panoramaExcludedGroupMembers: 0,\n panoramaCandidateNodes: 0,\n panoramaGroupInclusionReasons: {},\n coreNodeCount: 0,\n coreEdgeCount: 0,\n warnings: [],\n }\n}\n\nfunction updateLastInteraction(acc: NodeAccumulator, ts: number | null): void {\n acc.lastInteractionTs = maxNullableTs(acc.lastInteractionTs, ts)\n}\n\nfunction maxNullableTs(current: number | null, next: number | null): number | null {\n if (next === null) return current\n return Math.max(current ?? 0, next)\n}\n\nfunction edgeId(sourceKey: string, targetKey: string): string {\n return `${sourceKey}__${targetKey}`\n}\n\nfunction sortedEdgeId(aKey: string, bKey: string): string {\n return aKey < bKey ? edgeId(aKey, bKey) : edgeId(bKey, aKey)\n}\n\nfunction colorForCommunity(id: string): string {\n return COMMUNITY_COLORS[stableHash(id) % COMMUNITY_COLORS.length]\n}\n\nfunction stableHash(value: string): number {\n let hash = 2166136261\n for (let index = 0; index < value.length; index++) {\n hash ^= value.charCodeAt(index)\n hash = Math.imul(hash, 16777619)\n }\n return hash >>> 0\n}\n\nexport function compareNodes(a: PeopleRelationshipGraphNode, b: PeopleRelationshipGraphNode): number {\n return a.rank - b.rank || a.displayName.localeCompare(b.displayName)\n}\n\nexport function compareEdges(a: PeopleRelationshipGraphEdge, b: PeopleRelationshipGraphEdge): number {\n return b.weight - a.weight || a.id.localeCompare(b.id)\n}\n\nfunction sortEdgesForDisplay(edges: PeopleRelationshipGraphEdge[]): PeopleRelationshipGraphEdge[] {\n const anchorTs = edges.reduce((max, edge) => Math.max(max, edge.lastInteractionTs ?? 0), 0)\n return [...edges].sort((a, b) => compareEdgesForDisplay(a, b, anchorTs))\n}\n\nfunction compareEdgesForDisplay(\n a: PeopleRelationshipGraphEdge,\n b: PeopleRelationshipGraphEdge,\n anchorTs: number\n): number {\n const bRecentWeight = getRecentEdgeWeight(b, anchorTs)\n const aRecentWeight = getRecentEdgeWeight(a, anchorTs)\n if (bRecentWeight !== aRecentWeight) return bRecentWeight - aRecentWeight\n return compareEdges(a, b)\n}\n\nfunction getRecentEdgeWeight(edge: PeopleRelationshipGraphEdge, anchorTs: number): number {\n if (!edge.lastInteractionTs || anchorTs <= 0) return edge.weight\n const ageSeconds = Math.max(0, anchorTs - edge.lastInteractionTs)\n const recencyFactor =\n EDGE_RECENCY_FLOOR + (1 - EDGE_RECENCY_FLOOR) * Math.pow(0.5, ageSeconds / EDGE_RECENCY_HALF_LIFE_SECONDS)\n return edge.weight * recencyFactor\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\nfunction roundNum(value: number, digits = 4): number {\n const factor = 10 ** digits\n return Math.round(value * factor) / factor\n}\n"],"mappings":"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","names":["edgeId"]}