chatlab-cli 0.37.0 → 0.37.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/dist/{chat-command-WBAJBKIE.mjs → chat-command-G2EX7VHR.mjs} +24 -12
  2. package/dist/chat-command-G2EX7VHR.mjs.map +1 -0
  3. package/dist/{chunk-N2WGG3MS.mjs → chunk-E6LCCPIB.mjs} +6 -3
  4. package/dist/chunk-E6LCCPIB.mjs.map +1 -0
  5. package/dist/{chunk-EJBBVLUN.mjs → chunk-EFX66A2O.mjs} +2 -2
  6. package/dist/{chunk-DKIYHA3O.mjs → chunk-EK5QIJLZ.mjs} +20 -23
  7. package/dist/chunk-EK5QIJLZ.mjs.map +1 -0
  8. package/dist/{chunk-5YOHLJFB.mjs → chunk-F4HVCFZL.mjs} +48 -7
  9. package/dist/chunk-F4HVCFZL.mjs.map +1 -0
  10. package/dist/{chunk-SQWN4GTQ.mjs → chunk-FCUUAGGC.mjs} +2 -2
  11. package/dist/{chunk-DQR2YS3I.mjs → chunk-H46CLFWK.mjs} +8 -6
  12. package/dist/{chunk-DQR2YS3I.mjs.map → chunk-H46CLFWK.mjs.map} +1 -1
  13. package/dist/{chunk-4J4MA3RC.mjs → chunk-HBAQPF6X.mjs} +2180 -1941
  14. package/dist/chunk-HBAQPF6X.mjs.map +1 -0
  15. package/dist/{chunk-RXMSZIYD.mjs → chunk-HMVM7VCA.mjs} +3 -3
  16. package/dist/{chunk-FM3GXNEO.mjs → chunk-L6RJIVLS.mjs} +2 -2
  17. package/dist/{chunk-MSMDLDPB.mjs → chunk-T5H56AZV.mjs} +16 -8
  18. package/dist/chunk-T5H56AZV.mjs.map +1 -0
  19. package/dist/{chunk-2TV7M4CC.mjs → chunk-XQ6Z2KNN.mjs} +2 -2
  20. package/dist/{chunk-CR2MU7LN.mjs → chunk-YI3EQYEG.mjs} +12 -12
  21. package/dist/cli.mjs +9 -9
  22. package/dist/contacts-worker.mjs +2 -2
  23. package/dist/global-insight-worker.mjs +1 -1
  24. package/dist/{http-X6OEOOSH.mjs → http-5BRVWAJR.mjs} +12 -12
  25. package/dist/{import-WBJ6YIQ7.mjs → import-IAECQDKC.mjs} +7 -7
  26. package/dist/index.mjs +12 -12
  27. package/dist/{mcp-FR6OKWKL.mjs → mcp-XXSQJBGU.mjs} +8 -8
  28. package/dist/people-relationships-worker.mjs +2 -2
  29. package/dist/semantic-index-worker.mjs +1 -1
  30. package/dist/{src-WE5HNN2O.mjs → src-4HLKMQ7S.mjs} +6 -6
  31. package/dist/{stream-import-YYQVZDGQ.mjs → stream-import-PUG42I36.mjs} +7 -7
  32. package/dist/{worker-runtime-YGB5ZDE4.mjs → worker-runtime-X5563TVT.mjs} +5 -5
  33. package/dist-cli-web/assets/{AISettingsTab-Co2sM1iY.js → AISettingsTab-m_r2fFEj.js} +1 -1
  34. package/dist-cli-web/assets/{AboutTab-DQ8r6mYE.js → AboutTab-J65UqIuk.js} +1 -1
  35. package/dist-cli-web/assets/{Alert-DU1tLAAg.js → Alert-qBIGn_CW.js} +1 -1
  36. package/dist-cli-web/assets/{ApiSettingsTab-D5g5b79L.js → ApiSettingsTab-CxWRJI9j.js} +1 -1
  37. package/dist-cli-web/assets/{BatchManageTab-BFUyVaGU.js → BatchManageTab-BINNVwm1.js} +1 -1
  38. package/dist-cli-web/assets/{BufferResource-C-RfYwQW.js → BufferResource-C-jLPrHv.js} +1 -1
  39. package/dist-cli-web/assets/{CanvasRenderer-Dz7JAMrR.js → CanvasRenderer-kDLW8GSt.js} +1 -1
  40. package/dist-cli-web/assets/{CardDecoration-D28DI1G9.js → CardDecoration-CnYxleQM.js} +1 -1
  41. package/dist-cli-web/assets/{ChatRecordDrawer-Cph819yY.js → ChatRecordDrawer-CMg3FEWH.js} +1 -1
  42. package/dist-cli-web/assets/{ChatRecordWorkspace-BAfERdPc.js → ChatRecordWorkspace-AfFIMUbZ.js} +1 -1
  43. package/dist-cli-web/assets/{Checkbox-B8uqt13j.js → Checkbox-D-hAv15R.js} +1 -1
  44. package/dist-cli-web/assets/{DataDirCleanupNotice-3sGBE5vU.js → DataDirCleanupNotice-P80h2p4l.js} +1 -1
  45. package/dist-cli-web/assets/{DatePicker.vue_vue_type_script_setup_true_lang-CQuub8-f.js → DatePicker.vue_vue_type_script_setup_true_lang-CSp691Uv.js} +1 -1
  46. package/dist-cli-web/assets/{DebugToolsPanel-Cwkr0BOY.js → DebugToolsPanel-BK2VbKB0.js} +1 -1
  47. package/dist-cli-web/assets/{EChart.vue_vue_type_script_setup_true_lang-BuLujQCF.js → EChart.vue_vue_type_script_setup_true_lang-BcE0mE_N.js} +1 -1
  48. package/dist-cli-web/assets/{EChartBar.vue_vue_type_script_setup_true_lang-DuA5xbcC.js → EChartBar.vue_vue_type_script_setup_true_lang-BypaV5wc.js} +1 -1
  49. package/dist-cli-web/assets/{EChartLine.vue_vue_type_script_setup_true_lang-CvmMEfYq.js → EChartLine.vue_vue_type_script_setup_true_lang-D8SyOfWC.js} +1 -1
  50. package/dist-cli-web/assets/{EChartWordcloud-CHVtyPR7.js → EChartWordcloud-0rihhg03.js} +1 -1
  51. package/dist-cli-web/assets/{FileDropZone.vue_vue_type_script_setup_true_lang-BKPy9Qv-.js → FileDropZone.vue_vue_type_script_setup_true_lang-Dyuzh7Ex.js} +1 -1
  52. package/dist-cli-web/assets/{Filter-CMfRHIoK.js → Filter-rD_jWpbO.js} +1 -1
  53. package/dist-cli-web/assets/{GlobalTaskBar-Jz6MASCu.js → GlobalTaskBar-sXQuGvF-.js} +1 -1
  54. package/dist-cli-web/assets/{GroupRelationshipGalaxyView-Ch4obfEq.js → GroupRelationshipGalaxyView-BN1L9DFf.js} +1 -1
  55. package/dist-cli-web/assets/{Label-CuryuymO.js → Label-Ce4wl_Ph.js} +1 -1
  56. package/dist-cli-web/assets/{LoadingState.vue_vue_type_script_setup_true_lang-CoeQsaiL.js → LoadingState.vue_vue_type_script_setup_true_lang-DmVfwvnD.js} +1 -1
  57. package/dist-cli-web/assets/{MessageExportModal-JqGZ-iLr.js → MessageExportModal-C2IUNPhg.js} +1 -1
  58. package/dist-cli-web/assets/{PageHeader-D4up77TB.js → PageHeader-D5BcYKBs.js} +1 -1
  59. package/dist-cli-web/assets/{PageTabs.vue_vue_type_script_setup_true_lang-DYQRFk9s.js → PageTabs.vue_vue_type_script_setup_true_lang-DHly2-fe.js} +1 -1
  60. package/dist-cli-web/assets/{RelationshipGalaxyCanvas-Cn7Eta5U.js → RelationshipGalaxyCanvas-CHPqtqQz.js} +3 -3
  61. package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-LlaX57z6.js → RelationshipGalaxyThreeCanvas-qR4S-9n7.js} +1 -1
  62. package/dist-cli-web/assets/{RenderTargetSystem-JhA2PUFi.js → RenderTargetSystem-BaIFulUj.js} +1 -1
  63. package/dist-cli-web/assets/{RovingFocusItem-DHaiTb4j.js → RovingFocusItem-Bqgmzo42.js} +1 -1
  64. package/dist-cli-web/assets/{ScreenCaptureModal-Bxhyt5g1.js → ScreenCaptureModal-sJqB_hrK.js} +1 -1
  65. package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-BPt98e6z.js +1 -0
  66. package/dist-cli-web/assets/{Select-Beeqv0wU.js → Select-BAnN4HMs.js} +1 -1
  67. package/dist-cli-web/assets/SettingsModal-C4ANMlJJ.js +2 -0
  68. package/dist-cli-web/assets/{StorageTab-B7GHadia.js → StorageTab-BgNQliz4.js} +1 -1
  69. package/dist-cli-web/assets/{Switch-u4zlvUMa.js → Switch-DddEvDJa.js} +1 -1
  70. package/dist-cli-web/assets/{Tabs-B7-Koq3x.js → Tabs-DwjL-ueH.js} +1 -1
  71. package/dist-cli-web/assets/{Tabs-RWKHwtfM.js → Tabs-xVtuBuO0.js} +1 -1
  72. package/dist-cli-web/assets/{ThemeCard.vue_vue_type_script_setup_true_lang-ndsa3-aT.js → ThemeCard.vue_vue_type_script_setup_true_lang-CNCVa86b.js} +1 -1
  73. package/dist-cli-web/assets/{TimeSelect.vue_vue_type_script_setup_true_lang-DKa1flT-.js → TimeSelect.vue_vue_type_script_setup_true_lang-BehUIWki.js} +1 -1
  74. package/dist-cli-web/assets/{VisuallyHiddenInput-DGDqcvUy.js → VisuallyHiddenInput-ST6JNfSG.js} +1 -1
  75. package/dist-cli-web/assets/{WebGLRenderer-ifvgA1fe.js → WebGLRenderer-DRraqD4Y.js} +1 -1
  76. package/dist-cli-web/assets/{WebGPURenderer-DwzmuWF0.js → WebGPURenderer-CnzaYARg.js} +1 -1
  77. package/dist-cli-web/assets/aiChat-CeJy-gG-.js +8 -0
  78. package/dist-cli-web/assets/{base-BWDX-164.js → base-BHbtUviQ.js} +1 -1
  79. package/dist-cli-web/assets/{browserAll-CSIIETeE.js → browserAll-DYxkitnx.js} +1 -1
  80. package/dist-cli-web/assets/{chatMessages--r0obAVw.css → chatMessages-BGRbSlvD.css} +1 -1
  81. package/dist-cli-web/assets/{chatMessages-Zywjea1n.js → chatMessages-Dj1u-tFB.js} +20 -20
  82. package/dist-cli-web/assets/{cli-web-BRY49NWe.js → cli-web-BtH8nfjA.js} +1 -1
  83. package/dist-cli-web/assets/{fetch-DNSDHSqQ.js → fetch-BJ82mWvv.js} +1 -1
  84. package/dist-cli-web/assets/{fetch-D3nLtFBi.js → fetch-C1Tm66i9.js} +1 -1
  85. package/dist-cli-web/assets/{fetch-jx1VdM9j.js → fetch-CAEyoWQA.js} +1 -1
  86. package/dist-cli-web/assets/{fetch-qBaUXqFt.js → fetch-CGnWNSsG.js} +1 -1
  87. package/dist-cli-web/assets/{fetch-mYSeL4Ki.js → fetch-D7hfFyoG.js} +1 -1
  88. package/dist-cli-web/assets/{fetch-BiXab0IF.js → fetch-DNmRaRxX.js} +1 -1
  89. package/dist-cli-web/assets/{fetch-wzv71PYy.js → fetch-DogRrvT6.js} +1 -1
  90. package/dist-cli-web/assets/fetch-JEEBqR_7.js +1 -0
  91. package/dist-cli-web/assets/{fetch-r6mhEGrP.js → fetch-T5Gi1TUZ.js} +1 -1
  92. package/dist-cli-web/assets/{fetch-DeHqF6yL.js → fetch-U9QyidNZ.js} +1 -1
  93. package/dist-cli-web/assets/{fetch-Co2vLmiE.js → fetch-ejEMWPM2.js} +1 -1
  94. package/dist-cli-web/assets/{fetch-Bf4cqeM3.js → fetch-emZXHmoE.js} +1 -1
  95. package/dist-cli-web/assets/{host-1emQf9_4.js → host-BUvwCvZR.js} +1 -1
  96. package/dist-cli-web/assets/{host-jXJHyyOX.js → host-VTWF81F5.js} +1 -1
  97. package/dist-cli-web/assets/{index-CYc_VLgK.js → index-B3MCGDwx.js} +1 -1
  98. package/dist-cli-web/assets/index-BI2Rb_L3.js +2 -0
  99. package/dist-cli-web/assets/{index-BOcFyPCa.js → index-BLYSew5C.js} +1 -1
  100. package/dist-cli-web/assets/index-Bs7gk1gf.css +1 -0
  101. package/dist-cli-web/assets/{index-BgelKQTW.css → index-C75W1B5J.css} +1 -1
  102. package/dist-cli-web/assets/{index-DOqbUvhA.js → index-CVRBBnQw.js} +1 -1
  103. package/dist-cli-web/assets/index-Cza8BzZE.js +137 -0
  104. package/dist-cli-web/assets/{index-DnpnIyle.js → index-D9C3dyV8.js} +33 -33
  105. package/dist-cli-web/assets/{index-LV9LfFNu.js → index-DDt7Tfda.js} +1 -1
  106. package/dist-cli-web/assets/{index-B4mJkX_d.js → index-DQFcmWSa.js} +1 -1
  107. package/dist-cli-web/assets/{index-slDUxib2.js → index-DUBl6aBF.js} +1 -1
  108. package/dist-cli-web/assets/{index-B7biznha.js → index-DZK9Njvy.js} +1 -1
  109. package/dist-cli-web/assets/{index-Cw0IhKZ6.js → index-HjrJ3DXO.js} +1 -1
  110. package/dist-cli-web/assets/index-KpnLSzzX.js +64 -0
  111. package/dist-cli-web/assets/{index-BQ463TS7.js → index-e6fOnhNS.js} +1 -1
  112. package/dist-cli-web/assets/{index-ClHMs0-a.js → index-iEQnKg2R.js} +2 -2
  113. package/dist-cli-web/assets/{people-page-header-Cx5r9Bk2.js → people-page-header-DWXwFJ8M.js} +1 -1
  114. package/dist-cli-web/assets/{prompt-BggEDxxi.js → prompt-Cepfb-5-.js} +1 -1
  115. package/dist-cli-web/assets/{semantic-index-config-builder-1sO0aEc1.js → semantic-index-config-builder-hTY6QyCe.js} +1 -1
  116. package/dist-cli-web/assets/service-BjE0YJaM.js +1 -0
  117. package/dist-cli-web/assets/service-DQxttlJp.js +1 -0
  118. package/dist-cli-web/assets/{service-Bvq1fRkC.js → service-DwCmXFtI.js} +1 -1
  119. package/dist-cli-web/assets/useFormControl-CJyJY8AK.js +1 -0
  120. package/dist-cli-web/assets/useInsightTabAnalytics-CplGbsbe.js +75 -0
  121. package/dist-cli-web/assets/{ActionToolsPanel-CmYpw8Mt.css → useInsightTabAnalytics-DDNXXH8v.css} +1 -1
  122. package/dist-cli-web/assets/{useTable-DC7oxmBl.js → useTable-Dmpbk7fZ.js} +1 -1
  123. package/dist-cli-web/assets/{useToast-CCPBE-Sy.js → useToast-Bmgpd3sl.js} +1 -1
  124. package/dist-cli-web/assets/{webworkerAll-FCJk6reG.js → webworkerAll-DkPWEyVL.js} +1 -1
  125. package/dist-cli-web/index.html +1 -1
  126. package/package.json +3 -3
  127. package/dist/chat-command-WBAJBKIE.mjs.map +0 -1
  128. package/dist/chunk-4J4MA3RC.mjs.map +0 -1
  129. package/dist/chunk-5YOHLJFB.mjs.map +0 -1
  130. package/dist/chunk-DKIYHA3O.mjs.map +0 -1
  131. package/dist/chunk-MSMDLDPB.mjs.map +0 -1
  132. package/dist/chunk-N2WGG3MS.mjs.map +0 -1
  133. package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang--RPlmfIu.js +0 -75
  134. package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-BlAje3Tf.js +0 -1
  135. package/dist-cli-web/assets/SettingsModal-DV8yDBJs.js +0 -2
  136. package/dist-cli-web/assets/aiChat-BY4C3UQe.js +0 -8
  137. package/dist-cli-web/assets/fetch-C8CcoTWY.js +0 -1
  138. package/dist-cli-web/assets/index-2dJCozDq.js +0 -64
  139. package/dist-cli-web/assets/index-6ejRbH3h.js +0 -2
  140. package/dist-cli-web/assets/index-CIIyg_o9.css +0 -1
  141. package/dist-cli-web/assets/index-kePiJ3ca.js +0 -137
  142. package/dist-cli-web/assets/service-DkPheDt8.js +0 -1
  143. package/dist-cli-web/assets/service-DvI8O_7K.js +0 -1
  144. package/dist-cli-web/assets/useFormControl-B0aShtLp.js +0 -1
  145. /package/dist/{chunk-EJBBVLUN.mjs.map → chunk-EFX66A2O.mjs.map} +0 -0
  146. /package/dist/{chunk-SQWN4GTQ.mjs.map → chunk-FCUUAGGC.mjs.map} +0 -0
  147. /package/dist/{chunk-RXMSZIYD.mjs.map → chunk-HMVM7VCA.mjs.map} +0 -0
  148. /package/dist/{chunk-FM3GXNEO.mjs.map → chunk-L6RJIVLS.mjs.map} +0 -0
  149. /package/dist/{chunk-2TV7M4CC.mjs.map → chunk-XQ6Z2KNN.mjs.map} +0 -0
  150. /package/dist/{chunk-CR2MU7LN.mjs.map → chunk-YI3EQYEG.mjs.map} +0 -0
  151. /package/dist/{http-X6OEOOSH.mjs.map → http-5BRVWAJR.mjs.map} +0 -0
  152. /package/dist/{import-WBJ6YIQ7.mjs.map → import-IAECQDKC.mjs.map} +0 -0
  153. /package/dist/{mcp-FR6OKWKL.mjs.map → mcp-XXSQJBGU.mjs.map} +0 -0
  154. /package/dist/{src-WE5HNN2O.mjs.map → src-4HLKMQ7S.mjs.map} +0 -0
  155. /package/dist/{stream-import-YYQVZDGQ.mjs.map → stream-import-PUG42I36.mjs.map} +0 -0
  156. /package/dist/{worker-runtime-YGB5ZDE4.mjs.map → worker-runtime-X5563TVT.mjs.map} +0 -0
