chatlab-cli 0.25.1 → 0.26.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-5XMRLXZM.mjs → chat-command-ZP4WJVWC.mjs} +144 -6
- package/dist/chat-command-ZP4WJVWC.mjs.map +1 -0
- package/dist/{chunk-YZYP5ST6.mjs → chunk-6G7AIF7I.mjs} +12 -11
- package/dist/chunk-6G7AIF7I.mjs.map +1 -0
- package/dist/{chunk-WYHGWTA4.mjs → chunk-ILGSSXOG.mjs} +92 -28
- package/dist/chunk-ILGSSXOG.mjs.map +1 -0
- package/dist/{chunk-JE3FOFF7.mjs → chunk-IRZRIHIT.mjs} +49 -12
- package/dist/chunk-IRZRIHIT.mjs.map +1 -0
- package/dist/{chunk-7VG6QGQ2.mjs → chunk-QY67NN65.mjs} +841 -79
- package/dist/chunk-QY67NN65.mjs.map +1 -0
- package/dist/{chunk-GTCTDD2Z.mjs → chunk-SJIOPSMM.mjs} +8 -8
- package/dist/{chunk-PDQFSQO6.mjs → chunk-TC74HESZ.mjs} +2 -2
- package/dist/{chunk-FDK6SPCG.mjs → chunk-UB4XBM4R.mjs} +2 -2
- package/dist/{chunk-CUCDRAY6.mjs → chunk-WXC6RZCX.mjs} +2 -2
- package/dist/cli.mjs +3 -3
- package/dist/{http-C3CGXUBU.mjs → http-J3IOFFIA.mjs} +8 -8
- package/dist/{import-VS7L5JDX.mjs → import-POYG5LOX.mjs} +4 -4
- package/dist/index.mjs +8 -8
- package/dist/{mcp-6VXDPUQK.mjs → mcp-7HH7UMXT.mjs} +4 -4
- package/dist/{stream-import-TFDBNVIW.mjs → stream-import-O3H3FGV2.mjs} +3 -3
- package/dist-web/assets/{DebugToolsPanel-vn-bnUxn.css → DebugToolsPanel-CrqULdXp.css} +1 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BTJxEma5.js +182 -0
- package/dist-web/assets/{EChartWordcloud-CzhfrEF_.js → EChartWordcloud-B_YisOW5.js} +1 -1
- package/dist-web/assets/{MessageExportModal-B79pPBL9.js → MessageExportModal-C7NgEL8T.js} +1 -1
- package/dist-web/assets/{electron-DRkrjeIl.js → electron-B3DFm1cC.js} +1 -1
- package/dist-web/assets/electron-BMM4Nfty.js +1 -0
- package/dist-web/assets/{fetch-DfgdagMN.js → fetch-ArMjdMcy.js} +1 -1
- package/dist-web/assets/{fetch-BqLy63aj.js → fetch-BFtuJaa1.js} +1 -1
- package/dist-web/assets/{fetch-CDamdvtb.js → fetch-BGfaAqO9.js} +1 -1
- package/dist-web/assets/{fetch-Drk9dLaF.js → fetch-BaJDB31c.js} +1 -1
- package/dist-web/assets/{fetch-BmZIm6mS.js → fetch-BjDJgdN0.js} +1 -1
- package/dist-web/assets/{fetch-DWg-2kOp.js → fetch-C5CLcpQW.js} +1 -1
- package/dist-web/assets/{fetch-ph2_d79v.js → fetch-DQZvdHnG.js} +1 -1
- package/dist-web/assets/{fetch-Y-_WsCuk.js → fetch-Dhfbt1BQ.js} +3 -3
- package/dist-web/assets/{fetch-BstX-wd4.js → fetch-DnEP0uyF.js} +1 -1
- package/dist-web/assets/{fetch-Bvcxe5PI.js → fetch-SYrb-sZe.js} +1 -1
- package/dist-web/assets/index-CWfkrx37.js +173 -0
- package/dist-web/assets/index-D3N4NDFx.css +1 -0
- package/dist-web/assets/{index-DMWf5On7.js → index-D7_ch4zf.js} +1 -1
- package/dist-web/assets/{index-9urfJP1r.js → index-DK9LEEzT.js} +1 -1
- package/dist-web/assets/index-DqDa_on9.js +9 -0
- package/dist-web/assets/{index-D_hHm7bQ.js → index-DrN1G9bl.js} +2 -2
- package/dist-web/assets/{index-deYDOVUa.js → index-MdYeovYo.js} +4 -4
- package/dist-web/assets/{vendor-nuxt-ui-C_8CsH6e.js → vendor-nuxt-ui-Zy8txNdC.js} +1 -1
- package/dist-web/assets/{vendor-reka-ui-CIMXwvEp.js → vendor-reka-ui-mMvma1EP.js} +1 -1
- package/dist-web/assets/{web-XJV6Vj5t.js → web-zEXsvoZW.js} +1 -1
- package/dist-web/index.html +4 -4
- package/package.json +1 -1
- package/dist/chat-command-5XMRLXZM.mjs.map +0 -1
- package/dist/chunk-7VG6QGQ2.mjs.map +0 -1
- package/dist/chunk-JE3FOFF7.mjs.map +0 -1
- package/dist/chunk-WYHGWTA4.mjs.map +0 -1
- package/dist/chunk-YZYP5ST6.mjs.map +0 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-B1xZmp9r.js +0 -161
- package/dist-web/assets/electron-Dv8jdn_2.js +0 -1
- package/dist-web/assets/index-CfKjh0Jr.js +0 -9
- package/dist-web/assets/index-CprJOAB1.css +0 -1
- package/dist-web/assets/index-MCdpiX_O.js +0 -173
- /package/dist/{chunk-GTCTDD2Z.mjs.map → chunk-SJIOPSMM.mjs.map} +0 -0
- /package/dist/{chunk-PDQFSQO6.mjs.map → chunk-TC74HESZ.mjs.map} +0 -0
- /package/dist/{chunk-FDK6SPCG.mjs.map → chunk-UB4XBM4R.mjs.map} +0 -0
- /package/dist/{chunk-CUCDRAY6.mjs.map → chunk-WXC6RZCX.mjs.map} +0 -0
- /package/dist/{http-C3CGXUBU.mjs.map → http-J3IOFFIA.mjs.map} +0 -0
- /package/dist/{import-VS7L5JDX.mjs.map → import-POYG5LOX.mjs.map} +0 -0
- /package/dist/{mcp-6VXDPUQK.mjs.map → mcp-7HH7UMXT.mjs.map} +0 -0
- /package/dist/{stream-import-TFDBNVIW.mjs.map → stream-import-O3H3FGV2.mjs.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ai/logger.ts","../src/ai/tool-adapter.ts","../src/ai/llm-config.ts","../src/ai/assistant-loader.ts","../src/ai/agent.ts","../src/ai/agent-stream-runner.ts"],"sourcesContent":["/**\n * Server 端 AI 日志\n *\n * 复用 @openchatlab/node-runtime 的 AiLogger,\n * 在 startHttpServer() 中通过 initServerAiLogger() 初始化。\n */\n\nimport { AiLogger } from '@openchatlab/node-runtime'\n\nlet logger: AiLogger | null = null\n\nexport function initServerAiLogger(logsDir: string): AiLogger {\n if (!logger) {\n logger = new AiLogger(logsDir)\n }\n return logger\n}\n\nexport function getServerAiLogger(): AiLogger | null {\n return logger\n}\n\nexport function closeServerAiLogger(): void {\n if (logger) {\n logger.close()\n logger = null\n }\n}\n","/**\n * 工具适配层\n *\n * 将 @openchatlab/tools 的 ToolDefinition 适配为 @earendil-works/pi-agent-core 的 AgentTool 格式。\n * 消息类工具返回 rawMessages 时自动执行预处理管道(清洗、去噪、脱敏、截断、格式化)。\n */\n\nimport type { ToolDefinition, ToolExecutionContext } from '@openchatlab/tools'\nimport { CoreDataProvider } from '@openchatlab/tools'\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport {\n applyPreprocessingPipeline,\n batchSegmentWithFrequency,\n type AgentTool,\n type AgentToolResult,\n type PreprocessableMessage,\n type TruncationStrategy,\n createChartSchemaGateState,\n wrapWithChartSchemaGate,\n} from '@openchatlab/node-runtime'\nimport { getServerAiLogger } from './logger'\n\nconst DEFAULT_MAX_TOOL_RESULT_TOKENS = 8000\n\nconst TOOL_TRUNCATION_STRATEGY: Record<string, TruncationStrategy> = {\n search_messages: 'keep_first',\n deep_search_messages: 'keep_first',\n get_recent_messages: 'keep_last',\n get_message_context: 'keep_last',\n get_segment_messages: 'keep_last',\n get_conversation_between: 'keep_last',\n}\n\nexport interface ServerToolContext {\n db: DatabaseAdapter\n sessionId: string\n locale?: string\n}\n\nfunction convertJsonSchemaToParameters(schema: ToolDefinition['inputSchema']) {\n const properties: Record<string, unknown> = {}\n for (const [key, prop] of Object.entries(schema.properties)) {\n properties[key] = { ...prop }\n }\n return {\n type: 'object' as const,\n properties,\n required: schema.required || [],\n }\n}\n\nexport interface AdaptToolsOptions {\n maxToolResultTokens?: number\n}\n\nexport function adaptToolsForAgent(\n tools: ToolDefinition[],\n getContext: () => ServerToolContext,\n options?: AdaptToolsOptions\n): AgentTool<any, any>[] {\n const tokenBudget = options?.maxToolResultTokens ?? DEFAULT_MAX_TOOL_RESULT_TOKENS\n const chartSchemaGateState = createChartSchemaGateState()\n\n return tools.map((tool) =>\n wrapWithChartSchemaGate(\n {\n name: tool.name,\n label: tool.name,\n description: tool.description,\n parameters: convertJsonSchemaToParameters(tool.inputSchema) as any,\n async execute(_toolCallId: string, params: unknown): Promise<AgentToolResult<unknown>> {\n const toolParams = (params && typeof params === 'object' ? params : {}) as Record<string, unknown>\n const ctx = getContext()\n const execCtx: ToolExecutionContext = {\n db: ctx.db,\n dataProvider: new CoreDataProvider(ctx.db),\n sessionId: ctx.sessionId,\n locale: ctx.locale,\n segmentText: (texts, locale, options) => batchSegmentWithFrequency(texts, locale as any, options as any),\n }\n try {\n const result = await tool.handler(toolParams, execCtx)\n const chartDetails =\n result.chart || result.charts\n ? {\n ...(result.chart ? { chart: result.chart } : {}),\n ...(result.charts ? { charts: result.charts } : {}),\n }\n : {}\n\n if (result.rawMessages && result.rawMessages.length > 0) {\n const pipelineResult = applyPreprocessingPipeline({\n rawMessages: result.rawMessages as PreprocessableMessage[],\n locale: ctx.locale,\n maxToolResultTokens: tokenBudget,\n truncationStrategy: TOOL_TRUNCATION_STRATEGY[tool.name] ?? 'keep_last',\n extraDetails: (result.data ?? {}) as Record<string, unknown>,\n logger: getServerAiLogger() ?? undefined,\n })\n return {\n content: [{ type: 'text', text: pipelineResult.text }],\n details: Object.keys(chartDetails).length > 0 ? chartDetails : null,\n }\n }\n\n const baseDetails =\n typeof result.data === 'object' && result.data !== null\n ? (result.data as Record<string, unknown>)\n : result.data === undefined\n ? null\n : { value: result.data }\n\n return {\n content: [{ type: 'text', text: result.content }],\n details: Object.keys(chartDetails).length > 0 ? { ...(baseDetails ?? {}), ...chartDetails } : baseDetails,\n }\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n return { content: [{ type: 'text', text: `Error: ${msg}` }], details: null }\n }\n },\n },\n chartSchemaGateState\n )\n )\n}\n","/**\n * 服务端 LLM 配置加载\n *\n * API Key 从 ~/.chatlab/auth-profiles.json 读取,\n * 匹配顺序:config.authProfile 精确匹配 > config.provider 兜底匹配。\n *\n * 模型/Provider 等配置仍从 llm-config.json 读取。\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport type { PiModel, PiApi } from '@openchatlab/node-runtime'\nimport { buildPiModel as buildPiModelCore, type PiModelConfig } from '@openchatlab/node-runtime'\nimport { resolveApiKey, writeAuthProfile } from '@openchatlab/config'\nimport { randomUUID } from 'crypto'\n\n// ==================== Types ====================\n\nexport interface AIServiceConfig {\n id: string\n name: string\n provider: string\n apiKey: string\n model?: string\n baseUrl?: string\n maxTokens?: number\n apiFormat?: string\n customModels?: Array<{ id: string; name: string }>\n authProfile?: string\n}\n\ninterface ModelSlot {\n configId: string\n modelId?: string\n}\n\ninterface AIConfigStore {\n configs: AIServiceConfig[]\n defaultAssistant: ModelSlot | null\n fastModel: ModelSlot | null\n}\n\n// ==================== Config Loading ====================\n\nexport function loadLlmConfig(aiDataDir: string): AIConfigStore {\n const configPath = path.join(aiDataDir, 'llm-config.json')\n if (!fs.existsSync(configPath)) {\n return { configs: [], defaultAssistant: null, fastModel: null }\n }\n\n try {\n const data = JSON.parse(fs.readFileSync(configPath, 'utf-8'))\n\n const configs = (data.configs || []).map((c: Record<string, unknown>) => {\n const provider = (c.provider as string) || ''\n const authProfile = c.authProfile as string | undefined\n const profileKey = resolveApiKey(provider, authProfile)\n const rawKey = (c.apiKey as string) || ''\n const fallbackKey = rawKey.startsWith('enc:') ? '' : rawKey\n return {\n ...c,\n apiKey: profileKey || fallbackKey,\n }\n })\n return {\n configs,\n defaultAssistant: data.defaultAssistant ?? null,\n fastModel: data.fastModel ?? null,\n }\n } catch {\n return { configs: [], defaultAssistant: null, fastModel: null }\n }\n}\n\nfunction resolveSlot(slot: ModelSlot | null | undefined, configs: AIServiceConfig[]): ModelSlot | null {\n if (slot && configs.some((c) => c.id === slot.configId)) return slot\n const fallback = configs[0]\n if (!fallback) return null\n return { configId: fallback.id, modelId: fallback.model }\n}\n\nexport function getDefaultAssistantConfig(aiDataDir: string): AIServiceConfig | null {\n const store = loadLlmConfig(aiDataDir)\n const slot = resolveSlot(store.defaultAssistant, store.configs)\n if (!slot) return null\n const config = store.configs.find((c) => c.id === slot.configId)\n if (!config) return null\n return { ...config, model: slot.modelId || config.model }\n}\n\n// ==================== Config Write Operations ====================\n\nconst MAX_CONFIG_COUNT = 99\n\nfunction saveLlmConfig(aiDataDir: string, store: AIConfigStore): void {\n const configPath = path.join(aiDataDir, 'llm-config.json')\n const toSave = {\n ...store,\n configs: store.configs.map((c) => {\n const { apiKey: _k, ...rest } = c\n return rest\n }),\n schemaVersion: 3,\n }\n fs.writeFileSync(configPath, JSON.stringify(toSave, null, 2), 'utf-8')\n}\n\nexport function addLlmConfig(\n aiDataDir: string,\n config: Omit<AIServiceConfig, 'id'>\n): { success: boolean; config?: AIServiceConfig; error?: string } {\n const store = loadLlmConfig(aiDataDir)\n\n if (store.configs.length >= MAX_CONFIG_COUNT) {\n return { success: false, error: `Maximum ${MAX_CONFIG_COUNT} configs reached` }\n }\n\n const newConfig: AIServiceConfig = {\n ...config,\n id: randomUUID(),\n }\n\n const storeForSave = loadRawConfigStore(aiDataDir)\n storeForSave.configs.push({\n ...newConfig,\n apiKey: '',\n })\n\n if (storeForSave.configs.length === 1) {\n storeForSave.defaultAssistant = { configId: newConfig.id, modelId: newConfig.model || '' }\n }\n\n if (config.apiKey) {\n const profileName = config.name?.toLowerCase().replace(/\\s+/g, '-') || config.provider\n writeAuthProfile(profileName, {\n type: 'api_key',\n provider: config.provider,\n key: config.apiKey,\n })\n ;(storeForSave.configs[storeForSave.configs.length - 1] as unknown as Record<string, unknown>).authProfile =\n profileName\n }\n\n saveLlmConfig(aiDataDir, storeForSave)\n return { success: true, config: { ...newConfig, apiKey: '' } }\n}\n\nexport function updateLlmConfig(\n aiDataDir: string,\n id: string,\n updates: Partial<Omit<AIServiceConfig, 'id'>>\n): { success: boolean; error?: string } {\n const storeForSave = loadRawConfigStore(aiDataDir)\n const index = storeForSave.configs.findIndex((c) => c.id === id)\n\n if (index === -1) {\n return { success: false, error: 'Config not found' }\n }\n\n const { apiKey: newApiKey, ...restUpdates } = updates\n const updated = {\n ...storeForSave.configs[index],\n ...restUpdates,\n }\n storeForSave.configs[index] = updated\n\n if (newApiKey) {\n const profileName = updated.name?.toLowerCase().replace(/\\s+/g, '-') || updated.provider\n writeAuthProfile(profileName, {\n type: 'api_key',\n provider: updated.provider,\n key: newApiKey,\n })\n ;(storeForSave.configs[index] as unknown as Record<string, unknown>).authProfile = profileName\n }\n\n saveLlmConfig(aiDataDir, storeForSave)\n return { success: true }\n}\n\nexport function deleteLlmConfig(aiDataDir: string, id: string): { success: boolean; error?: string } {\n const storeForSave = loadRawConfigStore(aiDataDir)\n const index = storeForSave.configs.findIndex((c) => c.id === id)\n\n if (index === -1) {\n return { success: false, error: 'Config not found' }\n }\n\n storeForSave.configs.splice(index, 1)\n\n const fallback = storeForSave.configs[0]\n if (storeForSave.defaultAssistant?.configId === id) {\n storeForSave.defaultAssistant = fallback ? { configId: fallback.id, modelId: fallback.model || '' } : null\n }\n if (storeForSave.fastModel?.configId === id) {\n storeForSave.fastModel = fallback ? { configId: fallback.id, modelId: fallback.model || '' } : null\n }\n\n saveLlmConfig(aiDataDir, storeForSave)\n return { success: true }\n}\n\nexport function setDefaultAssistantSlot(\n aiDataDir: string,\n configId: string,\n modelId: string\n): { success: boolean; error?: string } {\n const storeForSave = loadRawConfigStore(aiDataDir)\n const config = storeForSave.configs.find((c) => c.id === configId)\n\n if (!config) {\n return { success: false, error: 'Config not found' }\n }\n\n storeForSave.defaultAssistant = { configId, modelId }\n saveLlmConfig(aiDataDir, storeForSave)\n return { success: true }\n}\n\nexport function setFastModelSlot(aiDataDir: string, slot: ModelSlot | null): { success: boolean; error?: string } {\n const storeForSave = loadRawConfigStore(aiDataDir)\n\n if (slot !== null) {\n const config = storeForSave.configs.find((c) => c.id === slot.configId)\n if (!config) {\n return { success: false, error: 'Config not found' }\n }\n }\n\n storeForSave.fastModel = slot\n saveLlmConfig(aiDataDir, storeForSave)\n return { success: true }\n}\n\nfunction loadRawConfigStore(aiDataDir: string): AIConfigStore {\n const configPath = path.join(aiDataDir, 'llm-config.json')\n if (!fs.existsSync(configPath)) {\n return { configs: [], defaultAssistant: null, fastModel: null }\n }\n try {\n const data = JSON.parse(fs.readFileSync(configPath, 'utf-8'))\n return {\n configs: data.configs || [],\n defaultAssistant: data.defaultAssistant ?? null,\n fastModel: data.fastModel ?? null,\n }\n } catch {\n return { configs: [], defaultAssistant: null, fastModel: null }\n }\n}\n\n// ==================== PiModel Builder ====================\n\nexport function buildPiModel(config: AIServiceConfig): PiModel<PiApi> {\n return buildPiModelCore(config as PiModelConfig)\n}\n","/**\n * 助手配置加载器\n *\n * 从 ~/.chatlab/ai/assistants/*.md 加载助手系统提示词。\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { parseAssistantFile } from '@openchatlab/node-runtime'\nimport type { AssistantConfig } from '@openchatlab/node-runtime'\n\nexport function loadAssistantConfig(aiDataDir: string, assistantId: string): AssistantConfig | null {\n const filePath = path.join(aiDataDir, 'assistants', `${assistantId}.md`)\n if (!fs.existsSync(filePath)) return null\n\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n return parseAssistantFile(content, filePath)\n } catch {\n return null\n }\n}\n","/**\n * 服务端 Agent\n *\n * 使用 @openchatlab/node-runtime 的 runAgentCore 编排对话流程,\n * 通过 AgentEventHandler 输出与 Electron 端一致的流式事件。\n */\n\nimport {\n DEFAULT_MAX_TOOL_ROUNDS,\n buildPlanGuidance,\n createAnalysisPlanner,\n createLlmRouteDecider,\n createPlanContentBlock,\n decideRequestRoute,\n runAgentCore,\n checkAndCompress,\n buildSystemPrompt,\n createAiTranslate,\n createCompressionLlmAdapter,\n AgentEventHandler,\n formatAIError,\n shouldUseChartCapabilityForMessage,\n getChartPlannerCapabilityForMessage,\n initTokenizer,\n type AgentStreamChunk,\n type PiMessage,\n type SimpleHistoryMessage,\n type AIChatManager,\n type CompressionConfig,\n type AgentTool,\n type DataSnapshot,\n type OwnerInfo,\n type MentionedMember,\n} from '@openchatlab/node-runtime'\nimport type { ChartAutoMode } from '@openchatlab/shared-types'\n\nimport { getDefaultAssistantConfig, buildPiModel } from './llm-config'\nimport { getServerAiLogger } from './logger'\n\nexport type { AgentStreamChunk }\n\nexport interface RunAgentOptions {\n userMessage: string\n aiChatId: string\n historyLeafMessageId?: string | null\n chatType?: 'group' | 'private'\n locale?: string\n assistantSystemPrompt?: string\n skillMenu?: string | null\n skillDef?: { name: string; prompt: string }\n compressionConfig?: CompressionConfig\n tools?: AgentTool[]\n aiDataDir: string\n aiChatManager: AIChatManager\n onEvent: (event: AgentStreamChunk) => void\n abortSignal?: AbortSignal\n ownerInfo?: OwnerInfo\n mentionedMembers?: MentionedMember[]\n dataSnapshot?: DataSnapshot\n thinkingLevel?: string\n chartAutoMode?: ChartAutoMode\n}\n\nexport async function runServerAgent(options: RunAgentOptions): Promise<void> {\n const {\n userMessage,\n aiChatId,\n historyLeafMessageId,\n chatType = 'group',\n locale = 'zh-CN',\n assistantSystemPrompt,\n skillMenu,\n skillDef,\n compressionConfig,\n tools = [],\n aiDataDir,\n aiChatManager,\n onEvent,\n abortSignal,\n ownerInfo,\n mentionedMembers,\n dataSnapshot,\n thinkingLevel,\n chartAutoMode = 'suggest',\n } = options\n\n const aiLogger = getServerAiLogger()\n\n // 确保 tokenizer rank 表已加载(compression + agent 路径均依赖)\n await initTokenizer()\n\n const llmConfig = getDefaultAssistantConfig(aiDataDir)\n if (!llmConfig) {\n onEvent({ type: 'error', error: { name: 'ConfigError', message: 'LLM service not configured' } })\n onEvent({ type: 'done', isFinished: true })\n return\n }\n\n const piModel = buildPiModel(llmConfig)\n const t = createAiTranslate(locale)\n\n let skillCtx: { skillDef?: { name: string; prompt: string }; skillMenu?: string } | undefined\n if (skillDef) {\n skillCtx = { skillDef }\n } else if (skillMenu) {\n skillCtx = { skillMenu }\n }\n\n const systemPrompt = buildSystemPrompt({\n t,\n chatType,\n assistantSystemPrompt,\n ownerInfo,\n locale,\n skillCtx,\n mentionedMembers,\n dataSnapshot,\n })\n\n const handler = new AgentEventHandler({\n onChunk: onEvent,\n context: {},\n systemPrompt,\n })\n\n if (compressionConfig?.enabled && historyLeafMessageId === undefined) {\n const llmAdapter = createCompressionLlmAdapter({\n piModel,\n apiKey: llmConfig.apiKey,\n onCompressing: () => handler.emitStatus('compressing', []),\n })\n const compressionResult = await checkAndCompress(\n aiChatId,\n compressionConfig,\n systemPrompt,\n llmAdapter,\n aiChatManager,\n aiLogger ?? undefined\n )\n if (compressionResult.compressed) {\n onEvent({\n type: 'compression_done',\n compressionResult: {\n summaryContent: compressionResult.summaryContent ?? '',\n tokensBefore: compressionResult.tokensBefore ?? 0,\n tokensAfter: compressionResult.tokensAfter ?? 0,\n timestamp: Date.now(),\n },\n })\n }\n } else if (compressionConfig?.enabled && historyLeafMessageId !== undefined) {\n aiLogger?.info?.('Compression', 'Skipping compression for edited branch request', {\n aiChatId,\n historyLeafMessageId,\n })\n }\n\n if (abortSignal?.aborted) {\n handler.emitStatus('aborted', [], { force: true })\n onEvent({ type: 'done', isFinished: true, usage: handler.cloneUsage() })\n return\n }\n\n let history: SimpleHistoryMessage[] = []\n try {\n history = aiChatManager.getHistoryForAgent(aiChatId, undefined, historyLeafMessageId)\n } catch {\n // empty history on failure\n }\n\n handler.emitStatus('preparing', [], { pendingUserMessage: userMessage, force: true })\n\n const steerMessage = t('ai.agent.answerWithoutTools')\n let cachedMessages: PiMessage[] = []\n const effectiveTools =\n chartAutoMode === 'explicit' && !shouldUseChartCapabilityForMessage(userMessage)\n ? tools.filter((tool) => tool.name !== 'render_chart')\n : tools\n\n try {\n const routeInput = {\n userMessage,\n chatType,\n locale,\n dataSnapshot,\n availableTools: effectiveTools.map((tool) => tool.name),\n availableCapabilities: [\n getChartPlannerCapabilityForMessage({\n userMessage,\n locale,\n availableTools: effectiveTools.map((tool) => tool.name),\n chartAutoMode,\n }),\n ].filter((capability) => capability !== null),\n skillSummary: skillDef?.name ?? (skillMenu ? 'auto_skill_menu' : undefined),\n }\n const routeStartedAt = Date.now()\n const routeDecision = await decideRequestRoute(routeInput, {\n llmRouter: createLlmRouteDecider({\n piModel,\n apiKey: llmConfig.apiKey,\n abortSignal,\n }),\n })\n aiLogger?.info('Router', 'Shadow route decision', {\n ...routeDecision,\n elapsedMs: Date.now() - routeStartedAt,\n availableToolCount: tools.length,\n shadowOnly: true,\n })\n onEvent({ type: 'route', routeDecision })\n\n let effectiveSystemPrompt = systemPrompt\n if (routeDecision.route === 'planned_execution') {\n const planStartedAt = Date.now()\n const planner = createAnalysisPlanner({\n piModel,\n apiKey: llmConfig.apiKey,\n onPlanDelta: (delta) => onEvent({ type: 'plan_delta', planDelta: delta }),\n onThinkingDelta: (delta) => onEvent({ type: 'think', content: delta, thinkTag: 'thinking' }),\n onThinkingEnd: (durationMs) =>\n onEvent({ type: 'think', content: '', thinkTag: 'thinking', thinkDurationMs: durationMs }),\n onValidationDelta: (delta) => onEvent({ type: 'think', content: delta, thinkTag: 'plan_validation' }),\n onValidationEnd: (durationMs) =>\n onEvent({ type: 'think', content: '', thinkTag: 'plan_validation', thinkDurationMs: durationMs }),\n })\n const plan = await planner(routeInput, abortSignal)\n if (plan) {\n const planBlock = createPlanContentBlock(plan)\n onEvent({ type: 'plan', plan: planBlock })\n effectiveSystemPrompt = `${systemPrompt}\\n\\n${buildPlanGuidance(plan)}`\n aiLogger?.info('Planner', 'Plan generated', {\n title: plan.title,\n steps: plan.steps.length,\n successCriteria: plan.successCriteria.length,\n elapsedMs: Date.now() - planStartedAt,\n })\n } else {\n aiLogger?.warn('Planner', 'Plan generation skipped or failed', {\n elapsedMs: Date.now() - planStartedAt,\n route: routeDecision.route,\n })\n onEvent({ type: 'plan_skipped' })\n }\n }\n\n const result = await runAgentCore({\n piModel,\n apiKey: llmConfig.apiKey,\n systemPrompt: effectiveSystemPrompt,\n tools: effectiveTools,\n history,\n userMessage,\n maxToolRounds: DEFAULT_MAX_TOOL_ROUNDS,\n abortSignal,\n steerMessage,\n thinkingLevel: thinkingLevel as import('@openchatlab/core').ThinkingLevel | undefined,\n onConvertToLlm: (filteredMessages) => {\n cachedMessages = filteredMessages as PiMessage[]\n },\n onEvent: (coreEvent) => handler.handleCoreEvent(coreEvent, cachedMessages),\n onDebugContext: (messages) => {\n try {\n aiChatManager.setPendingDebugContext(aiChatId, JSON.stringify(messages, null, 2))\n } catch {\n // silent\n }\n },\n })\n\n if (result.error) {\n const friendlyMessage = formatAIError(result.error)\n onEvent({ type: 'error', error: { name: 'AgentError', message: friendlyMessage } })\n }\n\n handler.emitStatus('completed', cachedMessages, { force: true })\n onEvent({ type: 'done', isFinished: true, usage: result.usage })\n } catch (error) {\n const friendlyMessage = formatAIError(error)\n aiLogger?.error('ServerAgent', 'Agent execution error', { error: String(error) })\n handler.emitStatus('error', cachedMessages, { force: true })\n onEvent({ type: 'error', error: { name: 'AgentError', message: friendlyMessage } })\n onEvent({ type: 'done', isFinished: true, usage: handler.cloneUsage() })\n }\n}\n","/**\n * CLI Agent stream runner — provides runAgentStream implementation\n * for the shared HTTP route context.\n */\n\nimport type { DatabaseManager, AIChatManager, AgentStreamChunk } from '@openchatlab/node-runtime'\nimport {\n CHART_CAPABILITY_CORE_TOOLS,\n SkillManager,\n buildSkillMenuWithBuiltinChart,\n createActivateSkillTool,\n createDataSnapshotFromOverview,\n getAllowedBuiltinToolsForChartAutoSkill,\n getChartCapabilitySkill,\n getSkillConfigWithBuiltinChart,\n resolveChartRuntimeForRequest,\n} from '@openchatlab/node-runtime'\nimport { getChatOverview, normalizeBuiltinToolNames } from '@openchatlab/core'\nimport type { ChartAutoMode } from '@openchatlab/shared-types'\nimport type { DataSnapshot } from '@openchatlab/node-runtime'\nimport type { AgentStreamRequest } from '@openchatlab/http-routes'\nimport { AGENT_TOOL_REGISTRY } from '@openchatlab/tools'\nimport { adaptToolsForAgent } from './tool-adapter'\nimport { getDefaultAssistantConfig, buildPiModel } from './llm-config'\nimport { loadAssistantConfig } from './assistant-loader'\nimport { runServerAgent } from './agent'\n\nfunction getAiDir(dbManager: DatabaseManager): string {\n const pathProvider = (dbManager as any)['pathProvider']\n if (!pathProvider) {\n throw Object.assign(new Error('PathProvider not available'), { statusCode: 500 })\n }\n return pathProvider.getAiDataDir()\n}\n\nconst RAW_SQL_TOOL_NAMES = new Set(['execute_sql'])\n\nexport function getAvailableToolDefs(isChartCapability: boolean, allowedToolSet: Set<string> | null) {\n if (isChartCapability) {\n const chartCoreTools = new Set<string>(CHART_CAPABILITY_CORE_TOOLS)\n return AGENT_TOOL_REGISTRY.filter(\n (tool) =>\n chartCoreTools.has(tool.name) ||\n (tool.category === 'analysis' && allowedToolSet?.has(tool.name) && !RAW_SQL_TOOL_NAMES.has(tool.name))\n )\n }\n\n return allowedToolSet\n ? AGENT_TOOL_REGISTRY.filter((tool) => tool.category !== 'analysis' || allowedToolSet.has(tool.name))\n : AGENT_TOOL_REGISTRY\n}\n\n// 区分“未配置白名单”和“配置为空白名单”:前者无限制,后者禁用 analysis 工具。\nexport function getAllowedToolSet(\n isChartCapability: boolean,\n allowedBuiltinTools?: readonly string[]\n): Set<string> | null {\n if (isChartCapability) {\n return new Set(normalizeBuiltinToolNames(allowedBuiltinTools ?? []))\n }\n return allowedBuiltinTools === undefined ? null : new Set(normalizeBuiltinToolNames(allowedBuiltinTools))\n}\n\nexport function createCliRunAgentStream(\n dbManager: DatabaseManager,\n aiChatManager: AIChatManager\n): (params: AgentStreamRequest, onEvent: (chunk: AgentStreamChunk) => void, abortSignal: AbortSignal) => Promise<void> {\n return async (params, onEvent, abortSignal) => {\n const {\n userMessage,\n aiChatId,\n historyLeafMessageId,\n sessionId,\n chatType,\n locale,\n assistantId,\n skillId,\n enableAutoSkill,\n chartAutoMode,\n compressionConfig,\n ownerInfo,\n mentionedMembers,\n thinkingLevel,\n } = params\n\n const aiDataDir = getAiDir(dbManager)\n\n let assistantSystemPrompt: string | undefined\n let assistantAllowedTools: string[] | undefined\n if (assistantId) {\n const assistantConfig = loadAssistantConfig(aiDataDir, assistantId)\n if (assistantConfig?.systemPrompt) {\n assistantSystemPrompt = assistantConfig.systemPrompt\n }\n assistantAllowedTools = assistantConfig?.allowedBuiltinTools\n }\n\n const llmConfig = getDefaultAssistantConfig(aiDataDir)\n const maxToolResultPercent = compressionConfig?.maxToolResultPercent ?? 50\n const contextWindow = llmConfig ? (buildPiModel(llmConfig).contextWindow ?? 128000) : 128000\n const maxToolResultTokens = Math.floor(contextWindow * (maxToolResultPercent / 100))\n\n const db = (dbManager as any).open?.(sessionId)\n const resolvedChartAutoMode: ChartAutoMode = chartAutoMode ?? 'suggest'\n const chartRuntime = resolveChartRuntimeForRequest({\n skillId,\n userMessage,\n locale,\n assistantAllowedTools,\n enableAutoDetection: enableAutoSkill === true,\n chartAutoMode: resolvedChartAutoMode,\n })\n const isChartCapability = chartRuntime.isChartCapability\n const autoSkillAllowedTools =\n !skillId && enableAutoSkill && resolvedChartAutoMode !== 'explicit'\n ? getAllowedBuiltinToolsForChartAutoSkill(assistantAllowedTools)\n : assistantAllowedTools\n const allowedToolSet = getAllowedToolSet(\n isChartCapability,\n isChartCapability ? chartRuntime.allowedBuiltinTools : autoSkillAllowedTools\n )\n const availableToolDefs = getAvailableToolDefs(isChartCapability, allowedToolSet)\n\n const agentTools = db\n ? adaptToolsForAgent(availableToolDefs, () => ({ db, sessionId, locale }), { maxToolResultTokens })\n : []\n\n const skillMgr = new SkillManager(aiDataDir)\n skillMgr.init()\n const toolNames = agentTools.map((t: { name: string }) => t.name)\n\n let resolvedSkillDef: { name: string; prompt: string } | undefined\n let resolvedSkillMenu: string | undefined\n if (isChartCapability) {\n const def = chartRuntime.skillDef ?? getChartCapabilitySkill(locale ?? 'zh-CN')\n resolvedSkillDef = { name: def.name, prompt: def.prompt }\n } else if (skillId) {\n const def = skillMgr.getSkillConfig(skillId)\n if (def) resolvedSkillDef = { name: def.name, prompt: def.prompt }\n } else if (enableAutoSkill) {\n const baseMenu = skillMgr.getSkillMenu(chatType ?? 'group', toolNames)\n const menu =\n resolvedChartAutoMode === 'aggressive' ? buildSkillMenuWithBuiltinChart(baseMenu, locale, toolNames) : baseMenu\n if (menu) resolvedSkillMenu = menu\n }\n\n if (resolvedSkillMenu) {\n const activateSkillTool = createActivateSkillTool({\n chatType: chatType ?? 'group',\n allowedTools: toolNames,\n coreToolNames: new Set<string>(CHART_CAPABILITY_CORE_TOOLS),\n locale,\n getSkillConfig: (id) =>\n resolvedChartAutoMode === 'aggressive'\n ? getSkillConfigWithBuiltinChart(id, locale, (skillConfigId) => skillMgr.getSkillConfig(skillConfigId))\n : skillMgr.getSkillConfig(id),\n })\n agentTools.push(activateSkillTool as any)\n }\n\n const resolvedCompression = compressionConfig?.enabled\n ? {\n enabled: true as const,\n tokenThresholdPercent: compressionConfig.tokenThresholdPercent ?? 75,\n bufferSizePercent: compressionConfig.bufferSizePercent ?? 20,\n maxToolResultPercent: compressionConfig.maxToolResultPercent,\n }\n : undefined\n\n let dataSnapshot: DataSnapshot | undefined\n if (db) {\n try {\n dataSnapshot = createDataSnapshotFromOverview(getChatOverview(db, 10))\n } catch {\n // non-fatal\n }\n }\n\n await runServerAgent({\n userMessage,\n aiChatId,\n historyLeafMessageId,\n chatType,\n locale,\n assistantSystemPrompt,\n skillMenu: resolvedSkillMenu,\n skillDef: resolvedSkillDef,\n compressionConfig: resolvedCompression,\n tools: agentTools,\n aiDataDir,\n aiChatManager,\n onEvent,\n abortSignal,\n ownerInfo,\n mentionedMembers,\n dataSnapshot,\n thinkingLevel,\n chartAutoMode: resolvedChartAutoMode,\n })\n }\n}\n"],"mappings":"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","names":["options","buildPiModel","fs","path","buildPiModel","buildPiModel"]}
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
getWeekdayActivity,
|
|
28
28
|
searchMessagesLike,
|
|
29
29
|
searchSegments
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-QY67NN65.mjs";
|
|
31
31
|
|
|
32
32
|
// ../../packages/tools/src/definitions/sql-query.ts
|
|
33
33
|
var inputSchema = {
|
|
@@ -81,17 +81,23 @@ var schemaTool = {
|
|
|
81
81
|
|
|
82
82
|
// ../../packages/tools/src/definitions/render-chart.ts
|
|
83
83
|
var DEFAULT_MAX_ROWS = 1e3;
|
|
84
|
+
var DEFAULT_PREVIEW_ROWS = 20;
|
|
84
85
|
var RE_SECONDS_TIMESTAMP_DIVIDED_AS_MILLISECONDS = /\b(?:\w+\.)?ts\s*\/\s*1000\b/i;
|
|
85
86
|
var inputSchema2 = {
|
|
86
87
|
type: "object",
|
|
87
88
|
properties: {
|
|
89
|
+
rows: {
|
|
90
|
+
type: "array",
|
|
91
|
+
description: "Pre-fetched data rows to render directly, skipping SQL execution. Pass the data array from a high-level tool result (e.g. the `data` field from get_time_stats, or member activity rows). Prefer this over sql when data is already available. Mutually exclusive with sql.",
|
|
92
|
+
items: { type: "object" }
|
|
93
|
+
},
|
|
88
94
|
sql: {
|
|
89
95
|
type: "string",
|
|
90
|
-
description: "Read-only SELECT or WITH SELECT SQL used to produce chart rows."
|
|
96
|
+
description: "Read-only SELECT or WITH SELECT SQL used to produce chart rows. Use only when pre-fetched data is unavailable or custom aggregation is required."
|
|
91
97
|
},
|
|
92
98
|
params: {
|
|
93
99
|
type: "object",
|
|
94
|
-
description: "Named SQL parameters.
|
|
100
|
+
description: "Named SQL parameters. Only used when sql is provided.",
|
|
95
101
|
additionalProperties: true,
|
|
96
102
|
default: {}
|
|
97
103
|
},
|
|
@@ -108,7 +114,7 @@ var inputSchema2 = {
|
|
|
108
114
|
maximum: 5e3
|
|
109
115
|
}
|
|
110
116
|
},
|
|
111
|
-
required: ["
|
|
117
|
+
required: ["chartSpec"]
|
|
112
118
|
};
|
|
113
119
|
function normalizeSql(sql, maxRows) {
|
|
114
120
|
if (typeof sql !== "string" || sql.trim().length === 0) {
|
|
@@ -133,6 +139,14 @@ function normalizeParams(raw) {
|
|
|
133
139
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
134
140
|
return raw;
|
|
135
141
|
}
|
|
142
|
+
function normalizeRows(raw, maxRows) {
|
|
143
|
+
if (!Array.isArray(raw)) throw new Error("rows must be an array");
|
|
144
|
+
const items = raw.filter(
|
|
145
|
+
(item) => typeof item === "object" && item !== null && !Array.isArray(item)
|
|
146
|
+
);
|
|
147
|
+
const truncated = items.length > maxRows;
|
|
148
|
+
return { rows: truncated ? items.slice(0, maxRows) : items, truncated };
|
|
149
|
+
}
|
|
136
150
|
function normalizeMaxRows(raw) {
|
|
137
151
|
const value = typeof raw === "number" ? raw : DEFAULT_MAX_ROWS;
|
|
138
152
|
if (!Number.isFinite(value)) return DEFAULT_MAX_ROWS;
|
|
@@ -144,17 +158,40 @@ function summarizeChart(type, title, rowCount, truncated, locale) {
|
|
|
144
158
|
}
|
|
145
159
|
return `Generated chart "${title}" (${type}, ${rowCount} rows${truncated ? ", truncated" : ""}).`;
|
|
146
160
|
}
|
|
161
|
+
function summarizeChartForModel(type, title, rows, truncated, locale) {
|
|
162
|
+
const summary = summarizeChart(type, title, rows.length, truncated, locale);
|
|
163
|
+
const previewRows = rows.slice(0, DEFAULT_PREVIEW_ROWS);
|
|
164
|
+
if (previewRows.length === 0) return summary;
|
|
165
|
+
const preview = JSON.stringify(previewRows);
|
|
166
|
+
if (locale?.startsWith("zh")) {
|
|
167
|
+
return `${summary}
|
|
168
|
+
\u6570\u636E\u9884\u89C8\uFF08\u524D ${previewRows.length} \u884C\uFF0C\u7528\u4E8E\u5206\u6790\u5CF0\u503C\u3001\u4F4E\u8C37\u548C\u5DEE\u5F02\uFF09\uFF1A${preview}`;
|
|
169
|
+
}
|
|
170
|
+
return `${summary}
|
|
171
|
+
Data preview (first ${previewRows.length} rows; use this to identify peaks, lows, and differences): ${preview}`;
|
|
172
|
+
}
|
|
147
173
|
async function handler2(params, context) {
|
|
148
174
|
if (!context.dataProvider) throw new Error("render_chart requires a data provider");
|
|
175
|
+
const hasSql = typeof params.sql === "string" && params.sql.trim().length > 0;
|
|
176
|
+
const hasRows = Array.isArray(params.rows);
|
|
177
|
+
if (!hasSql && !hasRows) throw new Error("render_chart requires either sql or rows");
|
|
149
178
|
const maxRows = normalizeMaxRows(params.maxRows);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
179
|
+
let rows;
|
|
180
|
+
let truncated;
|
|
181
|
+
if (hasRows) {
|
|
182
|
+
const normalized = normalizeRows(params.rows, maxRows);
|
|
183
|
+
rows = normalized.rows;
|
|
184
|
+
truncated = normalized.truncated;
|
|
185
|
+
} else {
|
|
186
|
+
const sql = normalizeSql(params.sql, maxRows);
|
|
187
|
+
const sqlParams = normalizeParams(params.params);
|
|
188
|
+
const fetchedRows = await context.dataProvider.executeParameterizedSql(sql, sqlParams);
|
|
189
|
+
truncated = fetchedRows.length > maxRows;
|
|
190
|
+
rows = truncated ? fetchedRows.slice(0, maxRows) : fetchedRows;
|
|
191
|
+
}
|
|
155
192
|
const chart = buildChartPayload(rows, params.chartSpec, { truncated });
|
|
156
193
|
return {
|
|
157
|
-
content:
|
|
194
|
+
content: summarizeChartForModel(chart.spec.type, chart.spec.title, rows, truncated, context.locale),
|
|
158
195
|
data: {
|
|
159
196
|
rowCount: rows.length,
|
|
160
197
|
truncated,
|
|
@@ -166,7 +203,7 @@ async function handler2(params, context) {
|
|
|
166
203
|
}
|
|
167
204
|
var renderChartTool = {
|
|
168
205
|
name: "render_chart",
|
|
169
|
-
description: "Generate a native ChatLab chart from read-only
|
|
206
|
+
description: "Generate a native ChatLab chart from ChartSpec v1. Provide either `rows` (pre-fetched data array from a tool result such as get_time_stats or member_stats) or `sql` (read-only SELECT). Prefer `rows` when data is already available \u2014 pass the tool's data array directly. Use `sql` only for custom aggregations not covered by existing tools. Never output HTML, JavaScript, SVG, ECharts options, or rendering code.",
|
|
170
207
|
inputSchema: inputSchema2,
|
|
171
208
|
handler: handler2,
|
|
172
209
|
category: "analysis"
|
|
@@ -1437,4 +1474,4 @@ export {
|
|
|
1437
1474
|
MCP_TOOL_REGISTRY,
|
|
1438
1475
|
CoreDataProvider
|
|
1439
1476
|
};
|
|
1440
|
-
//# sourceMappingURL=chunk-
|
|
1477
|
+
//# sourceMappingURL=chunk-IRZRIHIT.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../packages/tools/src/definitions/sql-query.ts","../../../packages/tools/src/definitions/render-chart.ts","../../../packages/tools/src/definitions/session-info.ts","../../../packages/tools/src/definitions/sessions.ts","../../../packages/tools/src/utils/format.ts","../../../packages/tools/src/definitions/chat-overview.ts","../../../packages/tools/src/utils/time-params.ts","../../../packages/tools/src/utils/schemas.ts","../../../packages/tools/src/definitions/search-messages.ts","../../../packages/tools/src/definitions/deep-search-messages.ts","../../../packages/tools/src/definitions/get-message-context.ts","../../../packages/tools/src/definitions/search-segments.ts","../../../packages/tools/src/definitions/get-segment-messages.ts","../../../packages/tools/src/definitions/get-members.ts","../../../packages/tools/src/definitions/member-stats.ts","../../../packages/tools/src/definitions/time-stats.ts","../../../packages/tools/src/definitions/recent-messages.ts","../../../packages/tools/src/definitions/get-member-name-history.ts","../../../packages/tools/src/definitions/get-conversation-between.ts","../../../packages/tools/src/definitions/get-segment-summaries.ts","../../../packages/tools/src/definitions/response-time-analysis.ts","../../../packages/tools/src/definitions/keyword-frequency.ts","../../../packages/tools/src/sql/definitions.ts","../../../packages/tools/src/sql/executor.ts","../../../packages/tools/src/registry.ts","../../../packages/tools/src/providers/core-data-provider.ts"],"sourcesContent":["/**\n * SQL 查询工具\n *\n * 对聊天数据库执行只读 SQL 查询。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n sql: {\n type: 'string',\n description: '要执行的 SELECT SQL 查询语句',\n },\n },\n required: ['sql'],\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const sql = params.sql as string\n\n try {\n const result = await context.dataProvider!.executeSql(sql)\n return {\n content: JSON.stringify(result),\n data: result,\n }\n } catch (err) {\n return {\n content: JSON.stringify({ error: err instanceof Error ? err.message : String(err) }),\n }\n }\n}\n\nconst schemaInputSchema: JsonSchema = {\n type: 'object',\n properties: {},\n}\n\nasync function schemaHandler(_params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const schema = await context.dataProvider!.getSchema()\n return {\n content: JSON.stringify({ tables: schema }),\n data: schema,\n }\n}\n\nexport const sqlQueryTool: ToolDefinition = {\n name: 'execute_sql',\n description: '对聊天数据库执行只读 SELECT 查询。使用前可先调用 get_schema 查看表结构。',\n inputSchema,\n handler,\n category: 'analysis',\n}\n\nexport const schemaTool: ToolDefinition = {\n name: 'get_schema',\n description: '查看聊天数据库的表结构(所有表的 CREATE TABLE 语句)',\n inputSchema: schemaInputSchema,\n handler: schemaHandler,\n}\n","/**\n * Dynamic chart rendering tool.\n *\n * The model provides read-only SQL plus a ChartSpec. ChatLab executes and\n * validates the result before producing a chart payload for the chat UI.\n */\n\nimport { buildChartPayload } from '@openchatlab/core'\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\n\nconst DEFAULT_MAX_ROWS = 1000\nconst DEFAULT_PREVIEW_ROWS = 20\nconst RE_SECONDS_TIMESTAMP_DIVIDED_AS_MILLISECONDS = /\\b(?:\\w+\\.)?ts\\s*\\/\\s*1000\\b/i\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n rows: {\n type: 'array',\n description:\n 'Pre-fetched data rows to render directly, skipping SQL execution. Pass the data array from a high-level tool result (e.g. the `data` field from get_time_stats, or member activity rows). Prefer this over sql when data is already available. Mutually exclusive with sql.',\n items: { type: 'object' },\n },\n sql: {\n type: 'string',\n description:\n 'Read-only SELECT or WITH SELECT SQL used to produce chart rows. Use only when pre-fetched data is unavailable or custom aggregation is required.',\n },\n params: {\n type: 'object',\n description: 'Named SQL parameters. Only used when sql is provided.',\n additionalProperties: true,\n default: {},\n },\n chartSpec: {\n type: 'object',\n description:\n 'ChartSpec v1. Required fields: version, type, title, encoding. Supported types: bar, line, pie, heatmap.',\n additionalProperties: true,\n },\n maxRows: {\n type: 'number',\n description: 'Maximum rows to fetch before chart normalization.',\n default: DEFAULT_MAX_ROWS,\n minimum: 1,\n maximum: 5000,\n },\n },\n required: ['chartSpec'],\n}\n\nfunction normalizeSql(sql: unknown, maxRows: number): string {\n if (typeof sql !== 'string' || sql.trim().length === 0) {\n throw new Error('sql must be a non-empty string')\n }\n\n const trimmed = sql.trim().replace(/;+\\s*$/, '')\n if (trimmed.includes(';')) {\n throw new Error('Only a single read-only SQL statement is allowed')\n }\n\n const statementStart = trimmed.replace(/^(\\s|--[^\\n]*(\\n|$)|\\/\\*[\\s\\S]*?\\*\\/)*/, '')\n if (!/^(SELECT|WITH)\\b/i.test(statementStart)) {\n throw new Error('render_chart only accepts SELECT or WITH SELECT SQL')\n }\n\n if (RE_SECONDS_TIMESTAMP_DIVIDED_AS_MILLISECONDS.test(trimmed)) {\n throw new Error('message.ts is already a Unix timestamp in seconds; do not divide ts by 1000')\n }\n\n return `SELECT * FROM (\\n${trimmed}\\n) AS chart_query LIMIT ${maxRows + 1}`\n}\n\nfunction normalizeParams(raw: unknown): Record<string, unknown> {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {}\n return raw as Record<string, unknown>\n}\n\nfunction normalizeRows(raw: unknown, maxRows: number): { rows: Record<string, unknown>[]; truncated: boolean } {\n if (!Array.isArray(raw)) throw new Error('rows must be an array')\n const items = raw.filter(\n (item): item is Record<string, unknown> => typeof item === 'object' && item !== null && !Array.isArray(item)\n )\n const truncated = items.length > maxRows\n return { rows: truncated ? items.slice(0, maxRows) : items, truncated }\n}\n\nfunction normalizeMaxRows(raw: unknown): number {\n const value = typeof raw === 'number' ? raw : DEFAULT_MAX_ROWS\n if (!Number.isFinite(value)) return DEFAULT_MAX_ROWS\n return Math.min(5000, Math.max(1, Math.floor(value)))\n}\n\nfunction summarizeChart(type: string, title: string, rowCount: number, truncated: boolean, locale?: string): string {\n if (locale?.startsWith('zh')) {\n return `已生成图表「${title}」(${type},${rowCount} 行数据${truncated ? ',已截断' : ''})。`\n }\n return `Generated chart \"${title}\" (${type}, ${rowCount} rows${truncated ? ', truncated' : ''}).`\n}\n\nfunction summarizeChartForModel(\n type: string,\n title: string,\n rows: Record<string, unknown>[],\n truncated: boolean,\n locale?: string\n): string {\n const summary = summarizeChart(type, title, rows.length, truncated, locale)\n const previewRows = rows.slice(0, DEFAULT_PREVIEW_ROWS)\n if (previewRows.length === 0) return summary\n\n const preview = JSON.stringify(previewRows)\n if (locale?.startsWith('zh')) {\n return `${summary}\\n数据预览(前 ${previewRows.length} 行,用于分析峰值、低谷和差异):${preview}`\n }\n return `${summary}\\nData preview (first ${previewRows.length} rows; use this to identify peaks, lows, and differences): ${preview}`\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n if (!context.dataProvider) throw new Error('render_chart requires a data provider')\n\n const hasSql = typeof params.sql === 'string' && params.sql.trim().length > 0\n const hasRows = Array.isArray(params.rows)\n if (!hasSql && !hasRows) throw new Error('render_chart requires either sql or rows')\n\n const maxRows = normalizeMaxRows(params.maxRows)\n let rows: Record<string, unknown>[]\n let truncated: boolean\n\n if (hasRows) {\n const normalized = normalizeRows(params.rows, maxRows)\n rows = normalized.rows\n truncated = normalized.truncated\n } else {\n const sql = normalizeSql(params.sql, maxRows)\n const sqlParams = normalizeParams(params.params)\n const fetchedRows = await context.dataProvider.executeParameterizedSql<Record<string, unknown>>(sql, sqlParams)\n truncated = fetchedRows.length > maxRows\n rows = truncated ? fetchedRows.slice(0, maxRows) : fetchedRows\n }\n\n const chart = buildChartPayload(rows, params.chartSpec, { truncated })\n\n return {\n content: summarizeChartForModel(chart.spec.type, chart.spec.title, rows, truncated, context.locale),\n data: {\n rowCount: rows.length,\n truncated,\n chartType: chart.spec.type,\n title: chart.spec.title,\n },\n chart,\n }\n}\n\nexport const renderChartTool: ToolDefinition = {\n name: 'render_chart',\n description:\n \"Generate a native ChatLab chart from ChartSpec v1. Provide either `rows` (pre-fetched data array from a tool result such as get_time_stats or member_stats) or `sql` (read-only SELECT). Prefer `rows` when data is already available — pass the tool's data array directly. Use `sql` only for custom aggregations not covered by existing tools. Never output HTML, JavaScript, SVG, ECharts options, or rendering code.\",\n inputSchema,\n handler,\n category: 'analysis',\n}\n","/**\n * 会话信息工具\n *\n * 获取指定会话的详细信息(概览统计)。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { getSessionMeta, getSessionOverview, getMembers } from '@openchatlab/core'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n include_members: {\n type: 'boolean',\n description: '是否包含成员列表(默认 false)',\n default: false,\n },\n },\n}\n\nfunction handler(params: Record<string, unknown>, context: ToolExecutionContext): ToolResult {\n const db = context.db!\n const meta = getSessionMeta(db)\n const overview = getSessionOverview(db)\n const includeMembers = params.include_members as boolean\n\n const data: Record<string, unknown> = { ...meta, ...overview }\n\n if (includeMembers) {\n data.members = getMembers(db)\n }\n\n return {\n content: JSON.stringify(data),\n data,\n }\n}\n\nexport const sessionInfoTool: ToolDefinition = {\n name: 'get_session_info',\n description: '获取当前会话的详细信息,包括名称、平台、消息总数、成员数、时间范围等',\n inputSchema,\n handler,\n}\n","/**\n * 会话列表工具\n *\n * MCP 场景下列出所有可用的聊天会话。\n * 这是 MCP 场景下的完整聊天记录发现工具,不是 segment 搜索工具。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { getSessionMeta, getSessionOverview } from '@openchatlab/core'\nimport type { DatabaseAdapter } from '@openchatlab/core'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n keyword: {\n type: 'string',\n description: '按名称筛选会话(可选)',\n },\n },\n}\n\nexport interface SessionListContext extends ToolExecutionContext {\n listSessionIds: () => string[]\n openDb: (sessionId: string) => DatabaseAdapter | null\n}\n\nfunction handler(_params: Record<string, unknown>, context: SessionListContext): ToolResult {\n const keyword = (_params.keyword as string | undefined)?.toLowerCase()\n const sessionIds = context.listSessionIds()\n\n const sessions = sessionIds\n .map((id) => {\n const db = context.openDb(id)\n if (!db) return null\n const meta = getSessionMeta(db)\n if (!meta) return null\n if (keyword && !meta.name.toLowerCase().includes(keyword)) return null\n const overview = getSessionOverview(db)\n return { id, ...meta, ...overview }\n })\n .filter(Boolean)\n\n return {\n content: JSON.stringify({ total: sessions.length, sessions }),\n data: sessions,\n }\n}\n\nexport const sessionsListTool: ToolDefinition = {\n name: 'list_sessions',\n description: '列出所有可用的聊天会话,返回会话名称、平台、消息数等基本信息',\n inputSchema,\n handler: handler as ToolDefinition['handler'],\n}\n","/**\n * 工具结果格式化 & i18n 辅助(平台无关)\n *\n * 从 @openchatlab/node-runtime 复制核心子集,避免引入 node-runtime 重依赖。\n */\n\nexport function isChineseLocale(locale?: string): boolean {\n return locale?.startsWith('zh') ?? false\n}\n\nexport const i18nTexts = {\n allTime: { zh: '全部时间', en: 'All time' },\n noContent: { zh: '[无内容]', en: '[No content]' },\n memberNotFound: { zh: '未找到该成员', en: 'Member not found' },\n untilNow: { zh: '至今', en: 'Present' },\n noChangeRecord: { zh: '无变更记录', en: 'No change record' },\n noConversation: { zh: '未找到这两人之间的对话', en: 'No conversation found between these two members' },\n noMessageContext: { zh: '未找到指定的消息或上下文', en: 'Message or context not found' },\n messages: { zh: '条', en: '' },\n alias: { zh: '别名', en: 'Alias' },\n}\n\ntype TextEntryKey = keyof typeof i18nTexts\n\nexport function t(key: TextEntryKey, locale?: string): string {\n const text = i18nTexts[key]\n if (typeof text === 'object' && 'zh' in text && 'en' in text) {\n return isChineseLocale(locale) ? text.zh : text.en\n }\n return ''\n}\n\nexport function formatTimeRange(\n timeFilter?: { startTs: number; endTs: number },\n locale?: string\n): string | { start: string; end: string } {\n if (!timeFilter) return t('allTime', locale)\n const localeStr = isChineseLocale(locale) ? 'zh-CN' : 'en-US'\n return {\n start: new Date(timeFilter.startTs * 1000).toLocaleString(localeStr),\n end: new Date(timeFilter.endTs * 1000).toLocaleString(localeStr),\n }\n}\n\nexport function formatMessageCompact(\n msg: {\n id?: number\n senderName: string\n content: string | null\n timestamp: number\n },\n locale?: string\n): string {\n const localeStr = isChineseLocale(locale) ? 'zh-CN' : 'en-US'\n const time = new Date(msg.timestamp * 1000).toLocaleString(localeStr)\n let content = msg.content || t('noContent', locale)\n if (content.length > 200) {\n content = content.slice(0, 200) + '...'\n }\n return `${time} ${msg.senderName}: ${content}`\n}\n","/**\n * 聊天概览工具\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { isChineseLocale } from '../utils/format'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n top_n: { type: 'number', description: '返回活跃度最高的前 N 个成员,默认 10' },\n },\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale } = context\n const topN = (params.top_n as number) || 10\n\n const result = await context.dataProvider!.getChatOverview(topN)\n if (!result) {\n const msg = isChineseLocale(locale) ? '无法获取聊天概览' : 'Unable to get chat overview'\n return { content: msg, data: { error: msg } }\n }\n\n const msgSuffix = isChineseLocale(locale) ? '条' : ''\n const lines: string[] = [\n `name: ${result.name}`,\n `platform: ${result.platform}`,\n `type: ${result.type}`,\n `totalMessages: ${result.totalMessages}`,\n `totalMembers: ${result.totalMembers}`,\n ]\n\n if (result.firstMessageTs != null && result.lastMessageTs != null) {\n const start = new Date(result.firstMessageTs * 1000).toLocaleDateString()\n const end = new Date(result.lastMessageTs * 1000).toLocaleDateString()\n lines.push(`timeRange: ${start} ~ ${end}`)\n }\n\n if (result.topMembers.length > 0) {\n lines.push(`topMembers:`)\n for (let i = 0; i < result.topMembers.length; i++) {\n const m = result.topMembers[i]\n const pct = result.totalMessages > 0 ? ((m.count / result.totalMessages) * 100).toFixed(1) : '0'\n lines.push(`${i + 1}. ${m.name} ${m.count}${msgSuffix}(${pct}%)`)\n }\n }\n\n return { content: lines.join('\\n'), data: result }\n}\n\nexport const chatOverviewTool: ToolDefinition = {\n name: 'get_chat_overview',\n description: '获取聊天概览信息,包括聊天名称、平台、总消息数、总成员数、时间范围和活跃成员排行',\n inputSchema,\n handler,\n category: 'core',\n}\n","/**\n * 时间参数解析工具\n *\n * 从 Electron 工具提取的共享实用函数,处理 start_time/end_time 字符串参数。\n */\n\nimport type { TimeFilter } from '../types'\n\nexport interface ExtendedTimeParams {\n start_time?: string\n end_time?: string\n}\n\n/**\n * 解析时间参数,返回时间过滤器\n * 优先级: start_time/end_time > contextTimeFilter\n */\nexport function parseExtendedTimeParams(\n params: ExtendedTimeParams,\n contextTimeFilter?: TimeFilter\n): TimeFilter | undefined {\n if (params.start_time || params.end_time) {\n let startTs: number | undefined\n let endTs: number | undefined\n\n if (params.start_time) {\n const startDate = new Date(params.start_time.replace(' ', 'T'))\n if (!isNaN(startDate.getTime())) {\n startTs = Math.floor(startDate.getTime() / 1000)\n }\n }\n\n if (params.end_time) {\n const endDate = new Date(params.end_time.replace(' ', 'T'))\n if (!isNaN(endDate.getTime())) {\n endTs = Math.floor(endDate.getTime() / 1000)\n }\n }\n\n if (startTs !== undefined || endTs !== undefined) {\n return {\n startTs: startTs ?? 0,\n endTs: endTs ?? Math.floor(Date.now() / 1000),\n }\n }\n }\n\n return contextTimeFilter\n}\n","/**\n * 共享 JSON Schema 片段\n */\n\nexport const timeParamProperties = {\n start_time: {\n type: 'string' as const,\n description: '起始时间, 格式: YYYY-MM-DD HH:mm',\n },\n end_time: {\n type: 'string' as const,\n description: '结束时间, 格式: YYYY-MM-DD HH:mm',\n },\n}\n","/**\n * 关键词搜索消息工具\n *\n * 支持多关键词、发送者过滤、时间范围、上下文扩展。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { parseExtendedTimeParams } from '../utils/time-params'\nimport { formatTimeRange } from '../utils/format'\nimport { timeParamProperties } from '../utils/schemas'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n keywords: { type: 'array', items: { type: 'string' }, description: '搜索关键词列表' },\n sender_id: { type: 'number', description: '按发送者 ID 过滤(通过 get_members 获取)' },\n limit: { type: 'number', description: '返回的最大消息条数' },\n ...timeParamProperties,\n },\n required: ['keywords'],\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale, timeFilter: contextTimeFilter, maxMessagesLimit } = context\n const keywords = params.keywords as string[]\n const limit = Math.min(maxMessagesLimit || (params.limit as number) || 1000, 50000)\n const effectiveTimeFilter = parseExtendedTimeParams(params as any, contextTimeFilter)\n\n const result = await context.dataProvider!.searchMessages(keywords, {\n timeFilter: effectiveTimeFilter,\n limit,\n senderId: params.sender_id as number | undefined,\n })\n\n const contextBefore = context.searchContextBefore ?? 2\n const contextAfter = context.searchContextAfter ?? 2\n let finalMessages = result.messages\n\n if ((contextBefore > 0 || contextAfter > 0) && result.messages.length > 0) {\n const hitIds = result.messages.map((m) => m.id).filter((id): id is number => id != null)\n if (hitIds.length > 0) {\n finalMessages = await context.dataProvider!.getSearchMessageContext(hitIds, contextBefore, contextAfter)\n }\n }\n\n const data = {\n total: result.total,\n returned: finalMessages.length,\n timeRange: formatTimeRange(effectiveTimeFilter, locale),\n rawMessages: finalMessages,\n }\n\n return { content: JSON.stringify(data), data, rawMessages: finalMessages }\n}\n\nexport const searchMessagesTool: ToolDefinition = {\n name: 'search_messages',\n description:\n '根据关键词搜索群聊记录。适用于用户想要查找特定话题、关键词相关的聊天内容。可以指定时间范围和发送者来筛选消息。',\n inputSchema,\n handler,\n category: 'core',\n truncationStrategy: 'keep_first',\n}\n","/**\n * 深度搜索消息工具\n *\n * LIKE 子串匹配,速度较慢但不会遗漏。与 search-messages 结构相同,底层使用不同搜索策略。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { parseExtendedTimeParams } from '../utils/time-params'\nimport { formatTimeRange } from '../utils/format'\nimport { timeParamProperties } from '../utils/schemas'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n keywords: { type: 'array', items: { type: 'string' }, description: '搜索关键词列表' },\n sender_id: { type: 'number', description: '按发送者 ID 过滤(通过 get_members 获取)' },\n limit: { type: 'number', description: '返回的最大消息条数' },\n ...timeParamProperties,\n },\n required: ['keywords'],\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale, timeFilter: contextTimeFilter, maxMessagesLimit } = context\n const keywords = params.keywords as string[]\n const limit = Math.min(maxMessagesLimit || (params.limit as number) || 1000, 50000)\n const effectiveTimeFilter = parseExtendedTimeParams(params as any, contextTimeFilter)\n\n const result = await context.dataProvider!.deepSearchMessages(keywords, {\n timeFilter: effectiveTimeFilter,\n limit,\n senderId: params.sender_id as number | undefined,\n })\n\n const contextBefore = context.searchContextBefore ?? 2\n const contextAfter = context.searchContextAfter ?? 2\n let finalMessages = result.messages\n\n if ((contextBefore > 0 || contextAfter > 0) && result.messages.length > 0) {\n const hitIds = result.messages.map((m) => m.id).filter((id): id is number => id != null)\n if (hitIds.length > 0) {\n finalMessages = await context.dataProvider!.getSearchMessageContext(hitIds, contextBefore, contextAfter)\n }\n }\n\n const data = {\n total: result.total,\n returned: finalMessages.length,\n timeRange: formatTimeRange(effectiveTimeFilter, locale),\n rawMessages: finalMessages,\n }\n\n return { content: JSON.stringify(data), data, rawMessages: finalMessages }\n}\n\nexport const deepSearchMessagesTool: ToolDefinition = {\n name: 'deep_search_messages',\n description:\n '深度搜索消息(LIKE 子串匹配,速度较慢但不会遗漏),适用于全文搜索精确匹配或 FTS 搜索无结果时的补充搜索。',\n inputSchema,\n handler,\n category: 'core',\n truncationStrategy: 'keep_first',\n}\n","/**\n * 消息上下文工具\n *\n * 根据消息 ID 获取前后的上下文消息。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { t } from '../utils/format'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n message_ids: { type: 'array', items: { type: 'number' }, description: '消息 ID 列表' },\n context_size: { type: 'number', description: '每条消息前后获取的上下文条数,默认 20' },\n },\n required: ['message_ids'],\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale } = context\n const messageIds = params.message_ids as number[]\n const contextSize = (params.context_size as number) || 20\n\n const messages = await context.dataProvider!.getMessageContext(messageIds, contextSize)\n\n if (messages.length === 0) {\n const data = {\n error: t('noMessageContext', locale),\n messageIds,\n }\n return { content: JSON.stringify(data), data }\n }\n\n const data = {\n totalMessages: messages.length,\n contextSize,\n requestedMessageIds: messageIds,\n rawMessages: messages,\n }\n\n return { content: JSON.stringify(data), data, rawMessages: messages }\n}\n\nexport const getMessageContextTool: ToolDefinition = {\n name: 'get_message_context',\n description: '根据消息 ID 获取前后的上下文消息。适用于需要查看某条消息前后聊天内容的场景。',\n inputSchema,\n handler,\n category: 'core',\n truncationStrategy: 'keep_last',\n}\n","/**\n * 段落搜索工具\n *\n * 搜索聊天段落(对话段落),返回匹配的段落列表及预览。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { parseExtendedTimeParams } from '../utils/time-params'\nimport { formatTimeRange, formatMessageCompact, isChineseLocale } from '../utils/format'\nimport { timeParamProperties } from '../utils/schemas'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n keywords: { type: 'array', items: { type: 'string' }, description: '搜索关键词列表' },\n limit: { type: 'number', description: '返回的最大段落数,默认 20' },\n ...timeParamProperties,\n },\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale, timeFilter: contextTimeFilter } = context\n const limit = (params.limit as number) || 20\n const effectiveTimeFilter = parseExtendedTimeParams(params as any, contextTimeFilter)\n\n const segments = await context.dataProvider!.searchSegments(\n params.keywords as string[] | undefined,\n effectiveTimeFilter,\n limit,\n 5\n )\n\n if (segments.length === 0) {\n const data = {\n total: 0,\n message: isChineseLocale(locale) ? '未找到匹配的段落' : 'No matching segments found',\n }\n return { content: JSON.stringify(data), data }\n }\n\n const localeStr = isChineseLocale(locale) ? 'zh-CN' : 'en-US'\n const msgSuffix = isChineseLocale(locale) ? '条消息' : ' messages'\n const completeLabel = isChineseLocale(locale) ? '完整段落' : 'complete'\n\n const data = {\n total: segments.length,\n timeRange: formatTimeRange(effectiveTimeFilter, locale),\n segments: segments.map((s) => {\n const startTime = new Date(s.startTs * 1000).toLocaleString(localeStr)\n const endTime = new Date(s.endTs * 1000).toLocaleString(localeStr)\n const completeTag = s.isComplete ? ` [${completeLabel}]` : ''\n\n return {\n segmentId: s.id,\n time: `${startTime} ~ ${endTime}`,\n messageCount: `${s.messageCount}${msgSuffix}${completeTag}`,\n preview: s.previewMessages.map((m) => formatMessageCompact(m, locale)),\n }\n }),\n }\n\n return { content: JSON.stringify(data), data }\n}\n\nexport const searchSegmentsTool: ToolDefinition = {\n name: 'search_segments',\n description:\n '搜索聊天段落(对话段落)。段落是根据消息时间间隔自动切分的对话单元。返回匹配的段落列表及每个段落的前5条消息预览。',\n inputSchema,\n handler,\n category: 'core',\n}\n","/**\n * 获取段落完整消息工具\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { isChineseLocale } from '../utils/format'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n segment_id: { type: 'number', description: '段落 ID(通过 search_segments 获取)' },\n limit: { type: 'number', description: '返回的最大消息条数' },\n },\n required: ['segment_id'],\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale, maxMessagesLimit } = context\n const limit = maxMessagesLimit || (params.limit as number) || 1000\n\n const result = await context.dataProvider!.getSegmentMessages(params.segment_id as number, limit)\n\n if (!result) {\n const data = {\n error: isChineseLocale(locale) ? '未找到指定的段落' : 'Segment not found',\n segmentId: params.segment_id,\n }\n return { content: JSON.stringify(data), data }\n }\n\n const localeStr = isChineseLocale(locale) ? 'zh-CN' : 'en-US'\n const startTime = new Date(result.startTs * 1000).toLocaleString(localeStr)\n const endTime = new Date(result.endTs * 1000).toLocaleString(localeStr)\n const rawMessages = result.messages.map((m) => ({\n id: m.id,\n senderName: m.senderName,\n content: m.content,\n timestamp: m.timestamp,\n }))\n\n const data = {\n segmentId: result.segmentId,\n time: `${startTime} ~ ${endTime}`,\n messageCount: result.messageCount,\n returnedCount: result.returnedCount,\n participants: result.participants,\n rawMessages,\n }\n\n return { content: JSON.stringify(data), data, rawMessages }\n}\n\nexport const getSegmentMessagesTool: ToolDefinition = {\n name: 'get_segment_messages',\n description: '获取指定段落的完整消息列表。用于在 search_segments 找到相关段落后,获取该段落的完整上下文。',\n inputSchema,\n handler,\n category: 'core',\n truncationStrategy: 'keep_last',\n}\n","/**\n * 获取成员列表工具\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { isChineseLocale, t } from '../utils/format'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n search: { type: 'string', description: '按名称/别名/平台ID搜索过滤' },\n limit: { type: 'number', description: '最大返回数量' },\n },\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale } = context\n const members = await context.dataProvider!.getMembers()\n\n let filteredMembers = members\n if (params.search) {\n const keyword = (params.search as string).toLowerCase()\n filteredMembers = members.filter((m) => {\n if (m.groupNickname && m.groupNickname.toLowerCase().includes(keyword)) return true\n if (m.accountName && m.accountName.toLowerCase().includes(keyword)) return true\n if (m.platformId.includes(keyword)) return true\n if (m.aliases.some((alias) => alias.toLowerCase().includes(keyword))) return true\n return false\n })\n }\n\n if (params.limit && (params.limit as number) > 0) {\n filteredMembers = filteredMembers.slice(0, params.limit as number)\n }\n\n const msgSuffix = isChineseLocale(locale) ? '条' : ''\n const aliasLabel = t('alias', locale)\n const data = {\n totalMembers: members.length,\n returnedMembers: filteredMembers.length,\n members: filteredMembers.map((m) => {\n const displayName = m.groupNickname || m.accountName || m.platformId\n const aliasStr = m.aliases.length > 0 ? `|${aliasLabel}:${m.aliases.join(',')}` : ''\n return `${m.id}|${m.platformId}|${displayName}|${m.messageCount}${msgSuffix}${aliasStr}`\n }),\n }\n\n return { content: JSON.stringify(data), data }\n}\n\nexport const getMembersTool: ToolDefinition = {\n name: 'get_members',\n description: '获取成员列表,包括成员的基本信息、别名和消息统计。适用于查询\"有哪些人\"、\"某人的别名是什么\"等问题。',\n inputSchema,\n handler,\n category: 'core',\n}\n","/**\n * 成员统计工具\n *\n * 获取成员活跃度排行和统计信息。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n top: {\n type: 'number',\n description: '返回前 N 个活跃成员',\n default: 20,\n },\n },\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const top = (params.top as number) || 20\n const members = await context.dataProvider!.getMemberStats({ timeFilter: context.timeFilter, top })\n\n const data = {\n total: members.length,\n members: members.map((m) => ({\n name: m.name,\n messageCount: m.messageCount,\n percentage: m.percentage,\n })),\n }\n\n return {\n content: JSON.stringify(data),\n data,\n }\n}\n\nexport const memberStatsTool: ToolDefinition = {\n name: 'get_member_stats',\n description: '获取成员活跃度排行,包括消息数量和占比',\n inputSchema,\n handler,\n category: 'core',\n}\n","/**\n * 时间统计工具\n *\n * 获取聊天活跃时段分布(小时、星期、每日趋势)。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n type: {\n type: 'string',\n description: '统计类型:hourly(按小时)、weekday(按星期)、daily(按天)',\n enum: ['hourly', 'weekday', 'daily'],\n default: 'hourly',\n },\n },\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const type = (params.type as 'hourly' | 'weekday' | 'daily') || 'hourly'\n const data = await context.dataProvider!.getTimeStats(type, { timeFilter: context.timeFilter })\n\n return {\n content: JSON.stringify({ type, data }),\n data,\n }\n}\n\nexport const timeStatsTool: ToolDefinition = {\n name: 'get_time_stats',\n description: '获取聊天活跃时段分布(按小时/星期/每日趋势)',\n inputSchema,\n handler,\n category: 'core',\n}\n","/**\n * 最近消息工具\n *\n * 获取指定时间段内的群聊消息,支持 start_time/end_time 参数。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { parseExtendedTimeParams } from '../utils/time-params'\nimport { formatTimeRange } from '../utils/format'\nimport { timeParamProperties } from '../utils/schemas'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n limit: {\n type: 'number',\n description: '返回的消息条数',\n default: 100,\n },\n ...timeParamProperties,\n },\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale, timeFilter: contextTimeFilter, maxMessagesLimit } = context\n const limit = maxMessagesLimit || (params.limit as number) || 100\n const effectiveTimeFilter = parseExtendedTimeParams(params as any, contextTimeFilter)\n\n const result = await context.dataProvider!.getRecentMessages({ timeFilter: effectiveTimeFilter, limit })\n\n const data = {\n total: result.total,\n returned: result.messages.length,\n timeRange: formatTimeRange(effectiveTimeFilter, locale),\n rawMessages: result.messages,\n }\n\n return {\n content: JSON.stringify(data),\n data,\n rawMessages: result.messages,\n }\n}\n\nexport const recentMessagesTool: ToolDefinition = {\n name: 'get_recent_messages',\n description: '获取指定时间段内的群聊消息。适用于回答\"最近大家聊了什么\"等概览性问题。支持精确到分钟级别的时间查询。',\n inputSchema,\n handler,\n category: 'core',\n truncationStrategy: 'keep_last',\n}\n","/**\n * 获取成员昵称变更历史工具\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { isChineseLocale, t } from '../utils/format'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n member_id: { type: 'number', description: '成员 ID(通过 get_members 获取)' },\n },\n required: ['member_id'],\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale } = context\n const memberId = params.member_id as number\n\n const members = await context.dataProvider!.getMembers()\n const member = members.find((m) => m.id === memberId)\n\n if (!member) {\n const data = {\n error: t('memberNotFound', locale),\n member_id: memberId,\n }\n return { content: JSON.stringify(data), data }\n }\n\n const history = await context.dataProvider!.getMemberNameHistory(memberId)\n\n const localeStr = isChineseLocale(locale) ? 'zh-CN' : 'en-US'\n const untilNow = t('untilNow', locale)\n const formatHistory = (h: { name: string; startTs: number; endTs: number | null }) => {\n const start = new Date(h.startTs * 1000).toLocaleDateString(localeStr)\n const end = h.endTs ? new Date(h.endTs * 1000).toLocaleDateString(localeStr) : untilNow\n return `${h.name} (${start} ~ ${end})`\n }\n\n const accountNames = history.filter((h) => h.nameType === 'account_name').map(formatHistory)\n const groupNicknames = history.filter((h) => h.nameType === 'group_nickname').map(formatHistory)\n\n const displayName = member.groupNickname || member.accountName || member.platformId\n const aliasLabel = t('alias', locale)\n const aliasStr = member.aliases.length > 0 ? `|${aliasLabel}:${member.aliases.join(',')}` : ''\n const noChangeRecord = t('noChangeRecord', locale)\n\n const data = {\n member: `${member.id}|${member.platformId}|${displayName}${aliasStr}`,\n accountNameHistory: accountNames.length > 0 ? accountNames : noChangeRecord,\n groupNicknameHistory: groupNicknames.length > 0 ? groupNicknames : noChangeRecord,\n }\n\n return { content: JSON.stringify(data), data }\n}\n\nexport const getMemberNameHistoryTool: ToolDefinition = {\n name: 'get_member_name_history',\n description: '获取成员的昵称变更历史记录。适用于回答\"某人以前叫什么名字\"、\"某人的昵称变化\"等问题。',\n inputSchema,\n handler,\n category: 'analysis',\n}\n","/**\n * 两人对话查询工具\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { parseExtendedTimeParams } from '../utils/time-params'\nimport { formatTimeRange, t } from '../utils/format'\nimport { timeParamProperties } from '../utils/schemas'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n member_id_1: { type: 'number', description: '第一个成员的 ID(通过 get_members 获取)' },\n member_id_2: { type: 'number', description: '第二个成员的 ID(通过 get_members 获取)' },\n limit: { type: 'number', description: '返回的最大消息条数' },\n ...timeParamProperties,\n },\n required: ['member_id_1', 'member_id_2'],\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale, timeFilter: contextTimeFilter, maxMessagesLimit } = context\n const limit = maxMessagesLimit || (params.limit as number) || 100\n const effectiveTimeFilter = parseExtendedTimeParams(params as any, contextTimeFilter)\n\n const result = await context.dataProvider!.getConversationBetween(\n params.member_id_1 as number,\n params.member_id_2 as number,\n effectiveTimeFilter,\n limit\n )\n\n if (result.messages.length === 0) {\n const data = {\n error: t('noConversation', locale),\n member1Id: params.member_id_1,\n member2Id: params.member_id_2,\n }\n return { content: JSON.stringify(data), data }\n }\n\n const data = {\n total: result.total,\n returned: result.messages.length,\n member1: result.member1Name,\n member2: result.member2Name,\n timeRange: formatTimeRange(effectiveTimeFilter, locale),\n rawMessages: result.messages,\n }\n\n return { content: JSON.stringify(data), data, rawMessages: result.messages }\n}\n\nexport const getConversationBetweenTool: ToolDefinition = {\n name: 'get_conversation_between',\n description: '获取两个群成员之间的对话记录。适用于回答\"A和B之间聊了什么\"等问题。需要先通过 get_members 获取成员 ID。',\n inputSchema,\n handler,\n category: 'analysis',\n truncationStrategy: 'keep_last',\n}\n","/**\n * 获取段落摘要列表工具\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { parseExtendedTimeParams } from '../utils/time-params'\nimport { formatTimeRange, isChineseLocale } from '../utils/format'\nimport { timeParamProperties } from '../utils/schemas'\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n keywords: { type: 'array', items: { type: 'string' }, description: '按关键词过滤摘要内容' },\n limit: { type: 'number', description: '返回的最大段落数,默认 20' },\n ...timeParamProperties,\n },\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale, timeFilter: contextTimeFilter } = context\n const limit = (params.limit as number) || 20\n const effectiveTimeFilter = parseExtendedTimeParams(params as any, contextTimeFilter)\n\n const segments = await context.dataProvider!.getSegmentSummaries({\n limit: limit * 2,\n timeFilter: effectiveTimeFilter,\n })\n\n if (!segments || segments.length === 0) {\n const data = {\n message: isChineseLocale(locale)\n ? '未找到带摘要的段落。可能还没有生成摘要,请在段落时间线中点击\"批量生成\"按钮。'\n : 'No segments with summaries found. Summaries may not have been generated yet.',\n }\n return { content: JSON.stringify(data), data }\n }\n\n let filteredSegments = segments\n const keywords = params.keywords as string[] | undefined\n if (keywords && keywords.length > 0) {\n const lowerKeywords = keywords.map((k) => k.toLowerCase())\n filteredSegments = segments.filter((s) =>\n lowerKeywords.some((keyword) => s.summary?.toLowerCase().includes(keyword))\n )\n }\n\n filteredSegments = filteredSegments.filter((s) => s.summary)\n const limitedSegments = filteredSegments.slice(0, limit)\n\n const localeStr = isChineseLocale(locale) ? 'zh-CN' : 'en-US'\n\n const data = {\n total: filteredSegments.length,\n returned: limitedSegments.length,\n timeRange: formatTimeRange(effectiveTimeFilter, locale),\n segments: limitedSegments.map((s) => {\n const startTime = new Date(s.startTs * 1000).toLocaleString(localeStr)\n const endTime = new Date(s.endTs * 1000).toLocaleString(localeStr)\n return {\n segmentId: s.id,\n time: `${startTime} ~ ${endTime}`,\n messageCount: s.messageCount,\n participants: s.participants,\n summary: s.summary,\n }\n }),\n }\n\n return { content: JSON.stringify(data), data }\n}\n\nexport const getSegmentSummariesTool: ToolDefinition = {\n name: 'get_segment_summaries',\n description: '获取段落摘要列表,快速了解群聊历史讨论的主题。可以按关键词搜索讨论过的话题。',\n inputSchema,\n handler,\n category: 'analysis',\n}\n","/**\n * 响应时间分析工具\n *\n * 基于参数化 SQL + JS 聚合的启发式回复延迟统计。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { isChineseLocale } from '../utils/format'\n\ninterface MsgRow {\n sender_id: number\n name: string\n ts: number\n}\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n days: { type: 'number', description: '分析最近多少天的数据,默认 30' },\n top_n: { type: 'number', description: '返回前多少名,默认 10' },\n },\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale } = context\n const isZh = isChineseLocale(locale)\n const days = (params.days as number) || 30\n const topN = (params.top_n as number) || 10\n\n const sql = `\n SELECT msg.sender_id, COALESCE(m.group_nickname, m.account_name) AS name, msg.ts\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE msg.type = 0\n AND msg.ts > unixepoch('now', '-' || @days || ' days')\n ORDER BY msg.ts ASC\n `\n const rows = await context.dataProvider!.executeParameterizedSql<MsgRow>(sql, { days })\n if (!rows || rows.length < 2) {\n const text = isZh\n ? '该时间范围内消息不足,无法分析响应时间'\n : 'Not enough messages in this time range to analyze response time'\n return { content: text, data: null }\n }\n\n const responseTimes = new Map<number, { name: string; times: number[] }>()\n\n for (let i = 1; i < rows.length; i++) {\n const prev = rows[i - 1]\n const curr = rows[i]\n if (curr.sender_id === prev.sender_id) continue\n\n const gap = curr.ts - prev.ts\n if (gap < 5 || gap > 1800) continue\n\n if (!responseTimes.has(curr.sender_id)) {\n responseTimes.set(curr.sender_id, { name: curr.name, times: [] })\n }\n responseTimes.get(curr.sender_id)!.times.push(gap)\n }\n\n const stats = [...responseTimes.entries()]\n .map(([id, { name, times }]) => {\n times.sort((a, b) => a - b)\n const avg = Math.round(times.reduce((s, t) => s + t, 0) / times.length)\n const median = times[Math.floor(times.length / 2)]\n return { id, name, avgSeconds: avg, medianSeconds: median, responseCount: times.length }\n })\n .filter((s) => s.responseCount >= 3)\n .sort((a, b) => a.medianSeconds - b.medianSeconds)\n .slice(0, topN)\n\n if (stats.length === 0) {\n const text = isZh ? '没有足够的响应数据进行分析' : 'Not enough response data for analysis'\n return { content: text, data: null }\n }\n\n const formatTime = (s: number) => {\n if (s < 60) return isZh ? `${s}秒` : `${s}s`\n const m = Math.floor(s / 60)\n const sec = s % 60\n return isZh ? `${m}分${sec}秒` : `${m}m${sec}s`\n }\n\n const ranking = stats.map((s, i) => ({\n rank: i + 1,\n name: s.name,\n median: formatTime(s.medianSeconds),\n avg: formatTime(s.avgSeconds),\n count: s.responseCount,\n }))\n\n const data = {\n period: isZh ? `近${days}天` : `Last ${days} days`,\n totalResponders: stats.length,\n ranking: ranking.map(\n (r) =>\n `${r.rank}. ${r.name} — ${isZh ? '中位数' : 'median'} ${r.median}, ${isZh ? '平均' : 'avg'} ${r.avg} (${r.count}${isZh ? '次' : ' responses'})`\n ),\n }\n\n return { content: JSON.stringify(data), data }\n}\n\nexport const responseTimeAnalysisTool: ToolDefinition = {\n name: 'response_time_analysis',\n description: '分析群成员的响应速度排行,基于回复间隔的中位数和平均值。',\n inputSchema,\n handler,\n category: 'analysis',\n}\n","/**\n * 关键词词频分析工具\n *\n * 通过 SQL 获取文本消息,使用 NLP 分词统计高频词。\n * NLP 分词能力通过 context.segmentText 回调注入。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, JsonSchema } from '../types'\nimport { isChineseLocale } from '../utils/format'\n\ninterface TextRow {\n content: string\n}\n\nconst inputSchema: JsonSchema = {\n type: 'object',\n properties: {\n days: { type: 'number', description: '分析最近多少天的数据,默认 30' },\n top_n: { type: 'number', description: '返回前多少个高频词,默认 50' },\n },\n}\n\nasync function handler(params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> {\n const { locale, segmentText } = context\n const isZh = isChineseLocale(locale)\n const days = (params.days as number) || 30\n const topN = (params.top_n as number) || 50\n\n if (!segmentText) {\n const text = isZh ? '当前环境不支持分词功能' : 'Text segmentation is not available in this environment'\n return { content: text, data: null }\n }\n\n const sql = `\n SELECT content FROM message\n WHERE type = 0 AND content IS NOT NULL AND LENGTH(content) > 1\n AND ts > unixepoch('now', '-' || @days || ' days')\n LIMIT 50000\n `\n const rows = await context.dataProvider!.executeParameterizedSql<TextRow>(sql, { days })\n if (!rows || rows.length === 0) {\n const text = isZh ? '该时间范围内没有文本消息' : 'No text messages in this time range'\n return { content: text, data: null }\n }\n\n const texts = rows.map((r) => r.content)\n const segLocale: string = locale?.startsWith('ja') ? 'ja-JP' : locale?.startsWith('zh') ? 'zh-CN' : 'en-US'\n const freqResult = segmentText(texts, segLocale, {\n minCount: 2,\n topN,\n posFilterMode: 'meaningful',\n enableStopwords: true,\n })\n\n if (freqResult.words.size === 0) {\n const text = isZh ? '分词后没有有意义的高频词' : 'No meaningful high-frequency words found after segmentation'\n return { content: text, data: null }\n }\n\n const ranking = [...freqResult.words.entries()].map(([word, count], i) => ({\n rank: i + 1,\n word,\n count,\n }))\n\n const data = {\n period: isZh ? `近${days}天` : `Last ${days} days`,\n totalMessages: rows.length,\n totalKeywords: ranking.length,\n keywords: ranking.map((r) => `${r.rank}. ${r.word} (${r.count}${isZh ? '次' : ''})`),\n }\n\n return { content: JSON.stringify(data), data }\n}\n\nexport const keywordFrequencyTool: ToolDefinition = {\n name: 'keyword_frequency',\n description: '统计群聊中的高频关键词,通过 NLP 分词分析消息内容。',\n inputSchema,\n handler,\n category: 'analysis',\n}\n","/**\n * 内置 SQL 分析工具定义集合\n *\n * 声明式 SQL 工具:每个定义在 LLM 眼中是一个 Function Calling 工具,\n * 执行时通过参数化 SQL 查询数据库,将结果格式化为文本返回给 LLM。\n */\n\nimport type { SqlToolDef } from '../types'\n\nexport const SQL_TOOL_DEFS: SqlToolDef[] = [\n // ==================== 通用分析 ====================\n {\n name: 'message_type_breakdown',\n description: '按消息类型统计近 N 天的消息分布(文本、图片、语音、表情等各有多少条)。适用于了解沟通方式偏好。',\n parameters: {\n type: 'object',\n properties: {\n days: { type: 'number', description: '统计最近多少天的数据' },\n },\n required: ['days'],\n },\n execution: {\n type: 'sqlite',\n query:\n \"SELECT CASE type WHEN 0 THEN '文本' WHEN 1 THEN '图片' WHEN 2 THEN '语音' WHEN 3 THEN '视频' WHEN 4 THEN '文件' WHEN 5 THEN '表情' WHEN 7 THEN '链接' WHEN 20 THEN '红包' WHEN 22 THEN '拍一拍' WHEN 80 THEN '系统消息' WHEN 81 THEN '撤回' ELSE '其他' 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\",\n rowTemplate: '{type_name}:{msg_count} 条(占 {percentage}%)',\n summaryTemplate: '消息类型分布(共 {rowCount} 种类型):',\n fallback: '该时间范围内没有消息记录',\n },\n },\n {\n name: 'peak_chat_hours_by_member',\n description:\n '分析指定成员在近 N 天内每小时的发言量分布,找出其最活跃的时段。需要先通过 get_members 获取 member_id。',\n parameters: {\n type: 'object',\n properties: {\n member_id: { type: 'number', description: '成员 ID(通过 get_members 获取)' },\n days: { type: 'number', description: '统计最近多少天的数据', default: 30 },\n },\n required: ['member_id'],\n },\n execution: {\n type: 'sqlite',\n query:\n \"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\",\n rowTemplate: '{hour}:00 — {msg_count} 条消息',\n summaryTemplate: '该成员各时段发言量(共 {rowCount} 个活跃时段):',\n fallback: '该成员在指定时间范围内没有发言记录',\n },\n },\n\n // ==================== 社群分析 ====================\n {\n name: 'member_activity_trend',\n description:\n '查看指定成员近 N 天的每日发言数量变化趋势。适用于观察某人是否变得更活跃或更沉默。需要先通过 get_members 获取 member_id。',\n parameters: {\n type: 'object',\n properties: {\n member_id: { type: 'number', description: '成员 ID(通过 get_members 获取)' },\n days: { type: 'number', description: '查看最近多少天的趋势' },\n },\n required: ['member_id', 'days'],\n },\n execution: {\n type: 'sqlite',\n query:\n \"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\",\n rowTemplate: '{day}:{msg_count} 条',\n summaryTemplate: '该成员近 {rowCount} 天有发言记录:',\n fallback: '该成员在指定时间范围内没有发言记录',\n },\n },\n {\n name: 'silent_members',\n description: '检测超过 N 天未发言的「沉默成员」。适用于社群运营中发现流失风险用户。',\n parameters: {\n type: 'object',\n properties: {\n days: { type: 'number', description: '多少天未发言算沉默', default: 7 },\n },\n required: ['days'],\n },\n execution: {\n type: 'sqlite',\n query:\n \"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\",\n rowTemplate: '{name} — 已沉默 {silent_days} 天',\n summaryTemplate: '共发现 {rowCount} 位沉默成员:',\n fallback: '没有发现超过指定天数未发言的成员,社群活跃度良好!',\n },\n },\n {\n name: 'reply_interaction_ranking',\n description: '分析群内的回复互动关系排行,找出谁回复谁最多。适用于发现社群中的核心互动关系和意见领袖。',\n parameters: {\n type: 'object',\n properties: {\n days: { type: 'number', description: '统计最近多少天的数据' },\n limit: { type: 'number', description: '返回前多少对互动关系', default: 10 },\n },\n required: ['days'],\n },\n execution: {\n type: 'sqlite',\n query:\n \"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\",\n rowTemplate: '{replier_name} → {original_name}:{reply_count} 次回复',\n summaryTemplate: '回复互动 Top {rowCount}:',\n fallback: '该时间范围内没有回复互动记录',\n },\n },\n\n // ==================== 情感分析 ====================\n {\n name: 'mutual_interaction_pairs',\n description:\n '找出互动最频繁的成员对,基于双向消息时间接近度(一方发言后 5 分钟内另一方也发言即视为一次互动)。适用于发现关系亲密的好友组合。',\n parameters: {\n type: 'object',\n properties: {\n days: { type: 'number', description: '统计最近多少天的数据' },\n limit: { type: 'number', description: '返回前多少对', default: 10 },\n },\n required: ['days'],\n },\n execution: {\n type: 'sqlite',\n query:\n \"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\",\n rowTemplate: '{member_a} ↔ {member_b}:{interaction_count} 次互动',\n summaryTemplate: '互动最频繁的 {rowCount} 对好友:',\n fallback: '该时间范围内没有检测到明显的互动关系',\n },\n },\n {\n name: 'member_message_length_stats',\n description: '统计各成员的平均消息长度(仅文本消息),长消息通常意味着更用心的交流。适用于发现深度交流者。',\n parameters: {\n type: 'object',\n properties: {\n days: { type: 'number', description: '统计最近多少天的数据' },\n top_n: { type: 'number', description: '返回前多少名', default: 10 },\n },\n required: ['days'],\n },\n execution: {\n type: 'sqlite',\n query:\n \"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\",\n rowTemplate: '{name} — 平均 {avg_length} 字/条(共 {msg_count} 条,最长 {max_length} 字)',\n summaryTemplate: '消息长度 Top {rowCount}(更长 = 更用心):',\n fallback: '该时间范围内没有足够的文本消息数据',\n },\n },\n\n // ==================== 活跃度趋势 ====================\n {\n name: 'daily_active_members',\n description:\n '统计每日独立发言人数(DAU)和消息量,用于观察群活力变化趋势。适用于\"群活跃度趋势怎么样\"、\"最近有多少人在说话\"。',\n parameters: {\n type: 'object',\n properties: {\n days: { type: 'number', description: '统计最近多少天的数据', default: 30 },\n },\n required: ['days'],\n },\n execution: {\n type: 'sqlite',\n query:\n \"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\",\n rowTemplate: '{day}:{active_members} 人活跃,{msg_count} 条消息',\n summaryTemplate: '近 {rowCount} 天的每日活跃人数趋势:',\n fallback: '该时间范围内没有消息记录',\n },\n },\n {\n name: 'conversation_initiator_stats',\n description: '统计每个成员发起会话(作为会话首条消息的发送者)的次数,找出谁最常开启话题。需要已生成会话索引。',\n parameters: {\n type: 'object',\n properties: {\n days: { type: 'number', description: '统计最近多少天的数据', default: 30 },\n limit: { type: 'number', description: '返回前多少名', default: 10 },\n },\n required: ['days'],\n },\n execution: {\n type: 'sqlite',\n query:\n \"SELECT COALESCE(m.group_nickname, m.account_name) AS name, COUNT(*) AS initiated_count FROM segment cs JOIN message_context mc ON mc.segment_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\",\n rowTemplate: '{name}:发起 {initiated_count} 次话题',\n summaryTemplate: '话题发起者 Top {rowCount}:',\n fallback: '该时间范围内没有会话记录,可能需要先生成会话索引',\n },\n },\n {\n name: 'activity_heatmap',\n description: '返回 星期×小时 的消息数矩阵,适合生成活跃度热力图。weekday: 0=周日, 1=周一, ..., 6=周六。',\n parameters: {\n type: 'object',\n properties: {\n days: { type: 'number', description: '统计最近多少天的数据', default: 30 },\n },\n required: ['days'],\n },\n execution: {\n type: 'sqlite',\n query:\n \"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\",\n rowTemplate: '星期{weekday} {hour}:00 — {msg_count} 条',\n summaryTemplate: '活跃度热力图数据(共 {rowCount} 个时段有消息):',\n fallback: '该时间范围内没有消息记录',\n },\n },\n\n // ==================== 客服分析 ====================\n {\n name: 'unanswered_messages',\n description:\n '查找近 N 天内未被回复的消息,这些可能是未解决的客户问题。仅统计文本消息且内容超过 10 字的(过滤简短寒暄)。',\n parameters: {\n type: 'object',\n properties: {\n days: { type: 'number', description: '查找最近多少天的数据' },\n limit: { type: 'number', description: '最多返回多少条', default: 20 },\n },\n required: ['days'],\n },\n execution: {\n type: 'sqlite',\n query:\n \"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\",\n rowTemplate: '[{send_time}] {sender_name}:{content_preview}',\n summaryTemplate: '共发现 {rowCount} 条可能未被回复的消息:',\n fallback: '该时间范围内所有消息都已得到回复,服务质量很好!',\n },\n },\n]\n","/**\n * 声明式 SQL 工具执行器\n *\n * 将 SqlToolDef 转换为 ToolDefinition,通过 dataProvider.executeParameterizedSql 执行。\n */\n\nimport type { ToolDefinition, ToolExecutionContext, ToolResult, SqlToolDef } from '../types'\n\nfunction formatRow(template: string, row: Record<string, unknown>): string {\n return template.replace(/\\{(\\w+)\\}/g, (_, col) => {\n const val = row[col]\n return val !== null && val !== undefined ? String(val) : ''\n })\n}\n\nfunction resolveTemplate(\n toolName: string,\n key: string,\n fallback: string,\n translateFn?: (key: string) => string | undefined\n): string {\n if (!translateFn) return fallback\n const i18nKey = `ai.tools.${toolName}.${key}`\n return translateFn(i18nKey) ?? fallback\n}\n\nexport function createSqlToolDefinition(def: SqlToolDef): ToolDefinition {\n return {\n name: def.name,\n description: def.description,\n inputSchema: def.parameters,\n category: 'analysis',\n handler: async (params: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult> => {\n const rows = await context.dataProvider!.executeParameterizedSql(def.execution.query, params)\n\n const fallback = resolveTemplate(def.name, 'fallback', def.execution.fallback, context.translateTemplate)\n\n if (!rows || rows.length === 0) {\n return {\n content: fallback,\n data: { rows: [], rowCount: 0 },\n }\n }\n\n const rowTemplate = resolveTemplate(def.name, 'rowTemplate', def.execution.rowTemplate, context.translateTemplate)\n const summaryTemplate = def.execution.summaryTemplate\n ? resolveTemplate(def.name, 'summaryTemplate', def.execution.summaryTemplate, context.translateTemplate)\n : undefined\n\n const lines: string[] = []\n\n if (summaryTemplate) {\n lines.push(summaryTemplate.replace(/\\{rowCount\\}/g, String(rows.length)))\n lines.push('')\n }\n\n for (const row of rows) {\n lines.push(formatRow(rowTemplate, row as Record<string, unknown>))\n }\n\n return {\n content: lines.join('\\n'),\n data: { rows, rowCount: rows.length },\n }\n },\n }\n}\n\nexport function createAllSqlToolDefinitions(defs: SqlToolDef[]): ToolDefinition[] {\n return defs.map(createSqlToolDefinition)\n}\n","/**\n * Tool registries\n *\n * AGENT_TOOL_REGISTRY: full toolset for Server Agent / Electron Agent\n * (core + analysis + raw SQL + declarative SQL tools).\n * MCP_TOOL_REGISTRY: slim toolset for MCP Server — core + analysis + raw SQL\n * plus MCP-specific session-discovery tools. Omits declarative SQL tools\n * to keep tool-schema token cost low for external AI agents.\n */\n\nimport type { ToolDefinition } from './types'\n\nimport { sqlQueryTool, schemaTool } from './definitions/sql-query'\nimport { renderChartTool } from './definitions/render-chart'\nimport { sessionInfoTool } from './definitions/session-info'\nimport { sessionsListTool } from './definitions/sessions'\nimport { chatOverviewTool } from './definitions/chat-overview'\nimport { searchMessagesTool } from './definitions/search-messages'\nimport { deepSearchMessagesTool } from './definitions/deep-search-messages'\nimport { getMessageContextTool } from './definitions/get-message-context'\nimport { searchSegmentsTool } from './definitions/search-segments'\nimport { getSegmentMessagesTool } from './definitions/get-segment-messages'\nimport { getMembersTool } from './definitions/get-members'\nimport { memberStatsTool } from './definitions/member-stats'\nimport { timeStatsTool } from './definitions/time-stats'\nimport { recentMessagesTool } from './definitions/recent-messages'\nimport { getMemberNameHistoryTool } from './definitions/get-member-name-history'\nimport { getConversationBetweenTool } from './definitions/get-conversation-between'\nimport { getSegmentSummariesTool } from './definitions/get-segment-summaries'\nimport { responseTimeAnalysisTool } from './definitions/response-time-analysis'\nimport { keywordFrequencyTool } from './definitions/keyword-frequency'\nimport { SQL_TOOL_DEFS, createAllSqlToolDefinitions } from './sql'\n\n/**\n * Core + analysis + raw SQL — shared between Agent and MCP.\n * New non-SQL tools added here will automatically appear in both registries.\n */\nconst SHARED_TOOLS: ToolDefinition[] = [\n // Core\n chatOverviewTool,\n searchMessagesTool,\n deepSearchMessagesTool,\n recentMessagesTool,\n getMessageContextTool,\n searchSegmentsTool,\n getSegmentMessagesTool,\n getMembersTool,\n schemaTool,\n\n // Analysis\n memberStatsTool,\n timeStatsTool,\n getMemberNameHistoryTool,\n getConversationBetweenTool,\n getSegmentSummariesTool,\n responseTimeAnalysisTool,\n keywordFrequencyTool,\n renderChartTool,\n\n // Raw SQL\n sqlQueryTool,\n]\n\n/**\n * Agent full toolset (Server Agent / Electron Agent).\n * Includes declarative SQL convenience tools on top of the shared set.\n */\nexport const AGENT_TOOL_REGISTRY: ToolDefinition[] = [...SHARED_TOOLS, ...createAllSqlToolDefinitions(SQL_TOOL_DEFS)]\n\n/**\n * MCP Server toolset — slim registry optimised for external AI agents.\n *\n * Includes MCP-specific session-discovery tools + shared core/analysis tools.\n * Omits declarative SQL tools to reduce tool-schema token overhead (~40% saving).\n * LLMs can use execute_sql + get_schema for any custom query.\n */\nexport const MCP_TOOL_REGISTRY: ToolDefinition[] = [\n // MCP-specific: session discovery & schema\n sessionsListTool,\n sessionInfoTool,\n // Shared core + analysis + raw SQL\n ...SHARED_TOOLS,\n]\n\n/**\n * 按名称查找工具(在所有注册表中查找)\n */\nexport function getToolByName(name: string): ToolDefinition | undefined {\n return AGENT_TOOL_REGISTRY.find((t) => t.name === name) || MCP_TOOL_REGISTRY.find((t) => t.name === name)\n}\n","/**\n * CoreDataProvider\n *\n * ToolDataProvider implementation backed by @openchatlab/core query functions.\n * Used by Server / MCP, accessing SQLite through DatabaseAdapter.\n */\n\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport {\n searchMessagesLike,\n getRecentMessages as coreGetRecentMessages,\n getMemberActivity,\n getHourlyActivity,\n getWeekdayActivity,\n getDailyActivity,\n executeReadonlySql,\n getDatabaseSchema,\n getMessageContext as coreGetMessageContext,\n getSearchMessageContext as coreGetSearchMessageContext,\n getConversationBetween as coreGetConversationBetween,\n getMemberNameHistory as coreGetMemberNameHistory,\n getMembersWithAliases,\n executeParameterizedSql as coreExecuteParameterizedSql,\n getChatOverview as coreGetChatOverview,\n searchSegments as coreSearchSegments,\n getSegmentMessages as coreGetSegmentMessages,\n getSegmentSummaries as coreGetSegmentSummaries,\n} from '@openchatlab/core'\nimport type {\n ToolDataProvider,\n SearchMessagesResult,\n MemberStatItem,\n SchemaTableInfo,\n TimeFilter,\n ChatOverviewResult,\n MemberInfo,\n NameHistoryItem,\n SegmentSearchResult,\n SegmentMessagesResult,\n ConversationResult,\n SegmentSummaryItem,\n RawMessage,\n} from '../types'\n\nexport class CoreDataProvider implements ToolDataProvider {\n constructor(private db: DatabaseAdapter) {}\n\n async searchMessages(\n keywords: string[],\n options?: { timeFilter?: TimeFilter; limit?: number; senderId?: number }\n ): Promise<SearchMessagesResult> {\n const keyword = keywords.join(' ')\n const result = searchMessagesLike(this.db, keyword, { limit: options?.limit ?? 50 })\n return {\n messages: result.messages.map((m) => ({\n id: m.id,\n senderId: m.senderId,\n senderName: m.senderName,\n senderPlatformId: m.senderPlatformId,\n content: m.content,\n timestamp: m.timestamp,\n })),\n total: result.total ?? result.messages.length,\n }\n }\n\n async deepSearchMessages(\n keywords: string[],\n options?: { timeFilter?: TimeFilter; limit?: number; senderId?: number }\n ): Promise<SearchMessagesResult> {\n return this.searchMessages(keywords, options)\n }\n\n async getSearchMessageContext(\n messageIds: number[],\n contextBefore: number,\n contextAfter: number\n ): Promise<RawMessage[]> {\n return coreGetSearchMessageContext(this.db, messageIds, contextBefore, contextAfter)\n }\n\n async getRecentMessages(options?: { timeFilter?: TimeFilter; limit?: number }): Promise<SearchMessagesResult> {\n const messages = coreGetRecentMessages(this.db, { limit: options?.limit ?? 50 })\n return {\n messages: messages.map((m) => ({\n id: m.id,\n senderId: m.senderId,\n senderName: m.senderName,\n senderPlatformId: m.senderPlatformId,\n content: m.content,\n timestamp: m.timestamp,\n })),\n total: messages.length,\n }\n }\n\n async getMessageContext(messageIds: number[], contextSize: number): Promise<RawMessage[]> {\n return coreGetMessageContext(this.db, messageIds, contextSize)\n }\n\n async getChatOverview(topN?: number): Promise<ChatOverviewResult | null> {\n return coreGetChatOverview(this.db, topN)\n }\n\n async getMembers(): Promise<MemberInfo[]> {\n return getMembersWithAliases(this.db)\n }\n\n async getMemberStats(options?: { timeFilter?: TimeFilter; top?: number }): Promise<MemberStatItem[]> {\n const top = options?.top ?? 20\n const members = getMemberActivity(this.db, options?.timeFilter)\n return members.slice(0, top).map((m) => ({\n name: m.name,\n messageCount: m.messageCount,\n percentage: m.percentage,\n }))\n }\n\n async getMemberNameHistory(memberId: number): Promise<NameHistoryItem[]> {\n return coreGetMemberNameHistory(this.db, memberId)\n }\n\n async getTimeStats(type: 'hourly' | 'weekday' | 'daily', options?: { timeFilter?: TimeFilter }): Promise<unknown[]> {\n const filter = options?.timeFilter\n switch (type) {\n case 'weekday':\n return getWeekdayActivity(this.db, filter)\n case 'daily':\n return getDailyActivity(this.db, filter)\n case 'hourly':\n default:\n return getHourlyActivity(this.db, filter)\n }\n }\n\n async searchSegments(\n keywords?: string[],\n timeFilter?: TimeFilter,\n limit?: number,\n previewCount?: number\n ): Promise<SegmentSearchResult[]> {\n return coreSearchSegments(this.db, keywords, timeFilter, limit, previewCount)\n }\n\n async getSegmentMessages(segmentId: number, limit?: number): Promise<SegmentMessagesResult | null> {\n return coreGetSegmentMessages(this.db, segmentId, limit)\n }\n\n async getSegmentSummaries(options?: { limit?: number; timeFilter?: TimeFilter }): Promise<SegmentSummaryItem[]> {\n return coreGetSegmentSummaries(this.db, options)\n }\n\n async getConversationBetween(\n memberId1: number,\n memberId2: number,\n timeFilter?: TimeFilter,\n limit?: number\n ): Promise<ConversationResult> {\n return coreGetConversationBetween(this.db, memberId1, memberId2, timeFilter, limit)\n }\n\n async executeSql(sql: string): Promise<unknown> {\n return executeReadonlySql(this.db, sql)\n }\n\n async executeParameterizedSql<T = Record<string, unknown>>(\n query: string,\n params: Record<string, unknown>\n ): Promise<T[]> {\n return coreExecuteParameterizedSql<T>(this.db, query, params)\n }\n\n async getSchema(): Promise<SchemaTableInfo[]> {\n return getDatabaseSchema(this.db)\n }\n}\n"],"mappings":"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","names":["inputSchema","handler","inputSchema","handler","inputSchema","handler","inputSchema","handler","inputSchema","handler","inputSchema","handler","inputSchema","handler","data","inputSchema","handler","data","inputSchema","handler","data","inputSchema","handler","inputSchema","handler","inputSchema","handler","inputSchema","handler","inputSchema","handler","data","inputSchema","handler","data","inputSchema","handler","data","inputSchema","handler","t","inputSchema","handler"]}
|