chatlab-cli 0.24.1 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/chat-command-5XMRLXZM.mjs +166 -0
  2. package/dist/chat-command-5XMRLXZM.mjs.map +1 -0
  3. package/dist/{chunk-N5DGZQB4.mjs → chunk-7VG6QGQ2.mjs} +1543 -664
  4. package/dist/chunk-7VG6QGQ2.mjs.map +1 -0
  5. package/dist/{chunk-QJI6AOAT.mjs → chunk-CUCDRAY6.mjs} +17 -25
  6. package/dist/chunk-CUCDRAY6.mjs.map +1 -0
  7. package/dist/chunk-FDK6SPCG.mjs +40 -0
  8. package/dist/chunk-FDK6SPCG.mjs.map +1 -0
  9. package/dist/{chunk-ZL322NVL.mjs → chunk-GTCTDD2Z.mjs} +116 -504
  10. package/dist/chunk-GTCTDD2Z.mjs.map +1 -0
  11. package/dist/{chunk-43XWYC3M.mjs → chunk-JE3FOFF7.mjs} +219 -123
  12. package/dist/chunk-JE3FOFF7.mjs.map +1 -0
  13. package/dist/{chunk-QY6P2YS7.mjs → chunk-PDQFSQO6.mjs} +59 -33
  14. package/dist/chunk-PDQFSQO6.mjs.map +1 -0
  15. package/dist/chunk-WYHGWTA4.mjs +485 -0
  16. package/dist/chunk-WYHGWTA4.mjs.map +1 -0
  17. package/dist/{chunk-CIIQ3S2X.mjs → chunk-YZYP5ST6.mjs} +37 -8
  18. package/dist/chunk-YZYP5ST6.mjs.map +1 -0
  19. package/dist/cli.mjs +3 -2
  20. package/dist/{http-TPX74BLK.mjs → http-C3CGXUBU.mjs} +8 -6
  21. package/dist/{import-B745DRHD.mjs → import-VS7L5JDX.mjs} +4 -4
  22. package/dist/index.mjs +10 -8
  23. package/dist/{mcp-YWAVG5XW.mjs → mcp-6VXDPUQK.mjs} +11 -6
  24. package/dist/mcp-6VXDPUQK.mjs.map +1 -0
  25. package/dist/{stream-import-OPALPVWF.mjs → stream-import-TFDBNVIW.mjs} +3 -3
  26. package/dist-web/assets/{DebugToolsPanel-DaSNc1o-.css → DebugToolsPanel-vn-bnUxn.css} +1 -1
  27. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-B1xZmp9r.js +161 -0
  28. package/dist-web/assets/{EChartWordcloud-VpNJDurs.js → EChartWordcloud-CzhfrEF_.js} +1 -1
  29. package/dist-web/assets/{MessageExportModal-CSfaY25B.js → MessageExportModal-B79pPBL9.js} +1 -1
  30. package/dist-web/assets/{electron-BUeXfvxT.js → electron-DRkrjeIl.js} +1 -1
  31. package/dist-web/assets/electron-Dv8jdn_2.js +1 -0
  32. package/dist-web/assets/{fetch-RDFt3hmp.js → fetch-BmZIm6mS.js} +1 -1
  33. package/dist-web/assets/fetch-BqLy63aj.js +1 -0
  34. package/dist-web/assets/fetch-BstX-wd4.js +1 -0
  35. package/dist-web/assets/{fetch-BohUj3CQ.js → fetch-Bvcxe5PI.js} +1 -1
  36. package/dist-web/assets/{fetch-BneHn5qk.js → fetch-CDamdvtb.js} +1 -1
  37. package/dist-web/assets/{fetch-DypPduyQ.js → fetch-DWg-2kOp.js} +1 -1
  38. package/dist-web/assets/{fetch-CARsBVl0.js → fetch-DfgdagMN.js} +1 -1
  39. package/dist-web/assets/{fetch-Ck3tjoLa.js → fetch-Drk9dLaF.js} +1 -1
  40. package/dist-web/assets/{fetch-B2q6973s.js → fetch-Y-_WsCuk.js} +1 -1
  41. package/dist-web/assets/{fetch-BGMRmQx0.js → fetch-ph2_d79v.js} +1 -1
  42. package/dist-web/assets/{index-Bvlj3dMn.js → index-9urfJP1r.js} +1 -1
  43. package/dist-web/assets/index-CfKjh0Jr.js +9 -0
  44. package/dist-web/assets/index-CprJOAB1.css +1 -0
  45. package/dist-web/assets/{index-BvaWRM6_.js → index-DMWf5On7.js} +1 -1
  46. package/dist-web/assets/{index-DCWvD-4L.js → index-D_hHm7bQ.js} +2 -2
  47. package/dist-web/assets/index-MCdpiX_O.js +173 -0
  48. package/dist-web/assets/{index-C5LrQG3N.js → index-deYDOVUa.js} +4 -4
  49. package/dist-web/assets/vendor-echarts-CITQeskI.js +11 -0
  50. package/dist-web/assets/{vendor-echarts-wordcloud-CZ9gsX8l.js → vendor-echarts-wordcloud-B1kQMklB.js} +1 -1
  51. package/dist-web/assets/{vendor-nuxt-ui-BYU7dz8t.js → vendor-nuxt-ui-C_8CsH6e.js} +1 -1
  52. package/dist-web/assets/{vendor-reka-ui-CnB0mMNX.js → vendor-reka-ui-CIMXwvEp.js} +1 -1
  53. package/dist-web/assets/{web-CXPkmhis.js → web-XJV6Vj5t.js} +1 -1
  54. package/dist-web/index.html +4 -4
  55. package/package.json +1 -1
  56. package/dist/chunk-43XWYC3M.mjs.map +0 -1
  57. package/dist/chunk-CIIQ3S2X.mjs.map +0 -1
  58. package/dist/chunk-N5DGZQB4.mjs.map +0 -1
  59. package/dist/chunk-QJI6AOAT.mjs.map +0 -1
  60. package/dist/chunk-QY6P2YS7.mjs.map +0 -1
  61. package/dist/chunk-ZL322NVL.mjs.map +0 -1
  62. package/dist/mcp-YWAVG5XW.mjs.map +0 -1
  63. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BBDrsJfe.js +0 -161
  64. package/dist-web/assets/electron-BynNUuEA.js +0 -1
  65. package/dist-web/assets/fetch-BtHwWs8A.js +0 -1
  66. package/dist-web/assets/fetch-DzNuFdFM.js +0 -1
  67. package/dist-web/assets/index-BRwEubUe.js +0 -173
  68. package/dist-web/assets/index-DB-iqXzV.css +0 -1
  69. package/dist-web/assets/index-DS1rzk1I.js +0 -8
  70. package/dist-web/assets/vendor-echarts-C5u62NnM.js +0 -11
  71. /package/dist/{http-TPX74BLK.mjs.map → http-C3CGXUBU.mjs.map} +0 -0
  72. /package/dist/{import-B745DRHD.mjs.map → import-VS7L5JDX.mjs.map} +0 -0
  73. /package/dist/{stream-import-OPALPVWF.mjs.map → stream-import-TFDBNVIW.mjs.map} +0 -0
