chatlab-cli 0.37.0 → 0.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/dist/{chat-command-WBAJBKIE.mjs → chat-command-YAFRLHMQ.mjs} +8 -8
  2. package/dist/{chunk-N2WGG3MS.mjs → chunk-2PY4XCH3.mjs} +6 -3
  3. package/dist/chunk-2PY4XCH3.mjs.map +1 -0
  4. package/dist/{chunk-FM3GXNEO.mjs → chunk-73TJ2S7W.mjs} +2 -2
  5. package/dist/{chunk-MSMDLDPB.mjs → chunk-ARIBCH47.mjs} +4 -4
  6. package/dist/{chunk-CR2MU7LN.mjs → chunk-FPVRSXQ4.mjs} +12 -12
  7. package/dist/{chunk-DQR2YS3I.mjs → chunk-GKCIRKOH.mjs} +8 -6
  8. package/dist/{chunk-DQR2YS3I.mjs.map → chunk-GKCIRKOH.mjs.map} +1 -1
  9. package/dist/{chunk-5YOHLJFB.mjs → chunk-JWJREWVN.mjs} +24 -7
  10. package/dist/chunk-JWJREWVN.mjs.map +1 -0
  11. package/dist/{chunk-RXMSZIYD.mjs → chunk-MMTEGUQA.mjs} +3 -3
  12. package/dist/{chunk-EJBBVLUN.mjs → chunk-N4SB6QZV.mjs} +2 -2
  13. package/dist/{chunk-4J4MA3RC.mjs → chunk-OTYNMDDL.mjs} +83 -55
  14. package/dist/chunk-OTYNMDDL.mjs.map +1 -0
  15. package/dist/{chunk-SQWN4GTQ.mjs → chunk-PDQV4RUA.mjs} +2 -2
  16. package/dist/{chunk-DKIYHA3O.mjs → chunk-UP6JIV3S.mjs} +15 -22
  17. package/dist/chunk-UP6JIV3S.mjs.map +1 -0
  18. package/dist/{chunk-2TV7M4CC.mjs → chunk-WDLO7OLX.mjs} +2 -2
  19. package/dist/cli.mjs +9 -9
  20. package/dist/contacts-worker.mjs +2 -2
  21. package/dist/global-insight-worker.mjs +1 -1
  22. package/dist/{http-X6OEOOSH.mjs → http-LPFPVTBC.mjs} +12 -12
  23. package/dist/{import-WBJ6YIQ7.mjs → import-T4WX2ZJL.mjs} +7 -7
  24. package/dist/index.mjs +12 -12
  25. package/dist/{mcp-FR6OKWKL.mjs → mcp-SMF3NWIN.mjs} +8 -8
  26. package/dist/people-relationships-worker.mjs +2 -2
  27. package/dist/semantic-index-worker.mjs +1 -1
  28. package/dist/{src-WE5HNN2O.mjs → src-EGLLMTAU.mjs} +6 -6
  29. package/dist/{stream-import-YYQVZDGQ.mjs → stream-import-2BXJTTCE.mjs} +7 -7
  30. package/dist/{worker-runtime-YGB5ZDE4.mjs → worker-runtime-RDLSPXFT.mjs} +5 -5
  31. package/dist-cli-web/assets/{AISettingsTab-Co2sM1iY.js → AISettingsTab-CEhrXlYR.js} +1 -1
  32. package/dist-cli-web/assets/{AboutTab-DQ8r6mYE.js → AboutTab-Bbu0SsMM.js} +1 -1
  33. package/dist-cli-web/assets/{ActionToolsPanel-CmYpw8Mt.css → ActionToolsPanel-DDNXXH8v.css} +1 -1
  34. package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-IB1yZxCE.js +75 -0
  35. package/dist-cli-web/assets/{Alert-DU1tLAAg.js → Alert-510jTN3l.js} +1 -1
  36. package/dist-cli-web/assets/{ApiSettingsTab-D5g5b79L.js → ApiSettingsTab-Bi1GRMN4.js} +1 -1
  37. package/dist-cli-web/assets/{BatchManageTab-BFUyVaGU.js → BatchManageTab-DhnqMCSM.js} +1 -1
  38. package/dist-cli-web/assets/{BufferResource-C-RfYwQW.js → BufferResource-CKZxnACp.js} +1 -1
  39. package/dist-cli-web/assets/{CanvasRenderer-Dz7JAMrR.js → CanvasRenderer-BeK5POBy.js} +1 -1
  40. package/dist-cli-web/assets/{CardDecoration-D28DI1G9.js → CardDecoration-RuBA3N2j.js} +1 -1
  41. package/dist-cli-web/assets/{ChatRecordDrawer-Cph819yY.js → ChatRecordDrawer-BY_-lIEj.js} +1 -1
  42. package/dist-cli-web/assets/{ChatRecordWorkspace-BAfERdPc.js → ChatRecordWorkspace-7prKd-Tq.js} +1 -1
  43. package/dist-cli-web/assets/{Checkbox-B8uqt13j.js → Checkbox-_eH64bb0.js} +1 -1
  44. package/dist-cli-web/assets/{DataDirCleanupNotice-3sGBE5vU.js → DataDirCleanupNotice-BHaXwlfr.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-D9Ul_GYz.js} +1 -1
  46. package/dist-cli-web/assets/{DebugToolsPanel-Cwkr0BOY.js → DebugToolsPanel-ByKItGrS.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-DubnuUXI.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-DKNCZuFm.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-Fl7xAOSR.js} +1 -1
  50. package/dist-cli-web/assets/{EChartWordcloud-CHVtyPR7.js → EChartWordcloud-C6XM4KOX.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-DYDdRSNE.js} +1 -1
  52. package/dist-cli-web/assets/{Filter-CMfRHIoK.js → Filter-CByj_rCR.js} +1 -1
  53. package/dist-cli-web/assets/{GlobalTaskBar-Jz6MASCu.js → GlobalTaskBar-BdELjzrs.js} +1 -1
  54. package/dist-cli-web/assets/{GroupRelationshipGalaxyView-Ch4obfEq.js → GroupRelationshipGalaxyView-CnzPEgUS.js} +1 -1
  55. package/dist-cli-web/assets/{Label-CuryuymO.js → Label-DCIcxkhU.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-BBcCt_Qm.js} +1 -1
  57. package/dist-cli-web/assets/{MessageExportModal-JqGZ-iLr.js → MessageExportModal-DNU16Bs8.js} +1 -1
  58. package/dist-cli-web/assets/{PageHeader-D4up77TB.js → PageHeader-CKeZjNNR.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-C8M3wNNH.js} +1 -1
  60. package/dist-cli-web/assets/{RelationshipGalaxyCanvas-Cn7Eta5U.js → RelationshipGalaxyCanvas-CDhljGyJ.js} +3 -3
  61. package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-LlaX57z6.js → RelationshipGalaxyThreeCanvas-BP8fsvvf.js} +1 -1
  62. package/dist-cli-web/assets/{RenderTargetSystem-JhA2PUFi.js → RenderTargetSystem-D76QrDT_.js} +1 -1
  63. package/dist-cli-web/assets/{RovingFocusItem-DHaiTb4j.js → RovingFocusItem-BJMUQ5DJ.js} +1 -1
  64. package/dist-cli-web/assets/{ScreenCaptureModal-Bxhyt5g1.js → ScreenCaptureModal-DkX4z-M1.js} +1 -1
  65. package/dist-cli-web/assets/{SectionTabs.vue_vue_type_script_setup_true_lang-BlAje3Tf.js → SectionTabs.vue_vue_type_script_setup_true_lang-qFhJO5B0.js} +1 -1
  66. package/dist-cli-web/assets/{Select-Beeqv0wU.js → Select-B5oJc5y1.js} +1 -1
  67. package/dist-cli-web/assets/SettingsModal-BO-QKQ2h.js +2 -0
  68. package/dist-cli-web/assets/{StorageTab-B7GHadia.js → StorageTab-CPPXXaRm.js} +1 -1
  69. package/dist-cli-web/assets/{Switch-u4zlvUMa.js → Switch-CDk3LZ4_.js} +1 -1
  70. package/dist-cli-web/assets/{Tabs-RWKHwtfM.js → Tabs-BMq9wODW.js} +1 -1
  71. package/dist-cli-web/assets/{Tabs-B7-Koq3x.js → Tabs-DqWTBV8x.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-D2outEOV.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-BcflxP_U.js} +1 -1
  74. package/dist-cli-web/assets/{VisuallyHiddenInput-DGDqcvUy.js → VisuallyHiddenInput-BAFpkxMI.js} +1 -1
  75. package/dist-cli-web/assets/{WebGLRenderer-ifvgA1fe.js → WebGLRenderer-DjCPvR_O.js} +1 -1
  76. package/dist-cli-web/assets/{WebGPURenderer-DwzmuWF0.js → WebGPURenderer-C2B_u03z.js} +1 -1
  77. package/dist-cli-web/assets/aiChat--CR4g2M7.js +8 -0
  78. package/dist-cli-web/assets/{base-BWDX-164.js → base-F6s0vTrr.js} +1 -1
  79. package/dist-cli-web/assets/{browserAll-CSIIETeE.js → browserAll-DiUAZg4L.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-Bd33SSXI.js} +20 -20
  82. package/dist-cli-web/assets/{cli-web-BRY49NWe.js → cli-web-DoibEdj2.js} +1 -1
  83. package/dist-cli-web/assets/{fetch-jx1VdM9j.js → fetch-4nh1LaVF.js} +1 -1
  84. package/dist-cli-web/assets/{fetch-qBaUXqFt.js → fetch-7lUvXbhU.js} +1 -1
  85. package/dist-cli-web/assets/{fetch-DNSDHSqQ.js → fetch-BEaW1j5H.js} +1 -1
  86. package/dist-cli-web/assets/{fetch-wzv71PYy.js → fetch-B_YFABxI.js} +1 -1
  87. package/dist-cli-web/assets/{fetch-D3nLtFBi.js → fetch-BeX69kpS.js} +1 -1
  88. package/dist-cli-web/assets/{fetch-Bf4cqeM3.js → fetch-CRocA-4C.js} +1 -1
  89. package/dist-cli-web/assets/{fetch-BiXab0IF.js → fetch-CXM-cjDS.js} +1 -1
  90. package/dist-cli-web/assets/{fetch-Co2vLmiE.js → fetch-CXqeAbnY.js} +1 -1
  91. package/dist-cli-web/assets/{fetch-r6mhEGrP.js → fetch-CxVcHNid.js} +1 -1
  92. package/dist-cli-web/assets/fetch-DBpDMy29.js +1 -0
  93. package/dist-cli-web/assets/{fetch-mYSeL4Ki.js → fetch-DKBBS9Op.js} +1 -1
  94. package/dist-cli-web/assets/{fetch-DeHqF6yL.js → fetch-epFXmPIQ.js} +1 -1
  95. package/dist-cli-web/assets/{host-jXJHyyOX.js → host-C4rU8pKA.js} +1 -1
  96. package/dist-cli-web/assets/{host-1emQf9_4.js → host-CtSqR_Zm.js} +1 -1
  97. package/dist-cli-web/assets/{index-Cw0IhKZ6.js → index-4DVZ2g0M.js} +1 -1
  98. package/dist-cli-web/assets/{index-DnpnIyle.js → index-B1oj9KDv.js} +33 -33
  99. package/dist-cli-web/assets/{index-slDUxib2.js → index-BFH1j_R9.js} +1 -1
  100. package/dist-cli-web/assets/{index-DOqbUvhA.js → index-BvMAw3Zd.js} +1 -1
  101. package/dist-cli-web/assets/{index-2dJCozDq.js → index-CaYtkONV.js} +4 -4
  102. package/dist-cli-web/assets/{index-B4mJkX_d.js → index-CuCQ1d9f.js} +1 -1
  103. package/dist-cli-web/assets/index-D3Hyjv4e.js +137 -0
  104. package/dist-cli-web/assets/{index-ClHMs0-a.js → index-D8cHMqV-.js} +2 -2
  105. package/dist-cli-web/assets/{index-BQ463TS7.js → index-DlHuILT_.js} +1 -1
  106. package/dist-cli-web/assets/{index-6ejRbH3h.js → index-R4LqAnx9.js} +2 -2
  107. package/dist-cli-web/assets/{index-LV9LfFNu.js → index-_m1mf1jm.js} +1 -1
  108. package/dist-cli-web/assets/{index-CYc_VLgK.js → index-ekvsNmfe.js} +1 -1
  109. package/dist-cli-web/assets/{index-B7biznha.js → index-mXe1LssU.js} +1 -1
  110. package/dist-cli-web/assets/{index-BOcFyPCa.js → index-mbLXPIyC.js} +1 -1
  111. package/dist-cli-web/assets/{people-page-header-Cx5r9Bk2.js → people-page-header-BbYSSpsO.js} +1 -1
  112. package/dist-cli-web/assets/{prompt-BggEDxxi.js → prompt-C-ofJ7t_.js} +1 -1
  113. package/dist-cli-web/assets/{semantic-index-config-builder-1sO0aEc1.js → semantic-index-config-builder-D3TYVYC-.js} +1 -1
  114. package/dist-cli-web/assets/service-BYOUkk7V.js +1 -0
  115. package/dist-cli-web/assets/{service-Bvq1fRkC.js → service-CppETV4a.js} +1 -1
  116. package/dist-cli-web/assets/service-tEx-5LUL.js +1 -0
  117. package/dist-cli-web/assets/useFormControl-CrWi_6Hn.js +1 -0
  118. package/dist-cli-web/assets/{useTable-DC7oxmBl.js → useTable-Clk7ecq-.js} +1 -1
  119. package/dist-cli-web/assets/{useToast-CCPBE-Sy.js → useToast-B5LaA6c-.js} +1 -1
  120. package/dist-cli-web/assets/{webworkerAll-FCJk6reG.js → webworkerAll-CyA4b4LT.js} +1 -1
  121. package/dist-cli-web/index.html +1 -1
  122. package/package.json +3 -3
  123. package/dist/chunk-4J4MA3RC.mjs.map +0 -1
  124. package/dist/chunk-5YOHLJFB.mjs.map +0 -1
  125. package/dist/chunk-DKIYHA3O.mjs.map +0 -1
  126. package/dist/chunk-N2WGG3MS.mjs.map +0 -1
  127. package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang--RPlmfIu.js +0 -75
  128. package/dist-cli-web/assets/SettingsModal-DV8yDBJs.js +0 -2
  129. package/dist-cli-web/assets/aiChat-BY4C3UQe.js +0 -8
  130. package/dist-cli-web/assets/fetch-C8CcoTWY.js +0 -1
  131. package/dist-cli-web/assets/index-kePiJ3ca.js +0 -137
  132. package/dist-cli-web/assets/service-DkPheDt8.js +0 -1
  133. package/dist-cli-web/assets/service-DvI8O_7K.js +0 -1
  134. package/dist-cli-web/assets/useFormControl-B0aShtLp.js +0 -1
  135. /package/dist/{chat-command-WBAJBKIE.mjs.map → chat-command-YAFRLHMQ.mjs.map} +0 -0
  136. /package/dist/{chunk-FM3GXNEO.mjs.map → chunk-73TJ2S7W.mjs.map} +0 -0
  137. /package/dist/{chunk-MSMDLDPB.mjs.map → chunk-ARIBCH47.mjs.map} +0 -0
  138. /package/dist/{chunk-CR2MU7LN.mjs.map → chunk-FPVRSXQ4.mjs.map} +0 -0
  139. /package/dist/{chunk-RXMSZIYD.mjs.map → chunk-MMTEGUQA.mjs.map} +0 -0
  140. /package/dist/{chunk-EJBBVLUN.mjs.map → chunk-N4SB6QZV.mjs.map} +0 -0
  141. /package/dist/{chunk-SQWN4GTQ.mjs.map → chunk-PDQV4RUA.mjs.map} +0 -0
  142. /package/dist/{chunk-2TV7M4CC.mjs.map → chunk-WDLO7OLX.mjs.map} +0 -0
  143. /package/dist/{http-X6OEOOSH.mjs.map → http-LPFPVTBC.mjs.map} +0 -0
  144. /package/dist/{import-WBJ6YIQ7.mjs.map → import-T4WX2ZJL.mjs.map} +0 -0
  145. /package/dist/{mcp-FR6OKWKL.mjs.map → mcp-SMF3NWIN.mjs.map} +0 -0
  146. /package/dist/{src-WE5HNN2O.mjs.map → src-EGLLMTAU.mjs.map} +0 -0
  147. /package/dist/{stream-import-YYQVZDGQ.mjs.map → stream-import-2BXJTTCE.mjs.map} +0 -0
  148. /package/dist/{worker-runtime-YGB5ZDE4.mjs.map → worker-runtime-RDLSPXFT.mjs.map} +0 -0