@@ -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-T5H56AZV.mjs";
10
+ import "./chunk-L6RJIVLS.mjs";
11
+ import "./chunk-F4HVCFZL.mjs";
12
+ import "./chunk-H46CLFWK.mjs";
13
+ import "./chunk-EFX66A2O.mjs";
14
+ import "./chunk-E6LCCPIB.mjs";
15
15
  import "./chunk-YKMQLAVP.mjs";
16
16
  import {
17
17
  getDefaultGeneralAssistantId
18
- } from "./chunk-4J4MA3RC.mjs";
18
+ } from "./chunk-HBAQPF6X.mjs";
19
19
  import "./chunk-VSOWNKPA.mjs";
20
20
 
21
21
  // src/ai/chat-command.ts
@@ -110,7 +110,7 @@ async function runChatTurn(options, deps) {
110
110
  const startedAt = Date.now();
111
111
  let answer = "";
112
112
  let tokenUsage = null;
113
- let streamError = null;
113
+ const streamState = { error: null };
114
114
  const events = [];
115
115
  const contentBlocks = [];
116
116
  let hasReplayContentBlocks = false;
@@ -204,7 +204,7 @@ async function runChatTurn(options, deps) {
204
204
  }
205
205
  if (chunk.type === "error") {
206
206
  removePlanDraftBlocks();
207
- streamError = createAgentStreamError(chunk.error);
207
+ streamState.error = createAgentStreamError(chunk.error);
208
208
  return;
209
209
  }
210
210
  if (chunk.type === "plan_delta" && chunk.planDelta) {
@@ -237,7 +237,7 @@ async function runChatTurn(options, deps) {
237
237
  for (let index = contentBlocks.length - 1; index >= 0; index--) {
238
238
  const block = contentBlocks[index];
239
239
  if (block.type === "plan") {
240
- block.status = "done";
240
+ block.status = streamState.error ? "skipped" : "done";
241
241
  break;
242
242
  }
243
243
  }
@@ -245,7 +245,15 @@ async function runChatTurn(options, deps) {
245
245
  },
246
246
  new AbortController().signal
247
247
  );
248
- if (streamError) throw streamError;
248
+ const streamError = streamState.error;
249
+ if (streamError && streamError.name !== "OutputLimitError") throw streamError;
250
+ if (streamError) {
251
+ contentBlocks.push({
252
+ type: "error",
253
+ error: { name: streamError.name, message: streamError.message, stack: null }
254
+ });
255
+ hasReplayContentBlocks = true;
256
+ }
249
257
  if (!options.json && options.stream !== false && answer && !answer.endsWith("\n")) {
250
258
  write(stdout, "\n");
251
259
  }
@@ -263,6 +271,7 @@ async function runChatTurn(options, deps) {
263
271
  aiChatId: target.aiChatId,
264
272
  question: options.question,
265
273
  answer,
274
+ ...streamError ? { error: { name: streamError.name, message: streamError.message } } : {},
266
275
  ...options.includeEvents ? { events } : {},
267
276
  ...hasReplayContentBlocks ? { contentBlocks } : {},
268
277
  usage: {
@@ -283,6 +292,7 @@ async function runChatCommand(options, deps) {
283
292
  write(stdout, `${result.answer}
284
293
  `);
285
294
  }
295
+ if (result.error) throw createAgentStreamError(result.error);
286
296
  return;
287
297
  }
288
298
  const rl = createInterface({
@@ -297,6 +307,8 @@ async function runChatCommand(options, deps) {
297
307
  try {
298
308
  const result = await runChatTurn({ ...options, aiChatId, question, json: false, stream: true }, deps);
299
309
  aiChatId = result.aiChatId;
310
+ if (result.error) write(stderr, `${result.error.message}
311
+ `);
300
312
  } catch (error) {
301
313
  write(stderr, `${error instanceof Error ? error.message : String(error)}
302
314
  `);
@@ -311,4 +323,4 @@ export {
311
323
  runChatCommand,
312
324
  runChatTurn
313
325
  };
314
- //# sourceMappingURL=chat-command-WBAJBKIE.mjs.map
326
+ //# sourceMappingURL=chat-command-G2EX7VHR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ai/chat-command.ts"],"sourcesContent":["import { createInterface } from 'node:readline/promises'\nimport type { Readable, Writable } from 'node:stream'\nimport type {\n DatabaseManager,\n AIChatManager,\n AgentStreamChunk,\n ContentBlock,\n PlanContentBlock,\n PlanDraftContentBlock,\n TokenUsageData,\n} from '@openchatlab/node-runtime'\nimport type { ChartPayload, PathProvider } from '@openchatlab/core'\nimport { getDefaultGeneralAssistantId } from '@openchatlab/shared-types'\nimport { createCliRunAgentStream } from './agent-stream-runner'\n\nexport interface ChatCommandOptions {\n sessionId?: string\n aiChatId?: string\n question?: string\n json?: boolean\n stream?: boolean\n locale?: string\n includeEvents?: boolean\n}\n\nexport interface ChatCommandDeps {\n dbManager: DatabaseManager\n pathProvider: PathProvider\n aiChatManager: AIChatManager\n stdout?: Pick<Writable, 'write'>\n stderr?: Pick<Writable, 'write'>\n stdin?: Readable\n createRunAgentStream?: typeof createCliRunAgentStream\n}\n\nexport interface ResolvedAIChatTarget {\n sessionId: string\n aiChatId: string\n assistantId: string\n created: boolean\n}\n\nexport interface ChatTurnResult {\n sessionId: string\n aiChatId: string\n question: string\n answer: string\n /** Present for a persisted but incomplete turn, never for a successful completion. */\n error?: { name: string; message: string }\n events?: AgentStreamChunk[]\n contentBlocks?: ContentBlock[]\n usage: {\n durationMs: number\n tokenUsage: TokenUsageData | null\n }\n}\n\nfunction write(stream: Pick<Writable, 'write'>, text: string): void {\n stream.write(text)\n}\n\nfunction buildTitle(question?: string): string {\n const trimmed = question?.trim()\n if (!trimmed) return 'CLI Chat'\n return trimmed.length > 50 ? `${trimmed.slice(0, 50)}...` : trimmed\n}\n\nfunction createAgentStreamError(error: unknown): Error {\n if (error instanceof Error) return error\n if (typeof error === 'string' && error) return new Error(error)\n if (typeof error === 'object' && error !== null) {\n const record = error as { name?: unknown; message?: unknown; stack?: unknown }\n const streamError = new Error(\n typeof record.message === 'string' && record.message ? record.message : 'Agent stream failed'\n )\n if (typeof record.name === 'string' && record.name) streamError.name = record.name\n if (typeof record.stack === 'string' && record.stack) streamError.stack = record.stack\n return streamError\n }\n return new Error('Agent stream failed')\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isChartPayload(value: unknown): value is ChartPayload {\n return isRecord(value) && value.version === 1 && isRecord(value.spec) && isRecord(value.dataset)\n}\n\nfunction extractChartPayloads(toolResult: unknown): ChartPayload[] {\n if (!isRecord(toolResult)) return []\n const details = isRecord(toolResult.details) ? toolResult.details : toolResult\n const charts: ChartPayload[] = []\n if (isChartPayload(details.chart)) charts.push(details.chart)\n if (Array.isArray(details.charts)) {\n for (const chart of details.charts) {\n if (isChartPayload(chart)) charts.push(chart)\n }\n }\n return charts\n}\n\nfunction toPersistedChartPayload(chart: ChartPayload): ChartPayload {\n return {\n ...chart,\n dataset: {\n ...chart.dataset,\n rows: [],\n },\n }\n}\n\nfunction resolveSingleSessionId(dbManager: DatabaseManager): string {\n const sessionIds = dbManager.listSessionIds()\n if (sessionIds.length === 1) return sessionIds[0]!\n if (sessionIds.length === 0) {\n throw new Error('No chat sessions found. Import data first or run `clb sessions` to verify available sessions.')\n }\n throw new Error('Missing --session-id. Run `clb sessions` to choose a session ID.')\n}\n\nfunction assertSessionExists(dbManager: DatabaseManager, sessionId: string): void {\n const db = dbManager.open(sessionId)\n if (!db) {\n throw new Error(`Session ${sessionId} not found`)\n }\n}\n\nexport function resolveAIChatTarget(\n options: Pick<ChatCommandOptions, 'sessionId' | 'aiChatId' | 'question' | 'locale'>,\n deps: Pick<ChatCommandDeps, 'dbManager' | 'aiChatManager'>\n): ResolvedAIChatTarget {\n if (options.aiChatId) {\n const aiChat = deps.aiChatManager.getAIChat(options.aiChatId)\n if (!aiChat) {\n throw new Error(`AI chat ${options.aiChatId} not found`)\n }\n if (options.sessionId && options.sessionId !== aiChat.sessionId) {\n throw new Error(`AI chat ${options.aiChatId} belongs to session ${aiChat.sessionId}, not ${options.sessionId}`)\n }\n assertSessionExists(deps.dbManager, aiChat.sessionId)\n return { sessionId: aiChat.sessionId, aiChatId: aiChat.id, assistantId: aiChat.assistantId, created: false }\n }\n\n const sessionId = options.sessionId ?? resolveSingleSessionId(deps.dbManager)\n assertSessionExists(deps.dbManager, sessionId)\n const aiChat = deps.aiChatManager.createAIChat(\n sessionId,\n buildTitle(options.question),\n getDefaultGeneralAssistantId(options.locale)\n )\n return { sessionId, aiChatId: aiChat.id, assistantId: aiChat.assistantId, created: true }\n}\n\nexport async function runChatTurn(\n options: Required<Pick<ChatCommandOptions, 'question'>> &\n Pick<ChatCommandOptions, 'sessionId' | 'aiChatId' | 'json' | 'stream' | 'locale' | 'includeEvents'>,\n deps: ChatCommandDeps\n): Promise<ChatTurnResult> {\n const stdout = deps.stdout ?? process.stdout\n const target = resolveAIChatTarget(options, deps)\n const startedAt = Date.now()\n let answer = ''\n let tokenUsage: TokenUsageData | null = null\n const streamState: { error: Error | null } = { error: null }\n const events: AgentStreamChunk[] = []\n const contentBlocks: ContentBlock[] = []\n let hasReplayContentBlocks = false\n\n const runAgentStream = (deps.createRunAgentStream ?? createCliRunAgentStream)(deps.dbManager, deps.aiChatManager)\n\n // 中文注释:CLI 历史回放依赖 contentBlocks 的时序;只要出现计划/思考块,\n // 就同步保留后续正文 text block,避免 UI 使用 blocks 渲染时丢失最终回答。\n const appendTextBlock = (text: string) => {\n if (!text) return\n const lastBlock = contentBlocks[contentBlocks.length - 1]\n if (lastBlock?.type === 'text') {\n lastBlock.text += text\n } else {\n contentBlocks.push({ type: 'text', text })\n }\n }\n\n const appendThinkBlock = (text: string, tag = 'thinking', durationMs?: number) => {\n if (!text && durationMs === undefined) return\n const lastBlock = contentBlocks[contentBlocks.length - 1]\n let targetBlock: ContentBlock | undefined\n\n if (lastBlock?.type === 'think' && lastBlock.tag === tag) {\n lastBlock.text += text\n targetBlock = lastBlock\n } else if (text.trim().length > 0) {\n targetBlock = { type: 'think', tag, text }\n contentBlocks.push(targetBlock)\n } else if (durationMs !== undefined) {\n for (let index = contentBlocks.length - 1; index >= 0; index--) {\n const block = contentBlocks[index]\n if (block.type === 'think' && block.tag === tag) {\n targetBlock = block\n break\n }\n }\n }\n\n if (durationMs !== undefined && targetBlock?.type === 'think') {\n targetBlock.durationMs = durationMs\n }\n if (targetBlock?.type === 'think') {\n hasReplayContentBlocks = true\n }\n }\n\n const appendChartBlocks = (charts: ChartPayload[]) => {\n if (charts.length === 0) return\n // 中文注释:图表 block 需要持久化给 CLI 生成的 AI 对话回放;\n // 原始 SQL 行数据可能较大,保存时只保留渲染数据和字段元信息。\n contentBlocks.push(...charts.map((chart) => ({ type: 'chart' as const, chart: toPersistedChartPayload(chart) })))\n hasReplayContentBlocks = true\n }\n\n const appendPlanDraftBlock = (delta: string) => {\n if (!delta) return\n const lastBlock = contentBlocks[contentBlocks.length - 1]\n if (lastBlock?.type === 'plan_draft') {\n lastBlock.text += delta\n } else {\n contentBlocks.push({ type: 'plan_draft', version: 1, status: 'streaming', text: delta } as PlanDraftContentBlock)\n }\n hasReplayContentBlocks = true\n }\n\n const removePlanDraftBlocks = () => {\n for (let index = contentBlocks.length - 1; index >= 0; index--) {\n if (contentBlocks[index]?.type === 'plan_draft') {\n contentBlocks.splice(index, 1)\n }\n }\n }\n\n const appendFinalPlanBlock = (plan: PlanContentBlock) => {\n const planBlock = JSON.parse(JSON.stringify(plan)) as PlanContentBlock\n for (let index = contentBlocks.length - 1; index >= 0; index--) {\n const block = contentBlocks[index]\n if (block?.type === 'plan_draft') {\n const displayText = block.text.trim()\n if (displayText) planBlock.displayText = displayText\n contentBlocks[index] = planBlock\n hasReplayContentBlocks = true\n return\n }\n }\n contentBlocks.push(planBlock)\n hasReplayContentBlocks = true\n }\n\n await runAgentStream(\n {\n userMessage: options.question,\n sessionId: target.sessionId,\n aiChatId: target.aiChatId,\n assistantId: target.assistantId,\n chatType: 'group',\n locale: options.locale ?? 'zh-CN',\n enableAutoSkill: true,\n chartAutoMode: 'suggest',\n },\n (chunk: AgentStreamChunk) => {\n if (options.includeEvents) {\n events.push(JSON.parse(JSON.stringify(chunk)) as AgentStreamChunk)\n }\n if (chunk.type === 'error') {\n removePlanDraftBlocks()\n streamState.error = createAgentStreamError(chunk.error)\n return\n }\n if (chunk.type === 'plan_delta' && chunk.planDelta) {\n appendPlanDraftBlock(chunk.planDelta)\n return\n }\n if (chunk.type === 'plan' && chunk.plan) {\n appendFinalPlanBlock(chunk.plan)\n return\n }\n if (chunk.type === 'plan_skipped') {\n removePlanDraftBlocks()\n return\n }\n if (chunk.type === 'think') {\n appendThinkBlock(chunk.content ?? '', chunk.thinkTag, chunk.thinkDurationMs)\n return\n }\n if (chunk.type === 'tool_result' && chunk.toolName === 'render_chart') {\n appendChartBlocks(extractChartPayloads(chunk.toolResult))\n return\n }\n if (chunk.type === 'content' && chunk.content) {\n answer += chunk.content\n appendTextBlock(chunk.content)\n if (!options.json && options.stream !== false) write(stdout, chunk.content)\n }\n if (chunk.type === 'done' && chunk.usage) {\n tokenUsage = chunk.usage\n for (let index = contentBlocks.length - 1; index >= 0; index--) {\n const block = contentBlocks[index]\n if (block.type === 'plan') {\n block.status = streamState.error ? 'skipped' : 'done'\n break\n }\n }\n }\n },\n new AbortController().signal\n )\n\n const streamError = streamState.error\n if (streamError && streamError.name !== 'OutputLimitError') throw streamError\n if (streamError) {\n contentBlocks.push({\n type: 'error',\n error: { name: streamError.name, message: streamError.message, stack: null },\n })\n hasReplayContentBlocks = true\n }\n\n if (!options.json && options.stream !== false && answer && !answer.endsWith('\\n')) {\n write(stdout, '\\n')\n }\n\n deps.aiChatManager.addMessagePair(\n target.aiChatId,\n { content: options.question },\n {\n content: answer,\n contentBlocks: hasReplayContentBlocks ? contentBlocks : undefined,\n tokenUsage: tokenUsage ?? undefined,\n }\n )\n\n return {\n sessionId: target.sessionId,\n aiChatId: target.aiChatId,\n question: options.question,\n answer,\n ...(streamError ? { error: { name: streamError.name, message: streamError.message } } : {}),\n ...(options.includeEvents ? { events } : {}),\n ...(hasReplayContentBlocks ? { contentBlocks } : {}),\n usage: {\n durationMs: Date.now() - startedAt,\n tokenUsage,\n },\n }\n}\n\nexport async function runChatCommand(options: ChatCommandOptions, deps: ChatCommandDeps): Promise<void> {\n const stdout = deps.stdout ?? process.stdout\n const stderr = deps.stderr ?? process.stderr\n\n if (options.question?.trim()) {\n const result = await runChatTurn({ ...options, question: options.question.trim() }, deps)\n if (options.json) {\n write(stdout, `${JSON.stringify(result, null, 2)}\\n`)\n } else if (options.stream === false) {\n write(stdout, `${result.answer}\\n`)\n }\n if (result.error) throw createAgentStreamError(result.error)\n return\n }\n\n const rl = createInterface({\n input: deps.stdin ?? process.stdin,\n output: stdout as Writable,\n })\n\n try {\n let aiChatId = options.aiChatId\n while (true) {\n const question = (await rl.question('chatlab> ')).trim()\n if (!question || question === 'exit' || question === 'quit') break\n try {\n const result = await runChatTurn({ ...options, aiChatId, question, json: false, stream: true }, deps)\n aiChatId = result.aiChatId\n if (result.error) write(stderr, `${result.error.message}\\n`)\n } catch (error) {\n write(stderr, `${error instanceof Error ? error.message : String(error)}\\n`)\n }\n }\n } finally {\n rl.close()\n }\n}\n"],"mappings":"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","names":["aiChat"]}
@@ -21,7 +21,7 @@ import {
21
21
  resolveOwnerMember,
22
22
  setCache,
23
23
  shouldScopeContactToSession
24
- } from "./chunk-4J4MA3RC.mjs";
24
+ } from "./chunk-HBAQPF6X.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-E6LCCPIB.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"]}
@@ -21,7 +21,7 @@ import {
21
21
  setCache,
22
22
  shouldEnableContactsEntry,
23
23
  shouldScopeContactToSession
24
- } from "./chunk-4J4MA3RC.mjs";
24
+ } from "./chunk-HBAQPF6X.mjs";
25
25
 
26
26
  // ../../packages/node-runtime/src/services/contacts/facts-cache.ts
27
27
  var CONTACTS_FACTS_FORMAT_VERSION = 1;
@@ -517,4 +517,4 @@ export {
517
517
  computeContactsSnapshot,
518
518
  createEmptyContactsDiagnostics
519
519
  };
520
- //# sourceMappingURL=chunk-EJBBVLUN.mjs.map
520
+ //# sourceMappingURL=chunk-EFX66A2O.mjs.map
@@ -11,15 +11,15 @@ import {
11
11
  getServerAiLogger,
12
12
  getSkillManagerCore,
13
13
  initServerAiLogger
14
- } from "./chunk-MSMDLDPB.mjs";
14
+ } from "./chunk-T5H56AZV.mjs";
15
15
  import {
16
16
  AGENT_TOOL_REGISTRY,
17
17
  CoreDataProvider
18
- } from "./chunk-FM3GXNEO.mjs";
18
+ } from "./chunk-L6RJIVLS.mjs";
19
19
  import {
20
20
  prepareUnixSocket,
21
21
  resolveCliLocalEmbeddingRuntimeConfig
22
- } from "./chunk-2TV7M4CC.mjs";
22
+ } from "./chunk-XQ6Z2KNN.mjs";
23
23
  import {
24
24
  analyzeIncrementalImport,
25
25
  analyzeNewImport,
@@ -27,10 +27,10 @@ import {
27
27
  autoImportBatch,
28
28
  incrementalImport,
29
29
  streamImport
30
- } from "./chunk-RXMSZIYD.mjs";
30
+ } from "./chunk-HMVM7VCA.mjs";
31
31
  import {
32
32
  assertCliDataDirCompatible
33
- } from "./chunk-SQWN4GTQ.mjs";
33
+ } from "./chunk-FCUUAGGC.mjs";
34
34
  import {
35
35
  getVersion
36
36
  } from "./chunk-5ADKYYNA.mjs";
