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.
- package/dist/chat-command-5XMRLXZM.mjs +166 -0
- package/dist/chat-command-5XMRLXZM.mjs.map +1 -0
- package/dist/{chunk-N5DGZQB4.mjs → chunk-7VG6QGQ2.mjs} +1543 -664
- package/dist/chunk-7VG6QGQ2.mjs.map +1 -0
- package/dist/{chunk-QJI6AOAT.mjs → chunk-CUCDRAY6.mjs} +17 -25
- package/dist/chunk-CUCDRAY6.mjs.map +1 -0
- package/dist/chunk-FDK6SPCG.mjs +40 -0
- package/dist/chunk-FDK6SPCG.mjs.map +1 -0
- package/dist/{chunk-ZL322NVL.mjs → chunk-GTCTDD2Z.mjs} +116 -504
- package/dist/chunk-GTCTDD2Z.mjs.map +1 -0
- package/dist/{chunk-43XWYC3M.mjs → chunk-JE3FOFF7.mjs} +219 -123
- package/dist/chunk-JE3FOFF7.mjs.map +1 -0
- package/dist/{chunk-QY6P2YS7.mjs → chunk-PDQFSQO6.mjs} +59 -33
- package/dist/chunk-PDQFSQO6.mjs.map +1 -0
- package/dist/chunk-WYHGWTA4.mjs +485 -0
- package/dist/chunk-WYHGWTA4.mjs.map +1 -0
- package/dist/{chunk-CIIQ3S2X.mjs → chunk-YZYP5ST6.mjs} +37 -8
- package/dist/chunk-YZYP5ST6.mjs.map +1 -0
- package/dist/cli.mjs +3 -2
- package/dist/{http-TPX74BLK.mjs → http-C3CGXUBU.mjs} +8 -6
- package/dist/{import-B745DRHD.mjs → import-VS7L5JDX.mjs} +4 -4
- package/dist/index.mjs +10 -8
- package/dist/{mcp-YWAVG5XW.mjs → mcp-6VXDPUQK.mjs} +11 -6
- package/dist/mcp-6VXDPUQK.mjs.map +1 -0
- package/dist/{stream-import-OPALPVWF.mjs → stream-import-TFDBNVIW.mjs} +3 -3
- package/dist-web/assets/{DebugToolsPanel-DaSNc1o-.css → DebugToolsPanel-vn-bnUxn.css} +1 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-B1xZmp9r.js +161 -0
- package/dist-web/assets/{EChartWordcloud-VpNJDurs.js → EChartWordcloud-CzhfrEF_.js} +1 -1
- package/dist-web/assets/{MessageExportModal-CSfaY25B.js → MessageExportModal-B79pPBL9.js} +1 -1
- package/dist-web/assets/{electron-BUeXfvxT.js → electron-DRkrjeIl.js} +1 -1
- package/dist-web/assets/electron-Dv8jdn_2.js +1 -0
- package/dist-web/assets/{fetch-RDFt3hmp.js → fetch-BmZIm6mS.js} +1 -1
- package/dist-web/assets/fetch-BqLy63aj.js +1 -0
- package/dist-web/assets/fetch-BstX-wd4.js +1 -0
- package/dist-web/assets/{fetch-BohUj3CQ.js → fetch-Bvcxe5PI.js} +1 -1
- package/dist-web/assets/{fetch-BneHn5qk.js → fetch-CDamdvtb.js} +1 -1
- package/dist-web/assets/{fetch-DypPduyQ.js → fetch-DWg-2kOp.js} +1 -1
- package/dist-web/assets/{fetch-CARsBVl0.js → fetch-DfgdagMN.js} +1 -1
- package/dist-web/assets/{fetch-Ck3tjoLa.js → fetch-Drk9dLaF.js} +1 -1
- package/dist-web/assets/{fetch-B2q6973s.js → fetch-Y-_WsCuk.js} +1 -1
- package/dist-web/assets/{fetch-BGMRmQx0.js → fetch-ph2_d79v.js} +1 -1
- package/dist-web/assets/{index-Bvlj3dMn.js → index-9urfJP1r.js} +1 -1
- package/dist-web/assets/index-CfKjh0Jr.js +9 -0
- package/dist-web/assets/index-CprJOAB1.css +1 -0
- package/dist-web/assets/{index-BvaWRM6_.js → index-DMWf5On7.js} +1 -1
- package/dist-web/assets/{index-DCWvD-4L.js → index-D_hHm7bQ.js} +2 -2
- package/dist-web/assets/index-MCdpiX_O.js +173 -0
- package/dist-web/assets/{index-C5LrQG3N.js → index-deYDOVUa.js} +4 -4
- package/dist-web/assets/vendor-echarts-CITQeskI.js +11 -0
- package/dist-web/assets/{vendor-echarts-wordcloud-CZ9gsX8l.js → vendor-echarts-wordcloud-B1kQMklB.js} +1 -1
- package/dist-web/assets/{vendor-nuxt-ui-BYU7dz8t.js → vendor-nuxt-ui-C_8CsH6e.js} +1 -1
- package/dist-web/assets/{vendor-reka-ui-CnB0mMNX.js → vendor-reka-ui-CIMXwvEp.js} +1 -1
- package/dist-web/assets/{web-CXPkmhis.js → web-XJV6Vj5t.js} +1 -1
- package/dist-web/index.html +4 -4
- package/package.json +1 -1
- package/dist/chunk-43XWYC3M.mjs.map +0 -1
- package/dist/chunk-CIIQ3S2X.mjs.map +0 -1
- package/dist/chunk-N5DGZQB4.mjs.map +0 -1
- package/dist/chunk-QJI6AOAT.mjs.map +0 -1
- package/dist/chunk-QY6P2YS7.mjs.map +0 -1
- package/dist/chunk-ZL322NVL.mjs.map +0 -1
- package/dist/mcp-YWAVG5XW.mjs.map +0 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BBDrsJfe.js +0 -161
- package/dist-web/assets/electron-BynNUuEA.js +0 -1
- package/dist-web/assets/fetch-BtHwWs8A.js +0 -1
- package/dist-web/assets/fetch-DzNuFdFM.js +0 -1
- package/dist-web/assets/index-BRwEubUe.js +0 -173
- package/dist-web/assets/index-DB-iqXzV.css +0 -1
- package/dist-web/assets/index-DS1rzk1I.js +0 -8
- package/dist-web/assets/vendor-echarts-C5u62NnM.js +0 -11
- /package/dist/{http-TPX74BLK.mjs.map → http-C3CGXUBU.mjs.map} +0 -0
- /package/dist/{import-B745DRHD.mjs.map → import-VS7L5JDX.mjs.map} +0 -0
- /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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,IAAI,SAA0B;AAEvB,SAAS,mBAAmB,SAA2B;AAC5D,MAAI,CAAC,QAAQ;AACX,aAAS,IAAI,SAAS,OAAO;AAAA,EAC/B;AACA,SAAO;AACT;AAEO,SAAS,oBAAqC;AACnD,SAAO;AACT;AAEO,SAAS,sBAA4B;AAC1C,MAAI,QAAQ;AACV,WAAO,MAAM;AACb,aAAS;AAAA,EACX;AACF;;;ACLA,IAAM,iCAAiC;AAEvC,IAAM,2BAA+D;AAAA,EACnE,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,sBAAsB;AAAA,EACtB,0BAA0B;AAC5B;AAQA,SAAS,8BAA8B,QAAuC;AAC5E,QAAM,aAAsC,CAAC;AAC7C,aAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,OAAO,UAAU,GAAG;AAC3D,eAAW,GAAG,IAAI,EAAE,GAAG,KAAK;AAAA,EAC9B;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,UAAU,OAAO,YAAY,CAAC;AAAA,EAChC;AACF;AAMO,SAAS,mBACd,OACA,YACA,SACuB;AACvB,QAAM,cAAc,SAAS,uBAAuB;AACpD,QAAM,uBAAuB,2BAA2B;AAExD,SAAO,MAAM;AAAA,IAAI,CAAC,SAChB;AAAA,MACE;AAAA,QACE,MAAM,KAAK;AAAA,QACX,OAAO,KAAK;AAAA,QACZ,aAAa,KAAK;AAAA,QAClB,YAAY,8BAA8B,KAAK,WAAW;AAAA,QAC1D,MAAM,QAAQ,aAAqB,QAAoD;AACrF,gBAAM,aAAc,UAAU,OAAO,WAAW,WAAW,SAAS,CAAC;AACrE,gBAAM,MAAM,WAAW;AACvB,gBAAM,UAAgC;AAAA,YACpC,IAAI,IAAI;AAAA,YACR,cAAc,IAAI,iBAAiB,IAAI,EAAE;AAAA,YACzC,WAAW,IAAI;AAAA,YACf,QAAQ,IAAI;AAAA,YACZ,aAAa,CAAC,OAAO,QAAQA,aAAY,0BAA0B,OAAO,QAAeA,QAAc;AAAA,UACzG;AACA,cAAI;AACF,kBAAM,SAAS,MAAM,KAAK,QAAQ,YAAY,OAAO;AACrD,kBAAM,eACJ,OAAO,SAAS,OAAO,SACnB;AAAA,cACE,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,cAC9C,GAAI,OAAO,SAAS,EAAE,QAAQ,OAAO,OAAO,IAAI,CAAC;AAAA,YACnD,IACA,CAAC;AAEP,gBAAI,OAAO,eAAe,OAAO,YAAY,SAAS,GAAG;AACvD,oBAAM,iBAAiB,2BAA2B;AAAA,gBAChD,aAAa,OAAO;AAAA,gBACpB,QAAQ,IAAI;AAAA,gBACZ,qBAAqB;AAAA,gBACrB,oBAAoB,yBAAyB,KAAK,IAAI,KAAK;AAAA,gBAC3D,cAAe,OAAO,QAAQ,CAAC;AAAA,gBAC/B,QAAQ,kBAAkB,KAAK;AAAA,cACjC,CAAC;AACD,qBAAO;AAAA,gBACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,eAAe,KAAK,CAAC;AAAA,gBACrD,SAAS,OAAO,KAAK,YAAY,EAAE,SAAS,IAAI,eAAe;AAAA,cACjE;AAAA,YACF;AAEA,kBAAM,cACJ,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS,OAC9C,OAAO,OACR,OAAO,SAAS,SACd,OACA,EAAE,OAAO,OAAO,KAAK;AAE7B,mBAAO;AAAA,cACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,QAAQ,CAAC;AAAA,cAChD,SAAS,OAAO,KAAK,YAAY,EAAE,SAAS,IAAI,EAAE,GAAI,eAAe,CAAC,GAAI,GAAG,aAAa,IAAI;AAAA,YAChG;AAAA,UACF,SAAS,OAAO;AACd,kBAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACjE,mBAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,SAAS,KAAK;AAAA,UAC7E;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;;;ACpHA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAkCf,SAAS,cAAc,WAAkC;AAC9D,QAAM,aAAkB,UAAK,WAAW,iBAAiB;AACzD,MAAI,CAAI,cAAW,UAAU,GAAG;AAC9B,WAAO,EAAE,SAAS,CAAC,GAAG,kBAAkB,MAAM,WAAW,KAAK;AAAA,EAChE;AAEA,MAAI;AACF,UAAM,OAAO,KAAK,MAAS,gBAAa,YAAY,OAAO,CAAC;AAE5D,UAAM,WAAW,KAAK,WAAW,CAAC,GAAG,IAAI,CAAC,MAA+B;AACvE,YAAM,WAAY,EAAE,YAAuB;AAC3C,YAAM,cAAc,EAAE;AACtB,YAAM,aAAa,cAAc,UAAU,WAAW;AACtD,YAAM,SAAU,EAAE,UAAqB;AACvC,YAAM,cAAc,OAAO,WAAW,MAAM,IAAI,KAAK;AACrD,aAAO;AAAA,QACL,GAAG;AAAA,QACH,QAAQ,cAAc;AAAA,MACxB;AAAA,IACF,CAAC;AACD,WAAO;AAAA,MACL;AAAA,MACA,kBAAkB,KAAK,oBAAoB;AAAA,MAC3C,WAAW,KAAK,aAAa;AAAA,IAC/B;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,SAAS,CAAC,GAAG,kBAAkB,MAAM,WAAW,KAAK;AAAA,EAChE;AACF;AAEA,SAAS,YAAY,MAAoC,SAA8C;AACrG,MAAI,QAAQ,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,QAAQ,EAAG,QAAO;AAChE,QAAM,WAAW,QAAQ,CAAC;AAC1B,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,EAAE,UAAU,SAAS,IAAI,SAAS,SAAS,MAAM;AAC1D;AAEO,SAAS,0BAA0B,WAA2C;AACnF,QAAM,QAAQ,cAAc,SAAS;AACrC,QAAM,OAAO,YAAY,MAAM,kBAAkB,MAAM,OAAO;AAC9D,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,SAAS,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,QAAQ;AAC/D,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,EAAE,GAAG,QAAQ,OAAO,KAAK,WAAW,OAAO,MAAM;AAC1D;AAqKO,SAASC,cAAa,QAAyC;AACpE,SAAO,aAAiB,MAAuB;AACjD;;;ACzPA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAIf,SAAS,oBAAoB,WAAmB,aAA6C;AAClG,QAAM,WAAgB,WAAK,WAAW,cAAc,GAAG,WAAW,KAAK;AACvE,MAAI,CAAI,eAAW,QAAQ,EAAG,QAAO;AAErC,MAAI;AACF,UAAM,UAAa,iBAAa,UAAU,OAAO;AACjD,WAAO,mBAAmB,SAAS,QAAQ;AAAA,EAC7C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACiCA,eAAsB,eAAe,SAAyC;AAC5E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,CAAC;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,WAAW,kBAAkB;AAGnC,QAAM,cAAc;AAEpB,QAAM,YAAY,0BAA0B,SAAS;AACrD,MAAI,CAAC,WAAW;AACd,YAAQ,EAAE,MAAM,SAAS,OAAO,EAAE,MAAM,eAAe,SAAS,6BAA6B,EAAE,CAAC;AAChG,YAAQ,EAAE,MAAM,QAAQ,YAAY,KAAK,CAAC;AAC1C;AAAA,EACF;AAEA,QAAM,UAAUC,cAAa,SAAS;AACtC,QAAM,IAAI,kBAAkB,MAAM;AAElC,MAAI;AACJ,MAAI,UAAU;AACZ,eAAW,EAAE,SAAS;AAAA,EACxB,WAAW,WAAW;AACpB,eAAW,EAAE,UAAU;AAAA,EACzB;AAEA,QAAM,eAAe,kBAAkB;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,UAAU,IAAI,kBAAkB;AAAA,IACpC,SAAS;AAAA,IACT,SAAS,CAAC;AAAA,IACV;AAAA,EACF,CAAC;AAED,MAAI,mBAAmB,WAAW,yBAAyB,QAAW;AACpE,UAAM,aAAa,4BAA4B;AAAA,MAC7C;AAAA,MACA,QAAQ,UAAU;AAAA,MAClB,eAAe,MAAM,QAAQ,WAAW,eAAe,CAAC,CAAC;AAAA,IAC3D,CAAC;AACD,UAAM,oBAAoB,MAAM;AAAA,MAC9B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,IACd;AACA,QAAI,kBAAkB,YAAY;AAChC,cAAQ;AAAA,QACN,MAAM;AAAA,QACN,mBAAmB;AAAA,UACjB,gBAAgB,kBAAkB,kBAAkB;AAAA,UACpD,cAAc,kBAAkB,gBAAgB;AAAA,UAChD,aAAa,kBAAkB,eAAe;AAAA,UAC9C,WAAW,KAAK,IAAI;AAAA,QACtB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,WAAW,mBAAmB,WAAW,yBAAyB,QAAW;AAC3E,cAAU,OAAO,eAAe,kDAAkD;AAAA,MAChF;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,aAAa,SAAS;AACxB,YAAQ,WAAW,WAAW,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC;AACjD,YAAQ,EAAE,MAAM,QAAQ,YAAY,MAAM,OAAO,QAAQ,WAAW,EAAE,CAAC;AACvE;AAAA,EACF;AAEA,MAAI,UAAkC,CAAC;AACvC,MAAI;AACF,cAAU,cAAc,mBAAmB,UAAU,QAAW,oBAAoB;AAAA,EACtF,QAAQ;AAAA,EAER;AAEA,UAAQ,WAAW,aAAa,CAAC,GAAG,EAAE,oBAAoB,aAAa,OAAO,KAAK,CAAC;AAEpF,QAAM,eAAe,EAAE,6BAA6B;AACpD,MAAI,iBAA8B,CAAC;AAEnC,MAAI;AACF,UAAM,SAAS,MAAM,aAAa;AAAA,MAChC;AAAA,MACA,QAAQ,UAAU;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB,CAAC,qBAAqB;AACpC,yBAAiB;AAAA,MACnB;AAAA,MACA,SAAS,CAAC,cAAc,QAAQ,gBAAgB,WAAW,cAAc;AAAA,MACzE,gBAAgB,CAAC,aAAa;AAC5B,YAAI;AACF,wBAAc,uBAAuB,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,QAClF,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,CAAC;AAED,QAAI,OAAO,OAAO;AAChB,YAAM,kBAAkB,cAAc,OAAO,KAAK;AAClD,cAAQ,EAAE,MAAM,SAAS,OAAO,EAAE,MAAM,cAAc,SAAS,gBAAgB,EAAE,CAAC;AAAA,IACpF;AAEA,YAAQ,WAAW,aAAa,gBAAgB,EAAE,OAAO,KAAK,CAAC;AAC/D,YAAQ,EAAE,MAAM,QAAQ,YAAY,MAAM,OAAO,OAAO,MAAM,CAAC;AAAA,EACjE,SAAS,OAAO;AACd,UAAM,kBAAkB,cAAc,KAAK;AAC3C,cAAU,MAAM,eAAe,yBAAyB,EAAE,OAAO,OAAO,KAAK,EAAE,CAAC;AAChF,YAAQ,WAAW,SAAS,gBAAgB,EAAE,OAAO,KAAK,CAAC;AAC3D,YAAQ,EAAE,MAAM,SAAS,OAAO,EAAE,MAAM,cAAc,SAAS,gBAAgB,EAAE,CAAC;AAClF,YAAQ,EAAE,MAAM,QAAQ,YAAY,MAAM,OAAO,QAAQ,WAAW,EAAE,CAAC;AAAA,EACzE;AACF;;;ACnLA,SAAS,SAAS,WAAoC;AACpD,QAAM,eAAgB,UAAkB,cAAc;AACtD,MAAI,CAAC,cAAc;AACjB,UAAM,OAAO,OAAO,IAAI,MAAM,4BAA4B,GAAG,EAAE,YAAY,IAAI,CAAC;AAAA,EAClF;AACA,SAAO,aAAa,aAAa;AACnC;AAEA,IAAM,qBAAqB,oBAAI,IAAI,CAAC,aAAa,CAAC;AAE3C,SAAS,qBAAqB,mBAA4B,gBAAoC;AACnG,MAAI,mBAAmB;AACrB,UAAM,iBAAiB,IAAI,IAAY,2BAA2B;AAClE,WAAO,oBAAoB;AAAA,MACzB,CAAC,SACC,eAAe,IAAI,KAAK,IAAI,KAC3B,KAAK,aAAa,cAAc,gBAAgB,IAAI,KAAK,IAAI,KAAK,CAAC,mBAAmB,IAAI,KAAK,IAAI;AAAA,IACxG;AAAA,EACF;AAEA,SAAO,iBACH,oBAAoB,OAAO,CAAC,SAAS,KAAK,aAAa,cAAc,eAAe,IAAI,KAAK,IAAI,CAAC,IAClG;AACN;AAGO,SAAS,kBACd,mBACA,qBACoB;AACpB,MAAI,mBAAmB;AACrB,WAAO,IAAI,IAAI,0BAA0B,uBAAuB,CAAC,CAAC,CAAC;AAAA,EACrE;AACA,SAAO,wBAAwB,SAAY,OAAO,IAAI,IAAI,0BAA0B,mBAAmB,CAAC;AAC1G;AAEO,SAAS,wBACd,WACA,eACqH;AACrH,SAAO,OAAO,QAAQ,SAAS,gBAAgB;AAC7C,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,IAAI;AAEJ,UAAM,YAAY,SAAS,SAAS;AAEpC,QAAI;AACJ,QAAI;AACJ,QAAI,aAAa;AACf,YAAM,kBAAkB,oBAAoB,WAAW,WAAW;AAClE,UAAI,iBAAiB,cAAc;AACjC,gCAAwB,gBAAgB;AAAA,MAC1C;AACA,8BAAwB,iBAAiB;AAAA,IAC3C;AAEA,UAAM,YAAY,0BAA0B,SAAS;AACrD,UAAM,uBAAuB,mBAAmB,wBAAwB;AACxE,UAAM,gBAAgB,YAAaC,cAAa,SAAS,EAAE,iBAAiB,QAAU;AACtF,UAAM,sBAAsB,KAAK,MAAM,iBAAiB,uBAAuB,IAAI;AAEnF,UAAM,KAAM,UAAkB,OAAO,SAAS;AAC9C,UAAM,eAAe,8BAA8B;AAAA,MACjD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,oBAAoB,aAAa;AACvC,UAAM,wBACJ,CAAC,WAAW,kBACR,wCAAwC,qBAAqB,IAC7D;AACN,UAAM,iBAAiB;AAAA,MACrB;AAAA,MACA,oBAAoB,aAAa,sBAAsB;AAAA,IACzD;AACA,UAAM,oBAAoB,qBAAqB,mBAAmB,cAAc;AAEhF,UAAM,aAAa,KACf,mBAAmB,mBAAmB,OAAO,EAAE,IAAI,WAAW,OAAO,IAAI,EAAE,oBAAoB,CAAC,IAChG,CAAC;AAEL,UAAM,WAAW,IAAI,aAAa,SAAS;AAC3C,aAAS,KAAK;AACd,UAAM,YAAY,WAAW,IAAI,CAAC,MAAwB,EAAE,IAAI;AAEhE,QAAI;AACJ,QAAI;AACJ,QAAI,mBAAmB;AACrB,YAAM,MAAM,aAAa,YAAY,wBAAwB,UAAU,OAAO;AAC9E,yBAAmB,EAAE,MAAM,IAAI,MAAM,QAAQ,IAAI,OAAO;AAAA,IAC1D,WAAW,SAAS;AAClB,YAAM,MAAM,SAAS,eAAe,OAAO;AAC3C,UAAI,IAAK,oBAAmB,EAAE,MAAM,IAAI,MAAM,QAAQ,IAAI,OAAO;AAAA,IACnE,WAAW,iBAAiB;AAC1B,YAAM,OAAO;AAAA,QACX,SAAS,aAAa,YAAY,SAAS,SAAS;AAAA,QACpD;AAAA,QACA;AAAA,MACF;AACA,UAAI,KAAM,qBAAoB;AAAA,IAChC;AAEA,QAAI,mBAAmB;AACrB,YAAM,oBAAoB,wBAAwB;AAAA,QAChD,UAAU,YAAY;AAAA,QACtB,cAAc;AAAA,QACd,eAAe,IAAI,IAAY,2BAA2B;AAAA,QAC1D;AAAA,QACA,gBAAgB,CAAC,OACf,+BAA+B,IAAI,QAAQ,CAAC,kBAAkB,SAAS,eAAe,aAAa,CAAC;AAAA,MACxG,CAAC;AACD,iBAAW,KAAK,iBAAwB;AAAA,IAC1C;AAEA,UAAM,sBAAsB,mBAAmB,UAC3C;AAAA,MACE,SAAS;AAAA,MACT,uBAAuB,kBAAkB,yBAAyB;AAAA,MAClE,mBAAmB,kBAAkB,qBAAqB;AAAA,MAC1D,sBAAsB,kBAAkB;AAAA,IAC1C,IACA;AAEJ,QAAI;AACJ,QAAI,IAAI;AACN,UAAI;AACF,cAAM,WAAW,gBAAgB,IAAI,CAAC;AACtC,YAAI,UAAU;AACZ,yBAAe;AAAA,YACb,MAAM,SAAS;AAAA,YACf,UAAU,SAAS;AAAA,YACnB,MAAM,SAAS;AAAA,YACf,eAAe,SAAS;AAAA,YACxB,cAAc,SAAS;AAAA,YACvB,gBAAgB,SAAS;AAAA,YACzB,eAAe,SAAS;AAAA,YACxB,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,UAC1C;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,UAAM,eAAe;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AACF;","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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
507
|
+
//# sourceMappingURL=chunk-YZYP5ST6.mjs.map
|