@@ -6,16 +6,16 @@ const __filename = __fileURLToPath(import.meta.url);
6
6
  const __dirname = __pathDirname(__filename);
7
7
  import {
8
8
  createCliRunAgentStream
9
- } from "./chunk-MSMDLDPB.mjs";
10
- import "./chunk-FM3GXNEO.mjs";
11
- import "./chunk-5YOHLJFB.mjs";
12
- import "./chunk-DQR2YS3I.mjs";
13
- import "./chunk-EJBBVLUN.mjs";
14
- import "./chunk-N2WGG3MS.mjs";
9
+ } from "./chunk-ARIBCH47.mjs";
10
+ import "./chunk-73TJ2S7W.mjs";
11
+ import "./chunk-JWJREWVN.mjs";
12
+ import "./chunk-GKCIRKOH.mjs";
13
+ import "./chunk-N4SB6QZV.mjs";
14
+ import "./chunk-2PY4XCH3.mjs";
15
15
  import "./chunk-YKMQLAVP.mjs";
16
16
  import {
17
17
  getDefaultGeneralAssistantId
18
- } from "./chunk-4J4MA3RC.mjs";
18
+ } from "./chunk-OTYNMDDL.mjs";
19
19
  import "./chunk-VSOWNKPA.mjs";
20
20
 
