chatlab-cli 0.0.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +115 -3
  2. package/bin/chatlab.mjs +2 -0
  3. package/dist/chunk-2AIOU3PW.mjs +26 -0
  4. package/dist/chunk-2AIOU3PW.mjs.map +1 -0
  5. package/dist/chunk-3DIWXYMJ.mjs +1344 -0
  6. package/dist/chunk-3DIWXYMJ.mjs.map +1 -0
  7. package/dist/chunk-EIRLEPXP.mjs +19145 -0
  8. package/dist/chunk-EIRLEPXP.mjs.map +1 -0
  9. package/dist/chunk-EWCSGYVT.mjs +144 -0
  10. package/dist/chunk-EWCSGYVT.mjs.map +1 -0
  11. package/dist/chunk-FBTHZU55.mjs +3400 -0
  12. package/dist/chunk-FBTHZU55.mjs.map +1 -0
  13. package/dist/chunk-NB4BIAYM.mjs +411 -0
  14. package/dist/chunk-NB4BIAYM.mjs.map +1 -0
  15. package/dist/chunk-XUHPCRVT.mjs +395 -0
  16. package/dist/chunk-XUHPCRVT.mjs.map +1 -0
  17. package/dist/cli.mjs +15 -0
  18. package/dist/cli.mjs.map +1 -0
  19. package/dist/http-QVYJ5HER.mjs +32 -0
  20. package/dist/http-QVYJ5HER.mjs.map +1 -0
  21. package/dist/import-AJKL6JPR.mjs +41 -0
  22. package/dist/import-AJKL6JPR.mjs.map +1 -0
  23. package/dist/index.mjs +24 -0
  24. package/dist/index.mjs.map +1 -0
  25. package/dist/mcp-FRF3YAN5.mjs +405 -0
  26. package/dist/mcp-FRF3YAN5.mjs.map +1 -0
  27. package/dist/src-KOIGBZJX.mjs +339 -0
  28. package/dist/src-KOIGBZJX.mjs.map +1 -0
  29. package/dist/stream-import-URGOCXUH.mjs +33 -0
  30. package/dist/stream-import-URGOCXUH.mjs.map +1 -0
  31. package/dist-web/assets/DebugToolsPanel-dUV6pVqD.css +1 -0
  32. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-DFN1Co1H.js +155 -0
  33. package/dist-web/assets/EChartWordcloud-BYLFxPAX.js +1 -0
  34. package/dist-web/assets/MessageExportModal-BAf68-wB.js +1 -0
  35. package/dist-web/assets/electron-2m4Vdk2z.js +1 -0
  36. package/dist-web/assets/electron-8qi0d92j.js +1 -0
  37. package/dist-web/assets/electron-BHjOx6B6.js +1 -0
  38. package/dist-web/assets/electron-CTK4NJag.js +1 -0
  39. package/dist-web/assets/electron-DPsrYwTt.js +1 -0
  40. package/dist-web/assets/electron-DfpXsXsS.js +1 -0
  41. package/dist-web/assets/electron-HoVzpIVc.js +1 -0
  42. package/dist-web/assets/fetch-Ayp337Ng.js +17 -0
  43. package/dist-web/assets/fetch-BS4SlQjA.js +1 -0
  44. package/dist-web/assets/fetch-BWtpQwBZ.js +1 -0
  45. package/dist-web/assets/fetch-CclRWOHf.js +1 -0
  46. package/dist-web/assets/fetch-W43zfemj.js +1 -0
  47. package/dist-web/assets/fetch-o5NJ2uWE.js +3 -0
  48. package/dist-web/assets/http-CeXMt-Fv.js +1 -0
  49. package/dist-web/assets/index-BKFGuwJu.css +1 -0
  50. package/dist-web/assets/index-Bd5r2oFG.js +163 -0
  51. package/dist-web/assets/index-BfZM-q0T.js +7 -0
  52. package/dist-web/assets/index-CLGv8Den.css +1 -0
  53. package/dist-web/assets/index-CLYHD_f4.js +2 -0
  54. package/dist-web/assets/index-CaTRpNBK.js +16 -0
  55. package/dist-web/assets/index-CmU6x-uJ.css +1 -0
  56. package/dist-web/assets/index-CzR1sElx.css +1 -0
  57. package/dist-web/assets/index-Ksa--o23.js +173 -0
  58. package/dist-web/assets/sse-CACcGGWv.js +2 -0
  59. package/dist-web/assets/vendor-echarts-C13vDgZW.js +11 -0
  60. package/dist-web/assets/vendor-echarts-wordcloud-uVdwYsYa.js +1 -0
  61. package/dist-web/assets/vendor-nuxt-ui-CYMNNdeD.js +15 -0
  62. package/dist-web/assets/vendor-reka-ui-CW2cIgMu.js +11 -0
  63. package/dist-web/assets/vendor-snapdom-XnBoh1gd.js +8 -0
  64. package/dist-web/assets/vendor-zrender-5DcYjSZk.js +8 -0
  65. package/dist-web/assets/web-C5yfcwjn.js +1 -0
  66. package/dist-web/index.html +18 -0
  67. package/package.json +70 -15
  68. package/cli.js +0 -3
