chatlab-cli 0.25.1 → 0.26.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 → chat-command-T3Z4O4M5.mjs} +144 -6
- package/dist/chat-command-T3Z4O4M5.mjs.map +1 -0
- package/dist/{chunk-CUCDRAY6.mjs → chunk-3APSMVOM.mjs} +2 -2
- package/dist/{chunk-WYHGWTA4.mjs → chunk-6O2SW5GL.mjs} +94 -29
- package/dist/chunk-6O2SW5GL.mjs.map +1 -0
- package/dist/{chunk-GTCTDD2Z.mjs → chunk-AZZYKUBR.mjs} +17 -39
- package/dist/chunk-AZZYKUBR.mjs.map +1 -0
- package/dist/{chunk-JE3FOFF7.mjs → chunk-FACKH2BR.mjs} +49 -12
- package/dist/chunk-FACKH2BR.mjs.map +1 -0
- package/dist/{chunk-PDQFSQO6.mjs → chunk-FAOZNMPK.mjs} +2 -2
- package/dist/{chunk-7VG6QGQ2.mjs → chunk-GUGKY2GV.mjs} +1169 -328
- package/dist/chunk-GUGKY2GV.mjs.map +1 -0
- package/dist/{chunk-FDK6SPCG.mjs → chunk-MZG2GLJR.mjs} +2 -2
- package/dist/{chunk-YZYP5ST6.mjs → chunk-SC77N66X.mjs} +12 -11
- package/dist/chunk-SC77N66X.mjs.map +1 -0
- package/dist/cli.mjs +3 -3
- package/dist/{http-C3CGXUBU.mjs → http-6LQUOYQ6.mjs} +8 -8
- package/dist/{import-VS7L5JDX.mjs → import-B2MAIBAK.mjs} +4 -4
- package/dist/index.mjs +8 -8
- package/dist/{mcp-6VXDPUQK.mjs → mcp-ZMMYW46I.mjs} +4 -4
- package/dist/{stream-import-TFDBNVIW.mjs → stream-import-FHFBCEDV.mjs} +3 -3
- package/dist-web/assets/{DebugToolsPanel-vn-bnUxn.css → DebugToolsPanel-CrqULdXp.css} +1 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-CgtTQTfN.js +182 -0
- package/dist-web/assets/{EChartWordcloud-CzhfrEF_.js → EChartWordcloud-B_YisOW5.js} +1 -1
- package/dist-web/assets/MessageExportModal-B8DKeSAH.js +1 -0
- package/dist-web/assets/{electron-DRkrjeIl.js → electron-C-aMus7e.js} +1 -1
- package/dist-web/assets/electron-dBbXcMDB.js +1 -0
- package/dist-web/assets/{fetch-CDamdvtb.js → fetch-B-LdQP9s.js} +1 -1
- package/dist-web/assets/{fetch-Drk9dLaF.js → fetch-B26JuD67.js} +1 -1
- package/dist-web/assets/{fetch-BmZIm6mS.js → fetch-BLKibnXa.js} +1 -1
- package/dist-web/assets/{fetch-BqLy63aj.js → fetch-BNNqdS2B.js} +1 -1
- package/dist-web/assets/fetch-BSJY7tRo.js +1 -0
- package/dist-web/assets/{fetch-DWg-2kOp.js → fetch-CV8jezsM.js} +1 -1
- package/dist-web/assets/{fetch-ph2_d79v.js → fetch-CvnJ4Jgf.js} +1 -1
- package/dist-web/assets/{fetch-DfgdagMN.js → fetch-DP3yNfJM.js} +1 -1
- package/dist-web/assets/{fetch-Bvcxe5PI.js → fetch-phbrO2bY.js} +1 -1
- package/dist-web/assets/{fetch-Y-_WsCuk.js → fetch-xB6cl_1L.js} +3 -3
- package/dist-web/assets/index-8Zcs8peI.css +1 -0
- package/dist-web/assets/index-B_lOzUD8.js +2 -0
- package/dist-web/assets/index-BvWcJc3c.js +173 -0
- package/dist-web/assets/{index-DMWf5On7.js → index-CSDI5pAA.js} +2 -2
- package/dist-web/assets/{index-CGNgUIT-.css → index-CX2FucK5.css} +1 -1
- package/dist-web/assets/{index-9urfJP1r.js → index-DJQFNVUJ.js} +1 -1
- package/dist-web/assets/index-DZ6Szt_y.js +163 -0
- package/dist-web/assets/index-DhpclWeF.js +11 -0
- package/dist-web/assets/index-Dhql4DJr.css +1 -0
- package/dist-web/assets/{vendor-nuxt-ui-C_8CsH6e.js → vendor-nuxt-ui-Zy8txNdC.js} +1 -1
- package/dist-web/assets/{vendor-reka-ui-CIMXwvEp.js → vendor-reka-ui-mMvma1EP.js} +1 -1
- package/dist-web/assets/{web-XJV6Vj5t.js → web-DUQU-v8U.js} +1 -1
- package/dist-web/index.html +4 -4
- package/package.json +1 -1
- package/dist/chat-command-5XMRLXZM.mjs.map +0 -1
- package/dist/chunk-7VG6QGQ2.mjs.map +0 -1
- package/dist/chunk-GTCTDD2Z.mjs.map +0 -1
- package/dist/chunk-JE3FOFF7.mjs.map +0 -1
- package/dist/chunk-WYHGWTA4.mjs.map +0 -1
- package/dist/chunk-YZYP5ST6.mjs.map +0 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-B1xZmp9r.js +0 -161
- package/dist-web/assets/MessageExportModal-B79pPBL9.js +0 -1
- package/dist-web/assets/electron-Dv8jdn_2.js +0 -1
- package/dist-web/assets/fetch-BstX-wd4.js +0 -1
- package/dist-web/assets/index-C3A_Rjh-.css +0 -1
- package/dist-web/assets/index-CfKjh0Jr.js +0 -9
- package/dist-web/assets/index-CprJOAB1.css +0 -1
- package/dist-web/assets/index-D_hHm7bQ.js +0 -2
- package/dist-web/assets/index-MCdpiX_O.js +0 -173
- package/dist-web/assets/index-deYDOVUa.js +0 -163
- /package/dist/{chunk-CUCDRAY6.mjs.map → chunk-3APSMVOM.mjs.map} +0 -0
- /package/dist/{chunk-PDQFSQO6.mjs.map → chunk-FAOZNMPK.mjs.map} +0 -0
- /package/dist/{chunk-FDK6SPCG.mjs.map → chunk-MZG2GLJR.mjs.map} +0 -0
- /package/dist/{http-C3CGXUBU.mjs.map → http-6LQUOYQ6.mjs.map} +0 -0
- /package/dist/{import-VS7L5JDX.mjs.map → import-B2MAIBAK.mjs.map} +0 -0
- /package/dist/{mcp-6VXDPUQK.mjs.map → mcp-ZMMYW46I.mjs.map} +0 -0
- /package/dist/{stream-import-TFDBNVIW.mjs.map → stream-import-FHFBCEDV.mjs.map} +0 -0
|
@@ -6,9 +6,9 @@ const __filename = __fileURLToPath(import.meta.url);
|
|
|
6
6
|
const __dirname = __pathDirname(__filename);
|
|
7
7
|
import {
|
|
8
8
|
createCliRunAgentStream
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-6O2SW5GL.mjs";
|
|
10
|
+
import "./chunk-FACKH2BR.mjs";
|
|
11
|
+
import "./chunk-GUGKY2GV.mjs";
|
|
12
12
|
import "./chunk-VSOWNKPA.mjs";
|
|
13
13
|
|
|
14
14
|
// src/ai/chat-command.ts
|
|
@@ -35,6 +35,33 @@ function createAgentStreamError(error) {
|
|
|
35
35
|
}
|
|
36
36
|
return new Error("Agent stream failed");
|
|
37
37
|
}
|
|
38
|
+
function isRecord(value) {
|
|
39
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
40
|
+
}
|
|
41
|
+
function isChartPayload(value) {
|
|
42
|
+
return isRecord(value) && value.version === 1 && isRecord(value.spec) && isRecord(value.dataset);
|
|
43
|
+
}
|
|
44
|
+
function extractChartPayloads(toolResult) {
|
|
45
|
+
if (!isRecord(toolResult)) return [];
|
|
46
|
+
const details = isRecord(toolResult.details) ? toolResult.details : toolResult;
|
|
47
|
+
const charts = [];
|
|
48
|
+
if (isChartPayload(details.chart)) charts.push(details.chart);
|
|
49
|
+
if (Array.isArray(details.charts)) {
|
|
50
|
+
for (const chart of details.charts) {
|
|
51
|
+
if (isChartPayload(chart)) charts.push(chart);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return charts;
|
|
55
|
+
}
|
|
56
|
+
function toPersistedChartPayload(chart) {
|
|
57
|
+
return {
|
|
58
|
+
...chart,
|
|
59
|
+
dataset: {
|
|
60
|
+
...chart.dataset,
|
|
61
|
+
rows: []
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
38
65
|
function resolveSingleSessionId(dbManager) {
|
|
39
66
|
const sessionIds = dbManager.listSessionIds();
|
|
40
67
|
if (sessionIds.length === 1) return sessionIds[0];
|
|
@@ -73,7 +100,82 @@ async function runChatTurn(options, deps) {
|
|
|
73
100
|
let answer = "";
|
|
74
101
|
let tokenUsage = null;
|
|
75
102
|
let streamError = null;
|
|
103
|
+
const events = [];
|
|
104
|
+
const contentBlocks = [];
|
|
105
|
+
let hasReplayContentBlocks = false;
|
|
76
106
|
const runAgentStream = (deps.createRunAgentStream ?? createCliRunAgentStream)(deps.dbManager, deps.aiChatManager);
|
|
107
|
+
const appendTextBlock = (text) => {
|
|
108
|
+
if (!text) return;
|
|
109
|
+
const lastBlock = contentBlocks[contentBlocks.length - 1];
|
|
110
|
+
if (lastBlock?.type === "text") {
|
|
111
|
+
lastBlock.text += text;
|
|
112
|
+
} else {
|
|
113
|
+
contentBlocks.push({ type: "text", text });
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const appendThinkBlock = (text, tag = "thinking", durationMs) => {
|
|
117
|
+
if (!text && durationMs === void 0) return;
|
|
118
|
+
const lastBlock = contentBlocks[contentBlocks.length - 1];
|
|
119
|
+
let targetBlock;
|
|
120
|
+
if (lastBlock?.type === "think" && lastBlock.tag === tag) {
|
|
121
|
+
lastBlock.text += text;
|
|
122
|
+
targetBlock = lastBlock;
|
|
123
|
+
} else if (text.trim().length > 0) {
|
|
124
|
+
targetBlock = { type: "think", tag, text };
|
|
125
|
+
contentBlocks.push(targetBlock);
|
|
126
|
+
} else if (durationMs !== void 0) {
|
|
127
|
+
for (let index = contentBlocks.length - 1; index >= 0; index--) {
|
|
128
|
+
const block = contentBlocks[index];
|
|
129
|
+
if (block.type === "think" && block.tag === tag) {
|
|
130
|
+
targetBlock = block;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (durationMs !== void 0 && targetBlock?.type === "think") {
|
|
136
|
+
targetBlock.durationMs = durationMs;
|
|
137
|
+
}
|
|
138
|
+
if (targetBlock?.type === "think") {
|
|
139
|
+
hasReplayContentBlocks = true;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const appendChartBlocks = (charts) => {
|
|
143
|
+
if (charts.length === 0) return;
|
|
144
|
+
contentBlocks.push(...charts.map((chart) => ({ type: "chart", chart: toPersistedChartPayload(chart) })));
|
|
145
|
+
hasReplayContentBlocks = true;
|
|
146
|
+
};
|
|
147
|
+
const appendPlanDraftBlock = (delta) => {
|
|
148
|
+
if (!delta) return;
|
|
149
|
+
const lastBlock = contentBlocks[contentBlocks.length - 1];
|
|
150
|
+
if (lastBlock?.type === "plan_draft") {
|
|
151
|
+
lastBlock.text += delta;
|
|
152
|
+
} else {
|
|
153
|
+
contentBlocks.push({ type: "plan_draft", version: 1, status: "streaming", text: delta });
|
|
154
|
+
}
|
|
155
|
+
hasReplayContentBlocks = true;
|
|
156
|
+
};
|
|
157
|
+
const removePlanDraftBlocks = () => {
|
|
158
|
+
for (let index = contentBlocks.length - 1; index >= 0; index--) {
|
|
159
|
+
if (contentBlocks[index]?.type === "plan_draft") {
|
|
160
|
+
contentBlocks.splice(index, 1);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
const appendFinalPlanBlock = (plan) => {
|
|
165
|
+
const planBlock = JSON.parse(JSON.stringify(plan));
|
|
166
|
+
for (let index = contentBlocks.length - 1; index >= 0; index--) {
|
|
167
|
+
const block = contentBlocks[index];
|
|
168
|
+
if (block?.type === "plan_draft") {
|
|
169
|
+
const displayText = block.text.trim();
|
|
170
|
+
if (displayText) planBlock.displayText = displayText;
|
|
171
|
+
contentBlocks[index] = planBlock;
|
|
172
|
+
hasReplayContentBlocks = true;
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
contentBlocks.push(planBlock);
|
|
177
|
+
hasReplayContentBlocks = true;
|
|
178
|
+
};
|
|
77
179
|
await runAgentStream(
|
|
78
180
|
{
|
|
79
181
|
userMessage: options.question,
|
|
@@ -81,19 +183,53 @@ async function runChatTurn(options, deps) {
|
|
|
81
183
|
aiChatId: target.aiChatId,
|
|
82
184
|
assistantId: target.assistantId,
|
|
83
185
|
chatType: "group",
|
|
84
|
-
locale: options.locale ?? "zh-CN"
|
|
186
|
+
locale: options.locale ?? "zh-CN",
|
|
187
|
+
enableAutoSkill: true,
|
|
188
|
+
chartAutoMode: "suggest"
|
|
85
189
|
},
|
|
86
190
|
(chunk) => {
|
|
191
|
+
if (options.includeEvents) {
|
|
192
|
+
events.push(JSON.parse(JSON.stringify(chunk)));
|
|
193
|
+
}
|
|
87
194
|
if (chunk.type === "error") {
|
|
195
|
+
removePlanDraftBlocks();
|
|
88
196
|
streamError = createAgentStreamError(chunk.error);
|
|
89
197
|
return;
|
|
90
198
|
}
|
|
199
|
+
if (chunk.type === "plan_delta" && chunk.planDelta) {
|
|
200
|
+
appendPlanDraftBlock(chunk.planDelta);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (chunk.type === "plan" && chunk.plan) {
|
|
204
|
+
appendFinalPlanBlock(chunk.plan);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (chunk.type === "plan_skipped") {
|
|
208
|
+
removePlanDraftBlocks();
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (chunk.type === "think") {
|
|
212
|
+
appendThinkBlock(chunk.content ?? "", chunk.thinkTag, chunk.thinkDurationMs);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (chunk.type === "tool_result" && chunk.toolName === "render_chart") {
|
|
216
|
+
appendChartBlocks(extractChartPayloads(chunk.toolResult));
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
91
219
|
if (chunk.type === "content" && chunk.content) {
|
|
92
220
|
answer += chunk.content;
|
|
221
|
+
appendTextBlock(chunk.content);
|
|
93
222
|
if (!options.json && options.stream !== false) write(stdout, chunk.content);
|
|
94
223
|
}
|
|
95
224
|
if (chunk.type === "done" && chunk.usage) {
|
|
96
225
|
tokenUsage = chunk.usage;
|
|
226
|
+
for (let index = contentBlocks.length - 1; index >= 0; index--) {
|
|
227
|
+
const block = contentBlocks[index];
|
|
228
|
+
if (block.type === "plan") {
|
|
229
|
+
block.status = "done";
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
97
233
|
}
|
|
98
234
|
},
|
|
99
235
|
new AbortController().signal
|
|
@@ -109,7 +245,7 @@ async function runChatTurn(options, deps) {
|
|
|
109
245
|
answer,
|
|
110
246
|
void 0,
|
|
111
247
|
void 0,
|
|
112
|
-
void 0,
|
|
248
|
+
hasReplayContentBlocks ? contentBlocks : void 0,
|
|
113
249
|
tokenUsage ?? void 0
|
|
114
250
|
);
|
|
115
251
|
return {
|
|
@@ -117,6 +253,8 @@ async function runChatTurn(options, deps) {
|
|
|
117
253
|
aiChatId: target.aiChatId,
|
|
118
254
|
question: options.question,
|
|
119
255
|
answer,
|
|
256
|
+
...options.includeEvents ? { events } : {},
|
|
257
|
+
...hasReplayContentBlocks ? { contentBlocks } : {},
|
|
120
258
|
usage: {
|
|
121
259
|
durationMs: Date.now() - startedAt,
|
|
122
260
|
tokenUsage
|
|
@@ -163,4 +301,4 @@ export {
|
|
|
163
301
|
runChatCommand,
|
|
164
302
|
runChatTurn
|
|
165
303
|
};
|
|
166
|
-
//# sourceMappingURL=chat-command-
|
|
304
|
+
//# sourceMappingURL=chat-command-T3Z4O4M5.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ai/chat-command.ts"],"sourcesContent":["import { createInterface } from 'node:readline/promises'\nimport type { Readable, Writable } from 'node:stream'\nimport type {\n DatabaseManager,\n AIChatManager,\n AgentStreamChunk,\n ContentBlock,\n PlanContentBlock,\n PlanDraftContentBlock,\n TokenUsageData,\n} from '@openchatlab/node-runtime'\nimport type { ChartPayload, PathProvider } from '@openchatlab/core'\nimport { createCliRunAgentStream } from './agent-stream-runner'\n\nexport interface ChatCommandOptions {\n sessionId?: string\n aiChatId?: string\n question?: string\n json?: boolean\n stream?: boolean\n locale?: string\n includeEvents?: boolean\n}\n\nexport interface ChatCommandDeps {\n dbManager: DatabaseManager\n pathProvider: PathProvider\n aiChatManager: AIChatManager\n stdout?: Pick<Writable, 'write'>\n stderr?: Pick<Writable, 'write'>\n stdin?: Readable\n createRunAgentStream?: typeof createCliRunAgentStream\n}\n\nexport interface ResolvedAIChatTarget {\n sessionId: string\n aiChatId: string\n assistantId: string\n created: boolean\n}\n\nexport interface ChatTurnResult {\n sessionId: string\n aiChatId: string\n question: string\n answer: string\n events?: AgentStreamChunk[]\n contentBlocks?: ContentBlock[]\n usage: {\n durationMs: number\n tokenUsage: TokenUsageData | null\n }\n}\n\nfunction write(stream: Pick<Writable, 'write'>, text: string): void {\n stream.write(text)\n}\n\nfunction buildTitle(question?: string): string {\n const trimmed = question?.trim()\n if (!trimmed) return 'CLI Chat'\n return trimmed.length > 50 ? `${trimmed.slice(0, 50)}...` : trimmed\n}\n\nfunction createAgentStreamError(error: unknown): Error {\n if (error instanceof Error) return error\n if (typeof error === 'string' && error) return new Error(error)\n if (typeof error === 'object' && error !== null) {\n const record = error as { name?: unknown; message?: unknown; stack?: unknown }\n const streamError = new Error(\n typeof record.message === 'string' && record.message ? record.message : 'Agent stream failed'\n )\n if (typeof record.name === 'string' && record.name) streamError.name = record.name\n if (typeof record.stack === 'string' && record.stack) streamError.stack = record.stack\n return streamError\n }\n return new Error('Agent stream failed')\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isChartPayload(value: unknown): value is ChartPayload {\n return isRecord(value) && value.version === 1 && isRecord(value.spec) && isRecord(value.dataset)\n}\n\nfunction extractChartPayloads(toolResult: unknown): ChartPayload[] {\n if (!isRecord(toolResult)) return []\n const details = isRecord(toolResult.details) ? toolResult.details : toolResult\n const charts: ChartPayload[] = []\n if (isChartPayload(details.chart)) charts.push(details.chart)\n if (Array.isArray(details.charts)) {\n for (const chart of details.charts) {\n if (isChartPayload(chart)) charts.push(chart)\n }\n }\n return charts\n}\n\nfunction toPersistedChartPayload(chart: ChartPayload): ChartPayload {\n return {\n ...chart,\n dataset: {\n ...chart.dataset,\n rows: [],\n },\n }\n}\n\nfunction resolveSingleSessionId(dbManager: DatabaseManager): string {\n const sessionIds = dbManager.listSessionIds()\n if (sessionIds.length === 1) return sessionIds[0]!\n if (sessionIds.length === 0) {\n throw new Error('No chat sessions found. Import data first or run `chatlab sessions` to verify available sessions.')\n }\n throw new Error('Missing --session-id. Run `chatlab sessions` to choose a session ID.')\n}\n\nfunction assertSessionExists(dbManager: DatabaseManager, sessionId: string): void {\n const db = dbManager.open(sessionId)\n if (!db) {\n throw new Error(`Session ${sessionId} not found`)\n }\n}\n\nexport function resolveAIChatTarget(\n options: Pick<ChatCommandOptions, 'sessionId' | 'aiChatId' | 'question'>,\n deps: Pick<ChatCommandDeps, 'dbManager' | 'aiChatManager'>\n): ResolvedAIChatTarget {\n if (options.aiChatId) {\n const aiChat = deps.aiChatManager.getAIChat(options.aiChatId)\n if (!aiChat) {\n throw new Error(`AI chat ${options.aiChatId} not found`)\n }\n if (options.sessionId && options.sessionId !== aiChat.sessionId) {\n throw new Error(`AI chat ${options.aiChatId} belongs to session ${aiChat.sessionId}, not ${options.sessionId}`)\n }\n assertSessionExists(deps.dbManager, aiChat.sessionId)\n return { sessionId: aiChat.sessionId, aiChatId: aiChat.id, assistantId: aiChat.assistantId, created: false }\n }\n\n const sessionId = options.sessionId ?? resolveSingleSessionId(deps.dbManager)\n assertSessionExists(deps.dbManager, sessionId)\n const aiChat = deps.aiChatManager.createAIChat(sessionId, buildTitle(options.question), 'general_cn')\n return { sessionId, aiChatId: aiChat.id, assistantId: aiChat.assistantId, created: true }\n}\n\nexport async function runChatTurn(\n options: Required<Pick<ChatCommandOptions, 'question'>> &\n Pick<ChatCommandOptions, 'sessionId' | 'aiChatId' | 'json' | 'stream' | 'locale' | 'includeEvents'>,\n deps: ChatCommandDeps\n): Promise<ChatTurnResult> {\n const stdout = deps.stdout ?? process.stdout\n const target = resolveAIChatTarget(options, deps)\n const startedAt = Date.now()\n let answer = ''\n let tokenUsage: TokenUsageData | null = null\n let streamError: Error | null = null\n const events: AgentStreamChunk[] = []\n const contentBlocks: ContentBlock[] = []\n let hasReplayContentBlocks = false\n\n const runAgentStream = (deps.createRunAgentStream ?? createCliRunAgentStream)(deps.dbManager, deps.aiChatManager)\n\n // 中文注释:CLI 历史回放依赖 contentBlocks 的时序;只要出现计划/思考块,\n // 就同步保留后续正文 text block,避免 UI 使用 blocks 渲染时丢失最终回答。\n const appendTextBlock = (text: string) => {\n if (!text) return\n const lastBlock = contentBlocks[contentBlocks.length - 1]\n if (lastBlock?.type === 'text') {\n lastBlock.text += text\n } else {\n contentBlocks.push({ type: 'text', text })\n }\n }\n\n const appendThinkBlock = (text: string, tag = 'thinking', durationMs?: number) => {\n if (!text && durationMs === undefined) return\n const lastBlock = contentBlocks[contentBlocks.length - 1]\n let targetBlock: ContentBlock | undefined\n\n if (lastBlock?.type === 'think' && lastBlock.tag === tag) {\n lastBlock.text += text\n targetBlock = lastBlock\n } else if (text.trim().length > 0) {\n targetBlock = { type: 'think', tag, text }\n contentBlocks.push(targetBlock)\n } else if (durationMs !== undefined) {\n for (let index = contentBlocks.length - 1; index >= 0; index--) {\n const block = contentBlocks[index]\n if (block.type === 'think' && block.tag === tag) {\n targetBlock = block\n break\n }\n }\n }\n\n if (durationMs !== undefined && targetBlock?.type === 'think') {\n targetBlock.durationMs = durationMs\n }\n if (targetBlock?.type === 'think') {\n hasReplayContentBlocks = true\n }\n }\n\n const appendChartBlocks = (charts: ChartPayload[]) => {\n if (charts.length === 0) return\n // 中文注释:图表 block 需要持久化给 CLI 生成的 AI 对话回放;\n // 原始 SQL 行数据可能较大,保存时只保留渲染数据和字段元信息。\n contentBlocks.push(...charts.map((chart) => ({ type: 'chart' as const, chart: toPersistedChartPayload(chart) })))\n hasReplayContentBlocks = true\n }\n\n const appendPlanDraftBlock = (delta: string) => {\n if (!delta) return\n const lastBlock = contentBlocks[contentBlocks.length - 1]\n if (lastBlock?.type === 'plan_draft') {\n lastBlock.text += delta\n } else {\n contentBlocks.push({ type: 'plan_draft', version: 1, status: 'streaming', text: delta } as PlanDraftContentBlock)\n }\n hasReplayContentBlocks = true\n }\n\n const removePlanDraftBlocks = () => {\n for (let index = contentBlocks.length - 1; index >= 0; index--) {\n if (contentBlocks[index]?.type === 'plan_draft') {\n contentBlocks.splice(index, 1)\n }\n }\n }\n\n const appendFinalPlanBlock = (plan: PlanContentBlock) => {\n const planBlock = JSON.parse(JSON.stringify(plan)) as PlanContentBlock\n for (let index = contentBlocks.length - 1; index >= 0; index--) {\n const block = contentBlocks[index]\n if (block?.type === 'plan_draft') {\n const displayText = block.text.trim()\n if (displayText) planBlock.displayText = displayText\n contentBlocks[index] = planBlock\n hasReplayContentBlocks = true\n return\n }\n }\n contentBlocks.push(planBlock)\n hasReplayContentBlocks = true\n }\n\n await runAgentStream(\n {\n userMessage: options.question,\n sessionId: target.sessionId,\n aiChatId: target.aiChatId,\n assistantId: target.assistantId,\n chatType: 'group',\n locale: options.locale ?? 'zh-CN',\n enableAutoSkill: true,\n chartAutoMode: 'suggest',\n },\n (chunk: AgentStreamChunk) => {\n if (options.includeEvents) {\n events.push(JSON.parse(JSON.stringify(chunk)) as AgentStreamChunk)\n }\n if (chunk.type === 'error') {\n removePlanDraftBlocks()\n streamError = createAgentStreamError(chunk.error)\n return\n }\n if (chunk.type === 'plan_delta' && chunk.planDelta) {\n appendPlanDraftBlock(chunk.planDelta)\n return\n }\n if (chunk.type === 'plan' && chunk.plan) {\n appendFinalPlanBlock(chunk.plan)\n return\n }\n if (chunk.type === 'plan_skipped') {\n removePlanDraftBlocks()\n return\n }\n if (chunk.type === 'think') {\n appendThinkBlock(chunk.content ?? '', chunk.thinkTag, chunk.thinkDurationMs)\n return\n }\n if (chunk.type === 'tool_result' && chunk.toolName === 'render_chart') {\n appendChartBlocks(extractChartPayloads(chunk.toolResult))\n return\n }\n if (chunk.type === 'content' && chunk.content) {\n answer += chunk.content\n appendTextBlock(chunk.content)\n if (!options.json && options.stream !== false) write(stdout, chunk.content)\n }\n if (chunk.type === 'done' && chunk.usage) {\n tokenUsage = chunk.usage\n for (let index = contentBlocks.length - 1; index >= 0; index--) {\n const block = contentBlocks[index]\n if (block.type === 'plan') {\n block.status = 'done'\n break\n }\n }\n }\n },\n new AbortController().signal\n )\n\n if (streamError) throw streamError\n\n if (!options.json && options.stream !== false && answer && !answer.endsWith('\\n')) {\n write(stdout, '\\n')\n }\n\n deps.aiChatManager.addMessage(target.aiChatId, 'user', options.question)\n deps.aiChatManager.addMessage(\n target.aiChatId,\n 'assistant',\n answer,\n undefined,\n undefined,\n hasReplayContentBlocks ? contentBlocks : undefined,\n tokenUsage ?? undefined\n )\n\n return {\n sessionId: target.sessionId,\n aiChatId: target.aiChatId,\n question: options.question,\n answer,\n ...(options.includeEvents ? { events } : {}),\n ...(hasReplayContentBlocks ? { contentBlocks } : {}),\n usage: {\n durationMs: Date.now() - startedAt,\n tokenUsage,\n },\n }\n}\n\nexport async function runChatCommand(options: ChatCommandOptions, deps: ChatCommandDeps): Promise<void> {\n const stdout = deps.stdout ?? process.stdout\n const stderr = deps.stderr ?? process.stderr\n\n if (options.question?.trim()) {\n const result = await runChatTurn({ ...options, question: options.question.trim() }, deps)\n if (options.json) {\n write(stdout, `${JSON.stringify(result, null, 2)}\\n`)\n } else if (options.stream === false) {\n write(stdout, `${result.answer}\\n`)\n }\n return\n }\n\n const rl = createInterface({\n input: deps.stdin ?? process.stdin,\n output: stdout as Writable,\n })\n\n try {\n let aiChatId = options.aiChatId\n while (true) {\n const question = (await rl.question('chatlab> ')).trim()\n if (!question || question === 'exit' || question === 'quit') break\n try {\n const result = await runChatTurn({ ...options, aiChatId, question, json: false, stream: true }, deps)\n aiChatId = result.aiChatId\n } catch (error) {\n write(stderr, `${error instanceof Error ? error.message : String(error)}\\n`)\n }\n }\n } finally {\n rl.close()\n }\n}\n"],"mappings":"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","names":["aiChat"]}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
buildMemberIdMap,
|
|
11
11
|
generateMessageKey,
|
|
12
12
|
writeParseResultToDb
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-GUGKY2GV.mjs";
|
|
14
14
|
|
|
15
15
|
// src/import/chatlab-reader.ts
|
|
16
16
|
import * as fs from "fs";
|
|
@@ -384,4 +384,4 @@ export {
|
|
|
384
384
|
parseFile,
|
|
385
385
|
importData
|
|
386
386
|
};
|
|
387
|
-
//# sourceMappingURL=chunk-
|
|
387
|
+
//# sourceMappingURL=chunk-3APSMVOM.mjs.map
|
|
@@ -7,7 +7,7 @@ const __dirname = __pathDirname(__filename);
|
|
|
7
7
|
import {
|
|
8
8
|
AGENT_TOOL_REGISTRY,
|
|
9
9
|
CoreDataProvider
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-FACKH2BR.mjs";
|
|
11
11
|
import {
|
|
12
12
|
AgentEventHandler,
|
|
13
13
|
AiLogger,
|
|
@@ -17,16 +17,23 @@ import {
|
|
|
17
17
|
applyPreprocessingPipeline,
|
|
18
18
|
batchSegmentWithFrequency,
|
|
19
19
|
buildPiModel,
|
|
20
|
+
buildPlanGuidance,
|
|
20
21
|
buildSkillMenuWithBuiltinChart,
|
|
21
22
|
buildSystemPrompt,
|
|
22
23
|
checkAndCompress,
|
|
23
24
|
createActivateSkillTool,
|
|
24
25
|
createAiTranslate,
|
|
26
|
+
createAnalysisPlanner,
|
|
25
27
|
createChartSchemaGateState,
|
|
26
28
|
createCompressionLlmAdapter,
|
|
29
|
+
createDataSnapshotFromOverview,
|
|
30
|
+
createLlmRouteDecider,
|
|
31
|
+
createPlanContentBlock,
|
|
32
|
+
decideRequestRoute,
|
|
27
33
|
formatAIError,
|
|
28
34
|
getAllowedBuiltinToolsForChartAutoSkill,
|
|
29
35
|
getChartCapabilitySkill,
|
|
36
|
+
getChartPlannerCapabilityForMessage,
|
|
30
37
|
getChatOverview,
|
|
31
38
|
getSkillConfigWithBuiltinChart,
|
|
32
39
|
initTokenizer,
|
|
@@ -35,8 +42,9 @@ import {
|
|
|
35
42
|
resolveApiKey,
|
|
36
43
|
resolveChartRuntimeForRequest,
|
|
37
44
|
runAgentCore,
|
|
45
|
+
shouldUseChartCapabilityForMessage,
|
|
38
46
|
wrapWithChartSchemaGate
|
|
39
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-GUGKY2GV.mjs";
|
|
40
48
|
|
|
41
49
|
// src/ai/logger.ts
|
|
42
50
|
var logger = null;
|
|
@@ -104,12 +112,13 @@ function adaptToolsForAgent(tools, getContext, options) {
|
|
|
104
112
|
...result.charts ? { charts: result.charts } : {}
|
|
105
113
|
} : {};
|
|
106
114
|
if (result.rawMessages && result.rawMessages.length > 0) {
|
|
115
|
+
const { rawMessages: _rawInData, ...extraDetails } = result.data ?? {};
|
|
107
116
|
const pipelineResult = applyPreprocessingPipeline({
|
|
108
117
|
rawMessages: result.rawMessages,
|
|
109
118
|
locale: ctx.locale,
|
|
110
119
|
maxToolResultTokens: tokenBudget,
|
|
111
120
|
truncationStrategy: TOOL_TRUNCATION_STRATEGY[tool.name] ?? "keep_last",
|
|
112
|
-
extraDetails
|
|
121
|
+
extraDetails,
|
|
113
122
|
logger: getServerAiLogger() ?? void 0
|
|
114
123
|
});
|
|
115
124
|
return {
|
|
@@ -215,7 +224,8 @@ async function runServerAgent(options) {
|
|
|
215
224
|
ownerInfo,
|
|
216
225
|
mentionedMembers,
|
|
217
226
|
dataSnapshot,
|
|
218
|
-
thinkingLevel
|
|
227
|
+
thinkingLevel,
|
|
228
|
+
chartAutoMode = "suggest"
|
|
219
229
|
} = options;
|
|
220
230
|
const aiLogger = getServerAiLogger();
|
|
221
231
|
await initTokenizer();
|
|
@@ -292,12 +302,77 @@ async function runServerAgent(options) {
|
|
|
292
302
|
handler.emitStatus("preparing", [], { pendingUserMessage: userMessage, force: true });
|
|
293
303
|
const steerMessage = t("ai.agent.answerWithoutTools");
|
|
294
304
|
let cachedMessages = [];
|
|
305
|
+
const effectiveTools = chartAutoMode === "explicit" && !shouldUseChartCapabilityForMessage(userMessage) ? tools.filter((tool) => tool.name !== "render_chart") : tools;
|
|
295
306
|
try {
|
|
307
|
+
const routeInput = {
|
|
308
|
+
userMessage,
|
|
309
|
+
chatType,
|
|
310
|
+
locale,
|
|
311
|
+
dataSnapshot,
|
|
312
|
+
availableTools: effectiveTools.map((tool) => tool.name),
|
|
313
|
+
availableCapabilities: [
|
|
314
|
+
getChartPlannerCapabilityForMessage({
|
|
315
|
+
userMessage,
|
|
316
|
+
locale,
|
|
317
|
+
availableTools: effectiveTools.map((tool) => tool.name),
|
|
318
|
+
chartAutoMode
|
|
319
|
+
})
|
|
320
|
+
].filter((capability) => capability !== null),
|
|
321
|
+
skillSummary: skillDef?.name ?? (skillMenu ? "auto_skill_menu" : void 0)
|
|
322
|
+
};
|
|
323
|
+
const routeStartedAt = Date.now();
|
|
324
|
+
const routeDecision = await decideRequestRoute(routeInput, {
|
|
325
|
+
llmRouter: createLlmRouteDecider({
|
|
326
|
+
piModel,
|
|
327
|
+
apiKey: llmConfig.apiKey,
|
|
328
|
+
abortSignal
|
|
329
|
+
})
|
|
330
|
+
});
|
|
331
|
+
aiLogger?.info("Router", "Shadow route decision", {
|
|
332
|
+
...routeDecision,
|
|
333
|
+
elapsedMs: Date.now() - routeStartedAt,
|
|
334
|
+
availableToolCount: tools.length,
|
|
335
|
+
shadowOnly: true
|
|
336
|
+
});
|
|
337
|
+
onEvent({ type: "route", routeDecision });
|
|
338
|
+
let effectiveSystemPrompt = systemPrompt;
|
|
339
|
+
if (routeDecision.route === "planned_execution") {
|
|
340
|
+
const planStartedAt = Date.now();
|
|
341
|
+
const planner = createAnalysisPlanner({
|
|
342
|
+
piModel,
|
|
343
|
+
apiKey: llmConfig.apiKey,
|
|
344
|
+
onPlanDelta: (delta) => onEvent({ type: "plan_delta", planDelta: delta }),
|
|
345
|
+
onThinkingDelta: (delta) => onEvent({ type: "think", content: delta, thinkTag: "thinking" }),
|
|
346
|
+
onThinkingEnd: (durationMs) => onEvent({ type: "think", content: "", thinkTag: "thinking", thinkDurationMs: durationMs }),
|
|
347
|
+
onValidationDelta: (delta) => onEvent({ type: "think", content: delta, thinkTag: "plan_validation" }),
|
|
348
|
+
onValidationEnd: (durationMs) => onEvent({ type: "think", content: "", thinkTag: "plan_validation", thinkDurationMs: durationMs })
|
|
349
|
+
});
|
|
350
|
+
const plan = await planner(routeInput, abortSignal);
|
|
351
|
+
if (plan) {
|
|
352
|
+
const planBlock = createPlanContentBlock(plan);
|
|
353
|
+
onEvent({ type: "plan", plan: planBlock });
|
|
354
|
+
effectiveSystemPrompt = `${systemPrompt}
|
|
355
|
+
|
|
356
|
+
${buildPlanGuidance(plan)}`;
|
|
357
|
+
aiLogger?.info("Planner", "Plan generated", {
|
|
358
|
+
title: plan.title,
|
|
359
|
+
steps: plan.steps.length,
|
|
360
|
+
successCriteria: plan.successCriteria.length,
|
|
361
|
+
elapsedMs: Date.now() - planStartedAt
|
|
362
|
+
});
|
|
363
|
+
} else {
|
|
364
|
+
aiLogger?.warn("Planner", "Plan generation skipped or failed", {
|
|
365
|
+
elapsedMs: Date.now() - planStartedAt,
|
|
366
|
+
route: routeDecision.route
|
|
367
|
+
});
|
|
368
|
+
onEvent({ type: "plan_skipped" });
|
|
369
|
+
}
|
|
370
|
+
}
|
|
296
371
|
const result = await runAgentCore({
|
|
297
372
|
piModel,
|
|
298
373
|
apiKey: llmConfig.apiKey,
|
|
299
|
-
systemPrompt,
|
|
300
|
-
tools,
|
|
374
|
+
systemPrompt: effectiveSystemPrompt,
|
|
375
|
+
tools: effectiveTools,
|
|
301
376
|
history,
|
|
302
377
|
userMessage,
|
|
303
378
|
maxToolRounds: DEFAULT_MAX_TOOL_ROUNDS,
|
|
@@ -366,6 +441,7 @@ function createCliRunAgentStream(dbManager, aiChatManager) {
|
|
|
366
441
|
assistantId,
|
|
367
442
|
skillId,
|
|
368
443
|
enableAutoSkill,
|
|
444
|
+
chartAutoMode,
|
|
369
445
|
compressionConfig,
|
|
370
446
|
ownerInfo,
|
|
371
447
|
mentionedMembers,
|
|
@@ -386,14 +462,17 @@ function createCliRunAgentStream(dbManager, aiChatManager) {
|
|
|
386
462
|
const contextWindow = llmConfig ? buildPiModel2(llmConfig).contextWindow ?? 128e3 : 128e3;
|
|
387
463
|
const maxToolResultTokens = Math.floor(contextWindow * (maxToolResultPercent / 100));
|
|
388
464
|
const db = dbManager.open?.(sessionId);
|
|
465
|
+
const resolvedChartAutoMode = chartAutoMode ?? "suggest";
|
|
389
466
|
const chartRuntime = resolveChartRuntimeForRequest({
|
|
390
467
|
skillId,
|
|
391
468
|
userMessage,
|
|
392
469
|
locale,
|
|
393
|
-
assistantAllowedTools
|
|
470
|
+
assistantAllowedTools,
|
|
471
|
+
enableAutoDetection: enableAutoSkill === true,
|
|
472
|
+
chartAutoMode: resolvedChartAutoMode
|
|
394
473
|
});
|
|
395
474
|
const isChartCapability = chartRuntime.isChartCapability;
|
|
396
|
-
const autoSkillAllowedTools = !skillId && enableAutoSkill ? getAllowedBuiltinToolsForChartAutoSkill(assistantAllowedTools) : assistantAllowedTools;
|
|
475
|
+
const autoSkillAllowedTools = !skillId && enableAutoSkill && resolvedChartAutoMode !== "explicit" ? getAllowedBuiltinToolsForChartAutoSkill(assistantAllowedTools) : assistantAllowedTools;
|
|
397
476
|
const allowedToolSet = getAllowedToolSet(
|
|
398
477
|
isChartCapability,
|
|
399
478
|
isChartCapability ? chartRuntime.allowedBuiltinTools : autoSkillAllowedTools
|
|
@@ -412,11 +491,8 @@ function createCliRunAgentStream(dbManager, aiChatManager) {
|
|
|
412
491
|
const def = skillMgr.getSkillConfig(skillId);
|
|
413
492
|
if (def) resolvedSkillDef = { name: def.name, prompt: def.prompt };
|
|
414
493
|
} else if (enableAutoSkill) {
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
locale,
|
|
418
|
-
toolNames
|
|
419
|
-
);
|
|
494
|
+
const baseMenu = skillMgr.getSkillMenu(chatType ?? "group", toolNames);
|
|
495
|
+
const menu = resolvedChartAutoMode === "aggressive" ? buildSkillMenuWithBuiltinChart(baseMenu, locale, toolNames) : baseMenu;
|
|
420
496
|
if (menu) resolvedSkillMenu = menu;
|
|
421
497
|
}
|
|
422
498
|
if (resolvedSkillMenu) {
|
|
@@ -425,7 +501,7 @@ function createCliRunAgentStream(dbManager, aiChatManager) {
|
|
|
425
501
|
allowedTools: toolNames,
|
|
426
502
|
coreToolNames: new Set(CHART_CAPABILITY_CORE_TOOLS),
|
|
427
503
|
locale,
|
|
428
|
-
getSkillConfig: (id) => getSkillConfigWithBuiltinChart(id, locale, (skillConfigId) => skillMgr.getSkillConfig(skillConfigId))
|
|
504
|
+
getSkillConfig: (id) => resolvedChartAutoMode === "aggressive" ? getSkillConfigWithBuiltinChart(id, locale, (skillConfigId) => skillMgr.getSkillConfig(skillConfigId)) : skillMgr.getSkillConfig(id)
|
|
429
505
|
});
|
|
430
506
|
agentTools.push(activateSkillTool);
|
|
431
507
|
}
|
|
@@ -438,19 +514,7 @@ function createCliRunAgentStream(dbManager, aiChatManager) {
|
|
|
438
514
|
let dataSnapshot;
|
|
439
515
|
if (db) {
|
|
440
516
|
try {
|
|
441
|
-
|
|
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
|
-
}
|
|
517
|
+
dataSnapshot = createDataSnapshotFromOverview(getChatOverview(db, 10));
|
|
454
518
|
} catch {
|
|
455
519
|
}
|
|
456
520
|
}
|
|
@@ -472,7 +536,8 @@ function createCliRunAgentStream(dbManager, aiChatManager) {
|
|
|
472
536
|
ownerInfo,
|
|
473
537
|
mentionedMembers,
|
|
474
538
|
dataSnapshot,
|
|
475
|
-
thinkingLevel
|
|
539
|
+
thinkingLevel,
|
|
540
|
+
chartAutoMode: resolvedChartAutoMode
|
|
476
541
|
});
|
|
477
542
|
};
|
|
478
543
|
}
|
|
@@ -482,4 +547,4 @@ export {
|
|
|
482
547
|
closeServerAiLogger,
|
|
483
548
|
createCliRunAgentStream
|
|
484
549
|
};
|
|
485
|
-
//# sourceMappingURL=chunk-
|
|
550
|
+
//# sourceMappingURL=chunk-6O2SW5GL.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 // tools may mirror rawMessages inside data; keep it out of extraDetails\n // so the pipeline only renders scalar metadata (same as the desktop adapter)\n const { rawMessages: _rawInData, ...extraDetails } = (result.data ?? {}) as Record<string, unknown>\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,\n logger: getServerAiLogger() ?? undefined,\n })\n return {\n content: [{ type: 'text', text: pipelineResult.text }],\n details: Object.keys(chartDetails).length > 0 ? chartDetails : null,\n }\n }\n\n const baseDetails =\n typeof result.data === 'object' && result.data !== null\n ? (result.data as Record<string, unknown>)\n : result.data === undefined\n ? null\n : { value: result.data }\n\n return {\n content: [{ type: 'text', text: result.content }],\n details: Object.keys(chartDetails).length > 0 ? { ...(baseDetails ?? {}), ...chartDetails } : baseDetails,\n }\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n return { content: [{ type: 'text', text: `Error: ${msg}` }], details: null }\n }\n },\n },\n chartSchemaGateState\n )\n )\n}\n","/**\n * 服务端 LLM 配置加载\n *\n * API Key 从 ~/.chatlab/auth-profiles.json 读取,\n * 匹配顺序:config.authProfile 精确匹配 > config.provider 兜底匹配。\n *\n * 模型/Provider 等配置仍从 llm-config.json 读取。\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport type { PiModel, PiApi } from '@openchatlab/node-runtime'\nimport { buildPiModel as buildPiModelCore, type PiModelConfig } from '@openchatlab/node-runtime'\nimport { resolveApiKey, writeAuthProfile } from '@openchatlab/config'\nimport { randomUUID } from 'crypto'\n\n// ==================== Types ====================\n\nexport interface AIServiceConfig {\n id: string\n name: string\n provider: string\n apiKey: string\n model?: string\n baseUrl?: string\n maxTokens?: number\n apiFormat?: string\n customModels?: Array<{ id: string; name: string }>\n authProfile?: string\n}\n\ninterface ModelSlot {\n configId: string\n modelId?: string\n}\n\ninterface AIConfigStore {\n configs: AIServiceConfig[]\n defaultAssistant: ModelSlot | null\n fastModel: ModelSlot | null\n}\n\n// ==================== Config Loading ====================\n\nexport function loadLlmConfig(aiDataDir: string): AIConfigStore {\n const configPath = path.join(aiDataDir, 'llm-config.json')\n if (!fs.existsSync(configPath)) {\n return { configs: [], defaultAssistant: null, fastModel: null }\n }\n\n try {\n const data = JSON.parse(fs.readFileSync(configPath, 'utf-8'))\n\n const configs = (data.configs || []).map((c: Record<string, unknown>) => {\n const provider = (c.provider as string) || ''\n const authProfile = c.authProfile as string | undefined\n const profileKey = resolveApiKey(provider, authProfile)\n const rawKey = (c.apiKey as string) || ''\n const fallbackKey = rawKey.startsWith('enc:') ? '' : rawKey\n return {\n ...c,\n apiKey: profileKey || fallbackKey,\n }\n })\n return {\n configs,\n defaultAssistant: data.defaultAssistant ?? null,\n fastModel: data.fastModel ?? null,\n }\n } catch {\n return { configs: [], defaultAssistant: null, fastModel: null }\n }\n}\n\nfunction resolveSlot(slot: ModelSlot | null | undefined, configs: AIServiceConfig[]): ModelSlot | null {\n if (slot && configs.some((c) => c.id === slot.configId)) return slot\n const fallback = configs[0]\n if (!fallback) return null\n return { configId: fallback.id, modelId: fallback.model }\n}\n\nexport function getDefaultAssistantConfig(aiDataDir: string): AIServiceConfig | null {\n const store = loadLlmConfig(aiDataDir)\n const slot = resolveSlot(store.defaultAssistant, store.configs)\n if (!slot) return null\n const config = store.configs.find((c) => c.id === slot.configId)\n if (!config) return null\n return { ...config, model: slot.modelId || config.model }\n}\n\n// ==================== Config Write Operations ====================\n\nconst MAX_CONFIG_COUNT = 99\n\nfunction saveLlmConfig(aiDataDir: string, store: AIConfigStore): void {\n const configPath = path.join(aiDataDir, 'llm-config.json')\n const toSave = {\n ...store,\n configs: store.configs.map((c) => {\n const { apiKey: _k, ...rest } = c\n return rest\n }),\n schemaVersion: 3,\n }\n fs.writeFileSync(configPath, JSON.stringify(toSave, null, 2), 'utf-8')\n}\n\nexport function addLlmConfig(\n aiDataDir: string,\n config: Omit<AIServiceConfig, 'id'>\n): { success: boolean; config?: AIServiceConfig; error?: string } {\n const store = loadLlmConfig(aiDataDir)\n\n if (store.configs.length >= MAX_CONFIG_COUNT) {\n return { success: false, error: `Maximum ${MAX_CONFIG_COUNT} configs reached` }\n }\n\n const newConfig: AIServiceConfig = {\n ...config,\n id: randomUUID(),\n }\n\n const storeForSave = loadRawConfigStore(aiDataDir)\n storeForSave.configs.push({\n ...newConfig,\n apiKey: '',\n })\n\n if (storeForSave.configs.length === 1) {\n storeForSave.defaultAssistant = { configId: newConfig.id, modelId: newConfig.model || '' }\n }\n\n if (config.apiKey) {\n const profileName = config.name?.toLowerCase().replace(/\\s+/g, '-') || config.provider\n writeAuthProfile(profileName, {\n type: 'api_key',\n provider: config.provider,\n key: config.apiKey,\n })\n ;(storeForSave.configs[storeForSave.configs.length - 1] as unknown as Record<string, unknown>).authProfile =\n profileName\n }\n\n saveLlmConfig(aiDataDir, storeForSave)\n return { success: true, config: { ...newConfig, apiKey: '' } }\n}\n\nexport function updateLlmConfig(\n aiDataDir: string,\n id: string,\n updates: Partial<Omit<AIServiceConfig, 'id'>>\n): { success: boolean; error?: string } {\n const storeForSave = loadRawConfigStore(aiDataDir)\n const index = storeForSave.configs.findIndex((c) => c.id === id)\n\n if (index === -1) {\n return { success: false, error: 'Config not found' }\n }\n\n const { apiKey: newApiKey, ...restUpdates } = updates\n const updated = {\n ...storeForSave.configs[index],\n ...restUpdates,\n }\n storeForSave.configs[index] = updated\n\n if (newApiKey) {\n const profileName = updated.name?.toLowerCase().replace(/\\s+/g, '-') || updated.provider\n writeAuthProfile(profileName, {\n type: 'api_key',\n provider: updated.provider,\n key: newApiKey,\n })\n ;(storeForSave.configs[index] as unknown as Record<string, unknown>).authProfile = profileName\n }\n\n saveLlmConfig(aiDataDir, storeForSave)\n return { success: true }\n}\n\nexport function deleteLlmConfig(aiDataDir: string, id: string): { success: boolean; error?: string } {\n const storeForSave = loadRawConfigStore(aiDataDir)\n const index = storeForSave.configs.findIndex((c) => c.id === id)\n\n if (index === -1) {\n return { success: false, error: 'Config not found' }\n }\n\n storeForSave.configs.splice(index, 1)\n\n const fallback = storeForSave.configs[0]\n if (storeForSave.defaultAssistant?.configId === id) {\n storeForSave.defaultAssistant = fallback ? { configId: fallback.id, modelId: fallback.model || '' } : null\n }\n if (storeForSave.fastModel?.configId === id) {\n storeForSave.fastModel = fallback ? { configId: fallback.id, modelId: fallback.model || '' } : null\n }\n\n saveLlmConfig(aiDataDir, storeForSave)\n return { success: true }\n}\n\nexport function setDefaultAssistantSlot(\n aiDataDir: string,\n configId: string,\n modelId: string\n): { success: boolean; error?: string } {\n const storeForSave = loadRawConfigStore(aiDataDir)\n const config = storeForSave.configs.find((c) => c.id === configId)\n\n if (!config) {\n return { success: false, error: 'Config not found' }\n }\n\n storeForSave.defaultAssistant = { configId, modelId }\n saveLlmConfig(aiDataDir, storeForSave)\n return { success: true }\n}\n\nexport function setFastModelSlot(aiDataDir: string, slot: ModelSlot | null): { success: boolean; error?: string } {\n const storeForSave = loadRawConfigStore(aiDataDir)\n\n if (slot !== null) {\n const config = storeForSave.configs.find((c) => c.id === slot.configId)\n if (!config) {\n return { success: false, error: 'Config not found' }\n }\n }\n\n storeForSave.fastModel = slot\n saveLlmConfig(aiDataDir, storeForSave)\n return { success: true }\n}\n\nfunction loadRawConfigStore(aiDataDir: string): AIConfigStore {\n const configPath = path.join(aiDataDir, 'llm-config.json')\n if (!fs.existsSync(configPath)) {\n return { configs: [], defaultAssistant: null, fastModel: null }\n }\n try {\n const data = JSON.parse(fs.readFileSync(configPath, 'utf-8'))\n return {\n configs: data.configs || [],\n defaultAssistant: data.defaultAssistant ?? null,\n fastModel: data.fastModel ?? null,\n }\n } catch {\n return { configs: [], defaultAssistant: null, fastModel: null }\n }\n}\n\n// ==================== PiModel Builder ====================\n\nexport function buildPiModel(config: AIServiceConfig): PiModel<PiApi> {\n return buildPiModelCore(config as PiModelConfig)\n}\n","/**\n * 助手配置加载器\n *\n * 从 ~/.chatlab/ai/assistants/*.md 加载助手系统提示词。\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { parseAssistantFile } from '@openchatlab/node-runtime'\nimport type { AssistantConfig } from '@openchatlab/node-runtime'\n\nexport function loadAssistantConfig(aiDataDir: string, assistantId: string): AssistantConfig | null {\n const filePath = path.join(aiDataDir, 'assistants', `${assistantId}.md`)\n if (!fs.existsSync(filePath)) return null\n\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n return parseAssistantFile(content, filePath)\n } catch {\n return null\n }\n}\n","/**\n * 服务端 Agent\n *\n * 使用 @openchatlab/node-runtime 的 runAgentCore 编排对话流程,\n * 通过 AgentEventHandler 输出与 Electron 端一致的流式事件。\n */\n\nimport {\n DEFAULT_MAX_TOOL_ROUNDS,\n buildPlanGuidance,\n createAnalysisPlanner,\n createLlmRouteDecider,\n createPlanContentBlock,\n decideRequestRoute,\n runAgentCore,\n checkAndCompress,\n buildSystemPrompt,\n createAiTranslate,\n createCompressionLlmAdapter,\n AgentEventHandler,\n formatAIError,\n shouldUseChartCapabilityForMessage,\n getChartPlannerCapabilityForMessage,\n initTokenizer,\n type AgentStreamChunk,\n type PiMessage,\n type SimpleHistoryMessage,\n type AIChatManager,\n type CompressionConfig,\n type AgentTool,\n type DataSnapshot,\n type OwnerInfo,\n type MentionedMember,\n} from '@openchatlab/node-runtime'\nimport type { ChartAutoMode } from '@openchatlab/shared-types'\n\nimport { getDefaultAssistantConfig, buildPiModel } from './llm-config'\nimport { getServerAiLogger } from './logger'\n\nexport type { AgentStreamChunk }\n\nexport interface RunAgentOptions {\n userMessage: string\n aiChatId: string\n historyLeafMessageId?: string | null\n chatType?: 'group' | 'private'\n locale?: string\n assistantSystemPrompt?: string\n skillMenu?: string | null\n skillDef?: { name: string; prompt: string }\n compressionConfig?: CompressionConfig\n tools?: AgentTool[]\n aiDataDir: string\n aiChatManager: AIChatManager\n onEvent: (event: AgentStreamChunk) => void\n abortSignal?: AbortSignal\n ownerInfo?: OwnerInfo\n mentionedMembers?: MentionedMember[]\n dataSnapshot?: DataSnapshot\n thinkingLevel?: string\n chartAutoMode?: ChartAutoMode\n}\n\nexport async function runServerAgent(options: RunAgentOptions): Promise<void> {\n const {\n userMessage,\n aiChatId,\n historyLeafMessageId,\n chatType = 'group',\n locale = 'zh-CN',\n assistantSystemPrompt,\n skillMenu,\n skillDef,\n compressionConfig,\n tools = [],\n aiDataDir,\n aiChatManager,\n onEvent,\n abortSignal,\n ownerInfo,\n mentionedMembers,\n dataSnapshot,\n thinkingLevel,\n chartAutoMode = 'suggest',\n } = options\n\n const aiLogger = getServerAiLogger()\n\n // 确保 tokenizer rank 表已加载(compression + agent 路径均依赖)\n await initTokenizer()\n\n const llmConfig = getDefaultAssistantConfig(aiDataDir)\n if (!llmConfig) {\n onEvent({ type: 'error', error: { name: 'ConfigError', message: 'LLM service not configured' } })\n onEvent({ type: 'done', isFinished: true })\n return\n }\n\n const piModel = buildPiModel(llmConfig)\n const t = createAiTranslate(locale)\n\n let skillCtx: { skillDef?: { name: string; prompt: string }; skillMenu?: string } | undefined\n if (skillDef) {\n skillCtx = { skillDef }\n } else if (skillMenu) {\n skillCtx = { skillMenu }\n }\n\n const systemPrompt = buildSystemPrompt({\n t,\n chatType,\n assistantSystemPrompt,\n ownerInfo,\n locale,\n skillCtx,\n mentionedMembers,\n dataSnapshot,\n })\n\n const handler = new AgentEventHandler({\n onChunk: onEvent,\n context: {},\n systemPrompt,\n })\n\n if (compressionConfig?.enabled && historyLeafMessageId === undefined) {\n const llmAdapter = createCompressionLlmAdapter({\n piModel,\n apiKey: llmConfig.apiKey,\n onCompressing: () => handler.emitStatus('compressing', []),\n })\n const compressionResult = await checkAndCompress(\n aiChatId,\n compressionConfig,\n systemPrompt,\n llmAdapter,\n aiChatManager,\n aiLogger ?? undefined\n )\n if (compressionResult.compressed) {\n onEvent({\n type: 'compression_done',\n compressionResult: {\n summaryContent: compressionResult.summaryContent ?? '',\n tokensBefore: compressionResult.tokensBefore ?? 0,\n tokensAfter: compressionResult.tokensAfter ?? 0,\n timestamp: Date.now(),\n },\n })\n }\n } else if (compressionConfig?.enabled && historyLeafMessageId !== undefined) {\n aiLogger?.info?.('Compression', 'Skipping compression for edited branch request', {\n aiChatId,\n historyLeafMessageId,\n })\n }\n\n if (abortSignal?.aborted) {\n handler.emitStatus('aborted', [], { force: true })\n onEvent({ type: 'done', isFinished: true, usage: handler.cloneUsage() })\n return\n }\n\n let history: SimpleHistoryMessage[] = []\n try {\n history = aiChatManager.getHistoryForAgent(aiChatId, undefined, historyLeafMessageId)\n } catch {\n // empty history on failure\n }\n\n handler.emitStatus('preparing', [], { pendingUserMessage: userMessage, force: true })\n\n const steerMessage = t('ai.agent.answerWithoutTools')\n let cachedMessages: PiMessage[] = []\n const effectiveTools =\n chartAutoMode === 'explicit' && !shouldUseChartCapabilityForMessage(userMessage)\n ? tools.filter((tool) => tool.name !== 'render_chart')\n : tools\n\n try {\n const routeInput = {\n userMessage,\n chatType,\n locale,\n dataSnapshot,\n availableTools: effectiveTools.map((tool) => tool.name),\n availableCapabilities: [\n getChartPlannerCapabilityForMessage({\n userMessage,\n locale,\n availableTools: effectiveTools.map((tool) => tool.name),\n chartAutoMode,\n }),\n ].filter((capability) => capability !== null),\n skillSummary: skillDef?.name ?? (skillMenu ? 'auto_skill_menu' : undefined),\n }\n const routeStartedAt = Date.now()\n const routeDecision = await decideRequestRoute(routeInput, {\n llmRouter: createLlmRouteDecider({\n piModel,\n apiKey: llmConfig.apiKey,\n abortSignal,\n }),\n })\n aiLogger?.info('Router', 'Shadow route decision', {\n ...routeDecision,\n elapsedMs: Date.now() - routeStartedAt,\n availableToolCount: tools.length,\n shadowOnly: true,\n })\n onEvent({ type: 'route', routeDecision })\n\n let effectiveSystemPrompt = systemPrompt\n if (routeDecision.route === 'planned_execution') {\n const planStartedAt = Date.now()\n const planner = createAnalysisPlanner({\n piModel,\n apiKey: llmConfig.apiKey,\n onPlanDelta: (delta) => onEvent({ type: 'plan_delta', planDelta: delta }),\n onThinkingDelta: (delta) => onEvent({ type: 'think', content: delta, thinkTag: 'thinking' }),\n onThinkingEnd: (durationMs) =>\n onEvent({ type: 'think', content: '', thinkTag: 'thinking', thinkDurationMs: durationMs }),\n onValidationDelta: (delta) => onEvent({ type: 'think', content: delta, thinkTag: 'plan_validation' }),\n onValidationEnd: (durationMs) =>\n onEvent({ type: 'think', content: '', thinkTag: 'plan_validation', thinkDurationMs: durationMs }),\n })\n const plan = await planner(routeInput, abortSignal)\n if (plan) {\n const planBlock = createPlanContentBlock(plan)\n onEvent({ type: 'plan', plan: planBlock })\n effectiveSystemPrompt = `${systemPrompt}\\n\\n${buildPlanGuidance(plan)}`\n aiLogger?.info('Planner', 'Plan generated', {\n title: plan.title,\n steps: plan.steps.length,\n successCriteria: plan.successCriteria.length,\n elapsedMs: Date.now() - planStartedAt,\n })\n } else {\n aiLogger?.warn('Planner', 'Plan generation skipped or failed', {\n elapsedMs: Date.now() - planStartedAt,\n route: routeDecision.route,\n })\n onEvent({ type: 'plan_skipped' })\n }\n }\n\n const result = await runAgentCore({\n piModel,\n apiKey: llmConfig.apiKey,\n systemPrompt: effectiveSystemPrompt,\n tools: effectiveTools,\n history,\n userMessage,\n maxToolRounds: DEFAULT_MAX_TOOL_ROUNDS,\n abortSignal,\n steerMessage,\n thinkingLevel: thinkingLevel as import('@openchatlab/core').ThinkingLevel | undefined,\n onConvertToLlm: (filteredMessages) => {\n cachedMessages = filteredMessages as PiMessage[]\n },\n onEvent: (coreEvent) => handler.handleCoreEvent(coreEvent, cachedMessages),\n onDebugContext: (messages) => {\n try {\n aiChatManager.setPendingDebugContext(aiChatId, JSON.stringify(messages, null, 2))\n } catch {\n // silent\n }\n },\n })\n\n if (result.error) {\n const friendlyMessage = formatAIError(result.error)\n onEvent({ type: 'error', error: { name: 'AgentError', message: friendlyMessage } })\n }\n\n handler.emitStatus('completed', cachedMessages, { force: true })\n onEvent({ type: 'done', isFinished: true, usage: result.usage })\n } catch (error) {\n const friendlyMessage = formatAIError(error)\n aiLogger?.error('ServerAgent', 'Agent execution error', { error: String(error) })\n handler.emitStatus('error', cachedMessages, { force: true })\n onEvent({ type: 'error', error: { name: 'AgentError', message: friendlyMessage } })\n onEvent({ type: 'done', isFinished: true, usage: handler.cloneUsage() })\n }\n}\n","/**\n * CLI Agent stream runner — provides runAgentStream implementation\n * for the shared HTTP route context.\n */\n\nimport type { DatabaseManager, AIChatManager, AgentStreamChunk } from '@openchatlab/node-runtime'\nimport {\n CHART_CAPABILITY_CORE_TOOLS,\n SkillManager,\n buildSkillMenuWithBuiltinChart,\n createActivateSkillTool,\n createDataSnapshotFromOverview,\n getAllowedBuiltinToolsForChartAutoSkill,\n getChartCapabilitySkill,\n getSkillConfigWithBuiltinChart,\n resolveChartRuntimeForRequest,\n} from '@openchatlab/node-runtime'\nimport { getChatOverview, normalizeBuiltinToolNames } from '@openchatlab/core'\nimport type { ChartAutoMode } from '@openchatlab/shared-types'\nimport type { DataSnapshot } from '@openchatlab/node-runtime'\nimport type { AgentStreamRequest } from '@openchatlab/http-routes'\nimport { AGENT_TOOL_REGISTRY } from '@openchatlab/tools'\nimport { adaptToolsForAgent } from './tool-adapter'\nimport { getDefaultAssistantConfig, buildPiModel } from './llm-config'\nimport { loadAssistantConfig } from './assistant-loader'\nimport { runServerAgent } from './agent'\n\nfunction getAiDir(dbManager: DatabaseManager): string {\n const pathProvider = (dbManager as any)['pathProvider']\n if (!pathProvider) {\n throw Object.assign(new Error('PathProvider not available'), { statusCode: 500 })\n }\n return pathProvider.getAiDataDir()\n}\n\nconst RAW_SQL_TOOL_NAMES = new Set(['execute_sql'])\n\nexport function getAvailableToolDefs(isChartCapability: boolean, allowedToolSet: Set<string> | null) {\n if (isChartCapability) {\n const chartCoreTools = new Set<string>(CHART_CAPABILITY_CORE_TOOLS)\n return AGENT_TOOL_REGISTRY.filter(\n (tool) =>\n chartCoreTools.has(tool.name) ||\n (tool.category === 'analysis' && allowedToolSet?.has(tool.name) && !RAW_SQL_TOOL_NAMES.has(tool.name))\n )\n }\n\n return allowedToolSet\n ? AGENT_TOOL_REGISTRY.filter((tool) => tool.category !== 'analysis' || allowedToolSet.has(tool.name))\n : AGENT_TOOL_REGISTRY\n}\n\n// 区分“未配置白名单”和“配置为空白名单”:前者无限制,后者禁用 analysis 工具。\nexport function getAllowedToolSet(\n isChartCapability: boolean,\n allowedBuiltinTools?: readonly string[]\n): Set<string> | null {\n if (isChartCapability) {\n return new Set(normalizeBuiltinToolNames(allowedBuiltinTools ?? []))\n }\n return allowedBuiltinTools === undefined ? null : new Set(normalizeBuiltinToolNames(allowedBuiltinTools))\n}\n\nexport function createCliRunAgentStream(\n dbManager: DatabaseManager,\n aiChatManager: AIChatManager\n): (params: AgentStreamRequest, onEvent: (chunk: AgentStreamChunk) => void, abortSignal: AbortSignal) => Promise<void> {\n return async (params, onEvent, abortSignal) => {\n const {\n userMessage,\n aiChatId,\n historyLeafMessageId,\n sessionId,\n chatType,\n locale,\n assistantId,\n skillId,\n enableAutoSkill,\n chartAutoMode,\n compressionConfig,\n ownerInfo,\n mentionedMembers,\n thinkingLevel,\n } = params\n\n const aiDataDir = getAiDir(dbManager)\n\n let assistantSystemPrompt: string | undefined\n let assistantAllowedTools: string[] | undefined\n if (assistantId) {\n const assistantConfig = loadAssistantConfig(aiDataDir, assistantId)\n if (assistantConfig?.systemPrompt) {\n assistantSystemPrompt = assistantConfig.systemPrompt\n }\n assistantAllowedTools = assistantConfig?.allowedBuiltinTools\n }\n\n const llmConfig = getDefaultAssistantConfig(aiDataDir)\n const maxToolResultPercent = compressionConfig?.maxToolResultPercent ?? 50\n const contextWindow = llmConfig ? (buildPiModel(llmConfig).contextWindow ?? 128000) : 128000\n const maxToolResultTokens = Math.floor(contextWindow * (maxToolResultPercent / 100))\n\n const db = (dbManager as any).open?.(sessionId)\n const resolvedChartAutoMode: ChartAutoMode = chartAutoMode ?? 'suggest'\n const chartRuntime = resolveChartRuntimeForRequest({\n skillId,\n userMessage,\n locale,\n assistantAllowedTools,\n enableAutoDetection: enableAutoSkill === true,\n chartAutoMode: resolvedChartAutoMode,\n })\n const isChartCapability = chartRuntime.isChartCapability\n const autoSkillAllowedTools =\n !skillId && enableAutoSkill && resolvedChartAutoMode !== 'explicit'\n ? getAllowedBuiltinToolsForChartAutoSkill(assistantAllowedTools)\n : assistantAllowedTools\n const allowedToolSet = getAllowedToolSet(\n isChartCapability,\n isChartCapability ? chartRuntime.allowedBuiltinTools : autoSkillAllowedTools\n )\n const availableToolDefs = getAvailableToolDefs(isChartCapability, allowedToolSet)\n\n const agentTools = db\n ? adaptToolsForAgent(availableToolDefs, () => ({ db, sessionId, locale }), { maxToolResultTokens })\n : []\n\n const skillMgr = new SkillManager(aiDataDir)\n skillMgr.init()\n const toolNames = agentTools.map((t: { name: string }) => t.name)\n\n let resolvedSkillDef: { name: string; prompt: string } | undefined\n let resolvedSkillMenu: string | undefined\n if (isChartCapability) {\n const def = chartRuntime.skillDef ?? getChartCapabilitySkill(locale ?? 'zh-CN')\n resolvedSkillDef = { name: def.name, prompt: def.prompt }\n } else if (skillId) {\n const def = skillMgr.getSkillConfig(skillId)\n if (def) resolvedSkillDef = { name: def.name, prompt: def.prompt }\n } else if (enableAutoSkill) {\n const baseMenu = skillMgr.getSkillMenu(chatType ?? 'group', toolNames)\n const menu =\n resolvedChartAutoMode === 'aggressive' ? buildSkillMenuWithBuiltinChart(baseMenu, locale, toolNames) : baseMenu\n if (menu) resolvedSkillMenu = menu\n }\n\n if (resolvedSkillMenu) {\n const activateSkillTool = createActivateSkillTool({\n chatType: chatType ?? 'group',\n allowedTools: toolNames,\n coreToolNames: new Set<string>(CHART_CAPABILITY_CORE_TOOLS),\n locale,\n getSkillConfig: (id) =>\n resolvedChartAutoMode === 'aggressive'\n ? getSkillConfigWithBuiltinChart(id, locale, (skillConfigId) => skillMgr.getSkillConfig(skillConfigId))\n : skillMgr.getSkillConfig(id),\n })\n agentTools.push(activateSkillTool as any)\n }\n\n const resolvedCompression = compressionConfig?.enabled\n ? {\n enabled: true as const,\n tokenThresholdPercent: compressionConfig.tokenThresholdPercent ?? 75,\n bufferSizePercent: compressionConfig.bufferSizePercent ?? 20,\n maxToolResultPercent: compressionConfig.maxToolResultPercent,\n }\n : undefined\n\n let dataSnapshot: DataSnapshot | undefined\n if (db) {\n try {\n dataSnapshot = createDataSnapshotFromOverview(getChatOverview(db, 10))\n } catch {\n // non-fatal\n }\n }\n\n await runServerAgent({\n userMessage,\n aiChatId,\n historyLeafMessageId,\n chatType,\n locale,\n assistantSystemPrompt,\n skillMenu: resolvedSkillMenu,\n skillDef: resolvedSkillDef,\n compressionConfig: resolvedCompression,\n tools: agentTools,\n aiDataDir,\n aiChatManager,\n onEvent,\n abortSignal,\n ownerInfo,\n mentionedMembers,\n dataSnapshot,\n thinkingLevel,\n chartAutoMode: resolvedChartAutoMode,\n })\n }\n}\n"],"mappings":"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","names":["options","buildPiModel","fs","path","buildPiModel","buildPiModel"]}
|