chatlab-cli 0.36.2 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/dist/{chat-command-SEV6N3AR.mjs → chat-command-WBAJBKIE.mjs} +15 -16
  2. package/dist/{chat-command-SEV6N3AR.mjs.map → chat-command-WBAJBKIE.mjs.map} +1 -1
  3. package/dist/chunk-2TV7M4CC.mjs +101 -0
  4. package/dist/chunk-2TV7M4CC.mjs.map +1 -0
  5. package/dist/{chunk-TRGR3KKJ.mjs → chunk-4J4MA3RC.mjs} +2605 -672
  6. package/dist/chunk-4J4MA3RC.mjs.map +1 -0
  7. package/dist/{chunk-TJ3NOBVI.mjs → chunk-5YOHLJFB.mjs} +98 -284
  8. package/dist/chunk-5YOHLJFB.mjs.map +1 -0
  9. package/dist/{chunk-WRBFV2GX.mjs → chunk-CR2MU7LN.mjs} +49 -55
  10. package/dist/chunk-CR2MU7LN.mjs.map +1 -0
  11. package/dist/{chunk-WJQOXOMR.mjs → chunk-DKIYHA3O.mjs} +281 -30
  12. package/dist/chunk-DKIYHA3O.mjs.map +1 -0
  13. package/dist/{chunk-VREVDZ4Q.mjs → chunk-DQR2YS3I.mjs} +1930 -33
  14. package/dist/chunk-DQR2YS3I.mjs.map +1 -0
  15. package/dist/{chunk-VUYWUHWW.mjs → chunk-EJBBVLUN.mjs} +3 -3
  16. package/dist/chunk-EJBBVLUN.mjs.map +1 -0
  17. package/dist/{chunk-HWIXK7SU.mjs → chunk-FM3GXNEO.mjs} +1643 -3
  18. package/dist/chunk-FM3GXNEO.mjs.map +1 -0
  19. package/dist/{chunk-YUH3H3QW.mjs → chunk-MSMDLDPB.mjs} +96 -5
  20. package/dist/chunk-MSMDLDPB.mjs.map +1 -0
  21. package/dist/{chunk-6EZPD3XJ.mjs → chunk-N2WGG3MS.mjs} +2 -2
  22. package/dist/{chunk-TWH422HC.mjs → chunk-RXMSZIYD.mjs} +3 -3
  23. package/dist/{chunk-CH5YAZ4Q.mjs → chunk-SQWN4GTQ.mjs} +2 -2
  24. package/dist/cli.mjs +9 -9
  25. package/dist/contacts-worker.mjs +2 -2
  26. package/dist/global-insight-worker.mjs +1 -1
  27. package/dist/{http-237XYNRT.mjs → http-X6OEOOSH.mjs} +12 -12
  28. package/dist/{import-R2E2TFAK.mjs → import-WBJ6YIQ7.mjs} +7 -7
  29. package/dist/index.mjs +12 -12
  30. package/dist/{mcp-BED4AARV.mjs → mcp-FR6OKWKL.mjs} +8 -8
  31. package/dist/people-relationships-worker.mjs +2 -2
  32. package/dist/semantic-index-worker.mjs +1 -1
  33. package/dist/{src-AYMALWU5.mjs → src-WE5HNN2O.mjs} +44 -14
  34. package/dist/{stream-import-RXNSOOYH.mjs → stream-import-YYQVZDGQ.mjs} +7 -7
  35. package/dist/{worker-runtime-JGAZU7MC.mjs → worker-runtime-YGB5ZDE4.mjs} +5 -5
  36. package/dist-cli-web/assets/AISettingsTab-Co2sM1iY.js +1 -0
  37. package/dist-cli-web/assets/{AboutTab-DCM1-glA.js → AboutTab-DQ8r6mYE.js} +1 -1
  38. package/dist-cli-web/assets/ActionToolsPanel-CmYpw8Mt.css +1 -0
  39. package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang--RPlmfIu.js +75 -0
  40. package/dist-cli-web/assets/{Alert-D-egBWcb.js → Alert-DU1tLAAg.js} +1 -1
  41. package/dist-cli-web/assets/ApiSettingsTab-D5g5b79L.js +2 -0
  42. package/dist-cli-web/assets/BatchManageTab-BFUyVaGU.js +1 -0
  43. package/dist-cli-web/assets/{BufferResource-BE2_2bZK.js → BufferResource-C-RfYwQW.js} +1 -1
  44. package/dist-cli-web/assets/{CanvasRenderer-DDKdvEYQ.js → CanvasRenderer-Dz7JAMrR.js} +1 -1
  45. package/dist-cli-web/assets/CardDecoration-D28DI1G9.js +1 -0
  46. package/dist-cli-web/assets/ChatRecordDrawer-Cph819yY.js +1 -0
  47. package/dist-cli-web/assets/{ChatRecordWorkspace-DqUI4QiU.js → ChatRecordWorkspace-BAfERdPc.js} +1 -1
  48. package/dist-cli-web/assets/Checkbox-B8uqt13j.js +1 -0
  49. package/dist-cli-web/assets/DataDirCleanupNotice-3sGBE5vU.js +1 -0
  50. package/dist-cli-web/assets/{DatePicker.vue_vue_type_script_setup_true_lang-sPDoPzZ9.js → DatePicker.vue_vue_type_script_setup_true_lang-CQuub8-f.js} +1 -1
  51. package/dist-cli-web/assets/{DebugToolsPanel-BIHA6jw4.js → DebugToolsPanel-Cwkr0BOY.js} +1 -1
  52. package/dist-cli-web/assets/EChart.vue_vue_type_script_setup_true_lang-BuLujQCF.js +1 -0
  53. package/dist-cli-web/assets/EChartBar.vue_vue_type_script_setup_true_lang-DuA5xbcC.js +1 -0
  54. package/dist-cli-web/assets/EChartLine.vue_vue_type_script_setup_true_lang-CvmMEfYq.js +1 -0
  55. package/dist-cli-web/assets/EChartWordcloud-CHVtyPR7.js +1 -0
  56. package/dist-cli-web/assets/FileDropZone.vue_vue_type_script_setup_true_lang-BKPy9Qv-.js +1 -0
  57. package/dist-cli-web/assets/{Filter-kO4dIDJ2.js → Filter-CMfRHIoK.js} +1 -1
  58. package/dist-cli-web/assets/GlobalTaskBar-Jz6MASCu.js +1 -0
  59. package/dist-cli-web/assets/GlobalTaskBar-haLzezqY.css +1 -0
  60. package/dist-cli-web/assets/GroupRelationshipGalaxyView-Ch4obfEq.js +1 -0
  61. package/dist-cli-web/assets/Label-CuryuymO.js +1 -0
  62. package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-CoeQsaiL.js +1 -0
  63. package/dist-cli-web/assets/MessageExportModal-JqGZ-iLr.js +1 -0
  64. package/dist-cli-web/assets/PageHeader-CPLgiKT8.css +1 -0
  65. package/dist-cli-web/assets/PageHeader-D4up77TB.js +1 -0
  66. package/dist-cli-web/assets/{PageTabs.vue_vue_type_script_setup_true_lang-Cgungbjl.js → PageTabs.vue_vue_type_script_setup_true_lang-DYQRFk9s.js} +1 -1
  67. package/dist-cli-web/assets/{RelationshipGalaxyCanvas-Cod1JX9m.js → RelationshipGalaxyCanvas-Cn7Eta5U.js} +3 -3
  68. package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-CkefzeFJ.js → RelationshipGalaxyThreeCanvas-LlaX57z6.js} +1 -1
  69. package/dist-cli-web/assets/{RenderTargetSystem-SuQ7cbze.js → RenderTargetSystem-JhA2PUFi.js} +1 -1
  70. package/dist-cli-web/assets/{RovingFocusItem-psoFYxrC.js → RovingFocusItem-DHaiTb4j.js} +1 -1
  71. package/dist-cli-web/assets/{ScreenCaptureModal-D4r9ixKh.js → ScreenCaptureModal-Bxhyt5g1.js} +1 -1
  72. package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-BlAje3Tf.js +1 -0
  73. package/dist-cli-web/assets/Select-Beeqv0wU.js +1 -0
  74. package/dist-cli-web/assets/SettingsModal-DV8yDBJs.js +2 -0
  75. package/dist-cli-web/assets/{StorageTab-DhSJ-0Ir.js → StorageTab-B7GHadia.js} +1 -1
  76. package/dist-cli-web/assets/Switch-u4zlvUMa.js +1 -0
  77. package/dist-cli-web/assets/{Tabs-Bnm_Q7yW.js → Tabs-B7-Koq3x.js} +1 -1
  78. package/dist-cli-web/assets/Tabs-RWKHwtfM.js +1 -0
  79. package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-ndsa3-aT.js +1 -0
  80. package/dist-cli-web/assets/{TimeSelect.vue_vue_type_script_setup_true_lang-Cjz8Wpg-.js → TimeSelect.vue_vue_type_script_setup_true_lang-DKa1flT-.js} +1 -1
  81. package/dist-cli-web/assets/{VisuallyHiddenInput-Bo_XwaLu.js → VisuallyHiddenInput-DGDqcvUy.js} +1 -1
  82. package/dist-cli-web/assets/{WebGLRenderer-D1x9WZRj.js → WebGLRenderer-ifvgA1fe.js} +1 -1
  83. package/dist-cli-web/assets/{WebGPURenderer-37Nw-tLm.js → WebGPURenderer-DwzmuWF0.js} +1 -1
  84. package/dist-cli-web/assets/aiChat-BY4C3UQe.js +8 -0
  85. package/dist-cli-web/assets/base-BWDX-164.js +1 -0
  86. package/dist-cli-web/assets/{browserAll-CWrz_-4U.js → browserAll-CSIIETeE.js} +1 -1
  87. package/dist-cli-web/assets/chatMessages--r0obAVw.css +1 -0
  88. package/dist-cli-web/assets/chatMessages-Zywjea1n.js +147 -0
  89. package/dist-cli-web/assets/cli-web-BRY49NWe.js +1 -0
  90. package/dist-cli-web/assets/{fetch-Du-wU0OO.js → fetch-Bf4cqeM3.js} +1 -1
  91. package/dist-cli-web/assets/{fetch-DPSWt6yX.js → fetch-BiXab0IF.js} +1 -1
  92. package/dist-cli-web/assets/{fetch-B69OkB6B.js → fetch-C8CcoTWY.js} +1 -1
  93. package/dist-cli-web/assets/{fetch-qd2FgBWQ.js → fetch-Co2vLmiE.js} +1 -1
  94. package/dist-cli-web/assets/{fetch-BGmCCyzz.js → fetch-D3nLtFBi.js} +1 -1
  95. package/dist-cli-web/assets/{fetch-CFpLXWTe.js → fetch-DNSDHSqQ.js} +1 -1
  96. package/dist-cli-web/assets/{fetch-DZ6OF-xW.js → fetch-DeHqF6yL.js} +1 -1
  97. package/dist-cli-web/assets/{fetch-CkXgOxcu.js → fetch-jx1VdM9j.js} +3 -3
  98. package/dist-cli-web/assets/{fetch-Gu4qQjUB.js → fetch-mYSeL4Ki.js} +1 -1
  99. package/dist-cli-web/assets/fetch-qBaUXqFt.js +1 -0
  100. package/dist-cli-web/assets/{fetch-6pcVxB9h.js → fetch-r6mhEGrP.js} +1 -1
  101. package/dist-cli-web/assets/{fetch-CgA1peJD.js → fetch-wzv71PYy.js} +1 -1
  102. package/dist-cli-web/assets/{host-CWsAWKgy.js → host-1emQf9_4.js} +1 -1
  103. package/dist-cli-web/assets/{host-D8zlA62K.js → host-jXJHyyOX.js} +1 -1
  104. package/dist-cli-web/assets/index-2dJCozDq.js +64 -0
  105. package/dist-cli-web/assets/index-6ejRbH3h.js +2 -0
  106. package/dist-cli-web/assets/index-B4mJkX_d.js +1 -0
  107. package/dist-cli-web/assets/index-B7biznha.js +1 -0
  108. package/dist-cli-web/assets/index-BOcFyPCa.js +1 -0
  109. package/dist-cli-web/assets/index-BQ463TS7.js +1 -0
  110. package/dist-cli-web/assets/index-BZpR015e.css +1 -0
  111. package/dist-cli-web/assets/index-BgelKQTW.css +1 -0
  112. package/dist-cli-web/assets/index-C1A1FwpO.css +1 -0
  113. package/dist-cli-web/assets/index-CIIyg_o9.css +1 -0
  114. package/dist-cli-web/assets/{index-a7CT8DQm.js → index-CYc_VLgK.js} +1 -1
  115. package/dist-cli-web/assets/index-ClHMs0-a.js +2 -0
  116. package/dist-cli-web/assets/{index-Dy0fRMDf.js → index-Cw0IhKZ6.js} +10 -10
  117. package/dist-cli-web/assets/{index-v6DlL4ue.js → index-DOqbUvhA.js} +1 -1
  118. package/dist-cli-web/assets/index-DnpnIyle.js +135 -0
  119. package/dist-cli-web/assets/index-LV9LfFNu.js +1 -0
  120. package/dist-cli-web/assets/index-kePiJ3ca.js +137 -0
  121. package/dist-cli-web/assets/index-slDUxib2.js +1 -0
  122. package/dist-cli-web/assets/{people-page-header-Czt9e1jv.js → people-page-header-Cx5r9Bk2.js} +1 -1
  123. package/dist-cli-web/assets/{prompt-DGazz143.js → prompt-BggEDxxi.js} +1 -1
  124. package/dist-cli-web/assets/{semantic-index-config-builder-BkFHzIUy.js → semantic-index-config-builder-1sO0aEc1.js} +1 -1
  125. package/dist-cli-web/assets/{service-CAp0YJoS.js → service-Bvq1fRkC.js} +1 -1
  126. package/dist-cli-web/assets/service-DkPheDt8.js +1 -0
  127. package/dist-cli-web/assets/service-DvI8O_7K.js +1 -0
  128. package/dist-cli-web/assets/useFormControl-B0aShtLp.js +1 -0
  129. package/dist-cli-web/assets/useTable-DC7oxmBl.js +1 -0
  130. package/dist-cli-web/assets/{useToast-YAHDkKDN.js → useToast-CCPBE-Sy.js} +1 -1
  131. package/dist-cli-web/assets/{vendor-echarts-hTLhTs52.js → vendor-echarts-C9ugTmum.js} +1 -1
  132. package/dist-cli-web/assets/{vendor-echarts-wordcloud-B8CF_NgP.js → vendor-echarts-wordcloud-DNiU8Psq.js} +1 -1
  133. package/dist-cli-web/assets/{webworkerAll-CvSp2Y8j.js → webworkerAll-FCJk6reG.js} +1 -1
  134. package/dist-cli-web/index.html +2 -2
  135. package/package.json +1 -1
  136. package/dist/chunk-6E46U4H2.mjs +0 -41
  137. package/dist/chunk-6E46U4H2.mjs.map +0 -1
  138. package/dist/chunk-HWIXK7SU.mjs.map +0 -1
  139. package/dist/chunk-TJ3NOBVI.mjs.map +0 -1
  140. package/dist/chunk-TRGR3KKJ.mjs.map +0 -1
  141. package/dist/chunk-VREVDZ4Q.mjs.map +0 -1
  142. package/dist/chunk-VUYWUHWW.mjs.map +0 -1
  143. package/dist/chunk-WJQOXOMR.mjs.map +0 -1
  144. package/dist/chunk-WRBFV2GX.mjs.map +0 -1
  145. package/dist/chunk-YUH3H3QW.mjs.map +0 -1
  146. package/dist-cli-web/assets/AISettingsTab-VgI7PiJd.js +0 -1
  147. package/dist-cli-web/assets/ActionToolsPanel-BTGgwXVD.css +0 -1
  148. package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-a7RMjjve.js +0 -112
  149. package/dist-cli-web/assets/ApiSettingsTab-Co3LWf1f.js +0 -2
  150. package/dist-cli-web/assets/BatchManageTab-2LrckmQm.js +0 -1
  151. package/dist-cli-web/assets/ChatRecordDrawer-DRhztqLG.js +0 -1
  152. package/dist-cli-web/assets/Checkbox-EfpQmv4A.js +0 -1
  153. package/dist-cli-web/assets/DataDirCleanupNotice-BYIiR58J.js +0 -1
  154. package/dist-cli-web/assets/EChart.vue_vue_type_script_setup_true_lang-DryMSoCz.js +0 -1
  155. package/dist-cli-web/assets/EChartBar.vue_vue_type_script_setup_true_lang-BdFW-hvt.js +0 -1
  156. package/dist-cli-web/assets/EChartLine.vue_vue_type_script_setup_true_lang-C69Kdw9X.js +0 -1
  157. package/dist-cli-web/assets/EChartWordcloud-DM7UEHdK.js +0 -1
  158. package/dist-cli-web/assets/GlobalTaskBar-BZS_h0zC.js +0 -1
  159. package/dist-cli-web/assets/GlobalTaskBar-COW_uBX6.css +0 -1
  160. package/dist-cli-web/assets/GroupRelationshipGalaxyView--WIaHx_F.js +0 -1
  161. package/dist-cli-web/assets/Label-CIuDXyeM.js +0 -1
  162. package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-B_7VHxTx.js +0 -1
  163. package/dist-cli-web/assets/MessageExportModal-BQsIpyxv.js +0 -1
  164. package/dist-cli-web/assets/PageHeader-DxhQHF_0.js +0 -1
  165. package/dist-cli-web/assets/PageHeader-XIXMTomn.css +0 -1
  166. package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-v_7aRBgM.js +0 -1
  167. package/dist-cli-web/assets/Select-CxurgslB.js +0 -1
  168. package/dist-cli-web/assets/SettingsModal-DBZucuf6.js +0 -2
  169. package/dist-cli-web/assets/Switch-3D81G6uV.js +0 -1
  170. package/dist-cli-web/assets/Tabs-DB603EEu.js +0 -1
  171. package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-BDTdkH5i.js +0 -1
  172. package/dist-cli-web/assets/aiChat-BCzYQeZq.js +0 -8
  173. package/dist-cli-web/assets/cli-web-C-8N0CJh.js +0 -1
  174. package/dist-cli-web/assets/fetch-z9M9FcIC.js +0 -1
  175. package/dist-cli-web/assets/index-BGd4ZfhO.js +0 -2
  176. package/dist-cli-web/assets/index-BYZF1ngN.js +0 -1
  177. package/dist-cli-web/assets/index-BcuwlCin.css +0 -1
  178. package/dist-cli-web/assets/index-BnaLj2dx.css +0 -1
  179. package/dist-cli-web/assets/index-Bq5q3kSU.js +0 -36
  180. package/dist-cli-web/assets/index-CPR3jdC2.js +0 -137
  181. package/dist-cli-web/assets/index-D3AjvaOX.js +0 -1
  182. package/dist-cli-web/assets/index-DAitScUA.js +0 -1
  183. package/dist-cli-web/assets/index-DBSMWAJ0.js +0 -1
  184. package/dist-cli-web/assets/index-DTn9htw-.js +0 -64
  185. package/dist-cli-web/assets/index-DV_rWxcH.js +0 -1
  186. package/dist-cli-web/assets/index-DxhAGqYM.css +0 -1
  187. package/dist-cli-web/assets/index-I0mIcDpE.css +0 -1
  188. package/dist-cli-web/assets/index-onwNShRw.js +0 -2
  189. package/dist-cli-web/assets/service-BMqwB74f.js +0 -1
  190. package/dist-cli-web/assets/service-CUQ8iHdS.js +0 -1
  191. package/dist-cli-web/assets/useFormControl-DcSMUc2d.js +0 -1
  192. package/dist-cli-web/assets/useTable-Clmq3sge.js +0 -1
  193. /package/dist/{chunk-6EZPD3XJ.mjs.map → chunk-N2WGG3MS.mjs.map} +0 -0
  194. /package/dist/{chunk-TWH422HC.mjs.map → chunk-RXMSZIYD.mjs.map} +0 -0
  195. /package/dist/{chunk-CH5YAZ4Q.mjs.map → chunk-SQWN4GTQ.mjs.map} +0 -0
  196. /package/dist/{http-237XYNRT.mjs.map → http-X6OEOOSH.mjs.map} +0 -0
  197. /package/dist/{import-R2E2TFAK.mjs.map → import-WBJ6YIQ7.mjs.map} +0 -0
  198. /package/dist/{mcp-BED4AARV.mjs.map → mcp-FR6OKWKL.mjs.map} +0 -0
  199. /package/dist/{src-AYMALWU5.mjs.map → src-WE5HNN2O.mjs.map} +0 -0
  200. /package/dist/{stream-import-RXNSOOYH.mjs.map → stream-import-YYQVZDGQ.mjs.map} +0 -0
  201. /package/dist/{worker-runtime-JGAZU7MC.mjs.map → worker-runtime-YGB5ZDE4.mjs.map} +0 -0