21
21
  // src/ai/chat-command.ts
@@ -311,4 +311,4 @@ export {
311
311
  runChatCommand,
312
312
  runChatTurn
313
313
  };
314
- //# sourceMappingURL=chat-command-WBAJBKIE.mjs.map
314
+ //# sourceMappingURL=chat-command-YAFRLHMQ.mjs.map
@@ -21,7 +21,7 @@ import {
21
21
  resolveOwnerMember,
22
22
  setCache,
23
23
  shouldScopeContactToSession
24
- } from "./chunk-4J4MA3RC.mjs";
24
+ } from "./chunk-OTYNMDDL.mjs";
25
25
 
26
26
  // ../../packages/node-runtime/src/services/people/relationships/facts-cache.ts
27
27
  var PEOPLE_RELATIONSHIPS_FACTS_FORMAT_VERSION = 3;
@@ -190,7 +190,7 @@ var COMMUNITY_COLORS = [
190
190
  ];
191
191
  function computePeopleRelationshipsSnapshot(options) {
192
192
  const startedAt = options.now?.() ?? Date.now();
193
- const sessionIds = options.adapter.listSessionIds();
193
+ const sessionIds = options.adapter.listSessionCandidateIds?.() ?? options.adapter.listSessionIds();
194
194
  const limits = normalizeLimits(options.limits);
195
195
  const factsCache = options.factsCacheDir ? createFactsCacheContext(options.factsCacheDir) : null;
196
196
  const timeRange = resolvePeopleRelationshipsTimeRange(
@@ -374,6 +374,7 @@ function computePeopleRelationships(options) {
374
374
  diagnostics.skippedFailedSessions++;
375
375
  appLogger.error("people-relationships", `failed to process people relationship session: ${sessionId}`, error);
376
376
  } finally {
377
+ options.adapter.closeSession(sessionId);
377
378
  processedSessions++;
378
379
  options.onProgress?.({ processedSessions, totalSessions: options.sessionIds.length, currentSessionId: sessionId });
379
380
  }
@@ -408,6 +409,8 @@ function findGlobalLatestMessageTs(adapter, sessionIds, factsCache) {
408
409
  `failed to inspect people relationship session range: ${sessionId}`,
409
410
  error
410
411
  );
412
+ } finally {
413
+ adapter.closeSession(sessionId);
411
414
  }
412
415
  }