@@ -0,0 +1,1344 @@
1
+ import { createRequire as __createRequire } from 'module';
2
+ import { dirname as __pathDirname } from 'path';
3
+ import { fileURLToPath as __fileURLToPath } from 'url';
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ import {
8
+ executeParameterizedSql,
9
+ executeReadonlySql,
10
+ getChatOverview,
11
+ getConversationBetween,
12
+ getDailyActivity,
13
+ getDatabaseSchema,
14
+ getHourlyActivity,
15
+ getMemberActivity,
16
+ getMemberNameHistory,
17
+ getMembers,
18
+ getMembersWithAliases,
19
+ getMessageContext,
20
+ getRecentMessages,
21
+ getSearchMessageContext,
22
+ getSessionMessages,
23
+ getSessionMeta,
24
+ getSessionOverview,
25
+ getSessionSummaries,
26
+ getWeekdayActivity,
27
+ searchMessagesLike,
28
+ searchSessions
29
+ } from "./chunk-EIRLEPXP.mjs";
30
+
31
+ // ../../packages/tools/src/definitions/sql-query.ts
32
+ var inputSchema = {
33
+ type: "object",
34
+ properties: {
35
+ sql: {
36
+ type: "string",
37
+ description: "\u8981\u6267\u884C\u7684 SELECT SQL \u67E5\u8BE2\u8BED\u53E5"
38
+ }
39
+ },
40
+ required: ["sql"]
41
+ };
42
+ async function handler(params, context) {
43
+ const sql = params.sql;
44
+ try {
45
+ const result = await context.dataProvider.executeSql(sql);
46
+ return {
47
+ content: JSON.stringify(result),
48
+ data: result
49
+ };
50
+ } catch (err) {
51
+ return {
52
+ content: JSON.stringify({ error: err instanceof Error ? err.message : String(err) })
53
+ };
54
+ }
55
+ }
56
+ var schemaInputSchema = {
57
+ type: "object",
58
+ properties: {}
59
+ };
60
+ async function schemaHandler(_params, context) {
61
+ const schema = await context.dataProvider.getSchema();
62
+ return {
63
+ content: JSON.stringify({ tables: schema }),
64
+ data: schema
65
+ };
66
+ }
67
+ var sqlQueryTool = {
68
+ name: "execute_sql",
69
+ description: "\u5BF9\u804A\u5929\u6570\u636E\u5E93\u6267\u884C\u53EA\u8BFB SELECT \u67E5\u8BE2\u3002\u4F7F\u7528\u524D\u53EF\u5148\u8C03\u7528 get_schema \u67E5\u770B\u8868\u7ED3\u6784\u3002",
70
+ inputSchema,
71
+ handler
72
+ };
73
+ var schemaTool = {
74
+ name: "get_schema",
75
+ description: "\u67E5\u770B\u804A\u5929\u6570\u636E\u5E93\u7684\u8868\u7ED3\u6784\uFF08\u6240\u6709\u8868\u7684 CREATE TABLE \u8BED\u53E5\uFF09",
76
+ inputSchema: schemaInputSchema,
77
+ handler: schemaHandler
78
+ };
79
+
80
+ // ../../packages/tools/src/definitions/session-info.ts
81
+ var inputSchema2 = {
82
+ type: "object",
83
+ properties: {
84
+ include_members: {
85
+ type: "boolean",
86
+ description: "\u662F\u5426\u5305\u542B\u6210\u5458\u5217\u8868\uFF08\u9ED8\u8BA4 false\uFF09",
87
+ default: false
88
+ }
89
+ }
90
+ };
91
+ function handler2(params, context) {
92
+ const db = context.db;
93
+ const meta = getSessionMeta(db);
94
+ const overview = getSessionOverview(db);
95
+ const includeMembers = params.include_members;
96
+ const data = { ...meta, ...overview };
97
+ if (includeMembers) {
98
+ data.members = getMembers(db);
99
+ }
100
+ return {
101
+ content: JSON.stringify(data),
102
+ data
103
+ };
104
+ }
105
+ var sessionInfoTool = {
106
+ name: "get_session_info",
107
+ description: "\u83B7\u53D6\u5F53\u524D\u4F1A\u8BDD\u7684\u8BE6\u7EC6\u4FE1\u606F\uFF0C\u5305\u62EC\u540D\u79F0\u3001\u5E73\u53F0\u3001\u6D88\u606F\u603B\u6570\u3001\u6210\u5458\u6570\u3001\u65F6\u95F4\u8303\u56F4\u7B49",
108
+ inputSchema: inputSchema2,
109
+ handler: handler2
110
+ };
111
+
112
+ // ../../packages/tools/src/definitions/sessions.ts
113
+ var inputSchema3 = {
114
+ type: "object",
115
+ properties: {
116
+ keyword: {
117
+ type: "string",
118
+ description: "\u6309\u540D\u79F0\u7B5B\u9009\u4F1A\u8BDD\uFF08\u53EF\u9009\uFF09"
119
+ }
120
+ }
121
+ };
122
+ function handler3(_params, context) {
123
+ const keyword = _params.keyword?.toLowerCase();
124
+ const sessionIds = context.listSessionIds();
125
+ const sessions = sessionIds.map((id) => {
126
+ const db = context.openDb(id);
127
+ if (!db) return null;
128
+ const meta = getSessionMeta(db);
129
+ if (!meta) return null;
130
+ if (keyword && !meta.name.toLowerCase().includes(keyword)) return null;
131
+ const overview = getSessionOverview(db);
132
+ return { id, ...meta, ...overview };
133
+ }).filter(Boolean);
134
+ return {
135
+ content: JSON.stringify({ total: sessions.length, sessions }),
136
+ data: sessions
137
+ };
138
+ }
139
+ var sessionsListTool = {
140
+ name: "list_sessions",
141
+ description: "\u5217\u51FA\u6240\u6709\u53EF\u7528\u7684\u804A\u5929\u4F1A\u8BDD\uFF0C\u8FD4\u56DE\u4F1A\u8BDD\u540D\u79F0\u3001\u5E73\u53F0\u3001\u6D88\u606F\u6570\u7B49\u57FA\u672C\u4FE1\u606F",
142
+ inputSchema: inputSchema3,
143
+ handler: handler3
144
+ };
145
+
146
+ // ../../packages/tools/src/utils/format.ts
147
+ function isChineseLocale(locale) {
148
+ return locale?.startsWith("zh") ?? false;
149
+ }
150
+ var i18nTexts = {
151
+ allTime: { zh: "\u5168\u90E8\u65F6\u95F4", en: "All time" },
152
+ noContent: { zh: "[\u65E0\u5185\u5BB9]", en: "[No content]" },
153
+ memberNotFound: { zh: "\u672A\u627E\u5230\u8BE5\u6210\u5458", en: "Member not found" },
154
+ untilNow: { zh: "\u81F3\u4ECA", en: "Present" },
155
+ noChangeRecord: { zh: "\u65E0\u53D8\u66F4\u8BB0\u5F55", en: "No change record" },
156
+ noConversation: { zh: "\u672A\u627E\u5230\u8FD9\u4E24\u4EBA\u4E4B\u95F4\u7684\u5BF9\u8BDD", en: "No conversation found between these two members" },
157
+ noMessageContext: { zh: "\u672A\u627E\u5230\u6307\u5B9A\u7684\u6D88\u606F\u6216\u4E0A\u4E0B\u6587", en: "Message or context not found" },
158
+ messages: { zh: "\u6761", en: "" },
159
+ alias: { zh: "\u522B\u540D", en: "Alias" }
160
+ };
161
+ function t(key, locale) {
162
+ const text = i18nTexts[key];
163
+ if (typeof text === "object" && "zh" in text && "en" in text) {
164
+ return isChineseLocale(locale) ? text.zh : text.en;
165
+ }
166
+ return "";
167
+ }
168
+ function formatTimeRange(timeFilter, locale) {
169
+ if (!timeFilter) return t("allTime", locale);
170
+ const localeStr = isChineseLocale(locale) ? "zh-CN" : "en-US";
171
+ return {
172
+ start: new Date(timeFilter.startTs * 1e3).toLocaleString(localeStr),
173
+ end: new Date(timeFilter.endTs * 1e3).toLocaleString(localeStr)
174
+ };
175
+ }
176
+ function formatMessageCompact(msg, locale) {
177
+ const localeStr = isChineseLocale(locale) ? "zh-CN" : "en-US";
178
+ const time = new Date(msg.timestamp * 1e3).toLocaleString(localeStr);
179
+ let content = msg.content || t("noContent", locale);
180
+ if (content.length > 200) {
181
+ content = content.slice(0, 200) + "...";
182
+ }
183
+ return `${time} ${msg.senderName}: ${content}`;
184
+ }
185
+
186
+ // ../../packages/tools/src/definitions/chat-overview.ts
187
+ var inputSchema4 = {
188
+ type: "object",
189
+ properties: {
190
+ top_n: { type: "number", description: "\u8FD4\u56DE\u6D3B\u8DC3\u5EA6\u6700\u9AD8\u7684\u524D N \u4E2A\u6210\u5458\uFF0C\u9ED8\u8BA4 10" }
191
+ }
192
+ };
193
+ async function handler4(params, context) {
194
+ const { locale } = context;
195
+ const topN = params.top_n || 10;
196
+ const result = await context.dataProvider.getChatOverview(topN);
197
+ if (!result) {
198
+ const msg = isChineseLocale(locale) ? "\u65E0\u6CD5\u83B7\u53D6\u804A\u5929\u6982\u89C8" : "Unable to get chat overview";
199
+ return { content: msg, data: { error: msg } };
200
+ }
201
+ const msgSuffix = isChineseLocale(locale) ? "\u6761" : "";
202
+ const lines = [
203
+ `name: ${result.name}`,
204
+ `platform: ${result.platform}`,
205
+ `type: ${result.type}`,
206
+ `totalMessages: ${result.totalMessages}`,
207
+ `totalMembers: ${result.totalMembers}`
208
+ ];
209
+ if (result.firstMessageTs != null && result.lastMessageTs != null) {
210
+ const start = new Date(result.firstMessageTs * 1e3).toLocaleDateString();
211
+ const end = new Date(result.lastMessageTs * 1e3).toLocaleDateString();
212
+ lines.push(`timeRange: ${start} ~ ${end}`);
213
+ }
214
+ if (result.topMembers.length > 0) {
215
+ lines.push(`topMembers:`);
216
+ for (let i = 0; i < result.topMembers.length; i++) {
217
+ const m = result.topMembers[i];
218
+ const pct = result.totalMessages > 0 ? (m.count / result.totalMessages * 100).toFixed(1) : "0";
219
+ lines.push(`${i + 1}. ${m.name} ${m.count}${msgSuffix}(${pct}%)`);
220
+ }
221
+ }
222
+ return { content: lines.join("\n"), data: result };
223
+ }
224
+ var chatOverviewTool = {
225
+ name: "get_chat_overview",
226
+ description: "\u83B7\u53D6\u804A\u5929\u6982\u89C8\u4FE1\u606F\uFF0C\u5305\u62EC\u804A\u5929\u540D\u79F0\u3001\u5E73\u53F0\u3001\u603B\u6D88\u606F\u6570\u3001\u603B\u6210\u5458\u6570\u3001\u65F6\u95F4\u8303\u56F4\u548C\u6D3B\u8DC3\u6210\u5458\u6392\u884C",
227
+ inputSchema: inputSchema4,
228
+ handler: handler4,
229
+ category: "core"
230
+ };
231
+
232
+ // ../../packages/tools/src/utils/time-params.ts
233
+ function parseExtendedTimeParams(params, contextTimeFilter) {
234
+ if (params.start_time || params.end_time) {
235
+ let startTs;
236
+ let endTs;
237
+ if (params.start_time) {
238
+ const startDate = new Date(params.start_time.replace(" ", "T"));
239
+ if (!isNaN(startDate.getTime())) {
240
+ startTs = Math.floor(startDate.getTime() / 1e3);
241
+ }
242
+ }
243
+ if (params.end_time) {
244
+ const endDate = new Date(params.end_time.replace(" ", "T"));
245
+ if (!isNaN(endDate.getTime())) {
246
+ endTs = Math.floor(endDate.getTime() / 1e3);
247
+ }
248
+ }
249
+ if (startTs !== void 0 || endTs !== void 0) {
250
+ return {
251
+ startTs: startTs ?? 0,
252
+ endTs: endTs ?? Math.floor(Date.now() / 1e3)
253
+ };
254
+ }
255
+ }
256
+ return contextTimeFilter;
257
+ }
258
+
259
+ // ../../packages/tools/src/utils/schemas.ts
260
+ var timeParamProperties = {
261
+ start_time: {
262
+ type: "string",
263
+ description: "\u8D77\u59CB\u65F6\u95F4, \u683C\u5F0F: YYYY-MM-DD HH:mm"
264
+ },
265
+ end_time: {
266
+ type: "string",
267
+ description: "\u7ED3\u675F\u65F6\u95F4, \u683C\u5F0F: YYYY-MM-DD HH:mm"
268
+ }
269
+ };
270
+
271
+ // ../../packages/tools/src/definitions/search-messages.ts
272
+ var inputSchema5 = {
273
+ type: "object",
274
+ properties: {
275
+ keywords: { type: "array", items: { type: "string" }, description: "\u641C\u7D22\u5173\u952E\u8BCD\u5217\u8868" },
276
+ sender_id: { type: "number", description: "\u6309\u53D1\u9001\u8005 ID \u8FC7\u6EE4\uFF08\u901A\u8FC7 get_members \u83B7\u53D6\uFF09" },
277
+ limit: { type: "number", description: "\u8FD4\u56DE\u7684\u6700\u5927\u6D88\u606F\u6761\u6570" },
278
+ ...timeParamProperties
279
+ },
280
+ required: ["keywords"]
281
+ };
282
+ async function handler5(params, context) {
283
+ const { locale, timeFilter: contextTimeFilter, maxMessagesLimit } = context;
284
+ const keywords = params.keywords;
285
+ const limit = Math.min(maxMessagesLimit || params.limit || 1e3, 5e4);
286
+ const effectiveTimeFilter = parseExtendedTimeParams(params, contextTimeFilter);
287
+ const result = await context.dataProvider.searchMessages(keywords, {
288
+ timeFilter: effectiveTimeFilter,
289
+ limit,
290
+ senderId: params.sender_id
291
+ });
292
+ const contextBefore = context.searchContextBefore ?? 2;
293
+ const contextAfter = context.searchContextAfter ?? 2;
294
+ let finalMessages = result.messages;
295
+ if ((contextBefore > 0 || contextAfter > 0) && result.messages.length > 0) {
296
+ const hitIds = result.messages.map((m) => m.id).filter((id) => id != null);
297
+ if (hitIds.length > 0) {
298
+ finalMessages = await context.dataProvider.getSearchMessageContext(hitIds, contextBefore, contextAfter);
299
+ }
300
+ }
301
+ const data = {
302
+ total: result.total,
303
+ returned: finalMessages.length,
304
+ timeRange: formatTimeRange(effectiveTimeFilter, locale),
305
+ rawMessages: finalMessages
306
+ };
307
+ return { content: JSON.stringify(data), data, rawMessages: finalMessages };
308
+ }
309
+ var searchMessagesTool = {
310
+ name: "search_messages",
311
+ description: "\u6839\u636E\u5173\u952E\u8BCD\u641C\u7D22\u7FA4\u804A\u8BB0\u5F55\u3002\u9002\u7528\u4E8E\u7528\u6237\u60F3\u8981\u67E5\u627E\u7279\u5B9A\u8BDD\u9898\u3001\u5173\u952E\u8BCD\u76F8\u5173\u7684\u804A\u5929\u5185\u5BB9\u3002\u53EF\u4EE5\u6307\u5B9A\u65F6\u95F4\u8303\u56F4\u548C\u53D1\u9001\u8005\u6765\u7B5B\u9009\u6D88\u606F\u3002",
312
+ inputSchema: inputSchema5,
313
+ handler: handler5,
314
+ category: "core",
315
+ truncationStrategy: "keep_first"
316
+ };
317
+
318
+ // ../../packages/tools/src/definitions/deep-search-messages.ts
319
+ var inputSchema6 = {
320
+ type: "object",
321
+ properties: {
322
+ keywords: { type: "array", items: { type: "string" }, description: "\u641C\u7D22\u5173\u952E\u8BCD\u5217\u8868" },
323
+ sender_id: { type: "number", description: "\u6309\u53D1\u9001\u8005 ID \u8FC7\u6EE4\uFF08\u901A\u8FC7 get_members \u83B7\u53D6\uFF09" },
324
+ limit: { type: "number", description: "\u8FD4\u56DE\u7684\u6700\u5927\u6D88\u606F\u6761\u6570" },
325
+ ...timeParamProperties
326
+ },
327
+ required: ["keywords"]
328
+ };
329
+ async function handler6(params, context) {
330
+ const { locale, timeFilter: contextTimeFilter, maxMessagesLimit } = context;
331
+ const keywords = params.keywords;
332
+ const limit = Math.min(maxMessagesLimit || params.limit || 1e3, 5e4);
333
+ const effectiveTimeFilter = parseExtendedTimeParams(params, contextTimeFilter);
334
+ const result = await context.dataProvider.deepSearchMessages(keywords, {
335
+ timeFilter: effectiveTimeFilter,
336
+ limit,
337
+ senderId: params.sender_id
338
+ });
339
+ const contextBefore = context.searchContextBefore ?? 2;
340
+ const contextAfter = context.searchContextAfter ?? 2;
341
+ let finalMessages = result.messages;
342
+ if ((contextBefore > 0 || contextAfter > 0) && result.messages.length > 0) {
343
+ const hitIds = result.messages.map((m) => m.id).filter((id) => id != null);
344
+ if (hitIds.length > 0) {
345
+ finalMessages = await context.dataProvider.getSearchMessageContext(hitIds, contextBefore, contextAfter);
346
+ }
347
+ }
348
+ const data = {
349
+ total: result.total,
350
+ returned: finalMessages.length,
351
+ timeRange: formatTimeRange(effectiveTimeFilter, locale),
352
+ rawMessages: finalMessages
353
+ };
354
+ return { content: JSON.stringify(data), data, rawMessages: finalMessages };
355
+ }
356
+ var deepSearchMessagesTool = {
357
+ name: "deep_search_messages",
358
+ description: "\u6DF1\u5EA6\u641C\u7D22\u6D88\u606F\uFF08LIKE \u5B50\u4E32\u5339\u914D\uFF0C\u901F\u5EA6\u8F83\u6162\u4F46\u4E0D\u4F1A\u9057\u6F0F\uFF09\uFF0C\u9002\u7528\u4E8E\u5168\u6587\u641C\u7D22\u7CBE\u786E\u5339\u914D\u6216 FTS \u641C\u7D22\u65E0\u7ED3\u679C\u65F6\u7684\u8865\u5145\u641C\u7D22\u3002",
359
+ inputSchema: inputSchema6,
360
+ handler: handler6,
361
+ category: "core",
362
+ truncationStrategy: "keep_first"
363
+ };
364
+
365
+ // ../../packages/tools/src/definitions/get-message-context.ts
366
+ var inputSchema7 = {
367
+ type: "object",
368
+ properties: {
369
+ message_ids: { type: "array", items: { type: "number" }, description: "\u6D88\u606F ID \u5217\u8868" },
370
+ context_size: { type: "number", description: "\u6BCF\u6761\u6D88\u606F\u524D\u540E\u83B7\u53D6\u7684\u4E0A\u4E0B\u6587\u6761\u6570\uFF0C\u9ED8\u8BA4 20" }
371
+ },
372
+ required: ["message_ids"]
373
+ };
374
+ async function handler7(params, context) {
375
+ const { locale } = context;
376
+ const messageIds = params.message_ids;
377
+ const contextSize = params.context_size || 20;
378
+ const messages = await context.dataProvider.getMessageContext(messageIds, contextSize);
379
+ if (messages.length === 0) {
380
+ const data2 = {
381
+ error: t("noMessageContext", locale),
382
+ messageIds
383
+ };
384
+ return { content: JSON.stringify(data2), data: data2 };
385
+ }
386
+ const data = {
387
+ totalMessages: messages.length,
388
+ contextSize,
389
+ requestedMessageIds: messageIds,
390
+ rawMessages: messages
391
+ };
392
+ return { content: JSON.stringify(data), data, rawMessages: messages };
393
+ }
394
+ var getMessageContextTool = {
395
+ name: "get_message_context",
396
+ description: "\u6839\u636E\u6D88\u606F ID \u83B7\u53D6\u524D\u540E\u7684\u4E0A\u4E0B\u6587\u6D88\u606F\u3002\u9002\u7528\u4E8E\u9700\u8981\u67E5\u770B\u67D0\u6761\u6D88\u606F\u524D\u540E\u804A\u5929\u5185\u5BB9\u7684\u573A\u666F\u3002",
397
+ inputSchema: inputSchema7,
398
+ handler: handler7,
399
+ category: "core",
400
+ truncationStrategy: "keep_last"
401
+ };
402
+
403
+ // ../../packages/tools/src/definitions/search-sessions.ts
404
+ var inputSchema8 = {
405
+ type: "object",
406
+ properties: {
407
+ keywords: { type: "array", items: { type: "string" }, description: "\u641C\u7D22\u5173\u952E\u8BCD\u5217\u8868" },
408
+ limit: { type: "number", description: "\u8FD4\u56DE\u7684\u6700\u5927\u4F1A\u8BDD\u6570\uFF0C\u9ED8\u8BA4 20" },
409
+ ...timeParamProperties
410
+ }
411
+ };
412
+ async function handler8(params, context) {
413
+ const { locale, timeFilter: contextTimeFilter } = context;
414
+ const limit = params.limit || 20;
415
+ const effectiveTimeFilter = parseExtendedTimeParams(params, contextTimeFilter);
416
+ const sessions = await context.dataProvider.searchSessions(
417
+ params.keywords,
418
+ effectiveTimeFilter,
419
+ limit,
420
+ 5
421
+ );
422
+ if (sessions.length === 0) {
423
+ const data2 = {
424
+ total: 0,
425
+ message: isChineseLocale(locale) ? "\u672A\u627E\u5230\u5339\u914D\u7684\u4F1A\u8BDD" : "No matching sessions found"
426
+ };
427
+ return { content: JSON.stringify(data2), data: data2 };
428
+ }
429
+ const localeStr = isChineseLocale(locale) ? "zh-CN" : "en-US";
430
+ const msgSuffix = isChineseLocale(locale) ? "\u6761\u6D88\u606F" : " messages";
431
+ const completeLabel = isChineseLocale(locale) ? "\u5B8C\u6574\u4F1A\u8BDD" : "complete";
432
+ const data = {
433
+ total: sessions.length,
434
+ timeRange: formatTimeRange(effectiveTimeFilter, locale),
435
+ sessions: sessions.map((s) => {
436
+ const startTime = new Date(s.startTs * 1e3).toLocaleString(localeStr);
437
+ const endTime = new Date(s.endTs * 1e3).toLocaleString(localeStr);
438
+ const completeTag = s.isComplete ? ` [${completeLabel}]` : "";
439
+ return {
440
+ sessionId: s.id,
441
+ time: `${startTime} ~ ${endTime}`,
442
+ messageCount: `${s.messageCount}${msgSuffix}${completeTag}`,
443
+ preview: s.previewMessages.map((m) => formatMessageCompact(m, locale))
444
+ };
445
+ })
446
+ };
447
+ return { content: JSON.stringify(data), data };
448
+ }
449
+ var searchSessionsTool = {
450
+ name: "search_sessions",
451
+ description: "\u641C\u7D22\u804A\u5929\u4F1A\u8BDD\uFF08\u5BF9\u8BDD\u6BB5\u843D\uFF09\u3002\u4F1A\u8BDD\u662F\u6839\u636E\u6D88\u606F\u65F6\u95F4\u95F4\u9694\u81EA\u52A8\u5207\u5206\u7684\u5BF9\u8BDD\u5355\u5143\u3002\u8FD4\u56DE\u5339\u914D\u7684\u4F1A\u8BDD\u5217\u8868\u53CA\u6BCF\u4E2A\u4F1A\u8BDD\u7684\u524D5\u6761\u6D88\u606F\u9884\u89C8\u3002",
452
+ inputSchema: inputSchema8,
453
+ handler: handler8,
454
+ category: "core"
455
+ };
456
+
457
+ // ../../packages/tools/src/definitions/get-session-messages.ts
458
+ var inputSchema9 = {
459
+ type: "object",
460
+ properties: {
461
+ session_id: { type: "number", description: "\u4F1A\u8BDD ID\uFF08\u901A\u8FC7 search_sessions \u83B7\u53D6\uFF09" },
462
+ limit: { type: "number", description: "\u8FD4\u56DE\u7684\u6700\u5927\u6D88\u606F\u6761\u6570" }
463
+ },
464
+ required: ["session_id"]
465
+ };
466
+ async function handler9(params, context) {
467
+ const { locale, maxMessagesLimit } = context;
468
+ const limit = maxMessagesLimit || params.limit || 1e3;
469
+ const result = await context.dataProvider.getSessionMessages(params.session_id, limit);
470
+ if (!result) {
471
+ const data2 = {
472
+ error: isChineseLocale(locale) ? "\u672A\u627E\u5230\u6307\u5B9A\u7684\u4F1A\u8BDD" : "Session not found",
473
+ sessionId: params.session_id
474
+ };
475
+ return { content: JSON.stringify(data2), data: data2 };
476
+ }
477
+ const localeStr = isChineseLocale(locale) ? "zh-CN" : "en-US";
478
+ const startTime = new Date(result.startTs * 1e3).toLocaleString(localeStr);
479
+ const endTime = new Date(result.endTs * 1e3).toLocaleString(localeStr);
480
+ const rawMessages = result.messages.map((m) => ({
481
+ id: m.id,
482
+ senderName: m.senderName,
483
+ content: m.content,
484
+ timestamp: m.timestamp
485
+ }));
486
+ const data = {
487
+ sessionId: result.sessionId,
488
+ time: `${startTime} ~ ${endTime}`,
489
+ messageCount: result.messageCount,
490
+ returnedCount: result.returnedCount,
491
+ participants: result.participants,
492
+ rawMessages
493
+ };
494
+ return { content: JSON.stringify(data), data, rawMessages };
495
+ }
496
+ var getSessionMessagesTool = {
497
+ name: "get_session_messages",
498
+ description: "\u83B7\u53D6\u6307\u5B9A\u4F1A\u8BDD\u7684\u5B8C\u6574\u6D88\u606F\u5217\u8868\u3002\u7528\u4E8E\u5728 search_sessions \u627E\u5230\u76F8\u5173\u4F1A\u8BDD\u540E\uFF0C\u83B7\u53D6\u8BE5\u4F1A\u8BDD\u7684\u5B8C\u6574\u4E0A\u4E0B\u6587\u3002",
499
+ inputSchema: inputSchema9,
500
+ handler: handler9,
501
+ category: "core",
502
+ truncationStrategy: "keep_last"
503
+ };
504
+
505
+ // ../../packages/tools/src/definitions/get-members.ts
506
+ var inputSchema10 = {
507
+ type: "object",
508
+ properties: {
509
+ search: { type: "string", description: "\u6309\u540D\u79F0/\u522B\u540D/\u5E73\u53F0ID\u641C\u7D22\u8FC7\u6EE4" },
510
+ limit: { type: "number", description: "\u6700\u5927\u8FD4\u56DE\u6570\u91CF" }
511
+ }
512
+ };
513
+ async function handler10(params, context) {
514
+ const { locale } = context;
515
+ const members = await context.dataProvider.getMembers();
516
+ let filteredMembers = members;
517
+ if (params.search) {
518
+ const keyword = params.search.toLowerCase();
519
+ filteredMembers = members.filter((m) => {
520
+ if (m.groupNickname && m.groupNickname.toLowerCase().includes(keyword)) return true;
521
+ if (m.accountName && m.accountName.toLowerCase().includes(keyword)) return true;
522
+ if (m.platformId.includes(keyword)) return true;
523
+ if (m.aliases.some((alias) => alias.toLowerCase().includes(keyword))) return true;
524
+ return false;
525
+ });
526
+ }
527
+ if (params.limit && params.limit > 0) {
528
+ filteredMembers = filteredMembers.slice(0, params.limit);
529
+ }
530
+ const msgSuffix = isChineseLocale(locale) ? "\u6761" : "";
531
+ const aliasLabel = t("alias", locale);
532
+ const data = {
533
+ totalMembers: members.length,
534
+ returnedMembers: filteredMembers.length,
535
+ members: filteredMembers.map((m) => {
536
+ const displayName = m.groupNickname || m.accountName || m.platformId;
537
+ const aliasStr = m.aliases.length > 0 ? `|${aliasLabel}:${m.aliases.join(",")}` : "";
538
+ return `${m.id}|${m.platformId}|${displayName}|${m.messageCount}${msgSuffix}${aliasStr}`;
539
+ })
540
+ };
541
+ return { content: JSON.stringify(data), data };
542
+ }
543
+ var getMembersTool = {
544
+ name: "get_members",
545
+ description: '\u83B7\u53D6\u6210\u5458\u5217\u8868\uFF0C\u5305\u62EC\u6210\u5458\u7684\u57FA\u672C\u4FE1\u606F\u3001\u522B\u540D\u548C\u6D88\u606F\u7EDF\u8BA1\u3002\u9002\u7528\u4E8E\u67E5\u8BE2"\u6709\u54EA\u4E9B\u4EBA"\u3001"\u67D0\u4EBA\u7684\u522B\u540D\u662F\u4EC0\u4E48"\u7B49\u95EE\u9898\u3002',
546
+ inputSchema: inputSchema10,
547
+ handler: handler10,
548
+ category: "core"
549
+ };
550
+
551
+ // ../../packages/tools/src/definitions/member-stats.ts
552
+ var inputSchema11 = {
553
+ type: "object",
554
+ properties: {
555
+ top: {
556
+ type: "number",
557
+ description: "\u8FD4\u56DE\u524D N \u4E2A\u6D3B\u8DC3\u6210\u5458",
558
+ default: 20
559
+ }
560
+ }
561
+ };
562
+ async function handler11(params, context) {
563
+ const top = params.top || 20;
564
+ const members = await context.dataProvider.getMemberStats({ timeFilter: context.timeFilter, top });
565
+ const data = {
566
+ total: members.length,
567
+ members: members.map((m) => ({
568
+ name: m.name,
569
+ messageCount: m.messageCount,
570
+ percentage: m.percentage
571
+ }))
572
+ };
573
+ return {
574
+ content: JSON.stringify(data),
575
+ data
576
+ };
577
+ }
578
+ var memberStatsTool = {
579
+ name: "get_member_stats",
580
+ description: "\u83B7\u53D6\u6210\u5458\u6D3B\u8DC3\u5EA6\u6392\u884C\uFF0C\u5305\u62EC\u6D88\u606F\u6570\u91CF\u548C\u5360\u6BD4",
581
+ inputSchema: inputSchema11,
582
+ handler: handler11,
583
+ category: "core"
584
+ };
585
+
586
+ // ../../packages/tools/src/definitions/time-stats.ts
587
+ var inputSchema12 = {
588
+ type: "object",
589
+ properties: {
590
+ type: {
591
+ type: "string",
592
+ description: "\u7EDF\u8BA1\u7C7B\u578B\uFF1Ahourly\uFF08\u6309\u5C0F\u65F6\uFF09\u3001weekday\uFF08\u6309\u661F\u671F\uFF09\u3001daily\uFF08\u6309\u5929\uFF09",
593
+ enum: ["hourly", "weekday", "daily"],
594
+ default: "hourly"
595
+ }
596
+ }
597
+ };
598
+ async function handler12(params, context) {
599
+ const type = params.type || "hourly";
600
+ const data = await context.dataProvider.getTimeStats(type, { timeFilter: context.timeFilter });
601
+ return {
602
+ content: JSON.stringify({ type, data }),
603
+ data
604
+ };
605
+ }
606
+ var timeStatsTool = {
607
+ name: "get_time_stats",
608
+ description: "\u83B7\u53D6\u804A\u5929\u6D3B\u8DC3\u65F6\u6BB5\u5206\u5E03\uFF08\u6309\u5C0F\u65F6/\u661F\u671F/\u6BCF\u65E5\u8D8B\u52BF\uFF09",
609
+ inputSchema: inputSchema12,
610
+ handler: handler12,
611
+ category: "core"
612
+ };
613
+
614
+ // ../../packages/tools/src/definitions/recent-messages.ts
615
+ var inputSchema13 = {
616
+ type: "object",
617
+ properties: {
618
+ limit: {
619
+ type: "number",
620
+ description: "\u8FD4\u56DE\u7684\u6D88\u606F\u6761\u6570",
621
+ default: 100
622
+ },
623
+ ...timeParamProperties
624
+ }
625
+ };
626
+ async function handler13(params, context) {
627
+ const { locale, timeFilter: contextTimeFilter, maxMessagesLimit } = context;
628
+ const limit = maxMessagesLimit || params.limit || 100;
629
+ const effectiveTimeFilter = parseExtendedTimeParams(params, contextTimeFilter);
630
+ const result = await context.dataProvider.getRecentMessages({ timeFilter: effectiveTimeFilter, limit });
631
+ const data = {
632
+ total: result.total,
633
+ returned: result.messages.length,
634
+ timeRange: formatTimeRange(effectiveTimeFilter, locale),
635
+ rawMessages: result.messages
636
+ };
637
+ return {
638
+ content: JSON.stringify(data),
639
+ data,
640
+ rawMessages: result.messages
641
+ };
642
+ }
643
+ var recentMessagesTool = {
644
+ name: "get_recent_messages",
645
+ description: '\u83B7\u53D6\u6307\u5B9A\u65F6\u95F4\u6BB5\u5185\u7684\u7FA4\u804A\u6D88\u606F\u3002\u9002\u7528\u4E8E\u56DE\u7B54"\u6700\u8FD1\u5927\u5BB6\u804A\u4E86\u4EC0\u4E48"\u7B49\u6982\u89C8\u6027\u95EE\u9898\u3002\u652F\u6301\u7CBE\u786E\u5230\u5206\u949F\u7EA7\u522B\u7684\u65F6\u95F4\u67E5\u8BE2\u3002',
646
+ inputSchema: inputSchema13,
647
+ handler: handler13,
648
+ category: "core",
649
+ truncationStrategy: "keep_last"
650
+ };
651
+
652
+ // ../../packages/tools/src/definitions/get-member-name-history.ts
653
+ var inputSchema14 = {
654
+ type: "object",
655
+ properties: {
656
+ member_id: { type: "number", description: "\u6210\u5458 ID\uFF08\u901A\u8FC7 get_members \u83B7\u53D6\uFF09" }
657
+ },
658
+ required: ["member_id"]
659
+ };
660
+ async function handler14(params, context) {
661
+ const { locale } = context;
662
+ const memberId = params.member_id;
663
+ const members = await context.dataProvider.getMembers();
664
+ const member = members.find((m) => m.id === memberId);
665
+ if (!member) {
666
+ const data2 = {
667
+ error: t("memberNotFound", locale),
668
+ member_id: memberId
669
+ };
670
+ return { content: JSON.stringify(data2), data: data2 };
671
+ }
672
+ const history = await context.dataProvider.getMemberNameHistory(memberId);
673
+ const localeStr = isChineseLocale(locale) ? "zh-CN" : "en-US";
674
+ const untilNow = t("untilNow", locale);
675
+ const formatHistory = (h) => {
676
+ const start = new Date(h.startTs * 1e3).toLocaleDateString(localeStr);
677
+ const end = h.endTs ? new Date(h.endTs * 1e3).toLocaleDateString(localeStr) : untilNow;
678
+ return `${h.name} (${start} ~ ${end})`;
679
+ };
680
+ const accountNames = history.filter((h) => h.nameType === "account_name").map(formatHistory);
681
+ const groupNicknames = history.filter((h) => h.nameType === "group_nickname").map(formatHistory);
682
+ const displayName = member.groupNickname || member.accountName || member.platformId;
683
+ const aliasLabel = t("alias", locale);
684
+ const aliasStr = member.aliases.length > 0 ? `|${aliasLabel}:${member.aliases.join(",")}` : "";
685
+ const noChangeRecord = t("noChangeRecord", locale);
686
+ const data = {
687
+ member: `${member.id}|${member.platformId}|${displayName}${aliasStr}`,
688
+ accountNameHistory: accountNames.length > 0 ? accountNames : noChangeRecord,
689
+ groupNicknameHistory: groupNicknames.length > 0 ? groupNicknames : noChangeRecord
690
+ };
691
+ return { content: JSON.stringify(data), data };
692
+ }
693
+ var getMemberNameHistoryTool = {
694
+ name: "get_member_name_history",
695
+ description: '\u83B7\u53D6\u6210\u5458\u7684\u6635\u79F0\u53D8\u66F4\u5386\u53F2\u8BB0\u5F55\u3002\u9002\u7528\u4E8E\u56DE\u7B54"\u67D0\u4EBA\u4EE5\u524D\u53EB\u4EC0\u4E48\u540D\u5B57"\u3001"\u67D0\u4EBA\u7684\u6635\u79F0\u53D8\u5316"\u7B49\u95EE\u9898\u3002',
696
+ inputSchema: inputSchema14,
697
+ handler: handler14,
698
+ category: "analysis"
699
+ };
700
+
701
+ // ../../packages/tools/src/definitions/get-conversation-between.ts
702
+ var inputSchema15 = {
703
+ type: "object",
704
+ properties: {
705
+ member_id_1: { type: "number", description: "\u7B2C\u4E00\u4E2A\u6210\u5458\u7684 ID\uFF08\u901A\u8FC7 get_members \u83B7\u53D6\uFF09" },
706
+ member_id_2: { type: "number", description: "\u7B2C\u4E8C\u4E2A\u6210\u5458\u7684 ID\uFF08\u901A\u8FC7 get_members \u83B7\u53D6\uFF09" },
707
+ limit: { type: "number", description: "\u8FD4\u56DE\u7684\u6700\u5927\u6D88\u606F\u6761\u6570" },
708
+ ...timeParamProperties
709
+ },
710
+ required: ["member_id_1", "member_id_2"]
711
+ };
712
+ async function handler15(params, context) {
713
+ const { locale, timeFilter: contextTimeFilter, maxMessagesLimit } = context;
714
+ const limit = maxMessagesLimit || params.limit || 100;
715
+ const effectiveTimeFilter = parseExtendedTimeParams(params, contextTimeFilter);
716
+ const result = await context.dataProvider.getConversationBetween(
717
+ params.member_id_1,
718
+ params.member_id_2,
719
+ effectiveTimeFilter,
720
+ limit
721
+ );
722
+ if (result.messages.length === 0) {
723
+ const data2 = {
724
+ error: t("noConversation", locale),
725
+ member1Id: params.member_id_1,
726
+ member2Id: params.member_id_2
727
+ };
728
+ return { content: JSON.stringify(data2), data: data2 };
729
+ }
730
+ const data = {
731
+ total: result.total,
732
+ returned: result.messages.length,
733
+ member1: result.member1Name,
734
+ member2: result.member2Name,
735
+ timeRange: formatTimeRange(effectiveTimeFilter, locale),
736
+ rawMessages: result.messages
737
+ };
738
+ return { content: JSON.stringify(data), data, rawMessages: result.messages };
739
+ }
740
+ var getConversationBetweenTool = {
741
+ name: "get_conversation_between",
742
+ description: '\u83B7\u53D6\u4E24\u4E2A\u7FA4\u6210\u5458\u4E4B\u95F4\u7684\u5BF9\u8BDD\u8BB0\u5F55\u3002\u9002\u7528\u4E8E\u56DE\u7B54"A\u548CB\u4E4B\u95F4\u804A\u4E86\u4EC0\u4E48"\u7B49\u95EE\u9898\u3002\u9700\u8981\u5148\u901A\u8FC7 get_members \u83B7\u53D6\u6210\u5458 ID\u3002',
743
+ inputSchema: inputSchema15,
744
+ handler: handler15,
745
+ category: "analysis",
746
+ truncationStrategy: "keep_last"
747
+ };
748
+
749
+ // ../../packages/tools/src/definitions/get-session-summaries.ts
750
+ var inputSchema16 = {
751
+ type: "object",
752
+ properties: {
753
+ keywords: { type: "array", items: { type: "string" }, description: "\u6309\u5173\u952E\u8BCD\u8FC7\u6EE4\u6458\u8981\u5185\u5BB9" },
754
+ limit: { type: "number", description: "\u8FD4\u56DE\u7684\u6700\u5927\u4F1A\u8BDD\u6570\uFF0C\u9ED8\u8BA4 20" },
755
+ ...timeParamProperties
756
+ }
757
+ };
758
+ async function handler16(params, context) {
759
+ const { locale, timeFilter: contextTimeFilter } = context;
760
+ const limit = params.limit || 20;
761
+ const effectiveTimeFilter = parseExtendedTimeParams(params, contextTimeFilter);
762
+ const sessions = await context.dataProvider.getSessionSummaries({
763
+ limit: limit * 2,
764
+ timeFilter: effectiveTimeFilter
765
+ });
766
+ if (!sessions || sessions.length === 0) {
767
+ const data2 = {
768
+ message: isChineseLocale(locale) ? '\u672A\u627E\u5230\u5E26\u6458\u8981\u7684\u4F1A\u8BDD\u3002\u53EF\u80FD\u8FD8\u6CA1\u6709\u751F\u6210\u6458\u8981\uFF0C\u8BF7\u5728\u4F1A\u8BDD\u65F6\u95F4\u7EBF\u4E2D\u70B9\u51FB"\u6279\u91CF\u751F\u6210"\u6309\u94AE\u3002' : "No sessions with summaries found. Summaries may not have been generated yet."
769
+ };
770
+ return { content: JSON.stringify(data2), data: data2 };
771
+ }
772
+ let filteredSessions = sessions;
773
+ const keywords = params.keywords;
774
+ if (keywords && keywords.length > 0) {
775
+ const lowerKeywords = keywords.map((k) => k.toLowerCase());
776
+ filteredSessions = sessions.filter(
777
+ (s) => lowerKeywords.some((keyword) => s.summary?.toLowerCase().includes(keyword))
778
+ );
779
+ }
780
+ filteredSessions = filteredSessions.filter((s) => s.summary);
781
+ const limitedSessions = filteredSessions.slice(0, limit);
782
+ const localeStr = isChineseLocale(locale) ? "zh-CN" : "en-US";
783
+ const data = {
784
+ total: filteredSessions.length,
785
+ returned: limitedSessions.length,
786
+ timeRange: formatTimeRange(effectiveTimeFilter, locale),
787
+ sessions: limitedSessions.map((s) => {
788
+ const startTime = new Date(s.startTs * 1e3).toLocaleString(localeStr);
789
+ const endTime = new Date(s.endTs * 1e3).toLocaleString(localeStr);
790
+ return {
791
+ sessionId: s.id,
792
+ time: `${startTime} ~ ${endTime}`,
793
+ messageCount: s.messageCount,
794
+ participants: s.participants,
795
+ summary: s.summary
796
+ };
797
+ })
798
+ };
799
+ return { content: JSON.stringify(data), data };
800
+ }
801
+ var getSessionSummariesTool = {
802
+ name: "get_session_summaries",
803
+ description: "\u83B7\u53D6\u4F1A\u8BDD\u6458\u8981\u5217\u8868\uFF0C\u5FEB\u901F\u4E86\u89E3\u7FA4\u804A\u5386\u53F2\u8BA8\u8BBA\u7684\u4E3B\u9898\u3002\u53EF\u4EE5\u6309\u5173\u952E\u8BCD\u641C\u7D22\u8BA8\u8BBA\u8FC7\u7684\u8BDD\u9898\u3002",
804
+ inputSchema: inputSchema16,
805
+ handler: handler16,
806
+ category: "analysis"
807
+ };
808
+
809
+ // ../../packages/tools/src/definitions/response-time-analysis.ts
810
+ var inputSchema17 = {
811
+ type: "object",
812
+ properties: {
813
+ days: { type: "number", description: "\u5206\u6790\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E\uFF0C\u9ED8\u8BA4 30" },
814
+ top_n: { type: "number", description: "\u8FD4\u56DE\u524D\u591A\u5C11\u540D\uFF0C\u9ED8\u8BA4 10" }
815
+ }
816
+ };
817
+ async function handler17(params, context) {
818
+ const { locale } = context;
819
+ const isZh = isChineseLocale(locale);
820
+ const days = params.days || 30;
821
+ const topN = params.top_n || 10;
822
+ const sql = `
823
+ SELECT msg.sender_id, COALESCE(m.group_nickname, m.account_name) AS name, msg.ts
824
+ FROM message msg
825
+ JOIN member m ON msg.sender_id = m.id
826
+ WHERE msg.type = 0
827
+ AND msg.ts > unixepoch('now', '-' || @days || ' days')
828
+ ORDER BY msg.ts ASC
829
+ `;
830
+ const rows = await context.dataProvider.executeParameterizedSql(sql, { days });
831
+ if (!rows || rows.length < 2) {
832
+ const text = isZh ? "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6D88\u606F\u4E0D\u8DB3\uFF0C\u65E0\u6CD5\u5206\u6790\u54CD\u5E94\u65F6\u95F4" : "Not enough messages in this time range to analyze response time";
833
+ return { content: text, data: null };
834
+ }
835
+ const responseTimes = /* @__PURE__ */ new Map();
836
+ for (let i = 1; i < rows.length; i++) {
837
+ const prev = rows[i - 1];
838
+ const curr = rows[i];
839
+ if (curr.sender_id === prev.sender_id) continue;
840
+ const gap = curr.ts - prev.ts;
841
+ if (gap < 5 || gap > 1800) continue;
842
+ if (!responseTimes.has(curr.sender_id)) {
843
+ responseTimes.set(curr.sender_id, { name: curr.name, times: [] });
844
+ }
845
+ responseTimes.get(curr.sender_id).times.push(gap);
846
+ }
847
+ const stats = [...responseTimes.entries()].map(([id, { name, times }]) => {
848
+ times.sort((a, b) => a - b);
849
+ const avg = Math.round(times.reduce((s, t2) => s + t2, 0) / times.length);
850
+ const median = times[Math.floor(times.length / 2)];
851
+ return { id, name, avgSeconds: avg, medianSeconds: median, responseCount: times.length };
852
+ }).filter((s) => s.responseCount >= 3).sort((a, b) => a.medianSeconds - b.medianSeconds).slice(0, topN);
853
+ if (stats.length === 0) {
854
+ const text = isZh ? "\u6CA1\u6709\u8DB3\u591F\u7684\u54CD\u5E94\u6570\u636E\u8FDB\u884C\u5206\u6790" : "Not enough response data for analysis";
855
+ return { content: text, data: null };
856
+ }
857
+ const formatTime = (s) => {
858
+ if (s < 60) return isZh ? `${s}\u79D2` : `${s}s`;
859
+ const m = Math.floor(s / 60);
860
+ const sec = s % 60;
861
+ return isZh ? `${m}\u5206${sec}\u79D2` : `${m}m${sec}s`;
862
+ };
863
+ const ranking = stats.map((s, i) => ({
864
+ rank: i + 1,
865
+ name: s.name,
866
+ median: formatTime(s.medianSeconds),
867
+ avg: formatTime(s.avgSeconds),
868
+ count: s.responseCount
869
+ }));
870
+ const data = {
871
+ period: isZh ? `\u8FD1${days}\u5929` : `Last ${days} days`,
872
+ totalResponders: stats.length,
873
+ ranking: ranking.map(
874
+ (r) => `${r.rank}. ${r.name} \u2014 ${isZh ? "\u4E2D\u4F4D\u6570" : "median"} ${r.median}, ${isZh ? "\u5E73\u5747" : "avg"} ${r.avg} (${r.count}${isZh ? "\u6B21" : " responses"})`
875
+ )
876
+ };
877
+ return { content: JSON.stringify(data), data };
878
+ }
879
+ var responseTimeAnalysisTool = {
880
+ name: "response_time_analysis",
881
+ description: "\u5206\u6790\u7FA4\u6210\u5458\u7684\u54CD\u5E94\u901F\u5EA6\u6392\u884C\uFF0C\u57FA\u4E8E\u56DE\u590D\u95F4\u9694\u7684\u4E2D\u4F4D\u6570\u548C\u5E73\u5747\u503C\u3002",
882
+ inputSchema: inputSchema17,
883
+ handler: handler17,
884
+ category: "analysis"
885
+ };
886
+
887
+ // ../../packages/tools/src/definitions/keyword-frequency.ts
888
+ var inputSchema18 = {
889
+ type: "object",
890
+ properties: {
891
+ days: { type: "number", description: "\u5206\u6790\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E\uFF0C\u9ED8\u8BA4 30" },
892
+ top_n: { type: "number", description: "\u8FD4\u56DE\u524D\u591A\u5C11\u4E2A\u9AD8\u9891\u8BCD\uFF0C\u9ED8\u8BA4 50" }
893
+ }
894
+ };
895
+ async function handler18(params, context) {
896
+ const { locale, segmentText } = context;
897
+ const isZh = isChineseLocale(locale);
898
+ const days = params.days || 30;
899
+ const topN = params.top_n || 50;
900
+ if (!segmentText) {
901
+ const text = isZh ? "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u5206\u8BCD\u529F\u80FD" : "Text segmentation is not available in this environment";
902
+ return { content: text, data: null };
903
+ }
904
+ const sql = `
905
+ SELECT content FROM message
906
+ WHERE type = 0 AND content IS NOT NULL AND LENGTH(content) > 1
907
+ AND ts > unixepoch('now', '-' || @days || ' days')
908
+ LIMIT 50000
909
+ `;
910
+ const rows = await context.dataProvider.executeParameterizedSql(sql, { days });
911
+ if (!rows || rows.length === 0) {
912
+ const text = isZh ? "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u6587\u672C\u6D88\u606F" : "No text messages in this time range";
913
+ return { content: text, data: null };
914
+ }
915
+ const texts = rows.map((r) => r.content);
916
+ const segLocale = locale?.startsWith("ja") ? "ja-JP" : locale?.startsWith("zh") ? "zh-CN" : "en-US";
917
+ const freqResult = segmentText(texts, segLocale, {
918
+ minCount: 2,
919
+ topN,
920
+ posFilterMode: "meaningful",
921
+ enableStopwords: true
922
+ });
923
+ if (freqResult.words.size === 0) {
924
+ const text = isZh ? "\u5206\u8BCD\u540E\u6CA1\u6709\u6709\u610F\u4E49\u7684\u9AD8\u9891\u8BCD" : "No meaningful high-frequency words found after segmentation";
925
+ return { content: text, data: null };
926
+ }
927
+ const ranking = [...freqResult.words.entries()].map(([word, count], i) => ({
928
+ rank: i + 1,
929
+ word,
930
+ count
931
+ }));
932
+ const data = {
933
+ period: isZh ? `\u8FD1${days}\u5929` : `Last ${days} days`,
934
+ totalMessages: rows.length,
935
+ totalKeywords: ranking.length,
936
+ keywords: ranking.map((r) => `${r.rank}. ${r.word} (${r.count}${isZh ? "\u6B21" : ""})`)
937
+ };
938
+ return { content: JSON.stringify(data), data };
939
+ }
940
+ var keywordFrequencyTool = {
941
+ name: "keyword_frequency",
942
+ description: "\u7EDF\u8BA1\u7FA4\u804A\u4E2D\u7684\u9AD8\u9891\u5173\u952E\u8BCD\uFF0C\u901A\u8FC7 NLP \u5206\u8BCD\u5206\u6790\u6D88\u606F\u5185\u5BB9\u3002",
943
+ inputSchema: inputSchema18,
944
+ handler: handler18,
945
+ category: "analysis"
946
+ };
947
+
948
+ // ../../packages/tools/src/sql/definitions.ts
949
+ var SQL_TOOL_DEFS = [
950
+ // ==================== 通用分析 ====================
951
+ {
952
+ name: "message_type_breakdown",
953
+ description: "\u6309\u6D88\u606F\u7C7B\u578B\u7EDF\u8BA1\u8FD1 N \u5929\u7684\u6D88\u606F\u5206\u5E03\uFF08\u6587\u672C\u3001\u56FE\u7247\u3001\u8BED\u97F3\u3001\u8868\u60C5\u7B49\u5404\u6709\u591A\u5C11\u6761\uFF09\u3002\u9002\u7528\u4E8E\u4E86\u89E3\u6C9F\u901A\u65B9\u5F0F\u504F\u597D\u3002",
954
+ parameters: {
955
+ type: "object",
956
+ properties: {
957
+ days: { type: "number", description: "\u7EDF\u8BA1\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E" }
958
+ },
959
+ required: ["days"]
960
+ },
961
+ execution: {
962
+ type: "sqlite",
963
+ query: "SELECT CASE type WHEN 0 THEN '\u6587\u672C' WHEN 1 THEN '\u56FE\u7247' WHEN 2 THEN '\u8BED\u97F3' WHEN 3 THEN '\u89C6\u9891' WHEN 4 THEN '\u6587\u4EF6' WHEN 5 THEN '\u8868\u60C5' WHEN 7 THEN '\u94FE\u63A5' WHEN 20 THEN '\u7EA2\u5305' WHEN 22 THEN '\u62CD\u4E00\u62CD' WHEN 80 THEN '\u7CFB\u7EDF\u6D88\u606F' WHEN 81 THEN '\u64A4\u56DE' ELSE '\u5176\u4ED6' END AS type_name, COUNT(*) AS msg_count, ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER(), 1) AS percentage FROM message WHERE ts > unixepoch('now', '-' || @days || ' days') GROUP BY type ORDER BY msg_count DESC",
964
+ rowTemplate: "{type_name}\uFF1A{msg_count} \u6761\uFF08\u5360 {percentage}%\uFF09",
965
+ summaryTemplate: "\u6D88\u606F\u7C7B\u578B\u5206\u5E03\uFF08\u5171 {rowCount} \u79CD\u7C7B\u578B\uFF09\uFF1A",
966
+ fallback: "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u6D88\u606F\u8BB0\u5F55"
967
+ }
968
+ },
969
+ {
970
+ name: "peak_chat_hours_by_member",
971
+ description: "\u5206\u6790\u6307\u5B9A\u6210\u5458\u5728\u8FD1 N \u5929\u5185\u6BCF\u5C0F\u65F6\u7684\u53D1\u8A00\u91CF\u5206\u5E03\uFF0C\u627E\u51FA\u5176\u6700\u6D3B\u8DC3\u7684\u65F6\u6BB5\u3002\u9700\u8981\u5148\u901A\u8FC7 get_members \u83B7\u53D6 member_id\u3002",
972
+ parameters: {
973
+ type: "object",
974
+ properties: {
975
+ member_id: { type: "number", description: "\u6210\u5458 ID\uFF08\u901A\u8FC7 get_members \u83B7\u53D6\uFF09" },
976
+ days: { type: "number", description: "\u7EDF\u8BA1\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E", default: 30 }
977
+ },
978
+ required: ["member_id"]
979
+ },
980
+ execution: {
981
+ type: "sqlite",
982
+ query: "SELECT CAST(strftime('%H', ts, 'unixepoch', 'localtime') AS INTEGER) AS hour, COUNT(*) AS msg_count FROM message WHERE sender_id = @member_id AND ts > unixepoch('now', '-' || @days || ' days') GROUP BY hour ORDER BY msg_count DESC",
983
+ rowTemplate: "{hour}:00 \u2014 {msg_count} \u6761\u6D88\u606F",
984
+ summaryTemplate: "\u8BE5\u6210\u5458\u5404\u65F6\u6BB5\u53D1\u8A00\u91CF\uFF08\u5171 {rowCount} \u4E2A\u6D3B\u8DC3\u65F6\u6BB5\uFF09\uFF1A",
985
+ fallback: "\u8BE5\u6210\u5458\u5728\u6307\u5B9A\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u53D1\u8A00\u8BB0\u5F55"
986
+ }
987
+ },
988
+ // ==================== 社群分析 ====================
989
+ {
990
+ name: "member_activity_trend",
991
+ description: "\u67E5\u770B\u6307\u5B9A\u6210\u5458\u8FD1 N \u5929\u7684\u6BCF\u65E5\u53D1\u8A00\u6570\u91CF\u53D8\u5316\u8D8B\u52BF\u3002\u9002\u7528\u4E8E\u89C2\u5BDF\u67D0\u4EBA\u662F\u5426\u53D8\u5F97\u66F4\u6D3B\u8DC3\u6216\u66F4\u6C89\u9ED8\u3002\u9700\u8981\u5148\u901A\u8FC7 get_members \u83B7\u53D6 member_id\u3002",
992
+ parameters: {
993
+ type: "object",
994
+ properties: {
995
+ member_id: { type: "number", description: "\u6210\u5458 ID\uFF08\u901A\u8FC7 get_members \u83B7\u53D6\uFF09" },
996
+ days: { type: "number", description: "\u67E5\u770B\u6700\u8FD1\u591A\u5C11\u5929\u7684\u8D8B\u52BF" }
997
+ },
998
+ required: ["member_id", "days"]
999
+ },
1000
+ execution: {
1001
+ type: "sqlite",
1002
+ query: "SELECT date(ts, 'unixepoch', 'localtime') AS day, COUNT(*) AS msg_count FROM message WHERE sender_id = @member_id AND ts > unixepoch('now', '-' || @days || ' days') GROUP BY day ORDER BY day",
1003
+ rowTemplate: "{day}\uFF1A{msg_count} \u6761",
1004
+ summaryTemplate: "\u8BE5\u6210\u5458\u8FD1 {rowCount} \u5929\u6709\u53D1\u8A00\u8BB0\u5F55\uFF1A",
1005
+ fallback: "\u8BE5\u6210\u5458\u5728\u6307\u5B9A\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u53D1\u8A00\u8BB0\u5F55"
1006
+ }
1007
+ },
1008
+ {
1009
+ name: "silent_members",
1010
+ description: "\u68C0\u6D4B\u8D85\u8FC7 N \u5929\u672A\u53D1\u8A00\u7684\u300C\u6C89\u9ED8\u6210\u5458\u300D\u3002\u9002\u7528\u4E8E\u793E\u7FA4\u8FD0\u8425\u4E2D\u53D1\u73B0\u6D41\u5931\u98CE\u9669\u7528\u6237\u3002",
1011
+ parameters: {
1012
+ type: "object",
1013
+ properties: {
1014
+ days: { type: "number", description: "\u591A\u5C11\u5929\u672A\u53D1\u8A00\u7B97\u6C89\u9ED8", default: 7 }
1015
+ },
1016
+ required: ["days"]
1017
+ },
1018
+ execution: {
1019
+ type: "sqlite",
1020
+ query: "SELECT m.id AS member_id, COALESCE(m.group_nickname, m.account_name, m.platform_id) AS name, MAX(msg.ts) AS last_msg_ts, CAST((unixepoch('now') - MAX(msg.ts)) / 86400 AS INTEGER) AS silent_days FROM member m JOIN message msg ON msg.sender_id = m.id GROUP BY m.id HAVING silent_days >= @days ORDER BY silent_days DESC LIMIT 30",
1021
+ rowTemplate: "{name} \u2014 \u5DF2\u6C89\u9ED8 {silent_days} \u5929",
1022
+ summaryTemplate: "\u5171\u53D1\u73B0 {rowCount} \u4F4D\u6C89\u9ED8\u6210\u5458\uFF1A",
1023
+ fallback: "\u6CA1\u6709\u53D1\u73B0\u8D85\u8FC7\u6307\u5B9A\u5929\u6570\u672A\u53D1\u8A00\u7684\u6210\u5458\uFF0C\u793E\u7FA4\u6D3B\u8DC3\u5EA6\u826F\u597D\uFF01"
1024
+ }
1025
+ },
1026
+ {
1027
+ name: "reply_interaction_ranking",
1028
+ description: "\u5206\u6790\u7FA4\u5185\u7684\u56DE\u590D\u4E92\u52A8\u5173\u7CFB\u6392\u884C\uFF0C\u627E\u51FA\u8C01\u56DE\u590D\u8C01\u6700\u591A\u3002\u9002\u7528\u4E8E\u53D1\u73B0\u793E\u7FA4\u4E2D\u7684\u6838\u5FC3\u4E92\u52A8\u5173\u7CFB\u548C\u610F\u89C1\u9886\u8896\u3002",
1029
+ parameters: {
1030
+ type: "object",
1031
+ properties: {
1032
+ days: { type: "number", description: "\u7EDF\u8BA1\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E" },
1033
+ limit: { type: "number", description: "\u8FD4\u56DE\u524D\u591A\u5C11\u5BF9\u4E92\u52A8\u5173\u7CFB", default: 10 }
1034
+ },
1035
+ required: ["days"]
1036
+ },
1037
+ execution: {
1038
+ type: "sqlite",
1039
+ query: "SELECT COALESCE(replier.group_nickname, replier.account_name) AS replier_name, COALESCE(original.group_nickname, original.account_name) AS original_name, COUNT(*) AS reply_count FROM message reply_msg JOIN message orig_msg ON reply_msg.reply_to_message_id = CAST(orig_msg.id AS TEXT) JOIN member replier ON reply_msg.sender_id = replier.id JOIN member original ON orig_msg.sender_id = original.id WHERE reply_msg.reply_to_message_id IS NOT NULL AND reply_msg.ts > unixepoch('now', '-' || @days || ' days') GROUP BY reply_msg.sender_id, orig_msg.sender_id ORDER BY reply_count DESC LIMIT @limit",
1040
+ rowTemplate: "{replier_name} \u2192 {original_name}\uFF1A{reply_count} \u6B21\u56DE\u590D",
1041
+ summaryTemplate: "\u56DE\u590D\u4E92\u52A8 Top {rowCount}\uFF1A",
1042
+ fallback: "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u56DE\u590D\u4E92\u52A8\u8BB0\u5F55"
1043
+ }
1044
+ },
1045
+ // ==================== 情感分析 ====================
1046
+ {
1047
+ name: "mutual_interaction_pairs",
1048
+ description: "\u627E\u51FA\u4E92\u52A8\u6700\u9891\u7E41\u7684\u6210\u5458\u5BF9\uFF0C\u57FA\u4E8E\u53CC\u5411\u6D88\u606F\u65F6\u95F4\u63A5\u8FD1\u5EA6\uFF08\u4E00\u65B9\u53D1\u8A00\u540E 5 \u5206\u949F\u5185\u53E6\u4E00\u65B9\u4E5F\u53D1\u8A00\u5373\u89C6\u4E3A\u4E00\u6B21\u4E92\u52A8\uFF09\u3002\u9002\u7528\u4E8E\u53D1\u73B0\u5173\u7CFB\u4EB2\u5BC6\u7684\u597D\u53CB\u7EC4\u5408\u3002",
1049
+ parameters: {
1050
+ type: "object",
1051
+ properties: {
1052
+ days: { type: "number", description: "\u7EDF\u8BA1\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E" },
1053
+ limit: { type: "number", description: "\u8FD4\u56DE\u524D\u591A\u5C11\u5BF9", default: 10 }
1054
+ },
1055
+ required: ["days"]
1056
+ },
1057
+ execution: {
1058
+ type: "sqlite",
1059
+ query: "SELECT COALESCE(m1.group_nickname, m1.account_name) AS member_a, COALESCE(m2.group_nickname, m2.account_name) AS member_b, COUNT(*) AS interaction_count FROM message a JOIN message b ON b.sender_id != a.sender_id AND b.ts > a.ts AND b.ts <= a.ts + 300 JOIN member m1 ON a.sender_id = m1.id JOIN member m2 ON b.sender_id = m2.id WHERE a.sender_id < b.sender_id AND a.ts > unixepoch('now', '-' || @days || ' days') AND a.type = 0 AND b.type = 0 GROUP BY a.sender_id, b.sender_id ORDER BY interaction_count DESC LIMIT @limit",
1060
+ rowTemplate: "{member_a} \u2194 {member_b}\uFF1A{interaction_count} \u6B21\u4E92\u52A8",
1061
+ summaryTemplate: "\u4E92\u52A8\u6700\u9891\u7E41\u7684 {rowCount} \u5BF9\u597D\u53CB\uFF1A",
1062
+ fallback: "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u68C0\u6D4B\u5230\u660E\u663E\u7684\u4E92\u52A8\u5173\u7CFB"
1063
+ }
1064
+ },
1065
+ {
1066
+ name: "member_message_length_stats",
1067
+ description: "\u7EDF\u8BA1\u5404\u6210\u5458\u7684\u5E73\u5747\u6D88\u606F\u957F\u5EA6\uFF08\u4EC5\u6587\u672C\u6D88\u606F\uFF09\uFF0C\u957F\u6D88\u606F\u901A\u5E38\u610F\u5473\u7740\u66F4\u7528\u5FC3\u7684\u4EA4\u6D41\u3002\u9002\u7528\u4E8E\u53D1\u73B0\u6DF1\u5EA6\u4EA4\u6D41\u8005\u3002",
1068
+ parameters: {
1069
+ type: "object",
1070
+ properties: {
1071
+ days: { type: "number", description: "\u7EDF\u8BA1\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E" },
1072
+ top_n: { type: "number", description: "\u8FD4\u56DE\u524D\u591A\u5C11\u540D", default: 10 }
1073
+ },
1074
+ required: ["days"]
1075
+ },
1076
+ execution: {
1077
+ type: "sqlite",
1078
+ query: "SELECT COALESCE(m.group_nickname, m.account_name) AS name, COUNT(*) AS msg_count, ROUND(AVG(LENGTH(msg.content)), 1) AS avg_length, MAX(LENGTH(msg.content)) AS max_length FROM message msg JOIN member m ON msg.sender_id = m.id WHERE msg.type = 0 AND msg.content IS NOT NULL AND LENGTH(msg.content) > 0 AND msg.ts > unixepoch('now', '-' || @days || ' days') GROUP BY msg.sender_id HAVING msg_count >= 5 ORDER BY avg_length DESC LIMIT @top_n",
1079
+ rowTemplate: "{name} \u2014 \u5E73\u5747 {avg_length} \u5B57/\u6761\uFF08\u5171 {msg_count} \u6761\uFF0C\u6700\u957F {max_length} \u5B57\uFF09",
1080
+ summaryTemplate: "\u6D88\u606F\u957F\u5EA6 Top {rowCount}\uFF08\u66F4\u957F = \u66F4\u7528\u5FC3\uFF09\uFF1A",
1081
+ fallback: "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u8DB3\u591F\u7684\u6587\u672C\u6D88\u606F\u6570\u636E"
1082
+ }
1083
+ },
1084
+ // ==================== 活跃度趋势 ====================
1085
+ {
1086
+ name: "daily_active_members",
1087
+ description: '\u7EDF\u8BA1\u6BCF\u65E5\u72EC\u7ACB\u53D1\u8A00\u4EBA\u6570\uFF08DAU\uFF09\u548C\u6D88\u606F\u91CF\uFF0C\u7528\u4E8E\u89C2\u5BDF\u7FA4\u6D3B\u529B\u53D8\u5316\u8D8B\u52BF\u3002\u9002\u7528\u4E8E"\u7FA4\u6D3B\u8DC3\u5EA6\u8D8B\u52BF\u600E\u4E48\u6837"\u3001"\u6700\u8FD1\u6709\u591A\u5C11\u4EBA\u5728\u8BF4\u8BDD"\u3002',
1088
+ parameters: {
1089
+ type: "object",
1090
+ properties: {
1091
+ days: { type: "number", description: "\u7EDF\u8BA1\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E", default: 30 }
1092
+ },
1093
+ required: ["days"]
1094
+ },
1095
+ execution: {
1096
+ type: "sqlite",
1097
+ query: "SELECT date(ts, 'unixepoch', 'localtime') AS day, COUNT(DISTINCT sender_id) AS active_members, COUNT(*) AS msg_count FROM message WHERE ts > unixepoch('now', '-' || @days || ' days') GROUP BY day ORDER BY day",
1098
+ rowTemplate: "{day}\uFF1A{active_members} \u4EBA\u6D3B\u8DC3\uFF0C{msg_count} \u6761\u6D88\u606F",
1099
+ summaryTemplate: "\u8FD1 {rowCount} \u5929\u7684\u6BCF\u65E5\u6D3B\u8DC3\u4EBA\u6570\u8D8B\u52BF\uFF1A",
1100
+ fallback: "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u6D88\u606F\u8BB0\u5F55"
1101
+ }
1102
+ },
1103
+ {
1104
+ name: "conversation_initiator_stats",
1105
+ description: "\u7EDF\u8BA1\u6BCF\u4E2A\u6210\u5458\u53D1\u8D77\u4F1A\u8BDD\uFF08\u4F5C\u4E3A\u4F1A\u8BDD\u9996\u6761\u6D88\u606F\u7684\u53D1\u9001\u8005\uFF09\u7684\u6B21\u6570\uFF0C\u627E\u51FA\u8C01\u6700\u5E38\u5F00\u542F\u8BDD\u9898\u3002\u9700\u8981\u5DF2\u751F\u6210\u4F1A\u8BDD\u7D22\u5F15\u3002",
1106
+ parameters: {
1107
+ type: "object",
1108
+ properties: {
1109
+ days: { type: "number", description: "\u7EDF\u8BA1\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E", default: 30 },
1110
+ limit: { type: "number", description: "\u8FD4\u56DE\u524D\u591A\u5C11\u540D", default: 10 }
1111
+ },
1112
+ required: ["days"]
1113
+ },
1114
+ execution: {
1115
+ type: "sqlite",
1116
+ query: "SELECT COALESCE(m.group_nickname, m.account_name) AS name, COUNT(*) AS initiated_count FROM chat_session cs JOIN message_context mc ON mc.session_id = cs.id JOIN message msg ON msg.id = mc.message_id JOIN member m ON msg.sender_id = m.id WHERE msg.ts = cs.start_ts AND cs.start_ts > unixepoch('now', '-' || @days || ' days') GROUP BY msg.sender_id ORDER BY initiated_count DESC LIMIT @limit",
1117
+ rowTemplate: "{name}\uFF1A\u53D1\u8D77 {initiated_count} \u6B21\u8BDD\u9898",
1118
+ summaryTemplate: "\u8BDD\u9898\u53D1\u8D77\u8005 Top {rowCount}\uFF1A",
1119
+ fallback: "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u4F1A\u8BDD\u8BB0\u5F55\uFF0C\u53EF\u80FD\u9700\u8981\u5148\u751F\u6210\u4F1A\u8BDD\u7D22\u5F15"
1120
+ }
1121
+ },
1122
+ {
1123
+ name: "activity_heatmap",
1124
+ description: "\u8FD4\u56DE \u661F\u671F\xD7\u5C0F\u65F6 \u7684\u6D88\u606F\u6570\u77E9\u9635\uFF0C\u9002\u5408\u751F\u6210\u6D3B\u8DC3\u5EA6\u70ED\u529B\u56FE\u3002weekday: 0=\u5468\u65E5, 1=\u5468\u4E00, ..., 6=\u5468\u516D\u3002",
1125
+ parameters: {
1126
+ type: "object",
1127
+ properties: {
1128
+ days: { type: "number", description: "\u7EDF\u8BA1\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E", default: 30 }
1129
+ },
1130
+ required: ["days"]
1131
+ },
1132
+ execution: {
1133
+ type: "sqlite",
1134
+ query: "SELECT CAST(strftime('%w', ts, 'unixepoch', 'localtime') AS INTEGER) AS weekday, CAST(strftime('%H', ts, 'unixepoch', 'localtime') AS INTEGER) AS hour, COUNT(*) AS msg_count FROM message WHERE ts > unixepoch('now', '-' || @days || ' days') GROUP BY weekday, hour ORDER BY weekday, hour",
1135
+ rowTemplate: "\u661F\u671F{weekday} {hour}:00 \u2014 {msg_count} \u6761",
1136
+ summaryTemplate: "\u6D3B\u8DC3\u5EA6\u70ED\u529B\u56FE\u6570\u636E\uFF08\u5171 {rowCount} \u4E2A\u65F6\u6BB5\u6709\u6D88\u606F\uFF09\uFF1A",
1137
+ fallback: "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u6D88\u606F\u8BB0\u5F55"
1138
+ }
1139
+ },
1140
+ // ==================== 客服分析 ====================
1141
+ {
1142
+ name: "unanswered_messages",
1143
+ description: "\u67E5\u627E\u8FD1 N \u5929\u5185\u672A\u88AB\u56DE\u590D\u7684\u6D88\u606F\uFF0C\u8FD9\u4E9B\u53EF\u80FD\u662F\u672A\u89E3\u51B3\u7684\u5BA2\u6237\u95EE\u9898\u3002\u4EC5\u7EDF\u8BA1\u6587\u672C\u6D88\u606F\u4E14\u5185\u5BB9\u8D85\u8FC7 10 \u5B57\u7684\uFF08\u8FC7\u6EE4\u7B80\u77ED\u5BD2\u6684\uFF09\u3002",
1144
+ parameters: {
1145
+ type: "object",
1146
+ properties: {
1147
+ days: { type: "number", description: "\u67E5\u627E\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E" },
1148
+ limit: { type: "number", description: "\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u6761", default: 20 }
1149
+ },
1150
+ required: ["days"]
1151
+ },
1152
+ execution: {
1153
+ type: "sqlite",
1154
+ query: "SELECT COALESCE(m.group_nickname, m.account_name) AS sender_name, datetime(msg.ts, 'unixepoch', 'localtime') AS send_time, SUBSTR(msg.content, 1, 100) AS content_preview FROM message msg JOIN member m ON msg.sender_id = m.id WHERE msg.type = 0 AND msg.content IS NOT NULL AND LENGTH(msg.content) > 10 AND msg.ts > unixepoch('now', '-' || @days || ' days') AND NOT EXISTS (SELECT 1 FROM message reply WHERE reply.reply_to_message_id = CAST(msg.id AS TEXT)) AND NOT EXISTS (SELECT 1 FROM message next WHERE next.sender_id != msg.sender_id AND next.ts > msg.ts AND next.ts <= msg.ts + 1800) ORDER BY msg.ts DESC LIMIT @limit",
1155
+ rowTemplate: "[{send_time}] {sender_name}\uFF1A{content_preview}",
1156
+ summaryTemplate: "\u5171\u53D1\u73B0 {rowCount} \u6761\u53EF\u80FD\u672A\u88AB\u56DE\u590D\u7684\u6D88\u606F\uFF1A",
1157
+ fallback: "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6240\u6709\u6D88\u606F\u90FD\u5DF2\u5F97\u5230\u56DE\u590D\uFF0C\u670D\u52A1\u8D28\u91CF\u5F88\u597D\uFF01"
1158
+ }
1159
+ }
1160
+ ];
1161
+
1162
+ // ../../packages/tools/src/sql/executor.ts
1163
+ function formatRow(template, row) {
1164
+ return template.replace(/\{(\w+)\}/g, (_, col) => {
1165
+ const val = row[col];
1166
+ return val !== null && val !== void 0 ? String(val) : "";
1167
+ });
1168
+ }
1169
+ function resolveTemplate(toolName, key, fallback, translateFn) {
1170
+ if (!translateFn) return fallback;
1171
+ const i18nKey = `ai.tools.${toolName}.${key}`;
1172
+ return translateFn(i18nKey) ?? fallback;
1173
+ }
1174
+ function createSqlToolDefinition(def) {
1175
+ return {
1176
+ name: def.name,
1177
+ description: def.description,
1178
+ inputSchema: def.parameters,
1179
+ category: "analysis",
1180
+ handler: async (params, context) => {
1181
+ const rows = await context.dataProvider.executeParameterizedSql(def.execution.query, params);
1182
+ const fallback = resolveTemplate(def.name, "fallback", def.execution.fallback, context.translateTemplate);
1183
+ if (!rows || rows.length === 0) {
1184
+ return {
1185
+ content: fallback,
1186
+ data: { rows: [], rowCount: 0 }
1187
+ };
1188
+ }
1189
+ const rowTemplate = resolveTemplate(def.name, "rowTemplate", def.execution.rowTemplate, context.translateTemplate);
1190
+ const summaryTemplate = def.execution.summaryTemplate ? resolveTemplate(def.name, "summaryTemplate", def.execution.summaryTemplate, context.translateTemplate) : void 0;
1191
+ const lines = [];
1192
+ if (summaryTemplate) {
1193
+ lines.push(summaryTemplate.replace(/\{rowCount\}/g, String(rows.length)));
1194
+ lines.push("");
1195
+ }
1196
+ for (const row of rows) {
1197
+ lines.push(formatRow(rowTemplate, row));
1198
+ }
1199
+ return {
1200
+ content: lines.join("\n"),
1201
+ data: { rows, rowCount: rows.length }
1202
+ };
1203
+ }
1204
+ };
1205
+ }
1206
+ function createAllSqlToolDefinitions(defs) {
1207
+ return defs.map(createSqlToolDefinition);
1208
+ }
1209
+
1210
+ // ../../packages/tools/src/registry.ts
1211
+ var SHARED_TOOLS = [
1212
+ // Core
1213
+ chatOverviewTool,
1214
+ searchMessagesTool,
1215
+ deepSearchMessagesTool,
1216
+ recentMessagesTool,
1217
+ getMessageContextTool,
1218
+ searchSessionsTool,
1219
+ getSessionMessagesTool,
1220
+ getMembersTool,
1221
+ // Analysis
1222
+ memberStatsTool,
1223
+ timeStatsTool,
1224
+ getMemberNameHistoryTool,
1225
+ getConversationBetweenTool,
1226
+ getSessionSummariesTool,
1227
+ responseTimeAnalysisTool,
1228
+ keywordFrequencyTool,
1229
+ // Raw SQL
1230
+ sqlQueryTool
1231
+ ];
1232
+ var AGENT_TOOL_REGISTRY = [...SHARED_TOOLS, ...createAllSqlToolDefinitions(SQL_TOOL_DEFS)];
1233
+ var MCP_TOOL_REGISTRY = [
1234
+ // MCP-specific: session discovery & schema
1235
+ sessionsListTool,
1236
+ sessionInfoTool,
1237
+ schemaTool,
1238
+ // Shared core + analysis + raw SQL
1239
+ ...SHARED_TOOLS
1240
+ ];
1241
+
1242
+ // ../../packages/tools/src/providers/core-data-provider.ts
1243
+ var CoreDataProvider = class {
1244
+ constructor(db) {
1245
+ this.db = db;
1246
+ }
1247
+ db;
1248
+ async searchMessages(keywords, options) {
1249
+ const keyword = keywords.join(" ");
1250
+ const result = searchMessagesLike(this.db, keyword, { limit: options?.limit ?? 50 });
1251
+ return {
1252
+ messages: result.messages.map((m) => ({
1253
+ id: m.id,
1254
+ senderId: m.senderId,
1255
+ senderName: m.senderName,
1256
+ senderPlatformId: m.senderPlatformId,
1257
+ content: m.content,
1258
+ timestamp: m.timestamp
1259
+ })),
1260
+ total: result.total ?? result.messages.length
1261
+ };
1262
+ }
1263
+ async deepSearchMessages(keywords, options) {
1264
+ return this.searchMessages(keywords, options);
1265
+ }
1266
+ async getSearchMessageContext(messageIds, contextBefore, contextAfter) {
1267
+ return getSearchMessageContext(this.db, messageIds, contextBefore, contextAfter);
1268
+ }
1269
+ async getRecentMessages(options) {
1270
+ const messages = getRecentMessages(this.db, { limit: options?.limit ?? 50 });
1271
+ return {
1272
+ messages: messages.map((m) => ({
1273
+ id: m.id,
1274
+ senderId: m.senderId,
1275
+ senderName: m.senderName,
1276
+ senderPlatformId: m.senderPlatformId,
1277
+ content: m.content,
1278
+ timestamp: m.timestamp
1279
+ })),
1280
+ total: messages.length
1281
+ };
1282
+ }
1283
+ async getMessageContext(messageIds, contextSize) {
1284
+ return getMessageContext(this.db, messageIds, contextSize);
1285
+ }
1286
+ async getChatOverview(topN) {
1287
+ return getChatOverview(this.db, topN);
1288
+ }
1289
+ async getMembers() {
1290
+ return getMembersWithAliases(this.db);
1291
+ }
1292
+ async getMemberStats(options) {
1293
+ const top = options?.top ?? 20;
1294
+ const members = getMemberActivity(this.db, options?.timeFilter);
1295
+ return members.slice(0, top).map((m) => ({
1296
+ name: m.name,
1297
+ messageCount: m.messageCount,
1298
+ percentage: m.percentage
1299
+ }));
1300
+ }
1301
+ async getMemberNameHistory(memberId) {
1302
+ return getMemberNameHistory(this.db, memberId);
1303
+ }
1304
+ async getTimeStats(type, options) {
1305
+ const filter = options?.timeFilter;
1306
+ switch (type) {
1307
+ case "weekday":
1308
+ return getWeekdayActivity(this.db, filter);
1309
+ case "daily":
1310
+ return getDailyActivity(this.db, filter);
1311
+ case "hourly":
1312
+ default:
1313
+ return getHourlyActivity(this.db, filter);
1314
+ }
1315
+ }
1316
+ async searchSessions(keywords, timeFilter, limit, previewCount) {
1317
+ return searchSessions(this.db, keywords, timeFilter, limit, previewCount);
1318
+ }
1319
+ async getSessionMessages(chatSessionId, limit) {
1320
+ return getSessionMessages(this.db, chatSessionId, limit);
1321
+ }
1322
+ async getSessionSummaries(options) {
1323
+ return getSessionSummaries(this.db, options);
1324
+ }
1325
+ async getConversationBetween(memberId1, memberId2, timeFilter, limit) {
1326
+ return getConversationBetween(this.db, memberId1, memberId2, timeFilter, limit);
1327
+ }
1328
+ async executeSql(sql) {
1329
+ return executeReadonlySql(this.db, sql);
1330
+ }
1331
+ async executeParameterizedSql(query, params) {
1332
+ return executeParameterizedSql(this.db, query, params);
1333
+ }
1334
+ async getSchema() {
1335
+ return getDatabaseSchema(this.db);
1336
+ }
1337
+ };
1338
+
1339
+ export {
1340
+ AGENT_TOOL_REGISTRY,
1341
+ MCP_TOOL_REGISTRY,
1342
+ CoreDataProvider
1343
+ };
1344
+ //# sourceMappingURL=chunk-3DIWXYMJ.mjs.map