@@ -0,0 +1,485 @@
1
+ import { createRequire as __createRequire } from 'module';
2
+ import { dirname as __pathDirname } from 'path';
3
+ import { fileURLToPath as __fileURLToPath } from 'url';
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ import {
8
+ AGENT_TOOL_REGISTRY,
9
+ CoreDataProvider
10
+ } from "./chunk-JE3FOFF7.mjs";
11
+ import {
12
+ AgentEventHandler,
13
+ AiLogger,
14
+ CHART_CAPABILITY_CORE_TOOLS,
15
+ DEFAULT_MAX_TOOL_ROUNDS,
16
+ SkillManager,
17
+ applyPreprocessingPipeline,
18
+ batchSegmentWithFrequency,
19
+ buildPiModel,
20
+ buildSkillMenuWithBuiltinChart,
21
+ buildSystemPrompt,
22
+ checkAndCompress,
23
+ createActivateSkillTool,
24
+ createAiTranslate,
25
+ createChartSchemaGateState,
26
+ createCompressionLlmAdapter,
27
+ formatAIError,
28
+ getAllowedBuiltinToolsForChartAutoSkill,
29
+ getChartCapabilitySkill,
30
+ getChatOverview,
31
+ getSkillConfigWithBuiltinChart,
32
+ initTokenizer,
33
+ normalizeBuiltinToolNames,
34
+ parseAssistantFile,
35
+ resolveApiKey,
36
+ resolveChartRuntimeForRequest,
37
+ runAgentCore,
38
+ wrapWithChartSchemaGate
39
+ } from "./chunk-7VG6QGQ2.mjs";
40
+
41
+ // src/ai/logger.ts
42
+ var logger = null;
43
+ function initServerAiLogger(logsDir) {
44
+ if (!logger) {
45
+ logger = new AiLogger(logsDir);
46
+ }
47
+ return logger;
48
+ }
49
+ function getServerAiLogger() {
50
+ return logger;
51
+ }
52
+ function closeServerAiLogger() {
53
+ if (logger) {
54
+ logger.close();
55
+ logger = null;
56
+ }
57
+ }
58
+
59
+ // src/ai/tool-adapter.ts
60
+ var DEFAULT_MAX_TOOL_RESULT_TOKENS = 8e3;
61
+ var TOOL_TRUNCATION_STRATEGY = {
62
+ search_messages: "keep_first",
63
+ deep_search_messages: "keep_first",
64
+ get_recent_messages: "keep_last",
65
+ get_message_context: "keep_last",
66
+ get_segment_messages: "keep_last",
67
+ get_conversation_between: "keep_last"
68
+ };
69
+ function convertJsonSchemaToParameters(schema) {
70
+ const properties = {};
71
+ for (const [key, prop] of Object.entries(schema.properties)) {
72
+ properties[key] = { ...prop };
73
+ }
74
+ return {
75
+ type: "object",
76
+ properties,
77
+ required: schema.required || []
78
+ };
79
+ }
80
+ function adaptToolsForAgent(tools, getContext, options) {
81
+ const tokenBudget = options?.maxToolResultTokens ?? DEFAULT_MAX_TOOL_RESULT_TOKENS;
82
+ const chartSchemaGateState = createChartSchemaGateState();
83
+ return tools.map(
84
+ (tool) => wrapWithChartSchemaGate(
85
+ {
86
+ name: tool.name,
87
+ label: tool.name,
88
+ description: tool.description,
89
+ parameters: convertJsonSchemaToParameters(tool.inputSchema),
90
+ async execute(_toolCallId, params) {
91
+ const toolParams = params && typeof params === "object" ? params : {};
92
+ const ctx = getContext();
93
+ const execCtx = {
94
+ db: ctx.db,
95
+ dataProvider: new CoreDataProvider(ctx.db),
96
+ sessionId: ctx.sessionId,
97
+ locale: ctx.locale,
98
+ segmentText: (texts, locale, options2) => batchSegmentWithFrequency(texts, locale, options2)
99
+ };
100
+ try {
101
+ const result = await tool.handler(toolParams, execCtx);
102
+ const chartDetails = result.chart || result.charts ? {
103
+ ...result.chart ? { chart: result.chart } : {},
104
+ ...result.charts ? { charts: result.charts } : {}
105
+ } : {};
106
+ if (result.rawMessages && result.rawMessages.length > 0) {
107
+ const pipelineResult = applyPreprocessingPipeline({
108
+ rawMessages: result.rawMessages,
109
+ locale: ctx.locale,
110
+ maxToolResultTokens: tokenBudget,
111
+ truncationStrategy: TOOL_TRUNCATION_STRATEGY[tool.name] ?? "keep_last",
112
+ extraDetails: result.data ?? {},
113
+ logger: getServerAiLogger() ?? void 0
114
+ });
115
+ return {
116
+ content: [{ type: "text", text: pipelineResult.text }],
117
+ details: Object.keys(chartDetails).length > 0 ? chartDetails : null
118
+ };
119
+ }
120
+ const baseDetails = typeof result.data === "object" && result.data !== null ? result.data : result.data === void 0 ? null : { value: result.data };
121
+ return {
122
+ content: [{ type: "text", text: result.content }],
123
+ details: Object.keys(chartDetails).length > 0 ? { ...baseDetails ?? {}, ...chartDetails } : baseDetails
124
+ };
125
+ } catch (error) {
126
+ const msg = error instanceof Error ? error.message : String(error);
127
+ return { content: [{ type: "text", text: `Error: ${msg}` }], details: null };
128
+ }
129
+ }
130
+ },
131
+ chartSchemaGateState
132
+ )
133
+ );
134
+ }
135
+
136
+ // src/ai/llm-config.ts
137
+ import * as fs from "fs";
138
+ import * as path from "path";
139
+ function loadLlmConfig(aiDataDir) {
140
+ const configPath = path.join(aiDataDir, "llm-config.json");
141
+ if (!fs.existsSync(configPath)) {
142
+ return { configs: [], defaultAssistant: null, fastModel: null };
143
+ }
144
+ try {
145
+ const data = JSON.parse(fs.readFileSync(configPath, "utf-8"));
146
+ const configs = (data.configs || []).map((c) => {
147
+ const provider = c.provider || "";
148
+ const authProfile = c.authProfile;
149
+ const profileKey = resolveApiKey(provider, authProfile);
150
+ const rawKey = c.apiKey || "";
151
+ const fallbackKey = rawKey.startsWith("enc:") ? "" : rawKey;
152
+ return {
153
+ ...c,
154
+ apiKey: profileKey || fallbackKey
155
+ };
156
+ });
157
+ return {
158
+ configs,
159
+ defaultAssistant: data.defaultAssistant ?? null,
160
+ fastModel: data.fastModel ?? null
161
+ };
162
+ } catch {
163
+ return { configs: [], defaultAssistant: null, fastModel: null };
164
+ }
165
+ }
166
+ function resolveSlot(slot, configs) {
167
+ if (slot && configs.some((c) => c.id === slot.configId)) return slot;
168
+ const fallback = configs[0];
169
+ if (!fallback) return null;
170
+ return { configId: fallback.id, modelId: fallback.model };
171
+ }
172
+ function getDefaultAssistantConfig(aiDataDir) {
173
+ const store = loadLlmConfig(aiDataDir);
174
+ const slot = resolveSlot(store.defaultAssistant, store.configs);
175
+ if (!slot) return null;
176
+ const config = store.configs.find((c) => c.id === slot.configId);
177
+ if (!config) return null;
178
+ return { ...config, model: slot.modelId || config.model };
179
+ }
180
+ function buildPiModel2(config) {
181
+ return buildPiModel(config);
182
+ }
183
+
184
+ // src/ai/assistant-loader.ts
185
+ import * as fs2 from "fs";
186
+ import * as path2 from "path";
187
+ function loadAssistantConfig(aiDataDir, assistantId) {
188
+ const filePath = path2.join(aiDataDir, "assistants", `${assistantId}.md`);
189
+ if (!fs2.existsSync(filePath)) return null;
190
+ try {
191
+ const content = fs2.readFileSync(filePath, "utf-8");
192
+ return parseAssistantFile(content, filePath);
193
+ } catch {
194
+ return null;
195
+ }
196
+ }
197
+
198
+ // src/ai/agent.ts
199
+ async function runServerAgent(options) {
200
+ const {
201
+ userMessage,
202
+ aiChatId,
203
+ historyLeafMessageId,
204
+ chatType = "group",
205
+ locale = "zh-CN",
206
+ assistantSystemPrompt,
207
+ skillMenu,
208
+ skillDef,
209
+ compressionConfig,
210
+ tools = [],
211
+ aiDataDir,
212
+ aiChatManager,
213
+ onEvent,
214
+ abortSignal,
215
+ ownerInfo,
216
+ mentionedMembers,
217
+ dataSnapshot,
218
+ thinkingLevel
219
+ } = options;
220
+ const aiLogger = getServerAiLogger();
221
+ await initTokenizer();
222
+ const llmConfig = getDefaultAssistantConfig(aiDataDir);
223
+ if (!llmConfig) {
224
+ onEvent({ type: "error", error: { name: "ConfigError", message: "LLM service not configured" } });
225
+ onEvent({ type: "done", isFinished: true });
226
+ return;
227
+ }
228
+ const piModel = buildPiModel2(llmConfig);
229
+ const t = createAiTranslate(locale);
230
+ let skillCtx;
231
+ if (skillDef) {
232
+ skillCtx = { skillDef };
233
+ } else if (skillMenu) {
234
+ skillCtx = { skillMenu };
235
+ }
236
+ const systemPrompt = buildSystemPrompt({
237
+ t,
238
+ chatType,
239
+ assistantSystemPrompt,
240
+ ownerInfo,
241
+ locale,
242
+ skillCtx,
243
+ mentionedMembers,
244
+ dataSnapshot
245
+ });
246
+ const handler = new AgentEventHandler({
247
+ onChunk: onEvent,
248
+ context: {},
249
+ systemPrompt
250
+ });
251
+ if (compressionConfig?.enabled && historyLeafMessageId === void 0) {
252
+ const llmAdapter = createCompressionLlmAdapter({
253
+ piModel,
254
+ apiKey: llmConfig.apiKey,
255
+ onCompressing: () => handler.emitStatus("compressing", [])
256
+ });
257
+ const compressionResult = await checkAndCompress(
258
+ aiChatId,
259
+ compressionConfig,
260
+ systemPrompt,
261
+ llmAdapter,
262
+ aiChatManager,
263
+ aiLogger ?? void 0
264
+ );
265
+ if (compressionResult.compressed) {
266
+ onEvent({
267
+ type: "compression_done",
268
+ compressionResult: {
269
+ summaryContent: compressionResult.summaryContent ?? "",
270
+ tokensBefore: compressionResult.tokensBefore ?? 0,
271
+ tokensAfter: compressionResult.tokensAfter ?? 0,
272
+ timestamp: Date.now()
273
+ }
274
+ });
275
+ }
276
+ } else if (compressionConfig?.enabled && historyLeafMessageId !== void 0) {
277
+ aiLogger?.info?.("Compression", "Skipping compression for edited branch request", {
278
+ aiChatId,
279
+ historyLeafMessageId
280
+ });
281
+ }
282
+ if (abortSignal?.aborted) {
283
+ handler.emitStatus("aborted", [], { force: true });
284
+ onEvent({ type: "done", isFinished: true, usage: handler.cloneUsage() });
285
+ return;
286
+ }
287
+ let history = [];
288
+ try {
289
+ history = aiChatManager.getHistoryForAgent(aiChatId, void 0, historyLeafMessageId);
290
+ } catch {
291
+ }
292
+ handler.emitStatus("preparing", [], { pendingUserMessage: userMessage, force: true });
293
+ const steerMessage = t("ai.agent.answerWithoutTools");
294
+ let cachedMessages = [];
295
+ try {
296
+ const result = await runAgentCore({
297
+ piModel,
298
+ apiKey: llmConfig.apiKey,
299
+ systemPrompt,
300
+ tools,
301
+ history,
302
+ userMessage,
303
+ maxToolRounds: DEFAULT_MAX_TOOL_ROUNDS,
304
+ abortSignal,
305
+ steerMessage,
306
+ thinkingLevel,
307
+ onConvertToLlm: (filteredMessages) => {
308
+ cachedMessages = filteredMessages;
309
+ },
310
+ onEvent: (coreEvent) => handler.handleCoreEvent(coreEvent, cachedMessages),
311
+ onDebugContext: (messages) => {
312
+ try {
313
+ aiChatManager.setPendingDebugContext(aiChatId, JSON.stringify(messages, null, 2));
314
+ } catch {
315
+ }
316
+ }
317
+ });
318
+ if (result.error) {
319
+ const friendlyMessage = formatAIError(result.error);
320
+ onEvent({ type: "error", error: { name: "AgentError", message: friendlyMessage } });
321
+ }
322
+ handler.emitStatus("completed", cachedMessages, { force: true });
323
+ onEvent({ type: "done", isFinished: true, usage: result.usage });
324
+ } catch (error) {
325
+ const friendlyMessage = formatAIError(error);
326
+ aiLogger?.error("ServerAgent", "Agent execution error", { error: String(error) });
327
+ handler.emitStatus("error", cachedMessages, { force: true });
328
+ onEvent({ type: "error", error: { name: "AgentError", message: friendlyMessage } });
329
+ onEvent({ type: "done", isFinished: true, usage: handler.cloneUsage() });
330
+ }
331
+ }
332
+
333
+ // src/ai/agent-stream-runner.ts
334
+ function getAiDir(dbManager) {
335
+ const pathProvider = dbManager["pathProvider"];
336
+ if (!pathProvider) {
337
+ throw Object.assign(new Error("PathProvider not available"), { statusCode: 500 });
338
+ }
339
+ return pathProvider.getAiDataDir();
340
+ }
341
+ var RAW_SQL_TOOL_NAMES = /* @__PURE__ */ new Set(["execute_sql"]);
342
+ function getAvailableToolDefs(isChartCapability, allowedToolSet) {
343
+ if (isChartCapability) {
344
+ const chartCoreTools = new Set(CHART_CAPABILITY_CORE_TOOLS);
345
+ return AGENT_TOOL_REGISTRY.filter(
346
+ (tool) => chartCoreTools.has(tool.name) || tool.category === "analysis" && allowedToolSet?.has(tool.name) && !RAW_SQL_TOOL_NAMES.has(tool.name)
347
+ );
348
+ }
349
+ return allowedToolSet ? AGENT_TOOL_REGISTRY.filter((tool) => tool.category !== "analysis" || allowedToolSet.has(tool.name)) : AGENT_TOOL_REGISTRY;
350
+ }
351
+ function getAllowedToolSet(isChartCapability, allowedBuiltinTools) {
352
+ if (isChartCapability) {
353
+ return new Set(normalizeBuiltinToolNames(allowedBuiltinTools ?? []));
354
+ }
355
+ return allowedBuiltinTools === void 0 ? null : new Set(normalizeBuiltinToolNames(allowedBuiltinTools));
356
+ }
357
+ function createCliRunAgentStream(dbManager, aiChatManager) {
358
+ return async (params, onEvent, abortSignal) => {
359
+ const {
360
+ userMessage,
361
+ aiChatId,
362
+ historyLeafMessageId,
363
+ sessionId,
364
+ chatType,
365
+ locale,
366
+ assistantId,
367
+ skillId,
368
+ enableAutoSkill,
369
+ compressionConfig,
370
+ ownerInfo,
371
+ mentionedMembers,
372
+ thinkingLevel
373
+ } = params;
374
+ const aiDataDir = getAiDir(dbManager);
375
+ let assistantSystemPrompt;
376
+ let assistantAllowedTools;
377
+ if (assistantId) {
378
+ const assistantConfig = loadAssistantConfig(aiDataDir, assistantId);
379
+ if (assistantConfig?.systemPrompt) {
380
+ assistantSystemPrompt = assistantConfig.systemPrompt;
381
+ }
382
+ assistantAllowedTools = assistantConfig?.allowedBuiltinTools;
383
+ }
384
+ const llmConfig = getDefaultAssistantConfig(aiDataDir);
385
+ const maxToolResultPercent = compressionConfig?.maxToolResultPercent ?? 50;
386
+ const contextWindow = llmConfig ? buildPiModel2(llmConfig).contextWindow ?? 128e3 : 128e3;
387
+ const maxToolResultTokens = Math.floor(contextWindow * (maxToolResultPercent / 100));
388
+ const db = dbManager.open?.(sessionId);
389
+ const chartRuntime = resolveChartRuntimeForRequest({
390
+ skillId,
391
+ userMessage,
392
+ locale,
393
+ assistantAllowedTools
394
+ });
395
+ const isChartCapability = chartRuntime.isChartCapability;
396
+ const autoSkillAllowedTools = !skillId && enableAutoSkill ? getAllowedBuiltinToolsForChartAutoSkill(assistantAllowedTools) : assistantAllowedTools;
397
+ const allowedToolSet = getAllowedToolSet(
398
+ isChartCapability,
399
+ isChartCapability ? chartRuntime.allowedBuiltinTools : autoSkillAllowedTools
400
+ );
401
+ const availableToolDefs = getAvailableToolDefs(isChartCapability, allowedToolSet);
402
+ const agentTools = db ? adaptToolsForAgent(availableToolDefs, () => ({ db, sessionId, locale }), { maxToolResultTokens }) : [];
403
+ const skillMgr = new SkillManager(aiDataDir);
404
+ skillMgr.init();
405
+ const toolNames = agentTools.map((t) => t.name);
406
+ let resolvedSkillDef;
407
+ let resolvedSkillMenu;
408
+ if (isChartCapability) {
409
+ const def = chartRuntime.skillDef ?? getChartCapabilitySkill(locale ?? "zh-CN");
410
+ resolvedSkillDef = { name: def.name, prompt: def.prompt };
411
+ } else if (skillId) {
412
+ const def = skillMgr.getSkillConfig(skillId);
413
+ if (def) resolvedSkillDef = { name: def.name, prompt: def.prompt };
414
+ } else if (enableAutoSkill) {
415
+ const menu = buildSkillMenuWithBuiltinChart(
416
+ skillMgr.getSkillMenu(chatType ?? "group", toolNames),
417
+ locale,
418
+ toolNames
419
+ );
420
+ if (menu) resolvedSkillMenu = menu;
421
+ }
422
+ if (resolvedSkillMenu) {
423
+ const activateSkillTool = createActivateSkillTool({
424
+ chatType: chatType ?? "group",
425
+ allowedTools: toolNames,
426
+ coreToolNames: new Set(CHART_CAPABILITY_CORE_TOOLS),
427
+ locale,
428
+ getSkillConfig: (id) => getSkillConfigWithBuiltinChart(id, locale, (skillConfigId) => skillMgr.getSkillConfig(skillConfigId))
429
+ });
430
+ agentTools.push(activateSkillTool);
431
+ }
432
+ const resolvedCompression = compressionConfig?.enabled ? {
433
+ enabled: true,
434
+ tokenThresholdPercent: compressionConfig.tokenThresholdPercent ?? 75,
435
+ bufferSizePercent: compressionConfig.bufferSizePercent ?? 20,
436
+ maxToolResultPercent: compressionConfig.maxToolResultPercent
437
+ } : void 0;
438
+ let dataSnapshot;
439
+ if (db) {
440
+ try {
441
+ const overview = getChatOverview(db, 5);
442
+ if (overview) {
443
+ dataSnapshot = {
444
+ name: overview.name,
445
+ platform: overview.platform,
446
+ type: overview.type,
447
+ totalMessages: overview.totalMessages,
448
+ totalMembers: overview.totalMembers,
449
+ firstMessageTs: overview.firstMessageTs,
450
+ lastMessageTs: overview.lastMessageTs,
451
+ capturedAt: Math.floor(Date.now() / 1e3)
452
+ };
453
+ }
454
+ } catch {
455
+ }
456
+ }
457
+ await runServerAgent({
458
+ userMessage,
459
+ aiChatId,
460
+ historyLeafMessageId,
461
+ chatType,
462
+ locale,
463
+ assistantSystemPrompt,
464
+ skillMenu: resolvedSkillMenu,
465
+ skillDef: resolvedSkillDef,
466
+ compressionConfig: resolvedCompression,
467
+ tools: agentTools,
468
+ aiDataDir,
469
+ aiChatManager,
470
+ onEvent,
471
+ abortSignal,
472
+ ownerInfo,
473
+ mentionedMembers,
474
+ dataSnapshot,
475
+ thinkingLevel
476
+ });
477
+ };
478
+ }
479
+
480
+ export {
481
+ initServerAiLogger,
482
+ closeServerAiLogger,
483
+ createCliRunAgentStream
484
+ };
485
+ //# sourceMappingURL=chunk-WYHGWTA4.mjs.map
@@ -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 runAgentCore,\n checkAndCompress,\n buildSystemPrompt,\n createAiTranslate,\n createCompressionLlmAdapter,\n AgentEventHandler,\n formatAIError,\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'\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}\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 } = 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\n try {\n const result = await runAgentCore({\n piModel,\n apiKey: llmConfig.apiKey,\n systemPrompt,\n tools,\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 getAllowedBuiltinToolsForChartAutoSkill,\n getChartCapabilitySkill,\n getSkillConfigWithBuiltinChart,\n resolveChartRuntimeForRequest,\n} from '@openchatlab/node-runtime'\nimport { getChatOverview, normalizeBuiltinToolNames } from '@openchatlab/core'\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 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 chartRuntime = resolveChartRuntimeForRequest({\n skillId,\n userMessage,\n locale,\n assistantAllowedTools,\n })\n const isChartCapability = chartRuntime.isChartCapability\n const autoSkillAllowedTools =\n !skillId && enableAutoSkill\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 menu = buildSkillMenuWithBuiltinChart(\n skillMgr.getSkillMenu(chatType ?? 'group', toolNames),\n locale,\n toolNames\n )\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 getSkillConfigWithBuiltinChart(id, locale, (skillConfigId) => skillMgr.getSkillConfig(skillConfigId)),\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 const overview = getChatOverview(db, 5)\n if (overview) {\n dataSnapshot = {\n name: overview.name,\n platform: overview.platform,\n type: overview.type,\n totalMessages: overview.totalMessages,\n totalMembers: overview.totalMembers,\n firstMessageTs: overview.firstMessageTs,\n lastMessageTs: overview.lastMessageTs,\n capturedAt: Math.floor(Date.now() / 1000),\n }\n }\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 })\n }\n}\n"],"mappings":"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","names":["options","buildPiModel","fs","path","buildPiModel","buildPiModel"]}
@@ -4,10 +4,14 @@ import { fileURLToPath as __fileURLToPath } from 'url';
4
4
  const require = __createRequire(import.meta.url);