413
416
  return latest;
@@ -1326,4 +1329,4 @@ export {
1326
1329
  compareNodes,
1327
1330
  compareEdges
1328
1331
  };
1329
- //# sourceMappingURL=chunk-N2WGG3MS.mjs.map
1332
+ //# sourceMappingURL=chunk-2PY4XCH3.mjs.map
@@ -0,0 +1 @@
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.listSessionCandidateIds?.() ?? 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 options.adapter.closeSession(sessionId)\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 } finally {\n adapter.closeSession(sessionId)\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"]}
@@ -28,7 +28,7 @@ import {
28
28
  getWeekdayActivity,
29
29
  queryMessages,
30
30
  searchMessagesByKeywords
31
- } from "./chunk-4J4MA3RC.mjs";
31
+ } from "./chunk-OTYNMDDL.mjs";
32
32
 
33
33
  // ../../packages/tools/src/utils/limits.ts
34
34
  function resolveMessageLimit(requestedLimit, defaultLimit, maxMessagesLimit) {
@@ -3578,4 +3578,4 @@ export {
3578
3578
  executeToolForAgent,
3579
3579
  createCrossChatAgentToolAdapters
3580
3580
  };
3581
- //# sourceMappingURL=chunk-FM3GXNEO.mjs.map
3581
+ //# sourceMappingURL=chunk-73TJ2S7W.mjs.map
@@ -11,12 +11,12 @@ import {
11
11
  createCrossChatAgentToolAdapters,
12
12
  executeToolForAgent,
13
13
  toAgentToolParameters
14
- } from "./chunk-FM3GXNEO.mjs";
14
+ } from "./chunk-73TJ2S7W.mjs";
15
15
  import {
16
16
  preprocessCrossChatLabel,
17
17
  preprocessCrossChatMessages,
18
18
  preprocessCrossChatSummaries
19
- } from "./chunk-DQR2YS3I.mjs";
19
+ } from "./chunk-GKCIRKOH.mjs";
20
20
  import {
21
21
  AgentEventHandler,
22
22
  AiLogger,
@@ -63,7 +63,7 @@ import {
63
63
  runCrossChatAgent,
64
64
  shouldUseChartCapabilityForMessage,
65
65
  wrapWithChartSchemaGate
66
- } from "./chunk-4J4MA3RC.mjs";
66
+ } from "./chunk-OTYNMDDL.mjs";
67
67
 