@@ -62,7 +62,7 @@ import {
62
62
  isDictDownloaded,
63
63
  segmentText,
64
64
  serializeChatLabToJsonl
65
- } from "./chunk-5YOHLJFB.mjs";
65
+ } from "./chunk-F4HVCFZL.mjs";
66
66
  import {
67
67
  ArchiveImportError,
68
68
  ArchiveImportSourceManager,
@@ -103,7 +103,7 @@ import {
103
103
  session_service_exports,
104
104
  streamParseFileInfo,
105
105
  summary_service_exports
106
- } from "./chunk-DQR2YS3I.mjs";
106
+ } from "./chunk-H46CLFWK.mjs";
107
107
  import {
108
108
  AIChatManager,
109
109
  AIMemoryService,
@@ -184,7 +184,7 @@ import {
184
184
  validateApiKey,
185
185
  verifyCliDataPath,
186
186
  writeConfigField
187
- } from "./chunk-4J4MA3RC.mjs";
187
+ } from "./chunk-HBAQPF6X.mjs";
188
188
 
189
189
  // src/http/index.ts
190
190
  import * as fs11 from "fs";
@@ -937,16 +937,6 @@ function registerAiAssistantRoutes(server2, ctx) {
937
937
  server2.post("/_web/ai/assistants/import", async (request) => {
938
938
  return mgr.importAssistantFromMd(request.body.rawMd);
939
939
  });
940
- server2.post("/_web/ai/assistants/import-builtin", async (request, reply) => {
941
- const result = mgr.importAssistant(request.body.builtinId);
942
- if (!result.success) return reply.code(400).send(result);
943
- return result;
944
- });
945
- server2.post("/_web/ai/assistants/:id/reimport", async (request, reply) => {
946
- const result = mgr.reimportAssistant(request.params.id);
947
- if (!result.success) return reply.code(400).send(result);
948
- return result;
949
- });
950
940
  }