@@ -28,7 +28,7 @@ import {
28
28
  getWeekdayActivity,
29
29
  queryMessages,
30
30
  searchMessagesByKeywords
31
- } from "./chunk-TRGR3KKJ.mjs";
31
+ } from "./chunk-4J4MA3RC.mjs";
32
32
 
33
33
  // ../../packages/tools/src/utils/limits.ts
34
34
  function resolveMessageLimit(requestedLimit, defaultLimit, maxMessagesLimit) {
@@ -1482,6 +1482,1612 @@ var retrieveChatEvidenceTool = {
1482
1482
  truncationStrategy: "keep_first"
1483
1483
  };
1484
1484
 
1485
+ // ../../packages/tools/src/definitions/cross-chat-tools.ts
1486
+ var scopeItems = {
1487
+ type: "object",
1488
+ properties: {
1489
+ sessionId: { type: "string", description: "Stable session ID returned by resolve_chat_entities" },
1490
+ memberIds: { type: "array", items: { type: "number" }, description: "Local member IDs for this session" },
1491
+ label: { type: "string", description: "Human-readable scope label" }
1492
+ },
1493
+ required: ["sessionId"]
1494
+ };
1495
+ var recentDaysProperty = {
1496
+ type: "number",
1497
+ minimum: 1,
1498
+ description: "Relative time window ending at the real current time. Use 30 for unspecified recent activity, but omit it for a latest available private-chat recap without an explicit date or duration."
1499
+ };
1500
+ var resolveSchema = {
1501
+ type: "object",
1502
+ properties: {
1503
+ entities: {
1504
+ type: "array",
1505
+ description: "Contact or session references. A contact may use a stable contactKey or a displayName lookup from the user text.",
1506
+ items: {
1507
+ type: "object",
1508
+ properties: {
1509
+ type: { type: "string", enum: ["contact", "session"] },
1510
+ contactKey: { type: "string" },
1511
+ sessionId: { type: "string" },
1512
+ displayName: { type: "string" },
1513
+ sessionType: { type: "string", enum: ["private", "group"] }
1514
+ },
1515
+ required: ["type", "displayName"]
1516
+ }
1517
+ }
1518
+ },
1519
+ required: ["entities"]
1520
+ };
1521
+ var searchSchema = {
1522
+ type: "object",
1523
+ properties: {
1524
+ keywords: {
1525
+ type: "array",
1526
+ items: { type: "string" },
1527
+ description: "Exact substring keywords. May be omitted only with explicit scopes to sample recent messages from those scopes."
1528
+ },
1529
+ scopes: {
1530
+ type: "array",
1531
+ description: "Resolved session/member scopes. Omit only when the user clearly requested global discovery.",
1532
+ items: scopeItems
1533
+ },
1534
+ match_mode: { type: "string", enum: ["any", "all"], description: "Whether any or all keywords must match" },
1535
+ recent_days: recentDaysProperty,
1536
+ sender: {
1537
+ type: "string",
1538
+ enum: ["all", "owner"],
1539
+ description: "Message sender filter. Use owner when the user's wording makes them the subject, such as 'I discussed buying a home'."
1540
+ },
1541
+ sort: { type: "string", enum: ["asc", "desc"], description: "Timestamp order; defaults to newest first" },
1542
+ ...timeParamProperties
1543
+ }
1544
+ };
1545
+ var recentSessionSchema = {
1546
+ type: "object",
1547
+ properties: {
1548
+ session_id: {
1549
+ type: "string",
1550
+ description: "One exact session ID returned by resolve_chat_entities"
1551
+ }
1552
+ },
1553
+ required: ["session_id"]
1554
+ };
1555
+ var contextSchema = {
1556
+ type: "object",
1557
+ properties: {
1558
+ session_id: { type: "string", description: "Source session ID from cross-chat search evidence" },
1559
+ message_id: { type: "number", description: "Message ID inside that source session" },
1560
+ context_size: { type: "number", description: "Messages before and after the source message; defaults to 10" }
1561
+ },
1562
+ required: ["session_id", "message_id"]
1563
+ };
1564
+ var overviewSchema = {
1565
+ type: "object",
1566
+ properties: {
1567
+ scopes: {
1568
+ type: "array",
1569
+ description: "Resolved session/member scopes to summarize separately",
1570
+ items: scopeItems
1571
+ },
1572
+ recent_days: recentDaysProperty,
1573
+ ...timeParamProperties
1574
+ },
1575
+ required: ["scopes"]
1576
+ };
1577
+ var rankPrivateContactsSchema = {
1578
+ type: "object",
1579
+ properties: {
1580
+ rank_by: {
1581
+ type: "string",
1582
+ enum: ["message_count", "active_days"],
1583
+ description: "Ranking metric. Defaults to total private-chat message count."
1584
+ },
1585
+ limit: { type: "number", minimum: 1, maximum: 50, description: "Number of ranked contacts to return" },
1586
+ recent_days: recentDaysProperty,
1587
+ ...timeParamProperties
1588
+ }
1589
+ };
1590
+ var rankGroupSessionsSchema = {
1591
+ type: "object",
1592
+ properties: {
1593
+ mode: {
1594
+ type: "string",
1595
+ enum: ["owner_activity", "total_activity"],
1596
+ description: "Use owner_activity for 'where was I most active' and total_activity for 'which groups were busiest overall'."
1597
+ },
1598
+ limit: { type: "number", minimum: 1, maximum: 50, description: "Number of ranked groups to return" },
1599
+ recent_days: recentDaysProperty,
1600
+ ...timeParamProperties
1601
+ },
1602
+ required: ["mode"]
1603
+ };
1604
+ var globalActivitySummarySchema = {
1605
+ type: "object",
1606
+ properties: {
1607
+ mode: {
1608
+ type: "string",
1609
+ enum: ["year", "recent_365"],
1610
+ description: "Use year for a calendar year and recent_365 for the rolling latest 365 days. Defaults to year."
1611
+ },
1612
+ year: {
1613
+ type: "number",
1614
+ minimum: 1970,
1615
+ description: "Calendar year for mode=year. Omit it to use the current year."
1616
+ }
1617
+ }
1618
+ };
1619
+ var inspectContactSessionsSchema = {
1620
+ type: "object",
1621
+ properties: {
1622
+ contact_key: {
1623
+ type: "string",
1624
+ description: "Stable contact key returned by resolve_chat_entities; display names are not accepted"
1625
+ },
1626
+ recent_days: recentDaysProperty,
1627
+ include_roster_only: {
1628
+ type: "boolean",
1629
+ description: "Include imported sessions where the member is recorded but did not speak in the selected range",
1630
+ default: true
1631
+ },
1632
+ cursor: { type: "string", description: "Opaque continuation cursor from the previous result" },
1633
+ page_size: { type: "number", description: "Maximum matching sessions returned in this batch" },
1634
+ max_wall_time_ms: { type: "number", description: "Maximum wall time for this inspection batch" },
1635
+ ...timeParamProperties
1636
+ },
1637
+ required: ["contact_key"]
1638
+ };
1639
+ var MAX_SHARED_ANCHORS_PER_PAIR = 8;
1640
+ var inspectSharedInteractionsSchema = {
1641
+ type: "object",
1642
+ properties: {
1643
+ participants: {
1644
+ type: "array",
1645
+ description: "Two to five distinct people. Use type=owner for the local user in each session or type=contact with a stable contact_key.",
1646
+ items: {
1647
+ type: "object",
1648
+ properties: {
1649
+ type: { type: "string", enum: ["owner", "contact"] },
1650
+ contact_key: { type: "string" }
1651
+ },
1652
+ required: ["type"]
1653
+ }
1654
+ },
1655
+ recent_days: recentDaysProperty,
1656
+ cursor: { type: "string", description: "Opaque continuation cursor from the previous result" },
1657
+ page_size: { type: "number", description: "Maximum common sessions returned in this batch" },
1658
+ max_anchors_per_pair: {
1659
+ type: "number",
1660
+ description: "Maximum direct-reply and proximity message anchors returned for each participant pair",
1661
+ minimum: 0,
1662
+ maximum: MAX_SHARED_ANCHORS_PER_PAIR
1663
+ },
1664
+ max_wall_time_ms: { type: "number", description: "Maximum wall time for this inspection batch" },
1665
+ ...timeParamProperties
1666
+ },
1667
+ required: ["participants"]
1668
+ };
1669
+ var DEFAULT_CONTACT_INSPECTION_PAGE_SIZE = 50;
1670
+ var MAX_CONTACT_INSPECTION_PAGE_SIZE = 100;
1671
+ var MODEL_LABEL_MAX_LENGTH = 80;
1672
+ var DEFAULT_SHARED_INSPECTION_PAGE_SIZE = 20;
1673
+ var DEFAULT_SHARED_ANCHORS_PER_PAIR = 4;
1674
+ var TOOL_RESULT_CHARS_PER_TOKEN = 4;
1675
+ var TOOL_RESULT_BASE_CHARS = 1500;
1676
+ var CONTACT_SESSION_ESTIMATED_CHARS = 650;
1677
+ var SHARED_SESSION_BASE_CHARS = 550;
1678
+ var SHARED_PARTICIPANT_ESTIMATED_CHARS = 240;
1679
+ var SHARED_PAIR_BASE_CHARS = 260;
1680
+ var SHARED_ANCHOR_ESTIMATED_CHARS = 180;
1681
+ async function resolveHandler(params, context) {
1682
+ const inputs = parseEntityInputs(params.entities);
1683
+ const refs = [];
1684
+ const contactLookups = [];
1685
+ for (const input of inputs) {
1686
+ if (input.type === "contact" && !input.contactKey) {
1687
+ const lookup = context.analysisService.lookupContact(input.displayName);
1688
+ contactLookups.push(lookup);
1689
+ if (lookup.status === "resolved" && lookup.candidates[0]) {
1690
+ refs.push({
1691
+ type: "contact",
1692
+ contactKey: lookup.candidates[0].contactKey,
1693
+ displayName: lookup.candidates[0].displayName
1694
+ });
1695
+ }
1696
+ continue;
1697
+ }
1698
+ refs.push(input);
1699
+ }
1700
+ const resolution = await context.analysisService.resolveEntities(dedupeEntityRefs(refs), {
1701
+ signal: context.abortSignal
1702
+ });
1703
+ trackResolvedEntityRefs(context.resolvedEntityRefs, resolution);
1704
+ const data = { ...resolution, contactLookups };
1705
+ const modelData = limitEntityResolutionToBudget(
1706
+ resolution,
1707
+ contactLookups,
1708
+ context.maxToolResultTokens,
1709
+ context.countTokens
1710
+ );
1711
+ return { content: JSON.stringify(modelData), data };
1712
+ }
1713
+ async function searchHandler(params, context) {
1714
+ const timeFilter = parseExtendedTimeParams(params);
1715
+ const keywords = params.keywords === void 0 ? [] : parseStringArray(params.keywords);
1716
+ const result = await context.analysisService.searchMessages(
1717
+ {
1718
+ keywords,
1719
+ scopes: params.scopes === void 0 ? void 0 : parseScopes(params.scopes),
1720
+ startTs: timeFilter?.startTs,
1721
+ endTs: timeFilter?.endTs,
1722
+ recentDays: parseOptionalNumber(params.recent_days),
1723
+ sender: params.sender === "owner" ? "owner" : "all",
1724
+ matchMode: params.match_mode === "all" ? "all" : "any",
1725
+ sort: params.sort === "asc" ? "asc" : "desc"
1726
+ },
1727
+ {
1728
+ signal: context.abortSignal,
1729
+ onProgress: (progress) => context.reportProgress?.({
1730
+ phase: "searching",
1731
+ current: progress.processedSessions,
1732
+ total: progress.totalSessions
1733
+ })
1734
+ }
1735
+ );
1736
+ const safeMessages = await preprocessBySession(context, result.messages);
1737
+ const buildCoverage = (budgetTruncated) => ({
1738
+ ...result.coverage,
1739
+ truncated: result.coverage.truncated || budgetTruncated,
1740
+ truncatedReasons: budgetTruncated ? [.../* @__PURE__ */ new Set([...result.coverage.truncatedReasons, "evidence_budget"])] : result.coverage.truncatedReasons
1741
+ });
1742
+ const buildModelData = (messages, budgetTruncated) => ({
1743
+ totalMatches: result.totalMatches,
1744
+ returned: messages.length,
1745
+ selection: summarizeEvidenceSelection(messages),
1746
+ appliedFilters: result.appliedFilters,
1747
+ coverage: buildCoverage(budgetTruncated),
1748
+ messages: messages.map(toModelMessage)
1749
+ });
1750
+ const limited = limitMessagesToBudget(safeMessages, context.maxToolResultTokens, buildModelData, {
1751
+ countTokens: context.countTokens
1752
+ });
1753
+ const modelData = buildModelData(limited.messages, limited.truncated);
1754
+ const evidence = {
1755
+ version: 1,
1756
+ query: keywords.join(" "),
1757
+ sources: limited.messages.map(toEvidenceSource),
1758
+ coverage: modelData.coverage
1759
+ };
1760
+ const data = {
1761
+ ...modelData,
1762
+ crossChatEvidence: evidence
1763
+ };
1764
+ return { content: JSON.stringify(modelData), data };
1765
+ }
1766
+ async function readRecentSessionHandler(params, context) {
1767
+ const result = context.analysisService.readRecentSession(requireString(params.session_id, "session_id"));
1768
+ const safeMessages = await preprocessBySession(context, result.messages);
1769
+ const safeSummaries = await context.preprocessSummariesBySession(result.source.sessionId, result.summaries);
1770
+ const sessionPseudonym = "Session1";
1771
+ const modelSource = {
1772
+ ...result.source,
1773
+ sessionName: context.preprocessModelLabel(result.source.sessionName, sessionPseudonym)
1774
+ };
1775
+ const toRecentModelMessage = (message) => ({
1776
+ ...toModelMessage(message),
1777
+ sessionName: context.preprocessModelLabel(message.sessionName, sessionPseudonym)
1778
+ });
1779
+ const buildModelData = (messages, budgetTruncated) => ({
1780
+ source: modelSource,
1781
+ selection: {
1782
+ strategy: "latest_session_slice",
1783
+ totalMessages: result.coverage.totalMessages,
1784
+ returnedMessages: messages.length,
1785
+ returnedSummaries: safeSummaries.length,
1786
+ hasEarlierMessages: result.coverage.hasEarlierMessages,
1787
+ toolResultTruncated: budgetTruncated
1788
+ },
1789
+ summaries: safeSummaries,
1790
+ messages: messages.map(toRecentModelMessage)
1791
+ });
1792
+ const limited = limitMessagesToBudget(safeMessages, context.maxToolResultTokens, buildModelData, {
1793
+ countTokens: context.countTokens
1794
+ });
1795
+ const modelData = buildModelData(limited.messages, limited.truncated);
1796
+ const evidence = {
1797
+ version: 1,
1798
+ query: "recent session recap",
1799
+ sources: limited.messages.map(toEvidenceSource),
1800
+ coverage: {
1801
+ candidateSessions: 1,
1802
+ scannedSessions: 1,
1803
+ matchedSessions: limited.messages.length > 0 ? 1 : 0,
1804
+ failedSessions: 0,
1805
+ truncated: limited.truncated,
1806
+ truncatedReasons: limited.truncated ? ["evidence_budget"] : []
1807
+ }
1808
+ };
1809
+ return {
1810
+ content: JSON.stringify(modelData),
1811
+ data: {
1812
+ ...modelData,
1813
+ source: result.source,
1814
+ messages: limited.messages.map(toModelMessage),
1815
+ crossChatEvidence: evidence
1816
+ }
1817
+ };
1818
+ }
1819
+ async function contextHandler(params, context) {
1820
+ const sessionId = requireString(params.session_id, "session_id");
1821
+ const messageId = requireNumber(params.message_id, "message_id");
1822
+ const contextSize = parseOptionalNumber(params.context_size);
1823
+ const result = context.analysisService.getMessageContext({ sessionId, messageId, contextSize });
1824
+ const safeMessages = await preprocessBySession(context, result.messages);
1825
+ const buildModelData = (messages, truncated) => ({
1826
+ source: result.source,
1827
+ returned: messages.length,
1828
+ truncated,
1829
+ messages: messages.map(toModelMessage)
1830
+ });
1831
+ const limited = limitMessagesToBudget(safeMessages, context.maxToolResultTokens, buildModelData, {
1832
+ priorityIndexes: buildContextPriority(safeMessages, messageId),
1833
+ continueAfterOverflow: true,
1834
+ countTokens: context.countTokens
1835
+ });
1836
+ const data = buildModelData(limited.messages, limited.truncated);
1837
+ return { content: JSON.stringify(data), data };
1838
+ }
1839
+ async function overviewHandler(params, context) {
1840
+ const timeFilter = parseExtendedTimeParams(params);
1841
+ const result = await context.analysisService.getOverview(
1842
+ {
1843
+ scopes: parseScopes(params.scopes),
1844
+ startTs: timeFilter?.startTs,
1845
+ endTs: timeFilter?.endTs,
1846
+ recentDays: parseOptionalNumber(params.recent_days)
1847
+ },
1848
+ {
1849
+ signal: context.abortSignal,
1850
+ onProgress: (progress) => context.reportProgress?.({
1851
+ phase: "analyzing",
1852
+ current: progress.processedSessions,
1853
+ total: progress.totalSessions
1854
+ })
1855
+ }
1856
+ );
1857
+ const data = {
1858
+ appliedRange: result.appliedRange,
1859
+ items: result.items.map(({ memberNames: _memberNames, ...item }) => item),
1860
+ coverage: result.coverage
1861
+ };
1862
+ const fullModelData = {
1863
+ ...data,
1864
+ items: data.items.map((item, index) => {
1865
+ const sessionPseudonym = `Session${index + 1}`;
1866
+ const preprocessMember = (member) => ({
1867
+ ...member,
1868
+ memberName: context.preprocessModelLabel(member.memberName, `U${member.memberId}@${item.sessionId}`)
1869
+ });
1870
+ return {
1871
+ ...item,
1872
+ sessionName: context.preprocessModelLabel(item.sessionName, sessionPseudonym),
1873
+ label: context.preprocessModelLabel(item.label, sessionPseudonym),
1874
+ memberActivities: item.memberActivities.map(preprocessMember),
1875
+ topMembers: item.topMembers.map(preprocessMember)
1876
+ };
1877
+ })
1878
+ };
1879
+ const modelData = limitOverviewToBudget(
1880
+ fullModelData,
1881
+ context.maxToolResultTokens,
1882
+ context.countTokens ?? estimatePayloadTokens
1883
+ );
1884
+ return { content: JSON.stringify(modelData), data };
1885
+ }
1886
+ async function rankPrivateContactsHandler(params, context) {
1887
+ const timeFilter = parseExtendedTimeParams(params);
1888
+ const result = await context.analysisService.rankPrivateContacts(
1889
+ {
1890
+ startTs: timeFilter?.startTs,
1891
+ endTs: timeFilter?.endTs,
1892
+ recentDays: parseOptionalNumber(params.recent_days),
1893
+ rankBy: params.rank_by === "active_days" ? "active_days" : "message_count",
1894
+ limit: parseOptionalNumber(params.limit)
1895
+ },
1896
+ {
1897
+ signal: context.abortSignal,
1898
+ onProgress: (progress) => context.reportProgress?.({
1899
+ phase: "analyzing",
1900
+ current: progress.processedSessions,
1901
+ total: progress.totalSessions
1902
+ })
1903
+ }
1904
+ );
1905
+ const modelData = {
1906
+ ...result,
1907
+ items: result.items.map((item) => ({
1908
+ ...item,
1909
+ displayName: context.preprocessModelLabel(item.displayName, `Contact${item.rank}`)
1910
+ }))
1911
+ };
1912
+ return { content: JSON.stringify(modelData), data: result };
1913
+ }
1914
+ async function rankGroupSessionsHandler(params, context) {
1915
+ const timeFilter = parseExtendedTimeParams(params);
1916
+ const mode = requireString(params.mode, "mode");
1917
+ if (mode !== "owner_activity" && mode !== "total_activity") {
1918
+ throw new Error("mode must be owner_activity or total_activity");
1919
+ }
1920
+ const result = await context.analysisService.rankGroupSessions(
1921
+ {
1922
+ mode,
1923
+ startTs: timeFilter?.startTs,
1924
+ endTs: timeFilter?.endTs,
1925
+ recentDays: parseOptionalNumber(params.recent_days),
1926
+ limit: parseOptionalNumber(params.limit)
1927
+ },
1928
+ {
1929
+ signal: context.abortSignal,
1930
+ onProgress: (progress) => context.reportProgress?.({
1931
+ phase: "analyzing",
1932
+ current: progress.processedSessions,
1933
+ total: progress.totalSessions
1934
+ })
1935
+ }
1936
+ );
1937
+ const modelData = {
1938
+ ...result,
1939
+ items: result.items.map((item) => ({
1940
+ ...item,
1941
+ sessionName: context.preprocessModelLabel(item.sessionName, `Group${item.rank}`)
1942
+ }))
1943
+ };
1944
+ return { content: JSON.stringify(modelData), data: result };
1945
+ }
1946
+ async function globalActivitySummaryHandler(params, context) {
1947
+ const mode = params.mode ?? "year";
1948
+ if (mode !== "year" && mode !== "recent_365") throw new Error("mode must be year or recent_365");
1949
+ const result = context.analysisService.getGlobalActivitySummary({
1950
+ mode,
1951
+ year: parseOptionalNumber(params.year)
1952
+ });
1953
+ const modelData = limitGlobalActivitySummaryToBudget(
1954
+ buildGlobalActivityModelData(result),
1955
+ context.maxToolResultTokens,
1956
+ context.countTokens ?? estimatePayloadTokens
1957
+ );
1958
+ return { content: JSON.stringify(modelData), data: result };
1959
+ }
1960
+ function buildGlobalActivityModelData(result) {
1961
+ const summary = result.summary;
1962
+ return {
1963
+ mode: result.mode,
1964
+ dataState: result.dataState,
1965
+ range: summary.range,
1966
+ availableDataYears: summary.availableDataYears,
1967
+ latestDataYear: summary.latestDataYear,
1968
+ metrics: summary.metrics,
1969
+ monthlyActivity: summary.monthlyActivity,
1970
+ monthlyDirectContacts: summary.monthlyDirectContacts,
1971
+ dailyActivity: summary.dailyActivity,
1972
+ messageTypes: summary.messageTypes,
1973
+ textLength: summary.textLength,
1974
+ coverage: summary.coverage,
1975
+ cache: {
1976
+ status: summary.cache.status,
1977
+ computedAt: summary.cache.computedAt,
1978
+ staleReason: summary.cache.staleReason
1979
+ },
1980
+ task: {
1981
+ status: summary.task.status,
1982
+ startedAt: summary.task.startedAt,
1983
+ finishedAt: summary.task.finishedAt,
1984
+ processedSessions: summary.task.processedSessions,
1985
+ totalSessions: summary.task.totalSessions
1986
+ },
1987
+ selection: {
1988
+ dailyActivityTotal: summary.dailyActivity.length,
1989
+ dailyActivityReturned: summary.dailyActivity.length,
1990
+ dailyActivityFormat: "objects",
1991
+ toolResultTruncated: false,
1992
+ truncatedReasons: []
1993
+ }
1994
+ };
1995
+ }
1996
+ async function inspectContactSessionsHandler(params, context) {
1997
+ const timeFilter = parseExtendedTimeParams(params);
1998
+ const requestedPageSize = parseOptionalNumber(params.page_size);
1999
+ const budgetedPageSize = limitContactPageSizeToBudget(requestedPageSize, context.maxToolResultTokens);
2000
+ const result = await context.analysisService.inspectContactSessions(
2001
+ {
2002
+ contactKey: requireString(params.contact_key, "contact_key"),
2003
+ startTs: timeFilter?.startTs,
2004
+ endTs: timeFilter?.endTs,
2005
+ recentDays: parseOptionalNumber(params.recent_days),
2006
+ includeRosterOnly: params.include_roster_only === void 0 ? true : requireBoolean(params.include_roster_only),
2007
+ cursor: typeof params.cursor === "string" && params.cursor.trim() ? params.cursor.trim() : void 0,
2008
+ pageSize: budgetedPageSize.value,
2009
+ maxWallTimeMs: parseOptionalNumber(params.max_wall_time_ms)
2010
+ },
2011
+ {
2012
+ signal: context.abortSignal,
2013
+ onProgress: (progress) => context.reportProgress?.({
2014
+ phase: "analyzing",
2015
+ current: progress.processedSessions,
2016
+ total: progress.totalSessions
2017
+ })
2018
+ }
2019
+ );
2020
+ const countTokens = context.countTokens ?? estimatePayloadTokens;
2021
+ const pageBudgetTruncated = budgetedPageSize.limited && result.coverage.truncatedReasons.includes("page_size");
2022
+ const payloadExceedsBudget = !!context.maxToolResultTokens && context.maxToolResultTokens > 0 && countTokens(JSON.stringify(result)) > context.maxToolResultTokens;
2023
+ const data = pageBudgetTruncated || payloadExceedsBudget ? withToolResultBudgetReason(result) : result;
2024
+ const modelData = limitContactResultToBudget(data, context.maxToolResultTokens, countTokens);
2025
+ return { content: JSON.stringify(modelData), data };
2026
+ }
2027
+ async function inspectSharedInteractionsHandler(params, context) {
2028
+ const timeFilter = parseExtendedTimeParams(params);
2029
+ const participants = parseParticipants(params.participants);
2030
+ const requestedPageSize = parseOptionalNumber(params.page_size);
2031
+ const requestedAnchorsPerPair = parseOptionalNumber(params.max_anchors_per_pair);
2032
+ const budget = limitSharedRequestToBudget(
2033
+ participants.length,
2034
+ requestedPageSize,
2035
+ requestedAnchorsPerPair,
2036
+ context.maxToolResultTokens
2037
+ );
2038
+ const result = await context.analysisService.inspectSharedInteractions(
2039
+ {
2040
+ participants,
2041
+ startTs: timeFilter?.startTs,
2042
+ endTs: timeFilter?.endTs,
2043
+ recentDays: parseOptionalNumber(params.recent_days),
2044
+ cursor: typeof params.cursor === "string" && params.cursor.trim() ? params.cursor.trim() : void 0,
2045
+ pageSize: budget.pageSize,
2046
+ maxAnchorsPerPair: budget.maxAnchorsPerPair,
2047
+ maxWallTimeMs: parseOptionalNumber(params.max_wall_time_ms)
2048
+ },
2049
+ {
2050
+ signal: context.abortSignal,
2051
+ onProgress: (progress) => context.reportProgress?.({
2052
+ phase: "analyzing",
2053
+ current: progress.processedSessions,
2054
+ total: progress.totalSessions
2055
+ })
2056
+ }
2057
+ );
2058
+ const pageBudgetTruncated = budget.pageLimited && result.coverage.nextCursor !== null && result.coverage.truncatedReasons.includes("page_size");
2059
+ const anchorBudgetTruncated = budget.anchorsLimited && result.sessions.some((session) => session.pairs.some((pair) => pair.anchorsTruncated));
2060
+ const countTokens = context.countTokens ?? estimatePayloadTokens;
2061
+ const payloadExceedsBudget = !!context.maxToolResultTokens && context.maxToolResultTokens > 0 && countTokens(JSON.stringify(result)) > context.maxToolResultTokens;
2062
+ const data = pageBudgetTruncated || anchorBudgetTruncated || payloadExceedsBudget ? withToolResultBudgetReason(result) : result;
2063
+ const modelData = limitSharedModelResultToBudget(data, context.maxToolResultTokens, countTokens);
2064
+ return { content: JSON.stringify(modelData), data };
2065
+ }
2066
+ var resolveChatEntitiesTool = {
2067
+ name: "resolve_chat_entities",
2068
+ description: "Resolve contacts and sessions into exact source scopes. Stable refs resolve directly; typed contact names search the contact catalog. Continue automatically for one candidate, ask the user to choose when contactLookups reports ambiguous, and never guess among multiple candidates.",
2069
+ inputSchema: resolveSchema,
2070
+ handler: resolveHandler,
2071
+ category: "core"
2072
+ };
2073
+ var readRecentSessionTool = {
2074
+ name: "read_recent_session",
2075
+ description: "Read one exactly resolved session for a concise latest-available recap. The tool internally returns a bounded newest message slice plus up to five existing segment summaries, with source evidence and preprocessing. Use it after resolve_chat_entities when the user asks what happened recently in one private chat or one explicitly selected session without a calendar range. Do not pass message budgets. For explicit dates, durations, keywords, multi-session work, or deeper history, use search_messages_globally instead.",
2076
+ inputSchema: recentSessionSchema,
2077
+ handler: readRecentSessionHandler,
2078
+ category: "core",
2079
+ truncationStrategy: "keep_first"
2080
+ };
2081
+ var searchMessagesGloballyTool = {
2082
+ name: "search_messages_globally",
2083
+ description: "Search exact keywords across resolved contacts or sessions. Use recent_days=30 for unspecified recent activity, but for a latest available private-chat recap without an explicit date or duration, scope only the direct private session and omit recent_days. Use sender=owner when the user is the subject of the requested action. The tool controls evidence volume internally, prioritizes private-chat evidence, ranks remaining group sources by matching activity, and automatically includes surrounding context. With explicit scopes, omit keywords to sample recent messages. Unscoped global discovery always requires keywords. Results include applied filters, source identity, coverage, and truncation status.",
2084
+ inputSchema: searchSchema,
2085
+ handler: searchHandler,
2086
+ category: "core",
2087
+ truncationStrategy: "keep_first"
2088
+ };
2089
+ var getCrossChatMessageContextTool = {
2090
+ name: "get_cross_chat_message_context",
2091
+ description: "Load surrounding messages for one cross-chat evidence item. Both session_id and message_id are required because message IDs are only unique inside a session.",
2092
+ inputSchema: contextSchema,
2093
+ handler: contextHandler,
2094
+ category: "core",
2095
+ truncationStrategy: "keep_last"
2096
+ };
2097
+ var getCrossChatOverviewTool = {
2098
+ name: "get_cross_chat_overview",
2099
+ description: "Compare already-resolved contact or session scopes within an exact time range. Returns deterministic message counts, active days and members, per-requested-member activity, owner coverage, fixed top members for groups, dataset cutoff, and completeness. It does not discover or rank sessions globally.",
2100
+ inputSchema: overviewSchema,
2101
+ handler: overviewHandler,
2102
+ category: "core"
2103
+ };
2104
+ var rankPrivateContactsTool = {
2105
+ name: "rank_private_contacts",
2106
+ description: "Deterministically rank private-chat contacts across all eligible imported sessions for an exact time range. Use this for questions such as who the user chatted with most or had the most active private-chat days. This tool counts non-system messages and never uses keyword-search hits as a ranking proxy. Results include owner/contact splits, dataset cutoff, identity coverage, and completeness.",
2107
+ inputSchema: rankPrivateContactsSchema,
2108
+ handler: rankPrivateContactsHandler,
2109
+ category: "core"
2110
+ };
2111
+ var rankGroupSessionsTool = {
2112
+ name: "rank_group_sessions",
2113
+ description: "Deterministically rank imported group sessions for an exact time range. mode=owner_activity answers where the user personally spoke most; mode=total_activity answers which groups were busiest overall. Never mix these metrics or substitute sampled search hits. Results include total and owner counts, active members/days, owner-resolution coverage, dataset cutoff, and completeness.",
2114
+ inputSchema: rankGroupSessionsSchema,
2115
+ handler: rankGroupSessionsHandler,
2116
+ category: "core"
2117
+ };
2118
+ var getGlobalActivitySummaryTool = {
2119
+ name: "get_global_activity_summary",
2120
+ description: "Read the existing deterministic owner activity snapshot for the current calendar year, another calendar year, or the rolling latest 365 days. Use it for total sent messages, active days, direct-contact counts, monthly/daily trends, message types, and text-length distribution. It never reads chat text or returns contact/group rankings. fresh and stale states contain usable metrics; preparing and failed do not and must not be described as zero activity. Do not poll a preparing result in the same answer.",
2121
+ inputSchema: globalActivitySummarySchema,
2122
+ handler: globalActivitySummaryHandler,
2123
+ category: "core"
2124
+ };
2125
+ var inspectContactSessionsTool = {
2126
+ name: "inspect_contact_sessions",
2127
+ description: "Inspect one resolved contact across imported private and group sessions. Returns the contact's own message counts, first/last speech, active days, roster-only presence, dataset cutoff, and coverage. Use recent_days=30 for recent activity without an explicit duration; never derive start_time from the dataset cutoff. Use only for person source/activity questions after exact identity resolution; this tool does not infer relationship labels or return message text.",
2128
+ inputSchema: inspectContactSessionsSchema,
2129
+ handler: inspectContactSessionsHandler,
2130
+ category: "core"
2131
+ };
2132
+ var inspectSharedInteractionsTool = {
2133
+ name: "inspect_shared_interactions",
2134
+ description: "Inspect common imported sessions for two to five exactly resolved people, including owner when requested. Returns per-person activity, directional replies, proximity signals, co-active days, message anchors, dataset cutoff, and coverage. Use recent_days=30 for recent interaction questions without an explicit duration; never derive start_time from the dataset cutoff. Common sessions contain every requested participant. These structural signals guide evidence reading and must never be treated as relationship labels or replace message context.",
2135
+ inputSchema: inspectSharedInteractionsSchema,
2136
+ handler: inspectSharedInteractionsHandler,
2137
+ category: "core"
2138
+ };
2139
+ function parseEntityInputs(value) {
2140
+ if (!Array.isArray(value)) throw new Error("entities must be an array");
2141
+ return value.map((item) => {
2142
+ if (!isRecord(item)) throw new Error("each entity must be an object");
2143
+ const type = requireString(item.type, "entity.type");
2144
+ const displayName = requireString(item.displayName, "entity.displayName");
2145
+ if (type === "contact") {
2146
+ return {
2147
+ type,
2148
+ ...typeof item.contactKey === "string" && item.contactKey.trim() ? { contactKey: item.contactKey.trim() } : {},
2149
+ displayName
2150
+ };
2151
+ }
2152
+ if (type === "session") {
2153
+ const sessionType = item.sessionType === "private" ? "private" : item.sessionType === "group" ? "group" : null;
2154
+ if (!sessionType) throw new Error("entity.sessionType must be private or group");
2155
+ return { type, sessionId: requireString(item.sessionId, "entity.sessionId"), displayName, sessionType };
2156
+ }
2157
+ throw new Error("entity.type must be contact or session");
2158
+ });
2159
+ }
2160
+ function dedupeEntityRefs(refs) {
2161
+ const byKey = /* @__PURE__ */ new Map();
2162
+ for (const ref of refs) {
2163
+ byKey.set(entityRefKey(ref), ref);
2164
+ }
2165
+ return [...byKey.values()];
2166
+ }
2167
+ function trackResolvedEntityRefs(currentRunRefs, resolution) {
2168
+ if (!currentRunRefs) return;
2169
+ const resolvedRefs = [
2170
+ ...resolution.contacts.filter((item) => item.status === "resolved" || item.status === "partial").map((item) => item.ref),
2171
+ ...resolution.sessions.filter((item) => item.status === "resolved").map((item) => item.ref)
2172
+ ];
2173
+ const existingKeys = new Set(currentRunRefs.map(entityRefKey));
2174
+ for (const ref of resolvedRefs) {
2175
+ const key = entityRefKey(ref);
2176
+ if (existingKeys.has(key)) continue;
2177
+ currentRunRefs.push(ref);
2178
+ existingKeys.add(key);
2179
+ }
2180
+ }
2181
+ function entityRefKey(ref) {
2182
+ return ref.type === "contact" ? `contact:${ref.contactKey}` : `session:${ref.sessionId}`;
2183
+ }
2184
+ function parseScopes(value) {
2185
+ if (!Array.isArray(value)) throw new Error("scopes must be an array");
2186
+ return value.map((item) => {
2187
+ if (!isRecord(item)) throw new Error("each scope must be an object");
2188
+ return {
2189
+ sessionId: requireString(item.sessionId, "scope.sessionId"),
2190
+ memberIds: item.memberIds === void 0 ? void 0 : parseNumberArray(item.memberIds),
2191
+ label: typeof item.label === "string" ? item.label : void 0
2192
+ };
2193
+ });
2194
+ }
2195
+ function parseParticipants(value) {
2196
+ if (!Array.isArray(value)) throw new Error("participants must be an array");
2197
+ return value.map((item) => {
2198
+ if (!isRecord(item)) throw new Error("each participant must be an object");
2199
+ if (item.type === "owner") return { type: "owner" };
2200
+ if (item.type === "contact") {
2201
+ return { type: "contact", contactKey: requireString(item.contact_key, "participant.contact_key") };
2202
+ }
2203
+ throw new Error("participant.type must be owner or contact");
2204
+ });
2205
+ }
2206
+ function limitOverviewToBudget(fullData, maxToolResultTokens, countTokens) {
2207
+ if (!maxToolResultTokens || maxToolResultTokens <= 0) return fullData;
2208
+ if (countTokens(JSON.stringify(fullData)) <= maxToolResultTokens) return fullData;
2209
+ const compactItems = fullData.items.map((item) => ({
2210
+ ...item,
2211
+ sessionName: truncateModelLabel(item.sessionName),
2212
+ label: truncateModelLabel(item.label),
2213
+ memberActivities: item.memberActivities.map((member) => ({
2214
+ memberId: member.memberId,
2215
+ memberName: truncateModelLabel(member.memberName),
2216
+ messageCount: member.messageCount,
2217
+ activeDays: member.activeDays,
2218
+ firstMessageTs: member.firstMessageTs,
2219
+ lastMessageTs: member.lastMessageTs
2220
+ })),
2221
+ topMembers: item.topMembers.map((member) => ({
2222
+ memberName: truncateModelLabel(member.memberName),
2223
+ messageCount: member.messageCount,
2224
+ activeDays: member.activeDays,
2225
+ lastMessageTs: member.lastMessageTs
2226
+ }))
2227
+ }));
2228
+ const buildCandidate = (items, itemFormat) => ({
2229
+ appliedRange: fullData.appliedRange,
2230
+ items,
2231
+ coverage: fullData.coverage,
2232
+ selection: buildOverviewSelection(fullData.items, items, itemFormat)
2233
+ });
2234
+ const fits = (candidate) => countTokens(JSON.stringify(candidate)) <= maxToolResultTokens;
2235
+ const maxTopMembers = compactItems.reduce((max, item) => Math.max(max, item.topMembers.length), 0);
2236
+ const topMemberCandidate = findLargestOverviewDetailLimit(
2237
+ maxTopMembers,
2238
+ (limit) => buildCandidate(
2239
+ compactItems.map((item) => ({ ...item, topMembers: item.topMembers.slice(0, limit) })),
2240
+ "compact"
2241
+ )
2242
+ );
2243
+ if (topMemberCandidate && fits(topMemberCandidate)) return topMemberCandidate;
2244
+ const maxMemberActivities = compactItems.reduce((max, item) => Math.max(max, item.memberActivities.length), 0);
2245
+ const memberActivityCandidate = findLargestOverviewDetailLimit(
2246
+ maxMemberActivities,
2247
+ (limit) => buildCandidate(
2248
+ compactItems.map((item) => ({
2249
+ ...item,
2250
+ memberActivities: item.memberActivities.slice(0, limit),
2251
+ topMembers: []
2252
+ })),
2253
+ "compact"
2254
+ )
2255
+ );
2256
+ if (memberActivityCandidate && fits(memberActivityCandidate)) return memberActivityCandidate;
2257
+ const coreItems = compactItems.map((item) => ({
2258
+ sessionId: item.sessionId,
2259
+ sessionName: item.sessionName,
2260
+ sessionType: item.sessionType,
2261
+ platform: item.platform,
2262
+ label: item.label,
2263
+ totalMessages: item.totalMessages,
2264
+ activeDays: item.activeDays,
2265
+ activeMembers: item.activeMembers,
2266
+ firstMessageTs: item.firstMessageTs,
2267
+ lastMessageTs: item.lastMessageTs,
2268
+ ownerStatus: item.ownerStatus,
2269
+ ownerMessages: item.ownerMessages,
2270
+ ownerActiveDays: item.ownerActiveDays
2271
+ }));
2272
+ const coreCandidate = buildCandidate(coreItems, "core");
2273
+ if (fits(coreCandidate)) return coreCandidate;
2274
+ const identityItems = compactItems.map((item) => ({
2275
+ sessionId: item.sessionId,
2276
+ sessionName: item.sessionName,
2277
+ sessionType: item.sessionType,
2278
+ label: item.label,
2279
+ totalMessages: item.totalMessages,
2280
+ activeDays: item.activeDays,
2281
+ lastMessageTs: item.lastMessageTs
2282
+ }));
2283
+ const identityCandidate = buildCandidate(identityItems, "identity");
2284
+ if (fits(identityCandidate)) return identityCandidate;
2285
+ const returnedItems = findLargestOverviewItemSlice(identityItems, (items) => buildCandidate(items, "identity"));
2286
+ if (returnedItems) return returnedItems;
2287
+ const omitted = buildCandidate([], "omitted");
2288
+ const candidates = [
2289
+ omitted,
2290
+ {
2291
+ selection: buildOverviewSelection(fullData.items, [], "omitted")
2292
+ },
2293
+ { toolResultTruncated: true, truncatedReasons: ["tool_result_budget"] },
2294
+ {}
2295
+ ];
2296
+ return candidates.find(fits) ?? {};
2297
+ function findLargestOverviewDetailLimit(maximum, build) {
2298
+ if (!fits(build(0))) return null;
2299
+ let low = 0;
2300
+ let high = maximum;
2301
+ while (low < high) {
2302
+ const middle = Math.ceil((low + high) / 2);
2303
+ if (fits(build(middle))) low = middle;
2304
+ else high = middle - 1;
2305
+ }
2306
+ return build(low);
2307
+ }
2308
+ function findLargestOverviewItemSlice(items, build) {
2309
+ if (!fits(build([]))) return null;
2310
+ let low = 0;
2311
+ let high = items.length;
2312
+ while (low < high) {
2313
+ const middle = Math.ceil((low + high) / 2);
2314
+ if (fits(build(items.slice(0, middle)))) low = middle;
2315
+ else high = middle - 1;
2316
+ }
2317
+ return build(items.slice(0, low));
2318
+ }
2319
+ }
2320
+ function buildOverviewSelection(fullItems, returnedItems, itemFormat) {
2321
+ const memberActivitiesTotal = fullItems.reduce((sum, item) => sum + item.memberActivities.length, 0);
2322
+ const topMembersTotal = fullItems.reduce((sum, item) => sum + item.topMembers.length, 0);
2323
+ const memberActivitiesReturned = returnedItems.reduce(
2324
+ (sum, item) => sum + (Array.isArray(item.memberActivities) ? item.memberActivities.length : 0),
2325
+ 0
2326
+ );
2327
+ const topMembersReturned = returnedItems.reduce(
2328
+ (sum, item) => sum + (Array.isArray(item.topMembers) ? item.topMembers.length : 0),
2329
+ 0
2330
+ );
2331
+ const truncatedReasons = [
2332
+ ...memberActivitiesReturned < memberActivitiesTotal ? ["member_activities_budget"] : [],
2333
+ ...topMembersReturned < topMembersTotal ? ["top_members_budget"] : [],
2334
+ ...returnedItems.length < fullItems.length ? ["session_items_budget"] : [],
2335
+ "item_fields_budget",
2336
+ "tool_result_budget"
2337
+ ];
2338
+ return {
2339
+ sessionItemsTotal: fullItems.length,
2340
+ sessionItemsReturned: returnedItems.length,
2341
+ memberActivitiesTotal,
2342
+ memberActivitiesReturned,
2343
+ topMembersTotal,
2344
+ topMembersReturned,
2345
+ itemFormat,
2346
+ toolResultTruncated: true,
2347
+ truncatedReasons
2348
+ };
2349
+ }
2350
+ function limitGlobalActivitySummaryToBudget(fullData, maxToolResultTokens, countTokens) {
2351
+ if (!maxToolResultTokens || maxToolResultTokens <= 0) return fullData;
2352
+ if (countTokens(JSON.stringify(fullData)) <= maxToolResultTokens) return fullData;
2353
+ const totalDailyPoints = fullData.dailyActivity.length;
2354
+ const buildCompactedDailyData = (returned) => ({
2355
+ ...fullData,
2356
+ dailyActivity: sampleEvenly(fullData.dailyActivity, returned).map(
2357
+ (point) => [point.date, point.messageCount]
2358
+ ),
2359
+ selection: {
2360
+ dailyActivityTotal: totalDailyPoints,
2361
+ dailyActivityReturned: returned,
2362
+ dailyActivityFormat: "date_message_count_tuples",
2363
+ toolResultTruncated: returned < totalDailyPoints,
2364
+ truncatedReasons: returned < totalDailyPoints ? ["daily_activity_budget"] : []
2365
+ }
2366
+ });
2367
+ let low = 0;
2368
+ let high = totalDailyPoints;
2369
+ while (low < high) {
2370
+ const middle = Math.ceil((low + high) / 2);
2371
+ if (countTokens(JSON.stringify(buildCompactedDailyData(middle))) <= maxToolResultTokens) low = middle;
2372
+ else high = middle - 1;
2373
+ }
2374
+ const compacted = buildCompactedDailyData(low);
2375
+ if (countTokens(JSON.stringify(compacted)) <= maxToolResultTokens) return compacted;
2376
+ const truncatedReasons = [...totalDailyPoints > 0 ? ["daily_activity_budget"] : [], "tool_result_budget"];
2377
+ const selection = {
2378
+ dailyActivityTotal: totalDailyPoints,
2379
+ dailyActivityReturned: 0,
2380
+ dailyActivityFormat: "omitted",
2381
+ toolResultTruncated: true,
2382
+ truncatedReasons
2383
+ };
2384
+ const candidates = [
2385
+ {
2386
+ mode: fullData.mode,
2387
+ dataState: fullData.dataState,
2388
+ range: fullData.range,
2389
+ metrics: fullData.metrics,
2390
+ monthlyActivity: fullData.monthlyActivity,
2391
+ monthlyDirectContacts: fullData.monthlyDirectContacts,
2392
+ messageTypes: fullData.messageTypes,
2393
+ textLength: fullData.textLength,
2394
+ coverage: fullData.coverage,
2395
+ cache: fullData.cache,
2396
+ selection
2397
+ },
2398
+ {
2399
+ mode: fullData.mode,
2400
+ dataState: fullData.dataState,
2401
+ range: fullData.range,
2402
+ metrics: fullData.metrics,
2403
+ coverage: fullData.coverage,
2404
+ cache: fullData.cache,
2405
+ selection
2406
+ },
2407
+ { mode: fullData.mode, dataState: fullData.dataState, metrics: fullData.metrics, selection },
2408
+ { dataState: fullData.dataState, selection },
2409
+ { toolResultTruncated: true, truncatedReasons: ["tool_result_budget"] },
2410
+ {}
2411
+ ];
2412
+ return candidates.find((candidate) => countTokens(JSON.stringify(candidate)) <= maxToolResultTokens) ?? {};
2413
+ }
2414
+ function sampleEvenly(items, limit) {
2415
+ if (limit <= 0 || items.length === 0) return [];
2416
+ if (limit >= items.length) return [...items];
2417
+ if (limit === 1) return [items[items.length - 1]];
2418
+ return Array.from({ length: limit }, (_, index) => {
2419
+ const sourceIndex = Math.round(index * (items.length - 1) / (limit - 1));
2420
+ return items[sourceIndex];
2421
+ });
2422
+ }
2423
+ function limitContactPageSizeToBudget(requestedPageSize, maxToolResultTokens) {
2424
+ if (!maxToolResultTokens || maxToolResultTokens <= 0) {
2425
+ return { value: requestedPageSize, limited: false };
2426
+ }
2427
+ const requested = Math.min(
2428
+ MAX_CONTACT_INSPECTION_PAGE_SIZE,
2429
+ Math.max(1, Math.floor(requestedPageSize ?? DEFAULT_CONTACT_INSPECTION_PAGE_SIZE))
2430
+ );
2431
+ const maxChars = maxToolResultTokens * TOOL_RESULT_CHARS_PER_TOKEN;
2432
+ const budgeted = Math.max(1, Math.floor((maxChars - TOOL_RESULT_BASE_CHARS) / CONTACT_SESSION_ESTIMATED_CHARS));
2433
+ return {
2434
+ value: budgeted < requested ? budgeted : requestedPageSize,
2435
+ limited: budgeted < requested
2436
+ };
2437
+ }
2438
+ function limitSharedRequestToBudget(participantCount, requestedPageSize, requestedAnchorsPerPair, maxToolResultTokens) {
2439
+ const normalizedAnchorsPerPair = requestedAnchorsPerPair === void 0 ? void 0 : Math.min(MAX_SHARED_ANCHORS_PER_PAIR, Math.max(0, Math.floor(requestedAnchorsPerPair)));
2440
+ if (!maxToolResultTokens || maxToolResultTokens <= 0) {
2441
+ return {
2442
+ pageSize: requestedPageSize,
2443
+ maxAnchorsPerPair: normalizedAnchorsPerPair,
2444
+ pageLimited: false,
2445
+ anchorsLimited: false
2446
+ };
2447
+ }
2448
+ const pairCount = participantCount * (participantCount - 1) / 2;
2449
+ const requestedPage = requestedPageSize ?? DEFAULT_SHARED_INSPECTION_PAGE_SIZE;
2450
+ const requestedAnchors = normalizedAnchorsPerPair ?? DEFAULT_SHARED_ANCHORS_PER_PAIR;
2451
+ const maxChars = maxToolResultTokens * TOOL_RESULT_CHARS_PER_TOKEN;
2452
+ let maxAnchorsPerPair = requestedAnchors;
2453
+ const estimateSessionChars = (anchorsPerPair) => SHARED_SESSION_BASE_CHARS + participantCount * SHARED_PARTICIPANT_ESTIMATED_CHARS + pairCount * (SHARED_PAIR_BASE_CHARS + anchorsPerPair * SHARED_ANCHOR_ESTIMATED_CHARS);
2454
+ while (maxAnchorsPerPair > 0 && TOOL_RESULT_BASE_CHARS + estimateSessionChars(maxAnchorsPerPair) > maxChars) {
2455
+ maxAnchorsPerPair--;
2456
+ }
2457
+ const pageSize = Math.min(
2458
+ requestedPage,
2459
+ Math.max(1, Math.floor((maxChars - TOOL_RESULT_BASE_CHARS) / estimateSessionChars(maxAnchorsPerPair)))
2460
+ );
2461
+ return {
2462
+ pageSize,
2463
+ maxAnchorsPerPair,
2464
+ pageLimited: pageSize < requestedPage,
2465
+ anchorsLimited: maxAnchorsPerPair < requestedAnchors
2466
+ };
2467
+ }
2468
+ function withToolResultBudgetReason(result) {
2469
+ return {
2470
+ ...result,
2471
+ coverage: {
2472
+ ...result.coverage,
2473
+ truncated: true,
2474
+ truncatedReasons: [.../* @__PURE__ */ new Set([...result.coverage.truncatedReasons, "tool_result_budget"])]
2475
+ }
2476
+ };
2477
+ }
2478
+ function limitContactResultToBudget(result, maxToolResultTokens, countTokens) {
2479
+ if (!maxToolResultTokens || maxToolResultTokens <= 0) return result;
2480
+ if (countTokens(JSON.stringify(result)) <= maxToolResultTokens) return result;
2481
+ const compactLabels = {
2482
+ ...result,
2483
+ contact: result.contact ? { ...result.contact, displayName: truncateModelLabel(result.contact.displayName) } : null,
2484
+ sessions: result.sessions.map((session) => ({
2485
+ ...session,
2486
+ sessionName: truncateModelLabel(session.sessionName),
2487
+ memberName: truncateModelLabel(session.memberName)
2488
+ }))
2489
+ };
2490
+ const coreFacts = {
2491
+ algorithmVersion: result.algorithmVersion,
2492
+ contact: compactLabels.contact,
2493
+ appliedRange: result.appliedRange,
2494
+ summary: result.summary,
2495
+ sessions: result.sessions.map((session) => ({
2496
+ sessionId: session.sessionId,
2497
+ sessionName: truncateModelLabel(session.sessionName),
2498
+ sessionType: session.sessionType,
2499
+ platform: session.platform,
2500
+ presence: session.presence,
2501
+ ownMessageCount: session.ownMessageCount,
2502
+ sessionMessageCount: session.sessionMessageCount,
2503
+ firstOwnMessageTs: session.firstOwnMessageTs,
2504
+ lastOwnMessageTs: session.lastOwnMessageTs,
2505
+ activeDays: session.activeDays,
2506
+ memberCount: session.memberCount,
2507
+ sessionFirstMessageTs: session.sessionFirstMessageTs,
2508
+ lastMessageTs: session.lastMessageTs
2509
+ })),
2510
+ coverage: result.coverage
2511
+ };
2512
+ const identityFacts = {
2513
+ contact: result.contact ? {
2514
+ contactKey: result.contact.contactKey,
2515
+ displayName: truncateModelLabel(result.contact.displayName),
2516
+ platform: result.contact.platform
2517
+ } : null,
2518
+ sessions: result.sessions.map((session) => ({
2519
+ sessionId: session.sessionId,
2520
+ sessionType: session.sessionType,
2521
+ presence: session.presence,
2522
+ ownMessageCount: session.ownMessageCount,
2523
+ lastOwnMessageTs: session.lastOwnMessageTs
2524
+ })),
2525
+ coverage: result.coverage
2526
+ };
2527
+ const coverageOnly = { coverage: result.coverage };
2528
+ const candidates = [compactLabels, coreFacts, identityFacts, coverageOnly];
2529
+ return candidates.find((candidate) => countTokens(JSON.stringify(candidate)) <= maxToolResultTokens) ?? {
2530
+ truncated: true,
2531
+ truncatedReasons: ["tool_result_budget"]
2532
+ };
2533
+ }
2534
+ function limitSharedModelResultToBudget(result, maxToolResultTokens, countTokens) {
2535
+ if (!maxToolResultTokens || maxToolResultTokens <= 0) return result;
2536
+ if (countTokens(JSON.stringify(result)) <= maxToolResultTokens) return result;
2537
+ const compactLabels = {
2538
+ ...result,
2539
+ participants: result.participants.map((participant) => ({
2540
+ ...participant,
2541
+ displayName: truncateModelLabel(participant.displayName)
2542
+ })),
2543
+ sessions: result.sessions.map((session) => ({
2544
+ ...session,
2545
+ sessionName: truncateModelLabel(session.sessionName),
2546
+ participants: session.participants.map((participant) => ({
2547
+ ...participant,
2548
+ memberName: truncateModelLabel(participant.memberName)
2549
+ }))
2550
+ }))
2551
+ };
2552
+ const withAnchorLimit = (maxAnchorsPerPair) => ({
2553
+ ...compactLabels,
2554
+ sessions: compactLabels.sessions.map((session) => ({
2555
+ ...session,
2556
+ pairs: session.pairs.map((pair) => ({
2557
+ ...pair,
2558
+ anchors: pair.anchors.slice(0, maxAnchorsPerPair).map(({ sessionId: _sessionId, ...anchor }) => anchor),
2559
+ anchorsTruncated: pair.anchorsTruncated || pair.anchors.length > maxAnchorsPerPair
2560
+ }))
2561
+ }))
2562
+ });
2563
+ const identityFacts = {
2564
+ algorithmVersion: result.algorithmVersion,
2565
+ proximityAlgorithmVersion: result.proximityAlgorithmVersion,
2566
+ participants: compactLabels.participants.map((participant) => ({
2567
+ index: participant.index,
2568
+ ref: participant.ref,
2569
+ status: participant.status,
2570
+ displayName: participant.displayName,
2571
+ platform: participant.platform
2572
+ })),
2573
+ appliedRange: result.appliedRange,
2574
+ summary: result.summary,
2575
+ sessions: compactLabels.sessions.map((session) => ({
2576
+ sessionId: session.sessionId,
2577
+ sessionName: session.sessionName,
2578
+ sessionType: session.sessionType,
2579
+ platform: session.platform,
2580
+ lastMessageTs: session.lastMessageTs,
2581
+ memberCount: session.memberCount,
2582
+ priorityReasons: session.priorityReasons,
2583
+ proximityStatus: session.proximityStatus
2584
+ })),
2585
+ coverage: result.coverage
2586
+ };
2587
+ const coverageOnly = {
2588
+ summary: result.summary,
2589
+ coverage: result.coverage
2590
+ };
2591
+ const continuationOnly = {
2592
+ coverage: {
2593
+ complete: result.coverage.complete,
2594
+ nextCursor: result.coverage.nextCursor,
2595
+ truncated: true,
2596
+ truncatedReasons: result.coverage.truncatedReasons
2597
+ }
2598
+ };
2599
+ const candidates = [
2600
+ compactLabels,
2601
+ withAnchorLimit(2),
2602
+ withAnchorLimit(1),
2603
+ withAnchorLimit(0),
2604
+ identityFacts,
2605
+ coverageOnly,
2606
+ continuationOnly,
2607
+ { truncated: true, truncatedReasons: ["tool_result_budget"] },
2608
+ {}
2609
+ ];
2610
+ return candidates.find((candidate) => countTokens(JSON.stringify(candidate)) <= maxToolResultTokens) ?? {};
2611
+ }
2612
+ function truncateModelLabel(value) {
2613
+ const chars = Array.from(value);
2614
+ return chars.length > MODEL_LABEL_MAX_LENGTH ? `${chars.slice(0, MODEL_LABEL_MAX_LENGTH).join("")}\u2026` : value;
2615
+ }
2616
+ async function preprocessBySession(context, messages) {
2617
+ const bySession = /* @__PURE__ */ new Map();
2618
+ for (const message of messages) {
2619
+ const group = bySession.get(message.sessionId) ?? [];
2620
+ group.push({ ...message });
2621
+ bySession.set(message.sessionId, group);
2622
+ }
2623
+ const safe = [];
2624
+ const processedBySession = /* @__PURE__ */ new Map();
2625
+ for (const [sessionId, group] of bySession) {
2626
+ const processed = await context.preprocessMessagesBySession(sessionId, group);
2627
+ const byMessageId = /* @__PURE__ */ new Map();
2628
+ for (const message of processed) {
2629
+ if (message.sessionId === sessionId) byMessageId.set(message.messageId, message);
2630
+ }
2631
+ processedBySession.set(sessionId, byMessageId);
2632
+ }
2633
+ for (const message of messages) {
2634
+ const processed = processedBySession.get(message.sessionId)?.get(message.messageId);
2635
+ if (processed) safe.push(processed);
2636
+ }
2637
+ return safe;
2638
+ }
2639
+ function limitMessagesToBudget(messages, maxToolResultTokens, buildPayload, options = {}) {
2640
+ const prepared = messages.map((message) => ({
2641
+ ...message,
2642
+ content: message.content.length > 500 ? `${message.content.slice(0, 500)}\u2026` : message.content
2643
+ }));
2644
+ if (!maxToolResultTokens || maxToolResultTokens <= 0) {
2645
+ return { messages: prepared, truncated: false };
2646
+ }
2647
+ const countTokens = options.countTokens ?? estimatePayloadTokens;
2648
+ const priorityIndexes = [...new Set(options.priorityIndexes ?? prepared.map((_, index) => index))].filter(
2649
+ (index) => index >= 0 && index < prepared.length
2650
+ );
2651
+ if (!options.continueAfterOverflow) {
2652
+ let low = 0;
2653
+ let high = priorityIndexes.length;
2654
+ while (low < high) {
2655
+ const middle = Math.ceil((low + high) / 2);
2656
+ const candidateIndexes = priorityIndexes.slice(0, middle).sort((left, right) => left - right);
2657
+ const candidateMessages = candidateIndexes.map((candidateIndex) => prepared[candidateIndex]);
2658
+ const truncated = candidateMessages.length < prepared.length;
2659
+ const serializedCandidate = JSON.stringify(buildPayload(candidateMessages, truncated));
2660
+ if (countTokens(serializedCandidate) <= maxToolResultTokens) low = middle;
2661
+ else high = middle - 1;
2662
+ }
2663
+ const limitedIndexes = priorityIndexes.slice(0, low).sort((left, right) => left - right);
2664
+ const limited2 = limitedIndexes.map((index) => prepared[index]);
2665
+ return { messages: limited2, truncated: limited2.length < prepared.length };
2666
+ }
2667
+ const selectedIndexes = /* @__PURE__ */ new Set();
2668
+ for (const index of priorityIndexes) {
2669
+ const candidateIndexes = [...selectedIndexes, index].sort((left, right) => left - right);
2670
+ const candidateMessages = candidateIndexes.map((candidateIndex) => prepared[candidateIndex]);
2671
+ const truncated = candidateMessages.length < prepared.length;
2672
+ const serializedCandidate = JSON.stringify(buildPayload(candidateMessages, truncated));
2673
+ if (countTokens(serializedCandidate) > maxToolResultTokens) {
2674
+ if (options.continueAfterOverflow) continue;
2675
+ break;
2676
+ }
2677
+ selectedIndexes.add(index);
2678
+ }
2679
+ const limited = [...selectedIndexes].sort((left, right) => left - right).map((index) => prepared[index]);
2680
+ return { messages: limited, truncated: limited.length < prepared.length };
2681
+ }
2682
+ function limitEntityResolutionToBudget(resolution, contactLookups, maxToolResultTokens, injectedCountTokens) {
2683
+ const fullData = { ...resolution, contactLookups };
2684
+ if (!maxToolResultTokens || maxToolResultTokens <= 0) return fullData;
2685
+ const countTokens = injectedCountTokens ?? estimatePayloadTokens;
2686
+ const scopeItems2 = buildEntityResolutionScopePriority(resolution);
2687
+ let includeLookupHints = true;
2688
+ let emptyPayload = buildEntityResolutionPayload(resolution, contactLookups, scopeItems2, 0, includeLookupHints);
2689
+ if (countTokens(JSON.stringify(emptyPayload)) > maxToolResultTokens) {
2690
+ includeLookupHints = false;
2691
+ emptyPayload = buildEntityResolutionPayload(resolution, contactLookups, scopeItems2, 0, includeLookupHints);
2692
+ }
2693
+ if (countTokens(JSON.stringify(emptyPayload)) > maxToolResultTokens) {
2694
+ return buildMinimalEntityResolutionPayload(resolution, contactLookups, maxToolResultTokens, countTokens);
2695
+ }
2696
+ let low = 0;
2697
+ let high = scopeItems2.length;
2698
+ while (low < high) {
2699
+ const middle = Math.ceil((low + high) / 2);
2700
+ const candidate = buildEntityResolutionPayload(resolution, contactLookups, scopeItems2, middle, includeLookupHints);
2701
+ if (countTokens(JSON.stringify(candidate)) <= maxToolResultTokens) low = middle;
2702
+ else high = middle - 1;
2703
+ }
2704
+ return buildEntityResolutionPayload(resolution, contactLookups, scopeItems2, low, includeLookupHints);
2705
+ }
2706
+ function buildEntityResolutionScopePriority(resolution) {
2707
+ const items = [];
2708
+ for (const [sessionIndex, session] of resolution.sessions.entries()) {
2709
+ if (session.status === "resolved" && session.session) items.push({ kind: "session", sessionIndex, session });
2710
+ }
2711
+ const contactSessions = resolution.contacts.map(
2712
+ (contact) => [...contact.sessions].sort(
2713
+ (left, right) => (right.lastMessageTs ?? Number.NEGATIVE_INFINITY) - (left.lastMessageTs ?? Number.NEGATIVE_INFINITY)
2714
+ )
2715
+ );
2716
+ for (let depth = 0; ; depth++) {
2717
+ let added = false;
2718
+ for (const [contactIndex, sessions] of contactSessions.entries()) {
2719
+ const session = sessions[depth];
2720
+ if (!session) continue;
2721
+ items.push({ kind: "contact", contactIndex, session });
2722
+ added = true;
2723
+ }
2724
+ if (!added) break;
2725
+ }
2726
+ return items;
2727
+ }
2728
+ function buildEntityResolutionPayload(resolution, contactLookups, scopeItems2, selectedScopeCount, includeLookupHints) {
2729
+ const selectedContactSessions = /* @__PURE__ */ new Map();
2730
+ const selectedSessionIndexes = /* @__PURE__ */ new Set();
2731
+ for (const item of scopeItems2.slice(0, selectedScopeCount)) {
2732
+ if (item.kind === "session") {
2733
+ selectedSessionIndexes.add(item.sessionIndex);
2734
+ continue;
2735
+ }
2736
+ const sessions2 = selectedContactSessions.get(item.contactIndex) ?? [];
2737
+ sessions2.push(item.session);
2738
+ selectedContactSessions.set(item.contactIndex, sessions2);
2739
+ }
2740
+ const truncated = selectedScopeCount < scopeItems2.length;
2741
+ const contacts = resolution.contacts.map((contact, index) => {
2742
+ const sessions2 = selectedContactSessions.get(index) ?? [];
2743
+ return {
2744
+ ref: contact.ref,
2745
+ status: contact.status,
2746
+ cacheStatus: contact.cacheStatus,
2747
+ sessionCount: contact.sessions.length,
2748
+ returnedSessions: sessions2.length,
2749
+ sessions: sessions2,
2750
+ unresolvedSessionCount: contact.unresolvedSessionIds.length,
2751
+ failedSessionCount: contact.failedSessionIds.length
2752
+ };
2753
+ });
2754
+ const sessions = resolution.sessions.filter(
2755
+ (session, index) => session.status === "unresolved" || selectedSessionIndexes.has(index)
2756
+ );
2757
+ return {
2758
+ contacts,
2759
+ sessions,
2760
+ unresolved: resolution.unresolved,
2761
+ coverage: {
2762
+ ...resolution.coverage,
2763
+ returnedContactEntities: contacts.length,
2764
+ returnedSessionEntities: sessions.length,
2765
+ returnedSourceScopes: selectedScopeCount,
2766
+ truncated,
2767
+ truncatedReasons: truncated ? ["tool_result_budget"] : []
2768
+ },
2769
+ contactLookups: buildModelContactLookups(contactLookups, includeLookupHints)
2770
+ };
2771
+ }
2772
+ function buildModelContactLookups(contactLookups, includeHints) {
2773
+ return contactLookups.map((lookup) => ({
2774
+ query: lookup.query,
2775
+ status: lookup.status,
2776
+ cacheStatus: lookup.cacheStatus,
2777
+ totalCandidates: lookup.totalCandidates,
2778
+ candidates: lookup.candidates.map((candidate) => {
2779
+ const hints = includeHints && lookup.status === "ambiguous" ? candidate.sourceSessions.slice(0, 3) : [];
2780
+ return {
2781
+ contactKey: candidate.contactKey,
2782
+ displayName: candidate.displayName,
2783
+ platform: candidate.platform,
2784
+ aliases: candidate.aliases.slice(0, 8),
2785
+ aliasCount: candidate.aliases.length,
2786
+ sourceSessionCount: candidate.sourceSessions.length,
2787
+ sourceSessionHintsReturned: hints.length,
2788
+ sourceSessionHintsTruncated: lookup.status === "ambiguous" && hints.length < candidate.sourceSessions.length,
2789
+ ...hints.length > 0 ? {
2790
+ sourceSessionHints: hints
2791
+ } : {}
2792
+ };
2793
+ })
2794
+ }));
2795
+ }
2796
+ function buildMinimalEntityResolutionPayload(resolution, contactLookups, maxToolResultTokens, countTokens) {
2797
+ const coverage = {
2798
+ ...resolution.coverage,
2799
+ returnedContactEntities: 0,
2800
+ returnedSessionEntities: 0,
2801
+ returnedSourceScopes: 0,
2802
+ truncated: true,
2803
+ truncatedReasons: ["tool_result_budget"]
2804
+ };
2805
+ const candidates = [
2806
+ {
2807
+ contacts: [],
2808
+ sessions: [],
2809
+ unresolved: [],
2810
+ coverage,
2811
+ contactLookups: contactLookups.map((lookup) => ({
2812
+ query: lookup.query,
2813
+ status: lookup.status,
2814
+ totalCandidates: lookup.totalCandidates
2815
+ }))
2816
+ },
2817
+ { coverage },
2818
+ { truncated: true, truncatedReasons: ["tool_result_budget"] }
2819
+ ];
2820
+ return candidates.find((candidate) => countTokens(JSON.stringify(candidate)) <= maxToolResultTokens) ?? {
2821
+ truncated: true
2822
+ };
2823
+ }
2824
+ function estimatePayloadTokens(text) {
2825
+ let cjkChars = 0;
2826
+ let otherChars = 0;
2827
+ for (const char of text) {
2828
+ const codePoint = char.codePointAt(0) ?? 0;
2829
+ if (codePoint >= 13312 && codePoint <= 19903 || codePoint >= 19968 && codePoint <= 40959 || codePoint >= 12352 && codePoint <= 12543 || codePoint >= 44032 && codePoint <= 55215) {
2830
+ cjkChars++;
2831
+ } else {
2832
+ otherChars++;
2833
+ }
2834
+ }
2835
+ return Math.ceil(cjkChars / 1.6 + otherChars / 4);
2836
+ }
2837
+ function buildContextPriority(messages, anchorMessageId) {
2838
+ const anchorIndex = messages.findIndex((message) => message.messageId === anchorMessageId);
2839
+ if (anchorIndex < 0) return messages.map((_, index) => index);
2840
+ const indexes = [anchorIndex];
2841
+ for (let distance = 1; indexes.length < messages.length; distance++) {
2842
+ const before = anchorIndex - distance;
2843
+ const after = anchorIndex + distance;
2844
+ if (before >= 0) indexes.push(before);
2845
+ if (after < messages.length) indexes.push(after);
2846
+ }
2847
+ return indexes;
2848
+ }
2849
+ function toModelMessage(message) {
2850
+ return {
2851
+ sessionId: message.sessionId,
2852
+ sessionName: message.sessionName,
2853
+ sessionType: message.sessionType,
2854
+ messageId: message.messageId,
2855
+ senderId: message.senderId,
2856
+ senderName: message.senderName,
2857
+ timestamp: message.timestamp,
2858
+ content: message.content,
2859
+ evidenceRole: message.evidenceRole
2860
+ };
2861
+ }
2862
+ function summarizeEvidenceSelection(messages) {
2863
+ let matchedMessages = 0;
2864
+ let contextMessages = 0;
2865
+ let privateMessages = 0;
2866
+ let groupMessages = 0;
2867
+ for (const message of messages) {
2868
+ if (message.evidenceRole === "context") contextMessages++;
2869
+ else matchedMessages++;
2870
+ if (message.sessionType === "private") privateMessages++;
2871
+ else groupMessages++;
2872
+ }
2873
+ return { matchedMessages, contextMessages, privateMessages, groupMessages };
2874
+ }
2875
+ function toEvidenceSource(message) {
2876
+ return {
2877
+ sessionId: message.sessionId,
2878
+ sessionName: message.sessionName,
2879
+ sessionType: message.sessionType,
2880
+ platform: message.platform,
2881
+ messageId: message.messageId,
2882
+ senderName: message.senderName,
2883
+ timestamp: message.timestamp,
2884
+ snippet: message.content
2885
+ };
2886
+ }
2887
+ function parseStringArray(value) {
2888
+ if (!Array.isArray(value)) throw new Error("keywords must be an array");
2889
+ return value.map((item) => requireString(item, "keyword")).filter(Boolean);
2890
+ }
2891
+ function parseNumberArray(value) {
2892
+ if (!Array.isArray(value)) throw new Error("memberIds must be an array");
2893
+ return value.map((item) => requireNumber(item, "memberId"));
2894
+ }
2895
+ function requireString(value, name) {
2896
+ if (typeof value !== "string" || !value.trim()) throw new Error(`${name} is required`);
2897
+ return value.trim();
2898
+ }
2899
+ function requireNumber(value, name) {
2900
+ if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${name} must be a number`);
2901
+ return value;
2902
+ }
2903
+ function requireBoolean(value) {
2904
+ if (typeof value !== "boolean") throw new Error("include_roster_only must be a boolean");
2905
+ return value;
2906
+ }
2907
+ function parseOptionalNumber(value) {
2908
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
2909
+ }
2910
+ function isRecord(value) {
2911
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2912
+ }
2913
+
2914
+ // ../../packages/tools/src/definitions/memory-tools.ts
2915
+ var readSchema = {
2916
+ type: "object",
2917
+ properties: {
2918
+ scope_type: { type: "string", enum: ["global", "self", "contact", "group"] },
2919
+ scope_id: {
2920
+ type: "string",
2921
+ description: "Stable contactKey or group sessionId. Omit for global and self memory."
2922
+ },
2923
+ query: {
2924
+ type: "string",
2925
+ description: "Current question or topic used to rank memories inside the authorized scope."
2926
+ },
2927
+ limit: { type: "number", minimum: 1, maximum: 50, default: 20 }
2928
+ },
2929
+ required: ["scope_type"]
2930
+ };
2931
+ var writeSchema = {
2932
+ type: "object",
2933
+ properties: {
2934
+ id: { type: "string", description: "Stable memory ID. Provide it to update an existing memory." },
2935
+ scope_type: { type: "string", enum: ["global", "self", "contact", "group"] },
2936
+ scope_id: {
2937
+ type: "string",
2938
+ description: "Stable contactKey or group sessionId. Omit for global and self memory."
2939
+ },
2940
+ content: { type: "string", description: "One durable fact or preference, without chat transcripts." },
2941
+ source_type: {
2942
+ type: "string",
2943
+ enum: ["user", "ai"],
2944
+ description: "Use user only for explicit user-provided facts or corrections; use ai for derived conclusions."
2945
+ }
2946
+ },
2947
+ required: ["scope_type", "content", "source_type"]
2948
+ };
2949
+ var forgetSchema = {
2950
+ type: "object",
2951
+ properties: {
2952
+ id: { type: "string", description: "Stable memory ID returned by memory_read or memory_write." }
2953
+ },
2954
+ required: ["id"]
2955
+ };
2956
+ var memoryReadTool = {
2957
+ name: "memory_read",
2958
+ description: "Read durable global preferences, facts about the user, or memories for one resolved contact/group. Pass the current question or topic as query to rank relevant entries inside that authorized scope. Self memory is read on demand and is never injected automatically. AI-derived memories are leads and must be re-verified against current chat evidence.",
2959
+ inputSchema: readSchema,
2960
+ handler: memoryReadHandler
2961
+ };
2962
+ var memoryWriteTool = {
2963
+ name: "memory_write",
2964
+ description: "Create or update one durable memory. Read the target scope first, use stable entity IDs, and never store transcripts, temporary requests, or ordinary statistics.",
2965
+ inputSchema: writeSchema,
2966
+ executionMode: "sequential",
2967
+ handler: memoryWriteHandler
2968
+ };
2969
+ var memoryForgetTool = {
2970
+ name: "memory_forget",
2971
+ description: "Permanently delete one memory by its stable ID after the user asks to forget or confirms it is invalid.",
2972
+ inputSchema: forgetSchema,
2973
+ executionMode: "sequential",
2974
+ handler: memoryForgetHandler
2975
+ };
2976
+ async function memoryReadHandler(params, context) {
2977
+ const scope = parseScope(params);
2978
+ await assertResolvableEntityScope(scope, context);
2979
+ const limit = parseLimit(params.limit);
2980
+ const query = optionalString(params.query);
2981
+ const searchResult = query ? context.memoryService.search(scope, query, context.locale) : { entries: context.memoryService.list(scope), retrievalMode: "recent_fallback" };
2982
+ const allEntries = searchResult.entries;
2983
+ const entries = allEntries.slice(0, limit);
2984
+ const truncated = allEntries.length > entries.length;
2985
+ const verificationGuidance = entries.some((entry) => entry.sourceType === "ai") ? context.locale?.startsWith("zh") ? "\u6807\u8BB0\u4E3A ai \u7684\u8BB0\u5FC6\u53EA\u662F\u8C03\u67E5\u7EBF\u7D22\uFF0C\u4F7F\u7528\u524D\u5FC5\u987B\u91CD\u65B0\u67E5\u8BE2\u539F\u59CB\u804A\u5929\u8BC1\u636E\u3002" : "Memories marked ai are investigation leads. Re-query the original chat evidence before using them." : null;
2986
+ const data = { entries, truncated, retrievalMode: searchResult.retrievalMode, verificationGuidance };
2987
+ return { content: JSON.stringify(data), data };
2988
+ }
2989
+ async function memoryWriteHandler(params, context) {
2990
+ const scope = parseScope(params);
2991
+ await assertResolvableEntityScope(scope, context);
2992
+ const content = requireString2(params.content, "content");
2993
+ const sourceType = parseSourceType(params.source_type);
2994
+ const id = optionalString(params.id);
2995
+ let entry;
2996
+ if (id) {
2997
+ const current = context.memoryService.get(id);
2998
+ if (!current) throw new Error(`AI memory not found: ${id}`);
2999
+ if (current.scopeType !== scope.scopeType || current.scopeId !== scope.scopeId) {
3000
+ throw new Error("AI memory scope does not match the existing memory");
3001
+ }
3002
+ entry = context.memoryService.update(id, {
3003
+ content,
3004
+ sourceType,
3005
+ sourceAIChatId: context.aiChatId,
3006
+ sourceMessageId: null
3007
+ });
3008
+ } else {
3009
+ entry = context.memoryService.create({
3010
+ ...scope,
3011
+ content,
3012
+ sourceType,
3013
+ sourceAIChatId: context.aiChatId,
3014
+ sourceMessageId: null
3015
+ });
3016
+ }
3017
+ context.reportMemoryChange?.(entry.id);
3018
+ return { content: JSON.stringify(entry), data: entry };
3019
+ }
3020
+ async function memoryForgetHandler(params, context) {
3021
+ const id = requireString2(params.id, "id");
3022
+ const current = context.memoryService.get(id);
3023
+ if (!current) {
3024
+ const data2 = { id, deleted: false };
3025
+ return { content: JSON.stringify(data2), data: data2 };
3026
+ }
3027
+ await assertResolvableEntityScope(current, context, true);
3028
+ const data = { id, deleted: context.memoryService.forget(id) };
3029
+ return { content: JSON.stringify(data), data };
3030
+ }
3031
+ function parseScope(params) {
3032
+ const scopeType = params.scope_type;
3033
+ if (scopeType !== "global" && scopeType !== "self" && scopeType !== "contact" && scopeType !== "group") {
3034
+ throw new Error("scope_type must be global, self, contact, or group");
3035
+ }
3036
+ const scopeId = optionalString(params.scope_id);
3037
+ if (scopeType === "global" || scopeType === "self") {
3038
+ if (scopeId) throw new Error(`${scopeType} memory does not accept scope_id`);
3039
+ return { scopeType, scopeId: null };
3040
+ }
3041
+ if (!scopeId) throw new Error(`${scopeType} memory requires a stable scope_id`);
3042
+ return { scopeType, scopeId };
3043
+ }
3044
+ function parseSourceType(value) {
3045
+ if (value !== "user" && value !== "ai") throw new Error("source_type must be user or ai");
3046
+ return value;
3047
+ }
3048
+ function parseLimit(value) {
3049
+ if (value === void 0) return 20;
3050
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 50) {
3051
+ throw new Error("limit must be an integer between 1 and 50");
3052
+ }
3053
+ return value;
3054
+ }
3055
+ function requireString2(value, field) {
3056
+ const normalized = optionalString(value);
3057
+ if (!normalized) throw new Error(`${field} is required`);
3058
+ return normalized;
3059
+ }
3060
+ function optionalString(value) {
3061
+ if (typeof value !== "string") return null;
3062
+ return value.trim() || null;
3063
+ }
3064
+ async function assertResolvableEntityScope(scope, context, requireCurrentTurnEntity = false) {
3065
+ if (scope.scopeType === "global" || scope.scopeType === "self") return;
3066
+ const currentTurnRefs = [...context.entityRefs ?? [], ...context.resolvedEntityRefs ?? []];
3067
+ const matchesCurrentTurn = currentTurnRefs.some((ref2) => matchesMemoryScope(ref2, scope));
3068
+ if ((requireCurrentTurnEntity || currentTurnRefs.length > 0) && !matchesCurrentTurn) {
3069
+ throw new Error("scope_id must match an entity selected or resolved for the current turn");
3070
+ }
3071
+ const ref = scope.scopeType === "contact" ? { type: "contact", contactKey: scope.scopeId, displayName: scope.scopeId } : { type: "session", sessionId: scope.scopeId, displayName: scope.scopeId, sessionType: "group" /* GROUP */ };
3072
+ const resolution = await context.analysisService.resolveEntities([ref], {
3073
+ signal: context.abortSignal
3074
+ });
3075
+ if (scope.scopeType === "contact") {
3076
+ const resolved2 = resolution.contacts.some(
3077
+ (item) => item.ref.contactKey === scope.scopeId && (item.status === "resolved" || item.status === "partial")
3078
+ );
3079
+ if (!resolved2) throw new Error("scope_id must be a resolvable stable contact ID");
3080
+ return;
3081
+ }
3082
+ const resolved = resolution.sessions.some(
3083
+ (item) => item.ref.sessionId === scope.scopeId && item.status === "resolved" && item.session?.sessionType === "group" /* GROUP */
3084
+ );
3085
+ if (!resolved) throw new Error("scope_id must be a resolvable stable group ID");
3086
+ }
3087
+ function matchesMemoryScope(ref, scope) {
3088
+ return scope.scopeType === "contact" ? ref.type === "contact" && ref.contactKey === scope.scopeId : ref.type === "session" && ref.sessionType === "group" /* GROUP */ && ref.sessionId === scope.scopeId;
3089
+ }
3090
+
1485
3091
  // ../../packages/tools/src/sql/definitions.ts
1486
3092
  var SQL_TOOL_DEFS = [
1487
3093
  // ==================== 通用分析 ====================
@@ -1773,6 +3379,21 @@ var AGENT_TOOL_REGISTRY = [
1773
3379
  retrieveChatEvidenceTool,
1774
3380
  ...createAllSqlToolDefinitions(SQL_TOOL_DEFS)
1775
3381
  ];
3382
+ var CROSS_CHAT_AGENT_TOOL_REGISTRY = [
3383
+ resolveChatEntitiesTool,
3384
+ readRecentSessionTool,
3385
+ searchMessagesGloballyTool,
3386
+ getCrossChatMessageContextTool,
3387
+ getCrossChatOverviewTool,
3388
+ rankPrivateContactsTool,
3389
+ rankGroupSessionsTool,
3390
+ getGlobalActivitySummaryTool,
3391
+ inspectContactSessionsTool,
3392
+ inspectSharedInteractionsTool,
3393
+ memoryReadTool,
3394
+ memoryWriteTool,
3395
+ memoryForgetTool
3396
+ ];
1776
3397
  var SEMANTIC_SEARCH_TOOL_NAME = semanticSearchCurrentChatTool.name;
1777
3398
  var RETRIEVE_CHAT_EVIDENCE_TOOL_NAME = retrieveChatEvidenceTool.name;
1778
3399
  var MCP_TOOL_REGISTRY = [
@@ -1929,6 +3550,24 @@ async function executeToolForAgent(tool, params, context) {
1929
3550
  return { content: [{ type: "text", text: `Error: ${message}` }], details: null, isError: true };
1930
3551
  }
1931
3552
  }
3553
+ function createCrossChatAgentToolAdapters(context) {
3554
+ const runContext = {
3555
+ ...context,
3556
+ resolvedEntityRefs: context.resolvedEntityRefs ?? []
3557
+ };
3558
+ return CROSS_CHAT_AGENT_TOOL_REGISTRY.map((tool) => ({
3559
+ name: tool.name,
3560
+ label: tool.name,
3561
+ description: tool.description,
3562
+ parameters: toAgentToolParameters(tool.inputSchema),
3563
+ executionMode: tool.executionMode,
3564
+ execute: async (_toolCallId, params, signal, onUpdate) => executeToolForAgent(tool, params, {
3565
+ ...runContext,
3566
+ abortSignal: signal,
3567
+ reportProgress: (progress) => onUpdate?.({ content: [], details: { progress } })
3568
+ })
3569
+ }));
3570
+ }
1932
3571
 
1933
3572
  export {
1934
3573
  AGENT_TOOL_REGISTRY,
@@ -1936,6 +3575,7 @@ export {
1936
3575
  MCP_TOOL_REGISTRY,
1937
3576
  CoreDataProvider,
1938
3577
  toAgentToolParameters,
1939
- executeToolForAgent
3578
+ executeToolForAgent,
3579
+ createCrossChatAgentToolAdapters
1940
3580
  };
1941
- //# sourceMappingURL=chunk-HWIXK7SU.mjs.map
3581
+ //# sourceMappingURL=chunk-FM3GXNEO.mjs.map