68
68
  // src/ai/logger.ts
69
69
  var logger = null;
@@ -661,4 +661,4 @@ export {
661
661
  getSkillManagerCore,
662
662
  createCliRunAgentStream
663
663
  };
664
- //# sourceMappingURL=chunk-MSMDLDPB.mjs.map
664
+ //# sourceMappingURL=chunk-ARIBCH47.mjs.map
@@ -8,14 +8,14 @@ import {
8
8
  formatPortInUseError,
9
9
  installCliLocalEmbeddingRuntime,
10
10
  isPortAvailable
11
- } from "./chunk-2TV7M4CC.mjs";
11
+ } from "./chunk-WDLO7OLX.mjs";
12
12
  import {
13
13
  analyzeAutoImport,
14
14
  autoImport
15
- } from "./chunk-RXMSZIYD.mjs";
15
+ } from "./chunk-MMTEGUQA.mjs";
16
16
  import {
17
17
  assertCliDataDirCompatible
18
- } from "./chunk-SQWN4GTQ.mjs";
18
+ } from "./chunk-PDQV4RUA.mjs";
19
19
  import {
20
20
  getVersion
21
21
  } from "./chunk-5ADKYYNA.mjs";
@@ -29,14 +29,14 @@ import {
29
29
  computeWordFrequency,
30
30
  getSystemLogsDir,
31
31
  hasPendingElectronDataWarning
32
- } from "./chunk-5YOHLJFB.mjs";
32
+ } from "./chunk-JWJREWVN.mjs";
33
33
  import {
34
34
  IMPORT_IN_PROGRESS_ERROR_KEY,
35
35
  detectFormat,
36
36
  logNativeParserStatus,
37
37
  owner_profile_service_exports,
38
38
  validateChatLabFile
39
- } from "./chunk-DQR2YS3I.mjs";
39
+ } from "./chunk-GKCIRKOH.mjs";
40
40
  import {
41
41
  AIChatManager,
42
42
  ConfigSetError,
@@ -79,7 +79,7 @@ import {
79
79
  searchMessagesLike,
80
80
  setConfigField,
81
81
  verifyCliDataPath
82
- } from "./chunk-4J4MA3RC.mjs";
82
+ } from "./chunk-OTYNMDDL.mjs";
83
83
 