5
5
  const __filename = __fileURLToPath(import.meta.url);
6
6
  const __dirname = __pathDirname(__filename);
7
+ import {
8
+ assertCliDataDirCompatible
9
+ } from "./chunk-FDK6SPCG.mjs";
7
10
  import {
8
11
  getVersion
9
12
  } from "./chunk-5ADKYYNA.mjs";
10
13
  import {
14
+ AIChatManager,
11
15
  DEFAULT_API_PORT,
12
16
  DatabaseManager,
13
17
  NodePathProvider,
@@ -22,7 +26,7 @@ import {
22
26
  loadConfig,
23
27
  searchMessagesLike,
24
28
  verifyCliDataPath
25
- } from "./chunk-N5DGZQB4.mjs";
29
+ } from "./chunk-7VG6QGQ2.mjs";
26
30
  import {
27
31
  resolveCliPath
28
32
  } from "./chunk-FJ35V3TE.mjs";
@@ -219,7 +223,7 @@ program.command("import <file>").description("Import a chat history file (14+ fo
219
223
  console.error(`File not found: ${file}`);
220
224
  process.exit(1);
221
225
  }
222
- const { streamImport, detectFormat } = await import("./import-B745DRHD.mjs");
226
+ const { streamImport, detectFormat } = await import("./import-VS7L5JDX.mjs");
223
227
  const { dbManager } = initRuntime();
