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.
- package/dist/{chat-command-SEV6N3AR.mjs → chat-command-WBAJBKIE.mjs} +15 -16
- package/dist/{chat-command-SEV6N3AR.mjs.map → chat-command-WBAJBKIE.mjs.map} +1 -1
- package/dist/chunk-2TV7M4CC.mjs +101 -0
- package/dist/chunk-2TV7M4CC.mjs.map +1 -0
- package/dist/{chunk-TRGR3KKJ.mjs → chunk-4J4MA3RC.mjs} +2605 -672
- package/dist/chunk-4J4MA3RC.mjs.map +1 -0
- package/dist/{chunk-TJ3NOBVI.mjs → chunk-5YOHLJFB.mjs} +98 -284
- package/dist/chunk-5YOHLJFB.mjs.map +1 -0
- package/dist/{chunk-WRBFV2GX.mjs → chunk-CR2MU7LN.mjs} +49 -55
- package/dist/chunk-CR2MU7LN.mjs.map +1 -0
- package/dist/{chunk-WJQOXOMR.mjs → chunk-DKIYHA3O.mjs} +281 -30
- package/dist/chunk-DKIYHA3O.mjs.map +1 -0
- package/dist/{chunk-VREVDZ4Q.mjs → chunk-DQR2YS3I.mjs} +1930 -33
- package/dist/chunk-DQR2YS3I.mjs.map +1 -0
- package/dist/{chunk-VUYWUHWW.mjs → chunk-EJBBVLUN.mjs} +3 -3
- package/dist/chunk-EJBBVLUN.mjs.map +1 -0
- package/dist/{chunk-HWIXK7SU.mjs → chunk-FM3GXNEO.mjs} +1643 -3
- package/dist/chunk-FM3GXNEO.mjs.map +1 -0
- package/dist/{chunk-YUH3H3QW.mjs → chunk-MSMDLDPB.mjs} +96 -5
- package/dist/chunk-MSMDLDPB.mjs.map +1 -0
- package/dist/{chunk-6EZPD3XJ.mjs → chunk-N2WGG3MS.mjs} +2 -2
- package/dist/{chunk-TWH422HC.mjs → chunk-RXMSZIYD.mjs} +3 -3
- package/dist/{chunk-CH5YAZ4Q.mjs → chunk-SQWN4GTQ.mjs} +2 -2
- package/dist/cli.mjs +9 -9
- package/dist/contacts-worker.mjs +2 -2
- package/dist/global-insight-worker.mjs +1 -1
- package/dist/{http-237XYNRT.mjs → http-X6OEOOSH.mjs} +12 -12
- package/dist/{import-R2E2TFAK.mjs → import-WBJ6YIQ7.mjs} +7 -7
- package/dist/index.mjs +12 -12
- package/dist/{mcp-BED4AARV.mjs → mcp-FR6OKWKL.mjs} +8 -8
- package/dist/people-relationships-worker.mjs +2 -2
- package/dist/semantic-index-worker.mjs +1 -1
- package/dist/{src-AYMALWU5.mjs → src-WE5HNN2O.mjs} +44 -14
- package/dist/{stream-import-RXNSOOYH.mjs → stream-import-YYQVZDGQ.mjs} +7 -7
- package/dist/{worker-runtime-JGAZU7MC.mjs → worker-runtime-YGB5ZDE4.mjs} +5 -5
- package/dist-cli-web/assets/AISettingsTab-Co2sM1iY.js +1 -0
- package/dist-cli-web/assets/{AboutTab-DCM1-glA.js → AboutTab-DQ8r6mYE.js} +1 -1
- package/dist-cli-web/assets/ActionToolsPanel-CmYpw8Mt.css +1 -0
- package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang--RPlmfIu.js +75 -0
- package/dist-cli-web/assets/{Alert-D-egBWcb.js → Alert-DU1tLAAg.js} +1 -1
- package/dist-cli-web/assets/ApiSettingsTab-D5g5b79L.js +2 -0
- package/dist-cli-web/assets/BatchManageTab-BFUyVaGU.js +1 -0
- package/dist-cli-web/assets/{BufferResource-BE2_2bZK.js → BufferResource-C-RfYwQW.js} +1 -1
- package/dist-cli-web/assets/{CanvasRenderer-DDKdvEYQ.js → CanvasRenderer-Dz7JAMrR.js} +1 -1
- package/dist-cli-web/assets/CardDecoration-D28DI1G9.js +1 -0
- package/dist-cli-web/assets/ChatRecordDrawer-Cph819yY.js +1 -0
- package/dist-cli-web/assets/{ChatRecordWorkspace-DqUI4QiU.js → ChatRecordWorkspace-BAfERdPc.js} +1 -1
- package/dist-cli-web/assets/Checkbox-B8uqt13j.js +1 -0
- package/dist-cli-web/assets/DataDirCleanupNotice-3sGBE5vU.js +1 -0
- 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
- package/dist-cli-web/assets/{DebugToolsPanel-BIHA6jw4.js → DebugToolsPanel-Cwkr0BOY.js} +1 -1
- package/dist-cli-web/assets/EChart.vue_vue_type_script_setup_true_lang-BuLujQCF.js +1 -0
- package/dist-cli-web/assets/EChartBar.vue_vue_type_script_setup_true_lang-DuA5xbcC.js +1 -0
- package/dist-cli-web/assets/EChartLine.vue_vue_type_script_setup_true_lang-CvmMEfYq.js +1 -0
- package/dist-cli-web/assets/EChartWordcloud-CHVtyPR7.js +1 -0
- package/dist-cli-web/assets/FileDropZone.vue_vue_type_script_setup_true_lang-BKPy9Qv-.js +1 -0
- package/dist-cli-web/assets/{Filter-kO4dIDJ2.js → Filter-CMfRHIoK.js} +1 -1
- package/dist-cli-web/assets/GlobalTaskBar-Jz6MASCu.js +1 -0
- package/dist-cli-web/assets/GlobalTaskBar-haLzezqY.css +1 -0
- package/dist-cli-web/assets/GroupRelationshipGalaxyView-Ch4obfEq.js +1 -0
- package/dist-cli-web/assets/Label-CuryuymO.js +1 -0
- package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-CoeQsaiL.js +1 -0
- package/dist-cli-web/assets/MessageExportModal-JqGZ-iLr.js +1 -0
- package/dist-cli-web/assets/PageHeader-CPLgiKT8.css +1 -0
- package/dist-cli-web/assets/PageHeader-D4up77TB.js +1 -0
- 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
- package/dist-cli-web/assets/{RelationshipGalaxyCanvas-Cod1JX9m.js → RelationshipGalaxyCanvas-Cn7Eta5U.js} +3 -3
- package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-CkefzeFJ.js → RelationshipGalaxyThreeCanvas-LlaX57z6.js} +1 -1
- package/dist-cli-web/assets/{RenderTargetSystem-SuQ7cbze.js → RenderTargetSystem-JhA2PUFi.js} +1 -1
- package/dist-cli-web/assets/{RovingFocusItem-psoFYxrC.js → RovingFocusItem-DHaiTb4j.js} +1 -1
- package/dist-cli-web/assets/{ScreenCaptureModal-D4r9ixKh.js → ScreenCaptureModal-Bxhyt5g1.js} +1 -1
- package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-BlAje3Tf.js +1 -0
- package/dist-cli-web/assets/Select-Beeqv0wU.js +1 -0
- package/dist-cli-web/assets/SettingsModal-DV8yDBJs.js +2 -0
- package/dist-cli-web/assets/{StorageTab-DhSJ-0Ir.js → StorageTab-B7GHadia.js} +1 -1
- package/dist-cli-web/assets/Switch-u4zlvUMa.js +1 -0
- package/dist-cli-web/assets/{Tabs-Bnm_Q7yW.js → Tabs-B7-Koq3x.js} +1 -1
- package/dist-cli-web/assets/Tabs-RWKHwtfM.js +1 -0
- package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-ndsa3-aT.js +1 -0
- 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
- package/dist-cli-web/assets/{VisuallyHiddenInput-Bo_XwaLu.js → VisuallyHiddenInput-DGDqcvUy.js} +1 -1
- package/dist-cli-web/assets/{WebGLRenderer-D1x9WZRj.js → WebGLRenderer-ifvgA1fe.js} +1 -1
- package/dist-cli-web/assets/{WebGPURenderer-37Nw-tLm.js → WebGPURenderer-DwzmuWF0.js} +1 -1
- package/dist-cli-web/assets/aiChat-BY4C3UQe.js +8 -0
- package/dist-cli-web/assets/base-BWDX-164.js +1 -0
- package/dist-cli-web/assets/{browserAll-CWrz_-4U.js → browserAll-CSIIETeE.js} +1 -1
- package/dist-cli-web/assets/chatMessages--r0obAVw.css +1 -0
- package/dist-cli-web/assets/chatMessages-Zywjea1n.js +147 -0
- package/dist-cli-web/assets/cli-web-BRY49NWe.js +1 -0
- package/dist-cli-web/assets/{fetch-Du-wU0OO.js → fetch-Bf4cqeM3.js} +1 -1
- package/dist-cli-web/assets/{fetch-DPSWt6yX.js → fetch-BiXab0IF.js} +1 -1
- package/dist-cli-web/assets/{fetch-B69OkB6B.js → fetch-C8CcoTWY.js} +1 -1
- package/dist-cli-web/assets/{fetch-qd2FgBWQ.js → fetch-Co2vLmiE.js} +1 -1
- package/dist-cli-web/assets/{fetch-BGmCCyzz.js → fetch-D3nLtFBi.js} +1 -1
- package/dist-cli-web/assets/{fetch-CFpLXWTe.js → fetch-DNSDHSqQ.js} +1 -1
- package/dist-cli-web/assets/{fetch-DZ6OF-xW.js → fetch-DeHqF6yL.js} +1 -1
- package/dist-cli-web/assets/{fetch-CkXgOxcu.js → fetch-jx1VdM9j.js} +3 -3
- package/dist-cli-web/assets/{fetch-Gu4qQjUB.js → fetch-mYSeL4Ki.js} +1 -1
- package/dist-cli-web/assets/fetch-qBaUXqFt.js +1 -0
- package/dist-cli-web/assets/{fetch-6pcVxB9h.js → fetch-r6mhEGrP.js} +1 -1
- package/dist-cli-web/assets/{fetch-CgA1peJD.js → fetch-wzv71PYy.js} +1 -1
- package/dist-cli-web/assets/{host-CWsAWKgy.js → host-1emQf9_4.js} +1 -1
- package/dist-cli-web/assets/{host-D8zlA62K.js → host-jXJHyyOX.js} +1 -1
- package/dist-cli-web/assets/index-2dJCozDq.js +64 -0
- package/dist-cli-web/assets/index-6ejRbH3h.js +2 -0
- package/dist-cli-web/assets/index-B4mJkX_d.js +1 -0
- package/dist-cli-web/assets/index-B7biznha.js +1 -0
- package/dist-cli-web/assets/index-BOcFyPCa.js +1 -0
- package/dist-cli-web/assets/index-BQ463TS7.js +1 -0
- package/dist-cli-web/assets/index-BZpR015e.css +1 -0
- package/dist-cli-web/assets/index-BgelKQTW.css +1 -0
- package/dist-cli-web/assets/index-C1A1FwpO.css +1 -0
- package/dist-cli-web/assets/index-CIIyg_o9.css +1 -0
- package/dist-cli-web/assets/{index-a7CT8DQm.js → index-CYc_VLgK.js} +1 -1
- package/dist-cli-web/assets/index-ClHMs0-a.js +2 -0
- package/dist-cli-web/assets/{index-Dy0fRMDf.js → index-Cw0IhKZ6.js} +10 -10
- package/dist-cli-web/assets/{index-v6DlL4ue.js → index-DOqbUvhA.js} +1 -1
- package/dist-cli-web/assets/index-DnpnIyle.js +135 -0
- package/dist-cli-web/assets/index-LV9LfFNu.js +1 -0
- package/dist-cli-web/assets/index-kePiJ3ca.js +137 -0
- package/dist-cli-web/assets/index-slDUxib2.js +1 -0
- package/dist-cli-web/assets/{people-page-header-Czt9e1jv.js → people-page-header-Cx5r9Bk2.js} +1 -1
- package/dist-cli-web/assets/{prompt-DGazz143.js → prompt-BggEDxxi.js} +1 -1
- package/dist-cli-web/assets/{semantic-index-config-builder-BkFHzIUy.js → semantic-index-config-builder-1sO0aEc1.js} +1 -1
- package/dist-cli-web/assets/{service-CAp0YJoS.js → service-Bvq1fRkC.js} +1 -1
- package/dist-cli-web/assets/service-DkPheDt8.js +1 -0
- package/dist-cli-web/assets/service-DvI8O_7K.js +1 -0
- package/dist-cli-web/assets/useFormControl-B0aShtLp.js +1 -0
- package/dist-cli-web/assets/useTable-DC7oxmBl.js +1 -0
- package/dist-cli-web/assets/{useToast-YAHDkKDN.js → useToast-CCPBE-Sy.js} +1 -1
- package/dist-cli-web/assets/{vendor-echarts-hTLhTs52.js → vendor-echarts-C9ugTmum.js} +1 -1
- package/dist-cli-web/assets/{vendor-echarts-wordcloud-B8CF_NgP.js → vendor-echarts-wordcloud-DNiU8Psq.js} +1 -1
- package/dist-cli-web/assets/{webworkerAll-CvSp2Y8j.js → webworkerAll-FCJk6reG.js} +1 -1
- package/dist-cli-web/index.html +2 -2
- package/package.json +1 -1
- package/dist/chunk-6E46U4H2.mjs +0 -41
- package/dist/chunk-6E46U4H2.mjs.map +0 -1
- package/dist/chunk-HWIXK7SU.mjs.map +0 -1
- package/dist/chunk-TJ3NOBVI.mjs.map +0 -1
- package/dist/chunk-TRGR3KKJ.mjs.map +0 -1
- package/dist/chunk-VREVDZ4Q.mjs.map +0 -1
- package/dist/chunk-VUYWUHWW.mjs.map +0 -1
- package/dist/chunk-WJQOXOMR.mjs.map +0 -1
- package/dist/chunk-WRBFV2GX.mjs.map +0 -1
- package/dist/chunk-YUH3H3QW.mjs.map +0 -1
- package/dist-cli-web/assets/AISettingsTab-VgI7PiJd.js +0 -1
- package/dist-cli-web/assets/ActionToolsPanel-BTGgwXVD.css +0 -1
- package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-a7RMjjve.js +0 -112
- package/dist-cli-web/assets/ApiSettingsTab-Co3LWf1f.js +0 -2
- package/dist-cli-web/assets/BatchManageTab-2LrckmQm.js +0 -1
- package/dist-cli-web/assets/ChatRecordDrawer-DRhztqLG.js +0 -1
- package/dist-cli-web/assets/Checkbox-EfpQmv4A.js +0 -1
- package/dist-cli-web/assets/DataDirCleanupNotice-BYIiR58J.js +0 -1
- package/dist-cli-web/assets/EChart.vue_vue_type_script_setup_true_lang-DryMSoCz.js +0 -1
- package/dist-cli-web/assets/EChartBar.vue_vue_type_script_setup_true_lang-BdFW-hvt.js +0 -1
- package/dist-cli-web/assets/EChartLine.vue_vue_type_script_setup_true_lang-C69Kdw9X.js +0 -1
- package/dist-cli-web/assets/EChartWordcloud-DM7UEHdK.js +0 -1
- package/dist-cli-web/assets/GlobalTaskBar-BZS_h0zC.js +0 -1
- package/dist-cli-web/assets/GlobalTaskBar-COW_uBX6.css +0 -1
- package/dist-cli-web/assets/GroupRelationshipGalaxyView--WIaHx_F.js +0 -1
- package/dist-cli-web/assets/Label-CIuDXyeM.js +0 -1
- package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-B_7VHxTx.js +0 -1
- package/dist-cli-web/assets/MessageExportModal-BQsIpyxv.js +0 -1
- package/dist-cli-web/assets/PageHeader-DxhQHF_0.js +0 -1
- package/dist-cli-web/assets/PageHeader-XIXMTomn.css +0 -1
- package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-v_7aRBgM.js +0 -1
- package/dist-cli-web/assets/Select-CxurgslB.js +0 -1
- package/dist-cli-web/assets/SettingsModal-DBZucuf6.js +0 -2
- package/dist-cli-web/assets/Switch-3D81G6uV.js +0 -1
- package/dist-cli-web/assets/Tabs-DB603EEu.js +0 -1
- package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-BDTdkH5i.js +0 -1
- package/dist-cli-web/assets/aiChat-BCzYQeZq.js +0 -8
- package/dist-cli-web/assets/cli-web-C-8N0CJh.js +0 -1
- package/dist-cli-web/assets/fetch-z9M9FcIC.js +0 -1
- package/dist-cli-web/assets/index-BGd4ZfhO.js +0 -2
- package/dist-cli-web/assets/index-BYZF1ngN.js +0 -1
- package/dist-cli-web/assets/index-BcuwlCin.css +0 -1
- package/dist-cli-web/assets/index-BnaLj2dx.css +0 -1
- package/dist-cli-web/assets/index-Bq5q3kSU.js +0 -36
- package/dist-cli-web/assets/index-CPR3jdC2.js +0 -137
- package/dist-cli-web/assets/index-D3AjvaOX.js +0 -1
- package/dist-cli-web/assets/index-DAitScUA.js +0 -1
- package/dist-cli-web/assets/index-DBSMWAJ0.js +0 -1
- package/dist-cli-web/assets/index-DTn9htw-.js +0 -64
- package/dist-cli-web/assets/index-DV_rWxcH.js +0 -1
- package/dist-cli-web/assets/index-DxhAGqYM.css +0 -1
- package/dist-cli-web/assets/index-I0mIcDpE.css +0 -1
- package/dist-cli-web/assets/index-onwNShRw.js +0 -2
- package/dist-cli-web/assets/service-BMqwB74f.js +0 -1
- package/dist-cli-web/assets/service-CUQ8iHdS.js +0 -1
- package/dist-cli-web/assets/useFormControl-DcSMUc2d.js +0 -1
- package/dist-cli-web/assets/useTable-Clmq3sge.js +0 -1
- /package/dist/{chunk-6EZPD3XJ.mjs.map → chunk-N2WGG3MS.mjs.map} +0 -0
- /package/dist/{chunk-TWH422HC.mjs.map → chunk-RXMSZIYD.mjs.map} +0 -0
- /package/dist/{chunk-CH5YAZ4Q.mjs.map → chunk-SQWN4GTQ.mjs.map} +0 -0
- /package/dist/{http-237XYNRT.mjs.map → http-X6OEOOSH.mjs.map} +0 -0
- /package/dist/{import-R2E2TFAK.mjs.map → import-WBJ6YIQ7.mjs.map} +0 -0
- /package/dist/{mcp-BED4AARV.mjs.map → mcp-FR6OKWKL.mjs.map} +0 -0
- /package/dist/{src-AYMALWU5.mjs.map → src-WE5HNN2O.mjs.map} +0 -0
- /package/dist/{stream-import-RXNSOOYH.mjs.map → stream-import-YYQVZDGQ.mjs.map} +0 -0
- /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-
|
|
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-
|
|
3581
|
+
//# sourceMappingURL=chunk-FM3GXNEO.mjs.map
|