chatlab-cli 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat-command-ZP4WJVWC.mjs +304 -0
- package/dist/chat-command-ZP4WJVWC.mjs.map +1 -0
- package/dist/{chunk-CIBTUWAE.mjs → chunk-6G7AIF7I.mjs} +38 -8
- package/dist/chunk-6G7AIF7I.mjs.map +1 -0
- package/dist/chunk-ILGSSXOG.mjs +549 -0
- package/dist/chunk-ILGSSXOG.mjs.map +1 -0
- package/dist/{chunk-E65ABM3C.mjs → chunk-IRZRIHIT.mjs} +100 -63
- package/dist/chunk-IRZRIHIT.mjs.map +1 -0
- package/dist/{chunk-4OMV4VNH.mjs → chunk-QY67NN65.mjs} +1900 -729
- package/dist/chunk-QY67NN65.mjs.map +1 -0
- package/dist/{chunk-FSHVDWI2.mjs → chunk-SJIOPSMM.mjs} +116 -569
- package/dist/chunk-SJIOPSMM.mjs.map +1 -0
- package/dist/{chunk-GNT5G74Z.mjs → chunk-TC74HESZ.mjs} +59 -33
- package/dist/chunk-TC74HESZ.mjs.map +1 -0
- package/dist/chunk-UB4XBM4R.mjs +40 -0
- package/dist/chunk-UB4XBM4R.mjs.map +1 -0
- package/dist/{chunk-TCPXAX3F.mjs → chunk-WXC6RZCX.mjs} +17 -25
- package/dist/chunk-WXC6RZCX.mjs.map +1 -0
- package/dist/cli.mjs +3 -2
- package/dist/{http-PMNZ42EN.mjs → http-J3IOFFIA.mjs} +8 -6
- package/dist/{import-OP7R4CYY.mjs → import-POYG5LOX.mjs} +4 -4
- package/dist/index.mjs +10 -8
- package/dist/{mcp-ABXNFUJO.mjs → mcp-7HH7UMXT.mjs} +11 -6
- package/dist/mcp-7HH7UMXT.mjs.map +1 -0
- package/dist/{stream-import-L6RJHH4L.mjs → stream-import-O3H3FGV2.mjs} +3 -3
- package/dist-web/assets/{DebugToolsPanel-DaSNc1o-.css → DebugToolsPanel-CrqULdXp.css} +1 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BTJxEma5.js +182 -0
- package/dist-web/assets/{EChartWordcloud-CzhfrEF_.js → EChartWordcloud-B_YisOW5.js} +1 -1
- package/dist-web/assets/{MessageExportModal-BAJKWX4y.js → MessageExportModal-C7NgEL8T.js} +1 -1
- package/dist-web/assets/{electron-CZt36wW5.js → electron-B3DFm1cC.js} +1 -1
- package/dist-web/assets/electron-BMM4Nfty.js +1 -0
- package/dist-web/assets/{fetch-qoGEVjNg.js → fetch-ArMjdMcy.js} +1 -1
- package/dist-web/assets/fetch-BFtuJaa1.js +1 -0
- package/dist-web/assets/{fetch-yWwp067l.js → fetch-BGfaAqO9.js} +1 -1
- package/dist-web/assets/{fetch-DD1EGRKV.js → fetch-BaJDB31c.js} +1 -1
- package/dist-web/assets/{fetch-JzVEwamn.js → fetch-BjDJgdN0.js} +1 -1
- package/dist-web/assets/{fetch-GMkMBzBS.js → fetch-C5CLcpQW.js} +1 -1
- package/dist-web/assets/{fetch-DXfZysDf.js → fetch-DQZvdHnG.js} +1 -1
- package/dist-web/assets/{fetch-D4p9vj1A.js → fetch-Dhfbt1BQ.js} +3 -3
- package/dist-web/assets/fetch-DnEP0uyF.js +1 -0
- package/dist-web/assets/{fetch-CgBIWyAo.js → fetch-SYrb-sZe.js} +1 -1
- package/dist-web/assets/index-CWfkrx37.js +173 -0
- package/dist-web/assets/index-D3N4NDFx.css +1 -0
- package/dist-web/assets/{index-O0PG26_v.js → index-D7_ch4zf.js} +1 -1
- package/dist-web/assets/{index-BrS_VrIY.js → index-DK9LEEzT.js} +1 -1
- package/dist-web/assets/index-DqDa_on9.js +9 -0
- package/dist-web/assets/{index-Cvlw0RLu.js → index-DrN1G9bl.js} +2 -2
- package/dist-web/assets/{index-DvWI6Yqa.js → index-MdYeovYo.js} +4 -4
- package/dist-web/assets/{vendor-nuxt-ui-C_8CsH6e.js → vendor-nuxt-ui-Zy8txNdC.js} +1 -1
- package/dist-web/assets/{vendor-reka-ui-CIMXwvEp.js → vendor-reka-ui-mMvma1EP.js} +1 -1
- package/dist-web/assets/{web-4rtrAcLu.js → web-zEXsvoZW.js} +1 -1
- package/dist-web/index.html +4 -4
- package/package.json +1 -1
- package/dist/chunk-4OMV4VNH.mjs.map +0 -1
- package/dist/chunk-CIBTUWAE.mjs.map +0 -1
- package/dist/chunk-E65ABM3C.mjs.map +0 -1
- package/dist/chunk-FSHVDWI2.mjs.map +0 -1
- package/dist/chunk-GNT5G74Z.mjs.map +0 -1
- package/dist/chunk-TCPXAX3F.mjs.map +0 -1
- package/dist/mcp-ABXNFUJO.mjs.map +0 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BUwpIerl.js +0 -161
- package/dist-web/assets/electron-CwERrTWp.js +0 -1
- package/dist-web/assets/fetch-BxFAkm7O.js +0 -1
- package/dist-web/assets/fetch-DO8lty7_.js +0 -1
- package/dist-web/assets/index-C1t-kzfo.css +0 -1
- package/dist-web/assets/index-DABpUm3J.js +0 -9
- package/dist-web/assets/index-OsjX5hXw.js +0 -173
- /package/dist/{http-PMNZ42EN.mjs.map → http-J3IOFFIA.mjs.map} +0 -0
- /package/dist/{import-OP7R4CYY.mjs.map → import-POYG5LOX.mjs.map} +0 -0
- /package/dist/{stream-import-L6RJHH4L.mjs.map → stream-import-O3H3FGV2.mjs.map} +0 -0
|
@@ -0,0 +1,549 @@
|
|
|
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-IRZRIHIT.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
|
+
buildPlanGuidance,
|
|
21
|
+
buildSkillMenuWithBuiltinChart,
|
|
22
|
+
buildSystemPrompt,
|
|
23
|
+
checkAndCompress,
|
|
24
|
+
createActivateSkillTool,
|
|
25
|
+
createAiTranslate,
|
|
26
|
+
createAnalysisPlanner,
|
|
27
|
+
createChartSchemaGateState,
|
|
28
|
+
createCompressionLlmAdapter,
|
|
29
|
+
createDataSnapshotFromOverview,
|
|
30
|
+
createLlmRouteDecider,
|
|
31
|
+
createPlanContentBlock,
|
|
32
|
+
decideRequestRoute,
|
|
33
|
+
formatAIError,
|
|
34
|
+
getAllowedBuiltinToolsForChartAutoSkill,
|
|
35
|
+
getChartCapabilitySkill,
|
|
36
|
+
getChartPlannerCapabilityForMessage,
|
|
37
|
+
getChatOverview,
|
|
38
|
+
getSkillConfigWithBuiltinChart,
|
|
39
|
+
initTokenizer,
|
|
40
|
+
normalizeBuiltinToolNames,
|
|
41
|
+
parseAssistantFile,
|
|
42
|
+
resolveApiKey,
|
|
43
|
+
resolveChartRuntimeForRequest,
|
|
44
|
+
runAgentCore,
|
|
45
|
+
shouldUseChartCapabilityForMessage,
|
|
46
|
+
wrapWithChartSchemaGate
|
|
47
|
+
} from "./chunk-QY67NN65.mjs";
|
|
48
|
+
|
|
49
|
+
// src/ai/logger.ts
|
|
50
|
+
var logger = null;
|
|
51
|
+
function initServerAiLogger(logsDir) {
|
|
52
|
+
if (!logger) {
|
|
53
|
+
logger = new AiLogger(logsDir);
|
|
54
|
+
}
|
|
55
|
+
return logger;
|
|
56
|
+
}
|
|
57
|
+
function getServerAiLogger() {
|
|
58
|
+
return logger;
|
|
59
|
+
}
|
|
60
|
+
function closeServerAiLogger() {
|
|
61
|
+
if (logger) {
|
|
62
|
+
logger.close();
|
|
63
|
+
logger = null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/ai/tool-adapter.ts
|
|
68
|
+
var DEFAULT_MAX_TOOL_RESULT_TOKENS = 8e3;
|
|
69
|
+
var TOOL_TRUNCATION_STRATEGY = {
|
|
70
|
+
search_messages: "keep_first",
|
|
71
|
+
deep_search_messages: "keep_first",
|
|
72
|
+
get_recent_messages: "keep_last",
|
|
73
|
+
get_message_context: "keep_last",
|
|
74
|
+
get_segment_messages: "keep_last",
|
|
75
|
+
get_conversation_between: "keep_last"
|
|
76
|
+
};
|
|
77
|
+
function convertJsonSchemaToParameters(schema) {
|
|
78
|
+
const properties = {};
|
|
79
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
80
|
+
properties[key] = { ...prop };
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
type: "object",
|
|
84
|
+
properties,
|
|
85
|
+
required: schema.required || []
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function adaptToolsForAgent(tools, getContext, options) {
|
|
89
|
+
const tokenBudget = options?.maxToolResultTokens ?? DEFAULT_MAX_TOOL_RESULT_TOKENS;
|
|
90
|
+
const chartSchemaGateState = createChartSchemaGateState();
|
|
91
|
+
return tools.map(
|
|
92
|
+
(tool) => wrapWithChartSchemaGate(
|
|
93
|
+
{
|
|
94
|
+
name: tool.name,
|
|
95
|
+
label: tool.name,
|
|
96
|
+
description: tool.description,
|
|
97
|
+
parameters: convertJsonSchemaToParameters(tool.inputSchema),
|
|
98
|
+
async execute(_toolCallId, params) {
|
|
99
|
+
const toolParams = params && typeof params === "object" ? params : {};
|
|
100
|
+
const ctx = getContext();
|
|
101
|
+
const execCtx = {
|
|
102
|
+
db: ctx.db,
|
|
103
|
+
dataProvider: new CoreDataProvider(ctx.db),
|
|
104
|
+
sessionId: ctx.sessionId,
|
|
105
|
+
locale: ctx.locale,
|
|
106
|
+
segmentText: (texts, locale, options2) => batchSegmentWithFrequency(texts, locale, options2)
|
|
107
|
+
};
|
|
108
|
+
try {
|
|
109
|
+
const result = await tool.handler(toolParams, execCtx);
|
|
110
|
+
const chartDetails = result.chart || result.charts ? {
|
|
111
|
+
...result.chart ? { chart: result.chart } : {},
|
|
112
|
+
...result.charts ? { charts: result.charts } : {}
|
|
113
|
+
} : {};
|
|
114
|
+
if (result.rawMessages && result.rawMessages.length > 0) {
|
|
115
|
+
const pipelineResult = applyPreprocessingPipeline({
|
|
116
|
+
rawMessages: result.rawMessages,
|
|
117
|
+
locale: ctx.locale,
|
|
118
|
+
maxToolResultTokens: tokenBudget,
|
|
119
|
+
truncationStrategy: TOOL_TRUNCATION_STRATEGY[tool.name] ?? "keep_last",
|
|
120
|
+
extraDetails: result.data ?? {},
|
|
121
|
+
logger: getServerAiLogger() ?? void 0
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
content: [{ type: "text", text: pipelineResult.text }],
|
|
125
|
+
details: Object.keys(chartDetails).length > 0 ? chartDetails : null
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const baseDetails = typeof result.data === "object" && result.data !== null ? result.data : result.data === void 0 ? null : { value: result.data };
|
|
129
|
+
return {
|
|
130
|
+
content: [{ type: "text", text: result.content }],
|
|
131
|
+
details: Object.keys(chartDetails).length > 0 ? { ...baseDetails ?? {}, ...chartDetails } : baseDetails
|
|
132
|
+
};
|
|
133
|
+
} catch (error) {
|
|
134
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
135
|
+
return { content: [{ type: "text", text: `Error: ${msg}` }], details: null };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
chartSchemaGateState
|
|
140
|
+
)
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/ai/llm-config.ts
|
|
145
|
+
import * as fs from "fs";
|
|
146
|
+
import * as path from "path";
|
|
147
|
+
function loadLlmConfig(aiDataDir) {
|
|
148
|
+
const configPath = path.join(aiDataDir, "llm-config.json");
|
|
149
|
+
if (!fs.existsSync(configPath)) {
|
|
150
|
+
return { configs: [], defaultAssistant: null, fastModel: null };
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
const data = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
154
|
+
const configs = (data.configs || []).map((c) => {
|
|
155
|
+
const provider = c.provider || "";
|
|
156
|
+
const authProfile = c.authProfile;
|
|
157
|
+
const profileKey = resolveApiKey(provider, authProfile);
|
|
158
|
+
const rawKey = c.apiKey || "";
|
|
159
|
+
const fallbackKey = rawKey.startsWith("enc:") ? "" : rawKey;
|
|
160
|
+
return {
|
|
161
|
+
...c,
|
|
162
|
+
apiKey: profileKey || fallbackKey
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
return {
|
|
166
|
+
configs,
|
|
167
|
+
defaultAssistant: data.defaultAssistant ?? null,
|
|
168
|
+
fastModel: data.fastModel ?? null
|
|
169
|
+
};
|
|
170
|
+
} catch {
|
|
171
|
+
return { configs: [], defaultAssistant: null, fastModel: null };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function resolveSlot(slot, configs) {
|
|
175
|
+
if (slot && configs.some((c) => c.id === slot.configId)) return slot;
|
|
176
|
+
const fallback = configs[0];
|
|
177
|
+
if (!fallback) return null;
|
|
178
|
+
return { configId: fallback.id, modelId: fallback.model };
|
|
179
|
+
}
|
|
180
|
+
function getDefaultAssistantConfig(aiDataDir) {
|
|
181
|
+
const store = loadLlmConfig(aiDataDir);
|
|
182
|
+
const slot = resolveSlot(store.defaultAssistant, store.configs);
|
|
183
|
+
if (!slot) return null;
|
|
184
|
+
const config = store.configs.find((c) => c.id === slot.configId);
|
|
185
|
+
if (!config) return null;
|
|
186
|
+
return { ...config, model: slot.modelId || config.model };
|
|
187
|
+
}
|
|
188
|
+
function buildPiModel2(config) {
|
|
189
|
+
return buildPiModel(config);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/ai/assistant-loader.ts
|
|
193
|
+
import * as fs2 from "fs";
|
|
194
|
+
import * as path2 from "path";
|
|
195
|
+
function loadAssistantConfig(aiDataDir, assistantId) {
|
|
196
|
+
const filePath = path2.join(aiDataDir, "assistants", `${assistantId}.md`);
|
|
197
|
+
if (!fs2.existsSync(filePath)) return null;
|
|
198
|
+
try {
|
|
199
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
200
|
+
return parseAssistantFile(content, filePath);
|
|
201
|
+
} catch {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/ai/agent.ts
|
|
207
|
+
async function runServerAgent(options) {
|
|
208
|
+
const {
|
|
209
|
+
userMessage,
|
|
210
|
+
aiChatId,
|
|
211
|
+
historyLeafMessageId,
|
|
212
|
+
chatType = "group",
|
|
213
|
+
locale = "zh-CN",
|
|
214
|
+
assistantSystemPrompt,
|
|
215
|
+
skillMenu,
|
|
216
|
+
skillDef,
|
|
217
|
+
compressionConfig,
|
|
218
|
+
tools = [],
|
|
219
|
+
aiDataDir,
|
|
220
|
+
aiChatManager,
|
|
221
|
+
onEvent,
|
|
222
|
+
abortSignal,
|
|
223
|
+
ownerInfo,
|
|
224
|
+
mentionedMembers,
|
|
225
|
+
dataSnapshot,
|
|
226
|
+
thinkingLevel,
|
|
227
|
+
chartAutoMode = "suggest"
|
|
228
|
+
} = options;
|
|
229
|
+
const aiLogger = getServerAiLogger();
|
|
230
|
+
await initTokenizer();
|
|
231
|
+
const llmConfig = getDefaultAssistantConfig(aiDataDir);
|
|
232
|
+
if (!llmConfig) {
|
|
233
|
+
onEvent({ type: "error", error: { name: "ConfigError", message: "LLM service not configured" } });
|
|
234
|
+
onEvent({ type: "done", isFinished: true });
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const piModel = buildPiModel2(llmConfig);
|
|
238
|
+
const t = createAiTranslate(locale);
|
|
239
|
+
let skillCtx;
|
|
240
|
+
if (skillDef) {
|
|
241
|
+
skillCtx = { skillDef };
|
|
242
|
+
} else if (skillMenu) {
|
|
243
|
+
skillCtx = { skillMenu };
|
|
244
|
+
}
|
|
245
|
+
const systemPrompt = buildSystemPrompt({
|
|
246
|
+
t,
|
|
247
|
+
chatType,
|
|
248
|
+
assistantSystemPrompt,
|
|
249
|
+
ownerInfo,
|
|
250
|
+
locale,
|
|
251
|
+
skillCtx,
|
|
252
|
+
mentionedMembers,
|
|
253
|
+
dataSnapshot
|
|
254
|
+
});
|
|
255
|
+
const handler = new AgentEventHandler({
|
|
256
|
+
onChunk: onEvent,
|
|
257
|
+
context: {},
|
|
258
|
+
systemPrompt
|
|
259
|
+
});
|
|
260
|
+
if (compressionConfig?.enabled && historyLeafMessageId === void 0) {
|
|
261
|
+
const llmAdapter = createCompressionLlmAdapter({
|
|
262
|
+
piModel,
|
|
263
|
+
apiKey: llmConfig.apiKey,
|
|
264
|
+
onCompressing: () => handler.emitStatus("compressing", [])
|
|
265
|
+
});
|
|
266
|
+
const compressionResult = await checkAndCompress(
|
|
267
|
+
aiChatId,
|
|
268
|
+
compressionConfig,
|
|
269
|
+
systemPrompt,
|
|
270
|
+
llmAdapter,
|
|
271
|
+
aiChatManager,
|
|
272
|
+
aiLogger ?? void 0
|
|
273
|
+
);
|
|
274
|
+
if (compressionResult.compressed) {
|
|
275
|
+
onEvent({
|
|
276
|
+
type: "compression_done",
|
|
277
|
+
compressionResult: {
|
|
278
|
+
summaryContent: compressionResult.summaryContent ?? "",
|
|
279
|
+
tokensBefore: compressionResult.tokensBefore ?? 0,
|
|
280
|
+
tokensAfter: compressionResult.tokensAfter ?? 0,
|
|
281
|
+
timestamp: Date.now()
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
} else if (compressionConfig?.enabled && historyLeafMessageId !== void 0) {
|
|
286
|
+
aiLogger?.info?.("Compression", "Skipping compression for edited branch request", {
|
|
287
|
+
aiChatId,
|
|
288
|
+
historyLeafMessageId
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
if (abortSignal?.aborted) {
|
|
292
|
+
handler.emitStatus("aborted", [], { force: true });
|
|
293
|
+
onEvent({ type: "done", isFinished: true, usage: handler.cloneUsage() });
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
let history = [];
|
|
297
|
+
try {
|
|
298
|
+
history = aiChatManager.getHistoryForAgent(aiChatId, void 0, historyLeafMessageId);
|
|
299
|
+
} catch {
|
|
300
|
+
}
|
|
301
|
+
handler.emitStatus("preparing", [], { pendingUserMessage: userMessage, force: true });
|
|
302
|
+
const steerMessage = t("ai.agent.answerWithoutTools");
|
|
303
|
+
let cachedMessages = [];
|
|
304
|
+
const effectiveTools = chartAutoMode === "explicit" && !shouldUseChartCapabilityForMessage(userMessage) ? tools.filter((tool) => tool.name !== "render_chart") : tools;
|
|
305
|
+
try {
|
|
306
|
+
const routeInput = {
|
|
307
|
+
userMessage,
|
|
308
|
+
chatType,
|
|
309
|
+
locale,
|
|
310
|
+
dataSnapshot,
|
|
311
|
+
availableTools: effectiveTools.map((tool) => tool.name),
|
|
312
|
+
availableCapabilities: [
|
|
313
|
+
getChartPlannerCapabilityForMessage({
|
|
314
|
+
userMessage,
|
|
315
|
+
locale,
|
|
316
|
+
availableTools: effectiveTools.map((tool) => tool.name),
|
|
317
|
+
chartAutoMode
|
|
318
|
+
})
|
|
319
|
+
].filter((capability) => capability !== null),
|
|
320
|
+
skillSummary: skillDef?.name ?? (skillMenu ? "auto_skill_menu" : void 0)
|
|
321
|
+
};
|
|
322
|
+
const routeStartedAt = Date.now();
|
|
323
|
+
const routeDecision = await decideRequestRoute(routeInput, {
|
|
324
|
+
llmRouter: createLlmRouteDecider({
|
|
325
|
+
piModel,
|
|
326
|
+
apiKey: llmConfig.apiKey,
|
|
327
|
+
abortSignal
|
|
328
|
+
})
|
|
329
|
+
});
|
|
330
|
+
aiLogger?.info("Router", "Shadow route decision", {
|
|
331
|
+
...routeDecision,
|
|
332
|
+
elapsedMs: Date.now() - routeStartedAt,
|
|
333
|
+
availableToolCount: tools.length,
|
|
334
|
+
shadowOnly: true
|
|
335
|
+
});
|
|
336
|
+
onEvent({ type: "route", routeDecision });
|
|
337
|
+
let effectiveSystemPrompt = systemPrompt;
|
|
338
|
+
if (routeDecision.route === "planned_execution") {
|
|
339
|
+
const planStartedAt = Date.now();
|
|
340
|
+
const planner = createAnalysisPlanner({
|
|
341
|
+
piModel,
|
|
342
|
+
apiKey: llmConfig.apiKey,
|
|
343
|
+
onPlanDelta: (delta) => onEvent({ type: "plan_delta", planDelta: delta }),
|
|
344
|
+
onThinkingDelta: (delta) => onEvent({ type: "think", content: delta, thinkTag: "thinking" }),
|
|
345
|
+
onThinkingEnd: (durationMs) => onEvent({ type: "think", content: "", thinkTag: "thinking", thinkDurationMs: durationMs }),
|
|
346
|
+
onValidationDelta: (delta) => onEvent({ type: "think", content: delta, thinkTag: "plan_validation" }),
|
|
347
|
+
onValidationEnd: (durationMs) => onEvent({ type: "think", content: "", thinkTag: "plan_validation", thinkDurationMs: durationMs })
|
|
348
|
+
});
|
|
349
|
+
const plan = await planner(routeInput, abortSignal);
|
|
350
|
+
if (plan) {
|
|
351
|
+
const planBlock = createPlanContentBlock(plan);
|
|
352
|
+
onEvent({ type: "plan", plan: planBlock });
|
|
353
|
+
effectiveSystemPrompt = `${systemPrompt}
|
|
354
|
+
|
|
355
|
+
${buildPlanGuidance(plan)}`;
|
|
356
|
+
aiLogger?.info("Planner", "Plan generated", {
|
|
357
|
+
title: plan.title,
|
|
358
|
+
steps: plan.steps.length,
|
|
359
|
+
successCriteria: plan.successCriteria.length,
|
|
360
|
+
elapsedMs: Date.now() - planStartedAt
|
|
361
|
+
});
|
|
362
|
+
} else {
|
|
363
|
+
aiLogger?.warn("Planner", "Plan generation skipped or failed", {
|
|
364
|
+
elapsedMs: Date.now() - planStartedAt,
|
|
365
|
+
route: routeDecision.route
|
|
366
|
+
});
|
|
367
|
+
onEvent({ type: "plan_skipped" });
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const result = await runAgentCore({
|
|
371
|
+
piModel,
|
|
372
|
+
apiKey: llmConfig.apiKey,
|
|
373
|
+
systemPrompt: effectiveSystemPrompt,
|
|
374
|
+
tools: effectiveTools,
|
|
375
|
+
history,
|
|
376
|
+
userMessage,
|
|
377
|
+
maxToolRounds: DEFAULT_MAX_TOOL_ROUNDS,
|
|
378
|
+
abortSignal,
|
|
379
|
+
steerMessage,
|
|
380
|
+
thinkingLevel,
|
|
381
|
+
onConvertToLlm: (filteredMessages) => {
|
|
382
|
+
cachedMessages = filteredMessages;
|
|
383
|
+
},
|
|
384
|
+
onEvent: (coreEvent) => handler.handleCoreEvent(coreEvent, cachedMessages),
|
|
385
|
+
onDebugContext: (messages) => {
|
|
386
|
+
try {
|
|
387
|
+
aiChatManager.setPendingDebugContext(aiChatId, JSON.stringify(messages, null, 2));
|
|
388
|
+
} catch {
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
if (result.error) {
|
|
393
|
+
const friendlyMessage = formatAIError(result.error);
|
|
394
|
+
onEvent({ type: "error", error: { name: "AgentError", message: friendlyMessage } });
|
|
395
|
+
}
|
|
396
|
+
handler.emitStatus("completed", cachedMessages, { force: true });
|
|
397
|
+
onEvent({ type: "done", isFinished: true, usage: result.usage });
|
|
398
|
+
} catch (error) {
|
|
399
|
+
const friendlyMessage = formatAIError(error);
|
|
400
|
+
aiLogger?.error("ServerAgent", "Agent execution error", { error: String(error) });
|
|
401
|
+
handler.emitStatus("error", cachedMessages, { force: true });
|
|
402
|
+
onEvent({ type: "error", error: { name: "AgentError", message: friendlyMessage } });
|
|
403
|
+
onEvent({ type: "done", isFinished: true, usage: handler.cloneUsage() });
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// src/ai/agent-stream-runner.ts
|
|
408
|
+
function getAiDir(dbManager) {
|
|
409
|
+
const pathProvider = dbManager["pathProvider"];
|
|
410
|
+
if (!pathProvider) {
|
|
411
|
+
throw Object.assign(new Error("PathProvider not available"), { statusCode: 500 });
|
|
412
|
+
}
|
|
413
|
+
return pathProvider.getAiDataDir();
|
|
414
|
+
}
|
|
415
|
+
var RAW_SQL_TOOL_NAMES = /* @__PURE__ */ new Set(["execute_sql"]);
|
|
416
|
+
function getAvailableToolDefs(isChartCapability, allowedToolSet) {
|
|
417
|
+
if (isChartCapability) {
|
|
418
|
+
const chartCoreTools = new Set(CHART_CAPABILITY_CORE_TOOLS);
|
|
419
|
+
return AGENT_TOOL_REGISTRY.filter(
|
|
420
|
+
(tool) => chartCoreTools.has(tool.name) || tool.category === "analysis" && allowedToolSet?.has(tool.name) && !RAW_SQL_TOOL_NAMES.has(tool.name)
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
return allowedToolSet ? AGENT_TOOL_REGISTRY.filter((tool) => tool.category !== "analysis" || allowedToolSet.has(tool.name)) : AGENT_TOOL_REGISTRY;
|
|
424
|
+
}
|
|
425
|
+
function getAllowedToolSet(isChartCapability, allowedBuiltinTools) {
|
|
426
|
+
if (isChartCapability) {
|
|
427
|
+
return new Set(normalizeBuiltinToolNames(allowedBuiltinTools ?? []));
|
|
428
|
+
}
|
|
429
|
+
return allowedBuiltinTools === void 0 ? null : new Set(normalizeBuiltinToolNames(allowedBuiltinTools));
|
|
430
|
+
}
|
|
431
|
+
function createCliRunAgentStream(dbManager, aiChatManager) {
|
|
432
|
+
return async (params, onEvent, abortSignal) => {
|
|
433
|
+
const {
|
|
434
|
+
userMessage,
|
|
435
|
+
aiChatId,
|
|
436
|
+
historyLeafMessageId,
|
|
437
|
+
sessionId,
|
|
438
|
+
chatType,
|
|
439
|
+
locale,
|
|
440
|
+
assistantId,
|
|
441
|
+
skillId,
|
|
442
|
+
enableAutoSkill,
|
|
443
|
+
chartAutoMode,
|
|
444
|
+
compressionConfig,
|
|
445
|
+
ownerInfo,
|
|
446
|
+
mentionedMembers,
|
|
447
|
+
thinkingLevel
|
|
448
|
+
} = params;
|
|
449
|
+
const aiDataDir = getAiDir(dbManager);
|
|
450
|
+
let assistantSystemPrompt;
|
|
451
|
+
let assistantAllowedTools;
|
|
452
|
+
if (assistantId) {
|
|
453
|
+
const assistantConfig = loadAssistantConfig(aiDataDir, assistantId);
|
|
454
|
+
if (assistantConfig?.systemPrompt) {
|
|
455
|
+
assistantSystemPrompt = assistantConfig.systemPrompt;
|
|
456
|
+
}
|
|
457
|
+
assistantAllowedTools = assistantConfig?.allowedBuiltinTools;
|
|
458
|
+
}
|
|
459
|
+
const llmConfig = getDefaultAssistantConfig(aiDataDir);
|
|
460
|
+
const maxToolResultPercent = compressionConfig?.maxToolResultPercent ?? 50;
|
|
461
|
+
const contextWindow = llmConfig ? buildPiModel2(llmConfig).contextWindow ?? 128e3 : 128e3;
|
|
462
|
+
const maxToolResultTokens = Math.floor(contextWindow * (maxToolResultPercent / 100));
|
|
463
|
+
const db = dbManager.open?.(sessionId);
|
|
464
|
+
const resolvedChartAutoMode = chartAutoMode ?? "suggest";
|
|
465
|
+
const chartRuntime = resolveChartRuntimeForRequest({
|
|
466
|
+
skillId,
|
|
467
|
+
userMessage,
|
|
468
|
+
locale,
|
|
469
|
+
assistantAllowedTools,
|
|
470
|
+
enableAutoDetection: enableAutoSkill === true,
|
|
471
|
+
chartAutoMode: resolvedChartAutoMode
|
|
472
|
+
});
|
|
473
|
+
const isChartCapability = chartRuntime.isChartCapability;
|
|
474
|
+
const autoSkillAllowedTools = !skillId && enableAutoSkill && resolvedChartAutoMode !== "explicit" ? getAllowedBuiltinToolsForChartAutoSkill(assistantAllowedTools) : assistantAllowedTools;
|
|
475
|
+
const allowedToolSet = getAllowedToolSet(
|
|
476
|
+
isChartCapability,
|
|
477
|
+
isChartCapability ? chartRuntime.allowedBuiltinTools : autoSkillAllowedTools
|
|
478
|
+
);
|
|
479
|
+
const availableToolDefs = getAvailableToolDefs(isChartCapability, allowedToolSet);
|
|
480
|
+
const agentTools = db ? adaptToolsForAgent(availableToolDefs, () => ({ db, sessionId, locale }), { maxToolResultTokens }) : [];
|
|
481
|
+
const skillMgr = new SkillManager(aiDataDir);
|
|
482
|
+
skillMgr.init();
|
|
483
|
+
const toolNames = agentTools.map((t) => t.name);
|
|
484
|
+
let resolvedSkillDef;
|
|
485
|
+
let resolvedSkillMenu;
|
|
486
|
+
if (isChartCapability) {
|
|
487
|
+
const def = chartRuntime.skillDef ?? getChartCapabilitySkill(locale ?? "zh-CN");
|
|
488
|
+
resolvedSkillDef = { name: def.name, prompt: def.prompt };
|
|
489
|
+
} else if (skillId) {
|
|
490
|
+
const def = skillMgr.getSkillConfig(skillId);
|
|
491
|
+
if (def) resolvedSkillDef = { name: def.name, prompt: def.prompt };
|
|
492
|
+
} else if (enableAutoSkill) {
|
|
493
|
+
const baseMenu = skillMgr.getSkillMenu(chatType ?? "group", toolNames);
|
|
494
|
+
const menu = resolvedChartAutoMode === "aggressive" ? buildSkillMenuWithBuiltinChart(baseMenu, locale, toolNames) : baseMenu;
|
|
495
|
+
if (menu) resolvedSkillMenu = menu;
|
|
496
|
+
}
|
|
497
|
+
if (resolvedSkillMenu) {
|
|
498
|
+
const activateSkillTool = createActivateSkillTool({
|
|
499
|
+
chatType: chatType ?? "group",
|
|
500
|
+
allowedTools: toolNames,
|
|
501
|
+
coreToolNames: new Set(CHART_CAPABILITY_CORE_TOOLS),
|
|
502
|
+
locale,
|
|
503
|
+
getSkillConfig: (id) => resolvedChartAutoMode === "aggressive" ? getSkillConfigWithBuiltinChart(id, locale, (skillConfigId) => skillMgr.getSkillConfig(skillConfigId)) : skillMgr.getSkillConfig(id)
|
|
504
|
+
});
|
|
505
|
+
agentTools.push(activateSkillTool);
|
|
506
|
+
}
|
|
507
|
+
const resolvedCompression = compressionConfig?.enabled ? {
|
|
508
|
+
enabled: true,
|
|
509
|
+
tokenThresholdPercent: compressionConfig.tokenThresholdPercent ?? 75,
|
|
510
|
+
bufferSizePercent: compressionConfig.bufferSizePercent ?? 20,
|
|
511
|
+
maxToolResultPercent: compressionConfig.maxToolResultPercent
|
|
512
|
+
} : void 0;
|
|
513
|
+
let dataSnapshot;
|
|
514
|
+
if (db) {
|
|
515
|
+
try {
|
|
516
|
+
dataSnapshot = createDataSnapshotFromOverview(getChatOverview(db, 10));
|
|
517
|
+
} catch {
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
await runServerAgent({
|
|
521
|
+
userMessage,
|
|
522
|
+
aiChatId,
|
|
523
|
+
historyLeafMessageId,
|
|
524
|
+
chatType,
|
|
525
|
+
locale,
|
|
526
|
+
assistantSystemPrompt,
|
|
527
|
+
skillMenu: resolvedSkillMenu,
|
|
528
|
+
skillDef: resolvedSkillDef,
|
|
529
|
+
compressionConfig: resolvedCompression,
|
|
530
|
+
tools: agentTools,
|
|
531
|
+
aiDataDir,
|
|
532
|
+
aiChatManager,
|
|
533
|
+
onEvent,
|
|
534
|
+
abortSignal,
|
|
535
|
+
ownerInfo,
|
|
536
|
+
mentionedMembers,
|
|
537
|
+
dataSnapshot,
|
|
538
|
+
thinkingLevel,
|
|
539
|
+
chartAutoMode: resolvedChartAutoMode
|
|
540
|
+
});
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
export {
|
|
545
|
+
initServerAiLogger,
|
|
546
|
+
closeServerAiLogger,
|
|
547
|
+
createCliRunAgentStream
|
|
548
|
+
};
|
|
549
|
+
//# sourceMappingURL=chunk-ILGSSXOG.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 buildPlanGuidance,\n createAnalysisPlanner,\n createLlmRouteDecider,\n createPlanContentBlock,\n decideRequestRoute,\n runAgentCore,\n checkAndCompress,\n buildSystemPrompt,\n createAiTranslate,\n createCompressionLlmAdapter,\n AgentEventHandler,\n formatAIError,\n shouldUseChartCapabilityForMessage,\n getChartPlannerCapabilityForMessage,\n initTokenizer,\n type AgentStreamChunk,\n type PiMessage,\n type SimpleHistoryMessage,\n type AIChatManager,\n type CompressionConfig,\n type AgentTool,\n type DataSnapshot,\n type OwnerInfo,\n type MentionedMember,\n} from '@openchatlab/node-runtime'\nimport type { ChartAutoMode } from '@openchatlab/shared-types'\n\nimport { getDefaultAssistantConfig, buildPiModel } from './llm-config'\nimport { getServerAiLogger } from './logger'\n\nexport type { AgentStreamChunk }\n\nexport interface RunAgentOptions {\n userMessage: string\n aiChatId: string\n historyLeafMessageId?: string | null\n chatType?: 'group' | 'private'\n locale?: string\n assistantSystemPrompt?: string\n skillMenu?: string | null\n skillDef?: { name: string; prompt: string }\n compressionConfig?: CompressionConfig\n tools?: AgentTool[]\n aiDataDir: string\n aiChatManager: AIChatManager\n onEvent: (event: AgentStreamChunk) => void\n abortSignal?: AbortSignal\n ownerInfo?: OwnerInfo\n mentionedMembers?: MentionedMember[]\n dataSnapshot?: DataSnapshot\n thinkingLevel?: string\n chartAutoMode?: ChartAutoMode\n}\n\nexport async function runServerAgent(options: RunAgentOptions): Promise<void> {\n const {\n userMessage,\n aiChatId,\n historyLeafMessageId,\n chatType = 'group',\n locale = 'zh-CN',\n assistantSystemPrompt,\n skillMenu,\n skillDef,\n compressionConfig,\n tools = [],\n aiDataDir,\n aiChatManager,\n onEvent,\n abortSignal,\n ownerInfo,\n mentionedMembers,\n dataSnapshot,\n thinkingLevel,\n chartAutoMode = 'suggest',\n } = options\n\n const aiLogger = getServerAiLogger()\n\n // 确保 tokenizer rank 表已加载(compression + agent 路径均依赖)\n await initTokenizer()\n\n const llmConfig = getDefaultAssistantConfig(aiDataDir)\n if (!llmConfig) {\n onEvent({ type: 'error', error: { name: 'ConfigError', message: 'LLM service not configured' } })\n onEvent({ type: 'done', isFinished: true })\n return\n }\n\n const piModel = buildPiModel(llmConfig)\n const t = createAiTranslate(locale)\n\n let skillCtx: { skillDef?: { name: string; prompt: string }; skillMenu?: string } | undefined\n if (skillDef) {\n skillCtx = { skillDef }\n } else if (skillMenu) {\n skillCtx = { skillMenu }\n }\n\n const systemPrompt = buildSystemPrompt({\n t,\n chatType,\n assistantSystemPrompt,\n ownerInfo,\n locale,\n skillCtx,\n mentionedMembers,\n dataSnapshot,\n })\n\n const handler = new AgentEventHandler({\n onChunk: onEvent,\n context: {},\n systemPrompt,\n })\n\n if (compressionConfig?.enabled && historyLeafMessageId === undefined) {\n const llmAdapter = createCompressionLlmAdapter({\n piModel,\n apiKey: llmConfig.apiKey,\n onCompressing: () => handler.emitStatus('compressing', []),\n })\n const compressionResult = await checkAndCompress(\n aiChatId,\n compressionConfig,\n systemPrompt,\n llmAdapter,\n aiChatManager,\n aiLogger ?? undefined\n )\n if (compressionResult.compressed) {\n onEvent({\n type: 'compression_done',\n compressionResult: {\n summaryContent: compressionResult.summaryContent ?? '',\n tokensBefore: compressionResult.tokensBefore ?? 0,\n tokensAfter: compressionResult.tokensAfter ?? 0,\n timestamp: Date.now(),\n },\n })\n }\n } else if (compressionConfig?.enabled && historyLeafMessageId !== undefined) {\n aiLogger?.info?.('Compression', 'Skipping compression for edited branch request', {\n aiChatId,\n historyLeafMessageId,\n })\n }\n\n if (abortSignal?.aborted) {\n handler.emitStatus('aborted', [], { force: true })\n onEvent({ type: 'done', isFinished: true, usage: handler.cloneUsage() })\n return\n }\n\n let history: SimpleHistoryMessage[] = []\n try {\n history = aiChatManager.getHistoryForAgent(aiChatId, undefined, historyLeafMessageId)\n } catch {\n // empty history on failure\n }\n\n handler.emitStatus('preparing', [], { pendingUserMessage: userMessage, force: true })\n\n const steerMessage = t('ai.agent.answerWithoutTools')\n let cachedMessages: PiMessage[] = []\n const effectiveTools =\n chartAutoMode === 'explicit' && !shouldUseChartCapabilityForMessage(userMessage)\n ? tools.filter((tool) => tool.name !== 'render_chart')\n : tools\n\n try {\n const routeInput = {\n userMessage,\n chatType,\n locale,\n dataSnapshot,\n availableTools: effectiveTools.map((tool) => tool.name),\n availableCapabilities: [\n getChartPlannerCapabilityForMessage({\n userMessage,\n locale,\n availableTools: effectiveTools.map((tool) => tool.name),\n chartAutoMode,\n }),\n ].filter((capability) => capability !== null),\n skillSummary: skillDef?.name ?? (skillMenu ? 'auto_skill_menu' : undefined),\n }\n const routeStartedAt = Date.now()\n const routeDecision = await decideRequestRoute(routeInput, {\n llmRouter: createLlmRouteDecider({\n piModel,\n apiKey: llmConfig.apiKey,\n abortSignal,\n }),\n })\n aiLogger?.info('Router', 'Shadow route decision', {\n ...routeDecision,\n elapsedMs: Date.now() - routeStartedAt,\n availableToolCount: tools.length,\n shadowOnly: true,\n })\n onEvent({ type: 'route', routeDecision })\n\n let effectiveSystemPrompt = systemPrompt\n if (routeDecision.route === 'planned_execution') {\n const planStartedAt = Date.now()\n const planner = createAnalysisPlanner({\n piModel,\n apiKey: llmConfig.apiKey,\n onPlanDelta: (delta) => onEvent({ type: 'plan_delta', planDelta: delta }),\n onThinkingDelta: (delta) => onEvent({ type: 'think', content: delta, thinkTag: 'thinking' }),\n onThinkingEnd: (durationMs) =>\n onEvent({ type: 'think', content: '', thinkTag: 'thinking', thinkDurationMs: durationMs }),\n onValidationDelta: (delta) => onEvent({ type: 'think', content: delta, thinkTag: 'plan_validation' }),\n onValidationEnd: (durationMs) =>\n onEvent({ type: 'think', content: '', thinkTag: 'plan_validation', thinkDurationMs: durationMs }),\n })\n const plan = await planner(routeInput, abortSignal)\n if (plan) {\n const planBlock = createPlanContentBlock(plan)\n onEvent({ type: 'plan', plan: planBlock })\n effectiveSystemPrompt = `${systemPrompt}\\n\\n${buildPlanGuidance(plan)}`\n aiLogger?.info('Planner', 'Plan generated', {\n title: plan.title,\n steps: plan.steps.length,\n successCriteria: plan.successCriteria.length,\n elapsedMs: Date.now() - planStartedAt,\n })\n } else {\n aiLogger?.warn('Planner', 'Plan generation skipped or failed', {\n elapsedMs: Date.now() - planStartedAt,\n route: routeDecision.route,\n })\n onEvent({ type: 'plan_skipped' })\n }\n }\n\n const result = await runAgentCore({\n piModel,\n apiKey: llmConfig.apiKey,\n systemPrompt: effectiveSystemPrompt,\n tools: effectiveTools,\n history,\n userMessage,\n maxToolRounds: DEFAULT_MAX_TOOL_ROUNDS,\n abortSignal,\n steerMessage,\n thinkingLevel: thinkingLevel as import('@openchatlab/core').ThinkingLevel | undefined,\n onConvertToLlm: (filteredMessages) => {\n cachedMessages = filteredMessages as PiMessage[]\n },\n onEvent: (coreEvent) => handler.handleCoreEvent(coreEvent, cachedMessages),\n onDebugContext: (messages) => {\n try {\n aiChatManager.setPendingDebugContext(aiChatId, JSON.stringify(messages, null, 2))\n } catch {\n // silent\n }\n },\n })\n\n if (result.error) {\n const friendlyMessage = formatAIError(result.error)\n onEvent({ type: 'error', error: { name: 'AgentError', message: friendlyMessage } })\n }\n\n handler.emitStatus('completed', cachedMessages, { force: true })\n onEvent({ type: 'done', isFinished: true, usage: result.usage })\n } catch (error) {\n const friendlyMessage = formatAIError(error)\n aiLogger?.error('ServerAgent', 'Agent execution error', { error: String(error) })\n handler.emitStatus('error', cachedMessages, { force: true })\n onEvent({ type: 'error', error: { name: 'AgentError', message: friendlyMessage } })\n onEvent({ type: 'done', isFinished: true, usage: handler.cloneUsage() })\n }\n}\n","/**\n * CLI Agent stream runner — provides runAgentStream implementation\n * for the shared HTTP route context.\n */\n\nimport type { DatabaseManager, AIChatManager, AgentStreamChunk } from '@openchatlab/node-runtime'\nimport {\n CHART_CAPABILITY_CORE_TOOLS,\n SkillManager,\n buildSkillMenuWithBuiltinChart,\n createActivateSkillTool,\n createDataSnapshotFromOverview,\n getAllowedBuiltinToolsForChartAutoSkill,\n getChartCapabilitySkill,\n getSkillConfigWithBuiltinChart,\n resolveChartRuntimeForRequest,\n} from '@openchatlab/node-runtime'\nimport { getChatOverview, normalizeBuiltinToolNames } from '@openchatlab/core'\nimport type { ChartAutoMode } from '@openchatlab/shared-types'\nimport type { DataSnapshot } from '@openchatlab/node-runtime'\nimport type { AgentStreamRequest } from '@openchatlab/http-routes'\nimport { AGENT_TOOL_REGISTRY } from '@openchatlab/tools'\nimport { adaptToolsForAgent } from './tool-adapter'\nimport { getDefaultAssistantConfig, buildPiModel } from './llm-config'\nimport { loadAssistantConfig } from './assistant-loader'\nimport { runServerAgent } from './agent'\n\nfunction getAiDir(dbManager: DatabaseManager): string {\n const pathProvider = (dbManager as any)['pathProvider']\n if (!pathProvider) {\n throw Object.assign(new Error('PathProvider not available'), { statusCode: 500 })\n }\n return pathProvider.getAiDataDir()\n}\n\nconst RAW_SQL_TOOL_NAMES = new Set(['execute_sql'])\n\nexport function getAvailableToolDefs(isChartCapability: boolean, allowedToolSet: Set<string> | null) {\n if (isChartCapability) {\n const chartCoreTools = new Set<string>(CHART_CAPABILITY_CORE_TOOLS)\n return AGENT_TOOL_REGISTRY.filter(\n (tool) =>\n chartCoreTools.has(tool.name) ||\n (tool.category === 'analysis' && allowedToolSet?.has(tool.name) && !RAW_SQL_TOOL_NAMES.has(tool.name))\n )\n }\n\n return allowedToolSet\n ? AGENT_TOOL_REGISTRY.filter((tool) => tool.category !== 'analysis' || allowedToolSet.has(tool.name))\n : AGENT_TOOL_REGISTRY\n}\n\n// 区分“未配置白名单”和“配置为空白名单”:前者无限制,后者禁用 analysis 工具。\nexport function getAllowedToolSet(\n isChartCapability: boolean,\n allowedBuiltinTools?: readonly string[]\n): Set<string> | null {\n if (isChartCapability) {\n return new Set(normalizeBuiltinToolNames(allowedBuiltinTools ?? []))\n }\n return allowedBuiltinTools === undefined ? null : new Set(normalizeBuiltinToolNames(allowedBuiltinTools))\n}\n\nexport function createCliRunAgentStream(\n dbManager: DatabaseManager,\n aiChatManager: AIChatManager\n): (params: AgentStreamRequest, onEvent: (chunk: AgentStreamChunk) => void, abortSignal: AbortSignal) => Promise<void> {\n return async (params, onEvent, abortSignal) => {\n const {\n userMessage,\n aiChatId,\n historyLeafMessageId,\n sessionId,\n chatType,\n locale,\n assistantId,\n skillId,\n enableAutoSkill,\n chartAutoMode,\n compressionConfig,\n ownerInfo,\n mentionedMembers,\n thinkingLevel,\n } = params\n\n const aiDataDir = getAiDir(dbManager)\n\n let assistantSystemPrompt: string | undefined\n let assistantAllowedTools: string[] | undefined\n if (assistantId) {\n const assistantConfig = loadAssistantConfig(aiDataDir, assistantId)\n if (assistantConfig?.systemPrompt) {\n assistantSystemPrompt = assistantConfig.systemPrompt\n }\n assistantAllowedTools = assistantConfig?.allowedBuiltinTools\n }\n\n const llmConfig = getDefaultAssistantConfig(aiDataDir)\n const maxToolResultPercent = compressionConfig?.maxToolResultPercent ?? 50\n const contextWindow = llmConfig ? (buildPiModel(llmConfig).contextWindow ?? 128000) : 128000\n const maxToolResultTokens = Math.floor(contextWindow * (maxToolResultPercent / 100))\n\n const db = (dbManager as any).open?.(sessionId)\n const resolvedChartAutoMode: ChartAutoMode = chartAutoMode ?? 'suggest'\n const chartRuntime = resolveChartRuntimeForRequest({\n skillId,\n userMessage,\n locale,\n assistantAllowedTools,\n enableAutoDetection: enableAutoSkill === true,\n chartAutoMode: resolvedChartAutoMode,\n })\n const isChartCapability = chartRuntime.isChartCapability\n const autoSkillAllowedTools =\n !skillId && enableAutoSkill && resolvedChartAutoMode !== 'explicit'\n ? getAllowedBuiltinToolsForChartAutoSkill(assistantAllowedTools)\n : assistantAllowedTools\n const allowedToolSet = getAllowedToolSet(\n isChartCapability,\n isChartCapability ? chartRuntime.allowedBuiltinTools : autoSkillAllowedTools\n )\n const availableToolDefs = getAvailableToolDefs(isChartCapability, allowedToolSet)\n\n const agentTools = db\n ? adaptToolsForAgent(availableToolDefs, () => ({ db, sessionId, locale }), { maxToolResultTokens })\n : []\n\n const skillMgr = new SkillManager(aiDataDir)\n skillMgr.init()\n const toolNames = agentTools.map((t: { name: string }) => t.name)\n\n let resolvedSkillDef: { name: string; prompt: string } | undefined\n let resolvedSkillMenu: string | undefined\n if (isChartCapability) {\n const def = chartRuntime.skillDef ?? getChartCapabilitySkill(locale ?? 'zh-CN')\n resolvedSkillDef = { name: def.name, prompt: def.prompt }\n } else if (skillId) {\n const def = skillMgr.getSkillConfig(skillId)\n if (def) resolvedSkillDef = { name: def.name, prompt: def.prompt }\n } else if (enableAutoSkill) {\n const baseMenu = skillMgr.getSkillMenu(chatType ?? 'group', toolNames)\n const menu =\n resolvedChartAutoMode === 'aggressive' ? buildSkillMenuWithBuiltinChart(baseMenu, locale, toolNames) : baseMenu\n if (menu) resolvedSkillMenu = menu\n }\n\n if (resolvedSkillMenu) {\n const activateSkillTool = createActivateSkillTool({\n chatType: chatType ?? 'group',\n allowedTools: toolNames,\n coreToolNames: new Set<string>(CHART_CAPABILITY_CORE_TOOLS),\n locale,\n getSkillConfig: (id) =>\n resolvedChartAutoMode === 'aggressive'\n ? getSkillConfigWithBuiltinChart(id, locale, (skillConfigId) => skillMgr.getSkillConfig(skillConfigId))\n : skillMgr.getSkillConfig(id),\n })\n agentTools.push(activateSkillTool as any)\n }\n\n const resolvedCompression = compressionConfig?.enabled\n ? {\n enabled: true as const,\n tokenThresholdPercent: compressionConfig.tokenThresholdPercent ?? 75,\n bufferSizePercent: compressionConfig.bufferSizePercent ?? 20,\n maxToolResultPercent: compressionConfig.maxToolResultPercent,\n }\n : undefined\n\n let dataSnapshot: DataSnapshot | undefined\n if (db) {\n try {\n dataSnapshot = createDataSnapshotFromOverview(getChatOverview(db, 10))\n } catch {\n // non-fatal\n }\n }\n\n await runServerAgent({\n userMessage,\n aiChatId,\n historyLeafMessageId,\n chatType,\n locale,\n assistantSystemPrompt,\n skillMenu: resolvedSkillMenu,\n skillDef: resolvedSkillDef,\n compressionConfig: resolvedCompression,\n tools: agentTools,\n aiDataDir,\n aiChatManager,\n onEvent,\n abortSignal,\n ownerInfo,\n mentionedMembers,\n dataSnapshot,\n thinkingLevel,\n chartAutoMode: resolvedChartAutoMode,\n })\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;;;AC0CA,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,IACA,gBAAgB;AAAA,EAClB,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;AACnC,QAAM,iBACJ,kBAAkB,cAAc,CAAC,mCAAmC,WAAW,IAC3E,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,cAAc,IACnD;AAEN,MAAI;AACF,UAAM,aAAa;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB,eAAe,IAAI,CAAC,SAAS,KAAK,IAAI;AAAA,MACtD,uBAAuB;AAAA,QACrB,oCAAoC;AAAA,UAClC;AAAA,UACA;AAAA,UACA,gBAAgB,eAAe,IAAI,CAAC,SAAS,KAAK,IAAI;AAAA,UACtD;AAAA,QACF,CAAC;AAAA,MACH,EAAE,OAAO,CAAC,eAAe,eAAe,IAAI;AAAA,MAC5C,cAAc,UAAU,SAAS,YAAY,oBAAoB;AAAA,IACnE;AACA,UAAM,iBAAiB,KAAK,IAAI;AAChC,UAAM,gBAAgB,MAAM,mBAAmB,YAAY;AAAA,MACzD,WAAW,sBAAsB;AAAA,QAC/B;AAAA,QACA,QAAQ,UAAU;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,cAAU,KAAK,UAAU,yBAAyB;AAAA,MAChD,GAAG;AAAA,MACH,WAAW,KAAK,IAAI,IAAI;AAAA,MACxB,oBAAoB,MAAM;AAAA,MAC1B,YAAY;AAAA,IACd,CAAC;AACD,YAAQ,EAAE,MAAM,SAAS,cAAc,CAAC;AAExC,QAAI,wBAAwB;AAC5B,QAAI,cAAc,UAAU,qBAAqB;AAC/C,YAAM,gBAAgB,KAAK,IAAI;AAC/B,YAAM,UAAU,sBAAsB;AAAA,QACpC;AAAA,QACA,QAAQ,UAAU;AAAA,QAClB,aAAa,CAAC,UAAU,QAAQ,EAAE,MAAM,cAAc,WAAW,MAAM,CAAC;AAAA,QACxE,iBAAiB,CAAC,UAAU,QAAQ,EAAE,MAAM,SAAS,SAAS,OAAO,UAAU,WAAW,CAAC;AAAA,QAC3F,eAAe,CAAC,eACd,QAAQ,EAAE,MAAM,SAAS,SAAS,IAAI,UAAU,YAAY,iBAAiB,WAAW,CAAC;AAAA,QAC3F,mBAAmB,CAAC,UAAU,QAAQ,EAAE,MAAM,SAAS,SAAS,OAAO,UAAU,kBAAkB,CAAC;AAAA,QACpG,iBAAiB,CAAC,eAChB,QAAQ,EAAE,MAAM,SAAS,SAAS,IAAI,UAAU,mBAAmB,iBAAiB,WAAW,CAAC;AAAA,MACpG,CAAC;AACD,YAAM,OAAO,MAAM,QAAQ,YAAY,WAAW;AAClD,UAAI,MAAM;AACR,cAAM,YAAY,uBAAuB,IAAI;AAC7C,gBAAQ,EAAE,MAAM,QAAQ,MAAM,UAAU,CAAC;AACzC,gCAAwB,GAAG,YAAY;AAAA;AAAA,EAAO,kBAAkB,IAAI,CAAC;AACrE,kBAAU,KAAK,WAAW,kBAAkB;AAAA,UAC1C,OAAO,KAAK;AAAA,UACZ,OAAO,KAAK,MAAM;AAAA,UAClB,iBAAiB,KAAK,gBAAgB;AAAA,UACtC,WAAW,KAAK,IAAI,IAAI;AAAA,QAC1B,CAAC;AAAA,MACH,OAAO;AACL,kBAAU,KAAK,WAAW,qCAAqC;AAAA,UAC7D,WAAW,KAAK,IAAI,IAAI;AAAA,UACxB,OAAO,cAAc;AAAA,QACvB,CAAC;AACD,gBAAQ,EAAE,MAAM,eAAe,CAAC;AAAA,MAClC;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,aAAa;AAAA,MAChC;AAAA,MACA,QAAQ,UAAU;AAAA,MAClB,cAAc;AAAA,MACd,OAAO;AAAA,MACP;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;;;ACjQA,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,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,wBAAuC,iBAAiB;AAC9D,UAAM,eAAe,8BAA8B;AAAA,MACjD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,qBAAqB,oBAAoB;AAAA,MACzC,eAAe;AAAA,IACjB,CAAC;AACD,UAAM,oBAAoB,aAAa;AACvC,UAAM,wBACJ,CAAC,WAAW,mBAAmB,0BAA0B,aACrD,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,WAAW,SAAS,aAAa,YAAY,SAAS,SAAS;AACrE,YAAM,OACJ,0BAA0B,eAAe,+BAA+B,UAAU,QAAQ,SAAS,IAAI;AACzG,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,0BAA0B,eACtB,+BAA+B,IAAI,QAAQ,CAAC,kBAAkB,SAAS,eAAe,aAAa,CAAC,IACpG,SAAS,eAAe,EAAE;AAAA,MAClC,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,uBAAe,+BAA+B,gBAAgB,IAAI,EAAE,CAAC;AAAA,MACvE,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,MACA,eAAe;AAAA,IACjB,CAAC;AAAA,EACH;AACF;","names":["options","buildPiModel","fs","path","buildPiModel","buildPiModel"]}
|