84
84
  // src/cli.ts
85
85
  import * as fs4 from "fs";
@@ -2314,7 +2314,7 @@ registerImportCommand(program);
2314
2314
  registerValidateCommand(program);
2315
2315
  registerRuntimeCommand(program);
2316
2316
  program.command("formats").description("List all supported chat history formats").action(async () => {
2317
- const { getSupportedFormats } = await import("./import-WBJ6YIQ7.mjs");
2317
+ const { getSupportedFormats } = await import("./import-T4WX2ZJL.mjs");
2318
2318
  const formats = getSupportedFormats();
2319
2319
  console.log(`${formats.length} supported format(s):
2320
2320
  `);
@@ -2323,11 +2323,11 @@ program.command("formats").description("List all supported chat history formats"
2323
2323
  }
2324
2324
  });
2325
2325
  program.command("mcp").description("Start MCP Server (stdio transport, for ClaudeCode / Cursor / AI agents)").action(async () => {
2326
- const { startCliMcpServer } = await import("./mcp-FR6OKWKL.mjs");
2326
+ const { startCliMcpServer } = await import("./mcp-SMF3NWIN.mjs");
2327
2327
  await startCliMcpServer();
2328
2328
  });
2329
2329
  program.command("chat").description("Ask ChatLab AI about an imported chat session").option("--session-id <id>", "Source chat session ID").option("--ai-chat-id <id>", "Existing AI chat ID to continue").option("-q, --question <text>", "Question to ask").option("--json", "Output structured JSON").option("--include-events", "Include all agent stream chunks in JSON output").option("--no-stream", "Disable streaming output").option("--locale <locale>", "AI response locale", "zh-CN").action(async (options) => {
2330
- const { runChatCommand } = await import("./chat-command-WBAJBKIE.mjs");
2330
+ const { runChatCommand } = await import("./chat-command-YAFRLHMQ.mjs");
2331
2331
  const { dbManager, pathProvider } = initRuntime();
2332
2332
  const aiChatManager = new AIChatManager(pathProvider.getAiDataDir(), { nativeBinding: resolveNativeBinding() });
2333
2333
  try {
@@ -2367,7 +2367,7 @@ program.command("web").alias("start").description("Start ChatLab (HTTP API + Web
2367
2367
  });
2368
2368
  return;
2369
2369
  }
2370
- const { startHttpServer } = await import("./http-X6OEOOSH.mjs");
2370
+ const { startHttpServer } = await import("./http-LPFPVTBC.mjs");
2371
2371
  const port = parseInt(options.port, 10);
2372
2372
  let webRoot;
2373
2373
  if (!options.headless) {
@@ -2430,7 +2430,7 @@ Press Ctrl+C to stop.
2430
2430
  }
2431
2431
  const shutdown = async () => {
2432
2432
  console.log("\nShutting down...");
2433
- const { stopHttpServer } = await import("./http-X6OEOOSH.mjs");
2433
+ const { stopHttpServer } = await import("./http-LPFPVTBC.mjs");
2434
2434
  await stopHttpServer();
2435
2435
  process.exit(0);
2436
2436
  };
@@ -2523,4 +2523,4 @@ if (isDirectRun) {
2523
2523
  export {
2524
2524
  run
2525
2525
  };
2526
- //# sourceMappingURL=chunk-CR2MU7LN.mjs.map
2526
+ //# sourceMappingURL=chunk-FPVRSXQ4.mjs.map
@@ -9,7 +9,7 @@ import {
9
9
  createEmptyContactsDiagnostics,
10
10
  normalizeContactsTimeRangePreset,
11
11
  resolveContactsTimeRange
12
- } from "./chunk-EJBBVLUN.mjs";
12
+ } from "./chunk-N4SB6QZV.mjs";
13
13
  import {
14
14
  PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION,
15
15
  buildPeopleRelationshipsNeighborhoodGraph,
@@ -18,7 +18,7 @@ import {
18
18
  computePeopleRelationshipsSnapshot,
19
19
  normalizePeopleRelationshipsTimeRangePreset,
20
20
  resolvePeopleRelationshipsTimeRange
21
- } from "./chunk-N2WGG3MS.mjs";
21
+ } from "./chunk-2PY4XCH3.mjs";
22
22
  import {
23
23
  ANNUAL_SUMMARY_ALGORITHM_VERSION,
24
24
  TIME_INVESTMENT_ALGORITHM_VERSION,
@@ -114,7 +114,7 @@ import {
114
114
  writeAuthProfile,
115
115
  writeParseResultToDb,
116
116
  writeTimeInvestmentSnapshot
117
- } from "./chunk-4J4MA3RC.mjs";
117
+ } from "./chunk-OTYNMDDL.mjs";
118
118
  import {
119
119
  __commonJS,
120
120
  __export,
@@ -14117,7 +14117,8 @@ function buildPeopleRelationshipsSignature(adapter, timeRangePreset) {
14117
14117
  `algorithm:${PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION}`,
14118
14118
  `range:${normalizePeopleRelationshipsTimeRangePreset(timeRangePreset)}`
14119
14119
  ];
14120
- for (const sessionId of [...adapter.listSessionIds()].sort()) {
14120
+ const sessionIds = adapter.listSessionCandidateIds?.() ?? adapter.listSessionIds();
14121
+ for (const sessionId of [...sessionIds].sort()) {
14121
14122
  const dbPath = adapter.getDbPath(sessionId);
14122
14123
  parts.push(`${sessionId}:${getDbFileVersion(dbPath)}`);
14123
14124
  }
@@ -14341,6 +14342,7 @@ var DefaultPeopleRelationshipsService = class {
14341
14342
  }
14342
14343
  ensureTaskStarted(signature, timeRangePreset) {
14343
14344
  if (this.inFlight) return;
14345
+ const sessionIds = this.deps.adapter.listSessionCandidateIds?.() ?? this.deps.adapter.listSessionIds();
14344
14346
  const taskId = `people_relationships_${this.now()}_${Math.random().toString(36).slice(2)}`;
14345
14347
  this.task = {
14346
14348
  id: taskId,
@@ -14348,7 +14350,7 @@ var DefaultPeopleRelationshipsService = class {
14348
14350
  startedAt: this.now(),
14349
14351
  finishedAt: null,
14350
14352
  processedSessions: 0,
14351
- totalSessions: this.deps.adapter.listSessionIds().length,
14353
+ totalSessions: sessionIds.length,
14352
14354
  timeRangePreset
14353
14355
  };
14354
14356
  const abortController = new AbortController();
@@ -18422,4 +18424,4 @@ export {
18422
18424
  hashImportBody,
18423
18425
  ImportIdempotencyCache
18424
18426
  };
18425
- //# sourceMappingURL=chunk-DQR2YS3I.mjs.map
18427
+ //# sourceMappingURL=chunk-GKCIRKOH.mjs.map