224
228
  const nativeBinding = resolveNativeBinding();
225
229
  const format = detectFormat(file);
@@ -261,7 +265,7 @@ Import error: ${err instanceof Error ? err.message : err}`);
261
265
  }
262
266
  });
263
267
  program.command("formats").description("List all supported chat history formats").action(async () => {
264
- const { getSupportedFormats } = await import("./import-B745DRHD.mjs");
268
+ const { getSupportedFormats } = await import("./import-VS7L5JDX.mjs");
265
269
  const formats = getSupportedFormats();
266
270
  console.log(`${formats.length} supported format(s):
267
271
  `);
@@ -270,9 +274,33 @@ program.command("formats").description("List all supported chat history formats"
270
274
  }
271
275
  });
272
276
  program.command("mcp").description("Start MCP Server (stdio transport, for ClaudeCode / Cursor / AI agents)").action(async () => {
273
- const { startCliMcpServer } = await import("./mcp-YWAVG5XW.mjs");
277
+ const { startCliMcpServer } = await import("./mcp-6VXDPUQK.mjs");
274
278
  await startCliMcpServer();
275
279
  });
280
+ program.command("chat").description("Ask ChatLab AI about an imported chat session").option("--session-id <id>", "Source chat session ID").option("--ai-chat-id <id>", "Existing AI chat ID to continue").option("-q, --question <text>", "Question to ask").option("--json", "Output structured JSON").option("--no-stream", "Disable streaming output").option("--locale <locale>", "AI response locale", "zh-CN").action(async (options) => {
281
+ const { runChatCommand } = await import("./chat-command-5XMRLXZM.mjs");
282
+ const { dbManager, pathProvider } = initRuntime();
283
+ const aiChatManager = new AIChatManager(pathProvider.getAiDataDir(), { nativeBinding: resolveNativeBinding() });
284
+ try {
285
+ await runChatCommand(
286
+ {
287
+ sessionId: options.sessionId,
288
+ aiChatId: options.aiChatId,
289
+ question: options.question,
290
+ json: !!options.json,
291
+ stream: options.stream,
292
+ locale: options.locale
293
+ },
294
+ { dbManager, pathProvider, aiChatManager }
295
+ );
296
+ } catch (error) {
297
+ console.error(error instanceof Error ? error.message : String(error));
298
+ process.exitCode = 1;
299
+ } finally {
300
+ aiChatManager.close();
301
+ dbManager.closeAll();
302
+ }
303
+ });
276
304
  program.command("start").description("Start ChatLab (HTTP API + Web UI)").option("--port <port>", "Server port", String(DEFAULT_API_PORT)).option("--host <host>", "Listen address", "127.0.0.1").option("--token <token>", "Custom Bearer Token (reads from config or auto-generates if omitted)").option("--headless", "API-only mode, do not serve the Web UI").option("--require-auth", "Require Bearer token for all routes including /_web/*").option("--no-open", "Do not auto-open the browser").option("--daemon", "Run as a resident system service (auto-start on login, macOS/Linux)").action(async (options) => {
277
305
  if (options.daemon) {
278
306
  const { serviceInstall } = await import("./service-KZTHBDQ5.mjs");
@@ -285,7 +313,7 @@ program.command("start").description("Start ChatLab (HTTP API + Web UI)").option
285
313
  });
286
314
  return;
287
315
  }
288
- const { startHttpServer } = await import("./http-TPX74BLK.mjs");
316
+ const { startHttpServer } = await import("./http-C3CGXUBU.mjs");
289
317
  const port = parseInt(options.port, 10);
290
318
  let webRoot;
291
319
  if (!options.headless) {
@@ -332,7 +360,7 @@ Press Ctrl+C to stop.
332
360
  }
333
361
  const shutdown = async () => {
334
362
  console.log("\nShutting down...");
335
- const { stopHttpServer } = await import("./http-TPX74BLK.mjs");
363
+ const { stopHttpServer } = await import("./http-C3CGXUBU.mjs");
336
364
  await stopHttpServer();
337
365
  process.exit(0);
338
366
  };
@@ -409,12 +437,13 @@ function initRuntime() {
409
437
  const userDataDir = config.data.user_data_dir || void 0;
410
438
  const pathProvider = new NodePathProvider(userDataDir);
411
439
  pathProvider.ensureAllDirs();
440
+ const runtime = assertCliDataDirCompatible(pathProvider, "cli");
412
441
  if (hasPendingElectronDataWarning() || !verifyCliDataPath(pathProvider.getDatabaseDir())) {
413
442
  printElectronDataError();
414
443
  process.exit(1);
415
444
  }
416
445
  const nativeBinding = resolveNativeBinding();
417
- const dbManager = new DatabaseManager(pathProvider, { nativeBinding });
446
+ const dbManager = new DatabaseManager(pathProvider, { nativeBinding, runtime });
418
447
  return { config, pathProvider, dbManager };
419
448
  }
420
449
  function printElectronDataError() {
@@ -475,4 +504,4 @@ if (isDirectRun) {
475
504
  export {
476
505
  run
477
506
  };
478
- //# sourceMappingURL=chunk-CIIQ3S2X.mjs.map
507
+ //# sourceMappingURL=chunk-YZYP5ST6.mjs.map