951
941
 
952
942
  // ../../packages/http-routes/src/routes/web/ai-chats.ts
@@ -2410,7 +2400,11 @@ function registerCacheRoutes(server2, ctx) {
2410
2400
  if (!fs3.existsSync(downloadsDir)) {
2411
2401
  fs3.mkdirSync(downloadsDir, { recursive: true });
2412
2402
  }
2413
- const filePath = path5.join(downloadsDir, filename);
2403
+ const safeName = path5.basename(filename);
2404
+ if (safeName !== filename || safeName === "." || safeName === "..") {
2405
+ return { success: false, error: "Invalid filename" };
2406
+ }
2407
+ const filePath = path5.join(downloadsDir, safeName);
2414
2408
  fs3.writeFileSync(filePath, buffer);
2415
2409
  return { success: true, filePath };
2416
2410
  });
@@ -2863,7 +2857,9 @@ import * as path7 from "path";
2863
2857
  function registerNlpRoutes(server2, ctx) {
2864
2858
  const nlpDir = path7.join(ctx.pathProvider.getSystemDir(), "nlp");
2865
2859
  initNlpDir(nlpDir);
2866
- ensureDefaultDict(nlpDir).catch((err) => console.warn("[NLP] Auto-download zh-CN dict failed:", err));
2860
+ ensureDefaultDict(nlpDir, ctx.bundledNlpDictDir).catch(
2861
+ (err) => console.warn("[NLP] Initialize zh-CN dict failed:", err)
2862
+ );
2867
2863
  server2.get("/_web/nlp/pos-tags", async () => {
2868
2864
  return POS_TAG_DEFINITIONS;
2869
2865
  });
@@ -3967,10 +3963,10 @@ function registerWebRoutes2(server2, dbManager2, options) {
3967
3963
  const resolvedPathProvider = options?.pathProvider ?? fallbackPathProvider;
3968
3964
  const ai = options?.aiContext;
3969
3965
  const cliStreamImport = async (dm, filePath) => {
3970
- const { streamImport: streamImport2 } = await import("./stream-import-YYQVZDGQ.mjs");
3966
+ const { streamImport: streamImport2 } = await import("./stream-import-PUG42I36.mjs");
3971
3967
  const result = await streamImport2(dm, filePath);
3972
3968
  if (!result.success) {
3973
- const { IMPORT_IN_PROGRESS_ERROR_KEY: IMPORT_IN_PROGRESS_ERROR_KEY2, ImportInProgressError: ImportInProgressError2 } = await import("./src-WE5HNN2O.mjs");
3969
+ const { IMPORT_IN_PROGRESS_ERROR_KEY: IMPORT_IN_PROGRESS_ERROR_KEY2, ImportInProgressError: ImportInProgressError2 } = await import("./src-4HLKMQ7S.mjs");
3974
3970
  if (result.error === IMPORT_IN_PROGRESS_ERROR_KEY2) throw new ImportInProgressError2();
3975
3971
  throw new Error(result.error || "Import failed");
3976
3972
  }
@@ -3994,6 +3990,7 @@ function registerWebRoutes2(server2, dbManager2, options) {
3994
3990
  runtimeIdentity: options?.runtimeIdentity,
3995
3991
  getVersion,
3996
3992
  nativeBinding: options?.nativeBinding,
3993
+ bundledNlpDictDir: process.env.CHATLAB_NLP_DICT_DIR?.trim() || void 0,
3997
3994
  semanticIndexService,
3998
3995
  contactsService: options?.contactsService,
3999
3996
  globalInsightService: options?.globalInsightService,
@@ -5196,4 +5193,4 @@ export {
5196
5193
  startHttpServer,
5197
5194
  stopHttpServer
5198
5195
  };
5199
- //# sourceMappingURL=chunk-DKIYHA3O.mjs.map
5196
+ //# sourceMappingURL=chunk-EK5QIJLZ.mjs.map