@huyooo/ai-chat-core 0.1.4 → 0.1.7

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/index.cjs CHANGED
@@ -4,132 +4,366 @@ var genai = require('@google/genai');
4
4
 
5
5
  // src/agent.ts
6
6
 
7
+ // src/constants.ts
8
+ var DEFAULT_ARK_URL = "https://ark.cn-beijing.volces.com/api/v3";
9
+ var DEFAULT_QWEN_URL = "https://dashscope.aliyuncs.com/api/v1";
10
+ var DEFAULT_QWEN_NATIVE_URL = "https://dashscope.aliyuncs.com/api/v1";
11
+ var DEFAULT_OPENROUTER_URL = "https://openrouter.ai/api/v1";
12
+ var DOUBAO_SEED_1_8 = {
13
+ id: "doubao-seed-1-8-251215"};
14
+ var DOUBAO_SEED_1_6 = {
15
+ id: "doubao-seed-1-6-250615",
16
+ displayName: "\u8C46\u5305 Seed 1.6",
17
+ provider: "\u8C46\u5305"
18
+ };
19
+ var DOUBAO_SEED_1_6_LATEST = {
20
+ id: "doubao-seed-1-6-251015"};
21
+ var DOUBAO_SEED_1_6_FLASH = {
22
+ id: "doubao-seed-1-6-flash-250617"};
23
+ var DEEPSEEK_V3 = {
24
+ id: "deepseek-v3-1-terminus",
25
+ displayName: "DeepSeek V3",
26
+ provider: "DeepSeek"
27
+ };
28
+ var QWEN_MAX = { id: "qwen-max"};
29
+ var QWEN_MAX_LATEST = { id: "qwen-max-latest"};
30
+ var QWEN_PLUS = { id: "qwen-plus"};
31
+ var QWEN_PLUS_LATEST = { id: "qwen-plus-latest"};
32
+ var QWEN_TURBO = { id: "qwen-turbo"};
33
+ var QWEN_TURBO_LATEST = { id: "qwen-turbo-latest"};
34
+ var QWEN3_235B = { id: "qwen3-235b-a22b"};
35
+ var QWEN3_MAX_PREVIEW = {
36
+ id: "qwen3-max-preview",
37
+ displayName: "\u901A\u4E49\u5343\u95EE 3 Max",
38
+ provider: "\u901A\u4E49\u5343\u95EE"
39
+ };
40
+ var GEMINI_2_5_FLASH = { id: "gemini-2.5-flash-preview-05-20"};
41
+ var GEMINI_2_5_PRO = { id: "gemini-2.5-pro-preview-05-06"};
42
+ var GEMINI_2_0_FLASH = { id: "gemini-2.0-flash"};
43
+ var GEMINI_2_0_FLASH_LITE = { id: "gemini-2.0-flash-lite"};
44
+ var GEMINI_3_PRO = {
45
+ id: "gemini-3-pro-preview",
46
+ displayName: "Gemini 3 Pro",
47
+ provider: "Gemini"
48
+ };
49
+ var GEMINI_IMAGE_MODEL = "gemini-2.0-flash";
50
+ var GEMINI_IMAGE_GEN_MODEL = "gemini-3-pro-image-preview";
51
+ var OR_GEMINI_2_5_FLASH = {
52
+ id: "google/gemini-2.5-flash-preview"};
53
+ var OR_GEMINI_2_5_PRO = {
54
+ id: "google/gemini-2.5-pro-preview"};
55
+ var OR_GEMINI_2_0_FLASH = {
56
+ id: "google/gemini-2.0-flash-001"};
57
+ var OR_GEMINI_3_PRO = {
58
+ id: "google/gemini-3-pro-preview",
59
+ displayName: "Gemini 3 Pro",
60
+ isOpenRouter: true,
61
+ provider: "Gemini"
62
+ };
63
+ var OR_GPT_5_2 = {
64
+ id: "openai/gpt-5.2",
65
+ displayName: "GPT-5.2",
66
+ isOpenRouter: true,
67
+ provider: "OpenAI"
68
+ };
69
+ var OR_CLAUDE_OPUS_4_5 = {
70
+ id: "anthropic/claude-opus-4.5",
71
+ displayName: "Claude Opus 4.5",
72
+ isOpenRouter: true,
73
+ provider: "Anthropic"
74
+ };
75
+ var OR_QWEN3_MAX = {
76
+ id: "qwen/qwen3-max",
77
+ displayName: "\u901A\u4E49\u5343\u95EE Max",
78
+ isOpenRouter: true,
79
+ provider: "\u901A\u4E49\u5343\u95EE"
80
+ };
81
+ var OR_DEEPSEEK_R1 = {
82
+ id: "deepseek/deepseek-r1"};
83
+ var OR_DEEPSEEK_CHAT_V3 = {
84
+ id: "deepseek/deepseek-chat-v3-0324"};
85
+ var OR_DEEPSEEK_V3_2 = {
86
+ id: "deepseek/deepseek-v3.2",
87
+ displayName: "DeepSeek V3.2",
88
+ isOpenRouter: true,
89
+ provider: "DeepSeek"
90
+ };
91
+ var DISPLAY_MODELS = [
92
+ // 原生模型
93
+ DOUBAO_SEED_1_6,
94
+ DEEPSEEK_V3,
95
+ QWEN3_MAX_PREVIEW,
96
+ GEMINI_3_PRO,
97
+ // OpenRouter 模型
98
+ OR_GPT_5_2,
99
+ OR_CLAUDE_OPUS_4_5,
100
+ OR_GEMINI_3_PRO,
101
+ OR_QWEN3_MAX,
102
+ OR_DEEPSEEK_V3_2
103
+ ];
104
+ var DEFAULT_MODEL = DOUBAO_SEED_1_6.id;
105
+ var PLAN_MODE_PROMPT = "\u5206\u6790\u9700\u6C42\u5E76\u5236\u5B9A\u6267\u884C\u8BA1\u5212\uFF0C\u4EE5 Markdown \u683C\u5F0F\u8F93\u51FA\u6B65\u9AA4\uFF0C\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u540E\u518D\u6267\u884C\u3002";
106
+ var AGENT_MODE_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684 AI \u52A9\u624B\u3002\u8BF7\u9075\u5FAA\u4EE5\u4E0B\u89C4\u5219\uFF1A
107
+ - \u8FD4\u56DE\u7684\u5185\u5BB9\u5C3D\u91CF\u4E0D\u8981\u4F7F\u7528\u8868\u60C5\u7B26\u53F7\uFF08emoji\uFF09\uFF0C\u9664\u975E\u7528\u6237\u8981\u6C42\u4F7F\u7528
108
+ - \u4F7F\u7528 Markdown \u683C\u5F0F\u7EC4\u7EC7\u5185\u5BB9
109
+ - \u4EE3\u7801\u5757\u4F7F\u7528\u6B63\u786E\u7684\u8BED\u8A00\u6807\u8BC6`;
110
+
7
111
  // src/types.ts
8
- var AVAILABLE_MODELS = [
9
- {
10
- provider: "doubao",
11
- model: "doubao-seed-1-6-251015",
12
- displayName: "\u8C46\u5305 Seed",
13
- supportsTools: true,
14
- supportsWebSearch: true,
15
- supportedThinkingModes: ["enabled", "disabled"]
16
- },
17
- {
18
- provider: "deepseek",
19
- model: "deepseek-v3-1-terminus",
20
- displayName: "DeepSeek V3",
21
- supportsTools: true,
22
- supportsWebSearch: true,
23
- // 注意:火山引擎托管的 DeepSeek V3.1 不支持 auto 模式
24
- supportedThinkingModes: ["enabled", "disabled"]
25
- },
26
- // 通义千问 - 混合思考模式(默认不开启)
27
- {
28
- provider: "qwen",
29
- model: "qwen3-max-preview",
30
- displayName: "\u901A\u4E49\u5343\u95EE Max",
31
- supportsTools: true,
32
- supportsWebSearch: true,
33
- supportedThinkingModes: ["enabled", "disabled"]
34
- },
35
- {
36
- provider: "qwen",
37
- model: "qwen-plus",
38
- displayName: "\u901A\u4E49\u5343\u95EE Plus",
39
- supportsTools: true,
40
- supportsWebSearch: true,
41
- supportedThinkingModes: ["enabled", "disabled"]
42
- },
43
- // Gemini 3 - Google 最新推理模型
44
- {
45
- provider: "gemini",
46
- model: "gemini-3-pro-preview",
47
- displayName: "Gemini 3 Pro",
48
- supportsTools: true,
49
- supportsWebSearch: true,
50
- // 支持 Google Search grounding
51
- supportedThinkingModes: ["enabled", "disabled"]
52
- // thinking_level: high/low
53
- },
54
- // OpenRouter - Claude Opus 4.5
55
- {
56
- provider: "openrouter",
57
- model: "anthropic/claude-opus-4.5",
58
- displayName: "Claude Opus 4.5",
59
- supportsTools: true,
60
- supportsWebSearch: true,
61
- // 通过 :online 后缀或 plugins 支持
62
- supportedThinkingModes: ["enabled", "disabled"]
63
- // Claude 扩展思考
112
+ var MODELS = DISPLAY_MODELS.map((m) => ({
113
+ modelId: m.id,
114
+ displayName: m.displayName,
115
+ group: m.isOpenRouter ? "OpenRouter" : "\u539F\u751F\u6A21\u578B",
116
+ // 后端决定分组
117
+ isOpenRouter: m.isOpenRouter,
118
+ provider: m.provider
119
+ }));
120
+ function getNativeModels() {
121
+ return MODELS.filter((m) => !m.isOpenRouter);
122
+ }
123
+ function getOpenRouterModels() {
124
+ return MODELS.filter((m) => m.isOpenRouter);
125
+ }
126
+ function getModelByModelId(modelId) {
127
+ return MODELS.find((m) => m.modelId === modelId);
128
+ }
129
+ function tool(t) {
130
+ return { tools: [t] };
131
+ }
132
+ function tools(ts) {
133
+ return { tools: ts };
134
+ }
135
+ async function resolveTools(items) {
136
+ const tools2 = [];
137
+ for (const item of items) {
138
+ const resolved = await item;
139
+ if ("tools" in resolved && Array.isArray(resolved.tools)) {
140
+ tools2.push(...resolved.tools);
141
+ } else {
142
+ console.warn("\u5DE5\u5177\u914D\u7F6E\u9879\u5FC5\u987B\u662F ToolPlugin \u5F62\u5F0F:", resolved);
143
+ }
64
144
  }
65
- ];
145
+ return tools2;
146
+ }
66
147
 
67
- // src/tools.ts
68
- var DEFAULT_WORKING_DIR = process.cwd();
69
- function createToolDefinitions(workingDir = DEFAULT_WORKING_DIR) {
70
- return [
71
- {
72
- type: "function",
73
- function: {
74
- name: "execute_command",
75
- description: `\u6267\u884C shell \u547D\u4EE4\u6765\u5B8C\u6210\u6587\u4EF6\u7BA1\u7406\u4EFB\u52A1\u3002\u652F\u6301\uFF1Als\u3001find\u3001cat\u3001head\u3001tail\u3001du\u3001file \u7B49\u547D\u4EE4\u3002\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55\uFF1A${workingDir}`,
76
- parameters: {
77
- type: "object",
78
- properties: {
79
- command: { type: "string", description: "\u8981\u6267\u884C\u7684 shell \u547D\u4EE4" },
80
- workingDir: { type: "string", description: "\u5DE5\u4F5C\u76EE\u5F55\uFF08\u53EF\u9009\uFF09" }
81
- },
82
- required: ["command"]
83
- }
84
- }
85
- },
86
- {
87
- type: "function",
88
- function: {
89
- name: "analyze_image",
90
- description: `\u5206\u6790\u56FE\u7247\u5185\u5BB9\u3002\u53EF\u4EE5\u8BC6\u522B\u56FE\u7247\u4E2D\u7684\u7269\u4F53\u3001\u573A\u666F\u3001\u6587\u5B57\u3001\u4EBA\u7269\u7B49\u3002\u652F\u6301\u7684\u56FE\u7247\u683C\u5F0F\uFF1Ajpg\u3001jpeg\u3001png\u3001gif\u3001webp`,
91
- parameters: {
92
- type: "object",
93
- properties: {
94
- imagePath: { type: "string", description: "\u56FE\u7247\u6587\u4EF6\u7684\u5B8C\u6574\u8DEF\u5F84" },
95
- question: { type: "string", description: "\u5173\u4E8E\u56FE\u7247\u7684\u95EE\u9898\uFF08\u53EF\u9009\uFF09" }
96
- },
97
- required: ["imagePath"]
98
- }
99
- }
100
- },
101
- {
102
- type: "function",
103
- function: {
104
- name: "generate_image",
105
- description: `\u6839\u636E\u6587\u5B57\u63CF\u8FF0\u751F\u6210\u56FE\u7247\u3002\u53EF\u4EE5\u751F\u6210\u5404\u79CD\u98CE\u683C\u7684\u56FE\u7247\u3002`,
106
- parameters: {
107
- type: "object",
108
- properties: {
109
- prompt: { type: "string", description: "\u56FE\u7247\u63CF\u8FF0" },
110
- outputPath: { type: "string", description: "\u8F93\u51FA\u56FE\u7247\u7684\u4FDD\u5B58\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09" }
111
- },
112
- required: ["prompt"]
113
- }
114
- }
115
- },
116
- {
117
- type: "function",
118
- function: {
119
- name: "analyze_video",
120
- description: `\u5206\u6790\u89C6\u9891\u5185\u5BB9\u3002\u53EF\u4EE5\u8BC6\u522B\u89C6\u9891\u4E2D\u7684\u573A\u666F\u3001\u52A8\u4F5C\u3001\u7269\u4F53\u7B49\u3002\u652F\u6301\u7684\u89C6\u9891\u683C\u5F0F\uFF1Amp4\u3001mov\u3001avi\u3001webm`,
121
- parameters: {
122
- type: "object",
123
- properties: {
124
- videoPath: { type: "string", description: "\u89C6\u9891\u6587\u4EF6\u7684\u5B8C\u6574\u8DEF\u5F84" },
125
- question: { type: "string", description: "\u5173\u4E8E\u89C6\u9891\u7684\u95EE\u9898\uFF08\u53EF\u9009\uFF09" }
126
- },
127
- required: ["videoPath"]
128
- }
129
- }
148
+ // src/events.ts
149
+ function createThinkingStart() {
150
+ return {
151
+ type: "thinking_start",
152
+ data: { startedAt: Date.now() }
153
+ };
154
+ }
155
+ function createThinkingDelta(content) {
156
+ return {
157
+ type: "thinking_delta",
158
+ data: { content }
159
+ };
160
+ }
161
+ function createThinkingEnd(startedAt) {
162
+ const endedAt = Date.now();
163
+ return {
164
+ type: "thinking_end",
165
+ data: {
166
+ endedAt,
167
+ duration: endedAt - startedAt
130
168
  }
131
- ];
169
+ };
170
+ }
171
+ function createSearchStart(query) {
172
+ return {
173
+ type: "search_start",
174
+ data: { query, startedAt: Date.now() }
175
+ };
176
+ }
177
+ function createSearchResult(results, startedAt) {
178
+ const endedAt = Date.now();
179
+ return {
180
+ type: "search_result",
181
+ data: {
182
+ results,
183
+ endedAt,
184
+ duration: endedAt - startedAt
185
+ }
186
+ };
187
+ }
188
+ function createSearchEnd(success, startedAt, error) {
189
+ const endedAt = Date.now();
190
+ return {
191
+ type: "search_end",
192
+ data: {
193
+ success,
194
+ error,
195
+ endedAt,
196
+ duration: endedAt - startedAt
197
+ }
198
+ };
199
+ }
200
+ function createToolCallStart(id, name, args) {
201
+ return {
202
+ type: "tool_call_start",
203
+ data: { id, name, args, startedAt: Date.now() }
204
+ };
205
+ }
206
+ function createToolCallResult(id, name, result, success, startedAt, error, sideEffects) {
207
+ const endedAt = Date.now();
208
+ return {
209
+ type: "tool_call_result",
210
+ data: {
211
+ id,
212
+ name,
213
+ result,
214
+ success,
215
+ error,
216
+ endedAt,
217
+ duration: endedAt - startedAt,
218
+ sideEffects
219
+ }
220
+ };
221
+ }
222
+ function createTextDelta(content) {
223
+ return {
224
+ type: "text_delta",
225
+ data: { content }
226
+ };
227
+ }
228
+ function createStreamStart(model, requestId) {
229
+ return {
230
+ type: "stream_start",
231
+ data: {
232
+ model,
233
+ requestId,
234
+ startedAt: Date.now()
235
+ }
236
+ };
237
+ }
238
+ function createDone(text, usage, duration) {
239
+ return {
240
+ type: "done",
241
+ data: { text, usage, duration }
242
+ };
243
+ }
244
+ function createError(details) {
245
+ return {
246
+ type: "error",
247
+ data: details
248
+ };
249
+ }
250
+ function createApiError(message, options = {}) {
251
+ return createError({
252
+ category: "api",
253
+ message,
254
+ ...options
255
+ });
256
+ }
257
+ function createRateLimitError(message, retryAfter, context) {
258
+ return createError({
259
+ category: "rate_limit",
260
+ message,
261
+ code: "RATE_LIMIT",
262
+ statusCode: 429,
263
+ retryable: true,
264
+ retryAfter,
265
+ context
266
+ });
267
+ }
268
+ function createToolError(message, toolName, cause) {
269
+ return createError({
270
+ category: "tool",
271
+ message,
272
+ code: "TOOL_ERROR",
273
+ context: toolName,
274
+ cause,
275
+ retryable: false
276
+ });
277
+ }
278
+ function createTimeoutError(message, context) {
279
+ return createError({
280
+ category: "timeout",
281
+ message,
282
+ code: "TIMEOUT",
283
+ retryable: true,
284
+ context
285
+ });
286
+ }
287
+ function createParseError(message, cause) {
288
+ return createError({
289
+ category: "parse",
290
+ message,
291
+ code: "PARSE_ERROR",
292
+ cause,
293
+ retryable: false
294
+ });
295
+ }
296
+ function createAbort(reason) {
297
+ return {
298
+ type: "abort",
299
+ data: {
300
+ reason,
301
+ abortedAt: Date.now()
302
+ }
303
+ };
304
+ }
305
+ function createStepStart(stepNumber, description) {
306
+ return {
307
+ type: "step_start",
308
+ data: { stepNumber, description, startedAt: Date.now() }
309
+ };
310
+ }
311
+ function createStepEnd(stepNumber, startedAt) {
312
+ const endedAt = Date.now();
313
+ return {
314
+ type: "step_end",
315
+ data: {
316
+ stepNumber,
317
+ endedAt,
318
+ duration: endedAt - startedAt
319
+ }
320
+ };
321
+ }
322
+ function createImage(options) {
323
+ return {
324
+ type: "image",
325
+ data: options
326
+ };
327
+ }
328
+ function createVideo(path) {
329
+ return {
330
+ type: "video",
331
+ data: { path }
332
+ };
333
+ }
334
+ function isThinkingEvent(event) {
335
+ return event.type.startsWith("thinking_");
336
+ }
337
+ function isSearchEvent(event) {
338
+ return event.type.startsWith("search_");
339
+ }
340
+ function isToolEvent(event) {
341
+ return event.type.startsWith("tool_");
342
+ }
343
+ function isTextEvent(event) {
344
+ return event.type === "text_delta";
345
+ }
346
+ function isMediaEvent(event) {
347
+ return event.type === "image" || event.type === "video";
348
+ }
349
+ function isStatusEvent(event) {
350
+ return event.type === "stream_start" || event.type === "done" || event.type === "error" || event.type === "abort";
132
351
  }
352
+ function isErrorEvent(event) {
353
+ return event.type === "error";
354
+ }
355
+ function isAbortEvent(event) {
356
+ return event.type === "abort";
357
+ }
358
+ function isStepEvent(event) {
359
+ return event.type.startsWith("step_");
360
+ }
361
+ function isRetryableError(event) {
362
+ if (event.type !== "error") return false;
363
+ return event.data.retryable === true;
364
+ }
365
+
366
+ // src/tools.ts
133
367
  var DANGEROUS_COMMANDS = [
134
368
  "rm -rf /",
135
369
  "rm -rf ~",
@@ -147,529 +381,877 @@ function isDangerousCommand(command) {
147
381
  (dangerous) => lowerCommand.includes(dangerous.toLowerCase())
148
382
  );
149
383
  }
150
- function createDefaultToolExecutor(workingDir = DEFAULT_WORKING_DIR) {
384
+ function createDefaultToolExecutor(defaultCwd = process.cwd()) {
151
385
  return {
152
- async executeCommand(command, cwd) {
386
+ async executeCommand(command, cwd, signal) {
387
+ if (signal?.aborted) {
388
+ return { success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88" };
389
+ }
153
390
  if (isDangerousCommand(command)) {
154
391
  return { success: false, error: "\u8BE5\u547D\u4EE4\u88AB\u5B89\u5168\u7B56\u7565\u963B\u6B62" };
155
392
  }
156
393
  try {
157
- const { exec } = await import('child_process');
158
- const { promisify } = await import('util');
159
- const execAsync = promisify(exec);
160
- const { stdout, stderr } = await execAsync(command, {
161
- cwd: cwd || workingDir,
162
- timeout: 3e4,
163
- maxBuffer: 1024 * 1024 * 10,
164
- shell: "/bin/sh"
394
+ const { spawn } = await import('child_process');
395
+ return new Promise((resolve) => {
396
+ let stdout = "";
397
+ let stderr = "";
398
+ let killed = false;
399
+ const child = spawn("sh", ["-c", command], {
400
+ cwd: cwd || defaultCwd,
401
+ env: process.env
402
+ });
403
+ const timeout = setTimeout(() => {
404
+ if (!killed) {
405
+ killed = true;
406
+ child.kill("SIGTERM");
407
+ resolve({ success: false, error: "\u547D\u4EE4\u6267\u884C\u8D85\u65F6\uFF0830\u79D2\uFF09" });
408
+ }
409
+ }, 3e4);
410
+ const abortHandler = () => {
411
+ if (!killed) {
412
+ killed = true;
413
+ child.kill("SIGTERM");
414
+ clearTimeout(timeout);
415
+ resolve({ success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88" });
416
+ }
417
+ };
418
+ signal?.addEventListener("abort", abortHandler);
419
+ child.stdout?.on("data", (data) => {
420
+ stdout += data.toString();
421
+ });
422
+ child.stderr?.on("data", (data) => {
423
+ stderr += data.toString();
424
+ });
425
+ child.on("close", (code) => {
426
+ clearTimeout(timeout);
427
+ signal?.removeEventListener("abort", abortHandler);
428
+ if (killed) return;
429
+ const output = stdout.trim();
430
+ const errOutput = stderr.trim();
431
+ if (code === 0) {
432
+ resolve({ success: true, output: output || errOutput || "\u6267\u884C\u6210\u529F" });
433
+ } else {
434
+ if (output) {
435
+ const warning = errOutput ? `
436
+ [\u8B66\u544A: ${errOutput}]` : "";
437
+ resolve({ success: true, output: output + warning });
438
+ } else if (errOutput) {
439
+ resolve({ success: false, error: errOutput });
440
+ } else {
441
+ resolve({ success: false, error: `\u547D\u4EE4\u9000\u51FA\u7801: ${code}` });
442
+ }
443
+ }
444
+ });
445
+ child.on("error", (err) => {
446
+ clearTimeout(timeout);
447
+ signal?.removeEventListener("abort", abortHandler);
448
+ if (!killed) {
449
+ resolve({ success: false, error: err.message });
450
+ }
451
+ });
165
452
  });
166
- return { success: true, output: (stdout || stderr || "").trim() };
167
453
  } catch (error) {
168
- return { success: false, error: String(error) };
454
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
169
455
  }
170
- },
171
- async readFile(path) {
172
- const fs = await import('fs/promises');
173
- return fs.readFile(path);
174
- },
175
- async writeFile(path, data) {
176
- const fs = await import('fs/promises');
177
- await fs.writeFile(path, data);
178
- },
179
- async readDirectory(path) {
180
- const fs = await import('fs/promises');
181
- return fs.readdir(path);
182
456
  }
183
457
  };
184
458
  }
185
459
 
186
- // src/constants.ts
187
- var DEFAULT_ARK_URL = "https://ark.cn-beijing.volces.com/api/v3";
188
- var DEFAULT_QWEN_NATIVE_URL = "https://dashscope.aliyuncs.com/api/v1";
189
- var DEFAULT_OPENROUTER_URL = "https://openrouter.ai/api/v1";
190
- var DEFAULT_MODEL = "doubao-seed-1-6-251015";
191
- var GEMINI_IMAGE_MODEL = "gemini-2.0-flash";
192
- var GEMINI_IMAGE_GEN_MODEL = "gemini-3-pro-image-preview";
193
- var MAX_ITERATIONS = 10;
194
- var MODE_PROMPTS = {
195
- agent: `\u4F60\u53EF\u4EE5\u4F7F\u7528\u5DE5\u5177\u5B8C\u6210\u4EFB\u52A1\u3002\u76F4\u63A5\u8C03\u7528\u5DE5\u5177\uFF0C\u5B8C\u6210\u540E\u7B80\u6D01\u603B\u7ED3\u7ED3\u679C\u3002`,
196
- plan: `\u5206\u6790\u9700\u6C42\u5E76\u5236\u5B9A\u6267\u884C\u8BA1\u5212\uFF0C\u4EE5 Markdown \u683C\u5F0F\u8F93\u51FA\uFF0C\u7B49\u5F85\u786E\u8BA4\u540E\u6267\u884C\u3002`,
197
- ask: `\u76F4\u63A5\u56DE\u7B54\u95EE\u9898\uFF0C\u4E0D\u77E5\u9053\u5C31\u8BF4\u4E0D\u77E5\u9053\u3002`
198
- };
199
-
200
- // src/providers/ark.ts
201
- var ARK_MODELS = [
202
- "doubao-seed-1-6-251015",
203
- "deepseek-v3-1-terminus"
204
- ];
205
- var MODEL_CAPABILITIES = {
206
- "doubao-seed-1-6-251015": {
207
- supportsTools: true,
208
- supportsWebSearch: true,
209
- supportsThinking: true,
210
- supportedThinkingModes: ["enabled", "disabled"]
460
+ // src/router.ts
461
+ var MODEL_ROUTES = [
462
+ // === 高优先级:OpenRouter 格式(包含 /)===
463
+ // 所有包含 / 的模型名都路由到 OpenRouter
464
+ // 例如:qwen/qwen3-max, anthropic/claude-opus-4.5, openai/gpt-5.2
465
+ {
466
+ type: "contains",
467
+ pattern: "/",
468
+ provider: "openrouter",
469
+ priority: 100,
470
+ description: "OpenRouter \u683C\u5F0F\u6A21\u578B\uFF08vendor/model\uFF09"
471
+ },
472
+ // === 中优先级:原生 Provider 前缀匹配 ===
473
+ {
474
+ type: "prefix",
475
+ pattern: "qwen",
476
+ provider: "qwen",
477
+ priority: 50,
478
+ description: "Qwen (DashScope) \u539F\u751F\u6A21\u578B"
479
+ },
480
+ {
481
+ type: "prefix",
482
+ pattern: "gemini",
483
+ provider: "gemini",
484
+ priority: 50,
485
+ description: "Gemini (Google AI) \u539F\u751F\u6A21\u578B"
486
+ },
487
+ // === 低优先级:ARK 火山引擎模型 ===
488
+ {
489
+ type: "prefix",
490
+ pattern: "doubao",
491
+ provider: "ark",
492
+ priority: 30,
493
+ description: "\u8C46\u5305\u6A21\u578B\uFF08\u706B\u5C71\u5F15\u64CE\uFF09"
211
494
  },
212
- "deepseek-v3-1-terminus": {
213
- supportsTools: true,
214
- supportsWebSearch: true,
215
- supportsThinking: true,
216
- supportedThinkingModes: ["enabled", "disabled"]
495
+ {
496
+ type: "prefix",
497
+ pattern: "deepseek",
498
+ provider: "ark",
499
+ priority: 30,
500
+ description: "DeepSeek \u6A21\u578B\uFF08\u706B\u5C71\u5F15\u64CE\uFF09"
217
501
  }
218
- };
219
- var DEFAULT_CAPABILITIES = {
220
- supportsTools: true,
221
- supportsWebSearch: true,
222
- supportsThinking: true,
223
- supportedThinkingModes: ["enabled", "disabled"]
224
- };
225
- function convertToResponsesApiTools(tools) {
226
- return tools.map((tool) => {
227
- const t = tool;
228
- return {
229
- type: "function",
230
- name: t.function.name,
231
- description: t.function.description,
232
- parameters: t.function.parameters
233
- };
234
- });
502
+ ];
503
+ var DEFAULT_PROVIDER = "ark";
504
+ var sortedRulesCache = null;
505
+ function getSortedRules() {
506
+ if (!sortedRulesCache) {
507
+ sortedRulesCache = [...MODEL_ROUTES].sort((a, b) => b.priority - a.priority);
508
+ }
509
+ return sortedRulesCache;
235
510
  }
236
- function convertToResponsesApiInput(systemPrompt, history, message) {
237
- const input = [];
238
- input.push({
239
- role: "system",
240
- content: systemPrompt
241
- });
242
- for (const msg of history) {
243
- if (msg.role === "user") {
244
- input.push({
245
- role: "user",
246
- content: [{ type: "input_text", text: msg.content }]
247
- });
248
- } else if (msg.role === "assistant") {
249
- input.push({
250
- role: "developer",
251
- content: `[\u4E0A\u4E00\u8F6EAI\u56DE\u590D]: ${msg.content}`
252
- });
511
+ function matchRule(model, rule) {
512
+ const lowerPattern = rule.pattern.toLowerCase();
513
+ switch (rule.type) {
514
+ case "exact":
515
+ return model === lowerPattern;
516
+ case "prefix":
517
+ return model.startsWith(lowerPattern);
518
+ case "contains":
519
+ return model.includes(lowerPattern);
520
+ case "regex":
521
+ return new RegExp(rule.pattern, "i").test(model);
522
+ default:
523
+ return false;
524
+ }
525
+ }
526
+ function routeModelToProvider(model) {
527
+ return routeModelWithDetails(model).provider;
528
+ }
529
+ function routeModelWithDetails(model) {
530
+ const lowerModel = model.toLowerCase();
531
+ const sortedRules = getSortedRules();
532
+ for (const rule of sortedRules) {
533
+ if (matchRule(lowerModel, rule)) {
534
+ return {
535
+ provider: rule.provider,
536
+ matchedRule: rule,
537
+ isDefault: false
538
+ };
253
539
  }
254
540
  }
255
- input.push({
256
- role: "user",
257
- content: [{ type: "input_text", text: message }]
258
- });
259
- return input;
541
+ return {
542
+ provider: DEFAULT_PROVIDER,
543
+ isDefault: true
544
+ };
260
545
  }
261
- var ArkProvider = class {
262
- name = "ark";
263
- supportedModels = ARK_MODELS;
264
- apiKey;
265
- apiUrl;
266
- enableCaching;
546
+ function getRouteRules() {
547
+ return MODEL_ROUTES;
548
+ }
549
+ function getDefaultProvider() {
550
+ return DEFAULT_PROVIDER;
551
+ }
552
+ function isModelForProvider(model, provider) {
553
+ return routeModelToProvider(model) === provider;
554
+ }
555
+
556
+ // src/providers/orchestrator.ts
557
+ var DEFAULT_MAX_ITERATIONS = 10;
558
+ var ChatOrchestrator = class {
559
+ config;
267
560
  constructor(config) {
268
- this.apiKey = config.apiKey;
269
- this.apiUrl = config.apiUrl || DEFAULT_ARK_URL;
270
- this.enableCaching = config.enableCaching ?? false;
271
- }
272
- supportsModel(model) {
273
- return ARK_MODELS.includes(model) || model.startsWith("doubao-") || model.startsWith("deepseek-");
274
- }
275
- getModelCapabilities(model) {
276
- return MODEL_CAPABILITIES[model] || DEFAULT_CAPABILITIES;
561
+ this.config = {
562
+ maxIterations: DEFAULT_MAX_ITERATIONS,
563
+ ...config
564
+ };
277
565
  }
278
566
  /**
279
- * 聊天入口 - 统一使用 Responses API
567
+ * 执行聊天
568
+ *
569
+ * @param adapter Provider 适配器
570
+ * @param message 用户消息
571
+ * @param context Orchestrator 上下文
572
+ * @param options 选项
280
573
  */
281
- async *chat(message, model, options, context) {
282
- const showThinking = options.thinkingMode === "enabled";
283
- const enableWebSearch = options.enableWebSearch === true;
284
- if (showThinking) {
285
- yield { type: "thinking", data: { content: "\u6B63\u5728\u601D\u8003...", isComplete: false } };
286
- }
287
- let searchStarted = false;
288
- const tools = [];
289
- if (enableWebSearch) {
290
- tools.push({
291
- type: "web_search",
292
- max_keyword: 5,
293
- limit: 20
294
- });
295
- }
296
- if (options.mode !== "ask") {
297
- const functionTools = createToolDefinitions(context.workingDir);
298
- tools.push(...convertToResponsesApiTools(functionTools));
299
- }
300
- const input = convertToResponsesApiInput(context.systemPrompt, context.history, message);
574
+ async *chat(adapter, message, context, options) {
575
+ const startedAt = Date.now();
576
+ const maxIterations = this.config.maxIterations ?? DEFAULT_MAX_ITERATIONS;
577
+ const messages = this.buildMessages(context, message);
301
578
  let iterations = 0;
302
579
  let finalText = "";
303
- let thinkingComplete = !showThinking;
304
- let searchComplete = !enableWebSearch;
580
+ let thinkingText = "";
581
+ let thinkingStartedAt = 0;
582
+ let thinkingComplete = !options.enableThinking;
305
583
  let searchResults = [];
306
- let hasExecutedTools = false;
307
- const pendingFunctionCalls = /* @__PURE__ */ new Map();
308
- while (iterations < MAX_ITERATIONS) {
584
+ let searchStartedAt = 0;
585
+ let searchStarted = false;
586
+ if (options.enableThinking) {
587
+ thinkingStartedAt = Date.now();
588
+ yield createThinkingStart();
589
+ }
590
+ while (iterations < maxIterations) {
309
591
  if (context.signal.aborted) {
310
- yield { type: "error", data: "\u8BF7\u6C42\u5DF2\u53D6\u6D88" };
592
+ yield createAbort("\u8BF7\u6C42\u5DF2\u53D6\u6D88");
311
593
  return;
312
594
  }
313
595
  iterations++;
314
596
  try {
315
- const requestBody = {
316
- model,
317
- stream: true,
318
- max_output_tokens: 32768,
319
- input
320
- };
321
- if (tools.length > 0) {
322
- requestBody.tools = tools;
323
- }
324
- const effectiveThinkingMode = hasExecutedTools ? "disabled" : options.thinkingMode;
325
- if (effectiveThinkingMode) {
326
- const thinkingConfig = {
327
- type: effectiveThinkingMode
328
- };
329
- if (effectiveThinkingMode === "enabled" && options.thinkingBudget) {
330
- thinkingConfig.budget_tokens = options.thinkingBudget;
331
- }
332
- requestBody.thinking = thinkingConfig;
333
- }
334
- if (this.enableCaching) {
335
- requestBody.caching = { type: "enabled" };
336
- }
337
- const response = await fetch(`${this.apiUrl}/responses`, {
338
- method: "POST",
339
- headers: {
340
- "Authorization": `Bearer ${this.apiKey}`,
341
- "Content-Type": "application/json"
342
- },
343
- body: JSON.stringify(requestBody),
597
+ const pendingToolCalls = [];
598
+ let hasToolCalls = false;
599
+ const stream = adapter.streamOnce(messages, context.tools, {
600
+ model: options.model,
601
+ enableThinking: options.enableThinking && iterations === 1,
602
+ // 只在第一轮启用思考
603
+ enableSearch: options.enableSearch && iterations === 1,
344
604
  signal: context.signal
345
605
  });
346
- if (!response.ok) {
347
- const errorText = await response.text();
348
- throw new Error(`Responses API \u9519\u8BEF: ${response.status} - ${errorText}`);
349
- }
350
- const reader = response.body?.getReader();
351
- if (!reader) {
352
- throw new Error("\u65E0\u6CD5\u83B7\u53D6\u54CD\u5E94\u6D41");
353
- }
354
- const decoder = new TextDecoder();
355
- let buffer = "";
356
- let currentFunctionCallId = null;
357
- let needsFunctionExecution = false;
358
- while (true) {
359
- if (context.signal.aborted) {
360
- reader.cancel();
361
- yield { type: "error", data: "\u8BF7\u6C42\u5DF2\u53D6\u6D88" };
362
- return;
363
- }
364
- const { done, value } = await reader.read();
365
- if (done) break;
366
- buffer += decoder.decode(value, { stream: true });
367
- const lines = buffer.split("\n");
368
- buffer = lines.pop() || "";
369
- for (const line of lines) {
370
- if (!line.startsWith("data:")) continue;
371
- const data = line.slice(5).trim();
372
- if (data === "[DONE]") continue;
373
- try {
374
- const event = JSON.parse(data);
375
- switch (event.type) {
376
- // 添加输出项
377
- case "response.output_item.added": {
378
- const item = event.item;
379
- if (item?.type === "web_search_call") {
380
- if (!searchStarted) {
381
- searchStarted = true;
382
- yield {
383
- type: "search_start",
384
- data: { query: item.query || message }
385
- };
386
- }
387
- } else if (item?.type === "function_call") {
388
- const callId = item.id || `call_${Date.now()}`;
389
- currentFunctionCallId = callId;
390
- pendingFunctionCalls.set(callId, {
391
- name: item.name || "",
392
- arguments: ""
393
- });
394
- }
395
- break;
396
- }
397
- // 函数名称
398
- case "response.function_call.name": {
399
- if (currentFunctionCallId) {
400
- const call = pendingFunctionCalls.get(currentFunctionCallId);
401
- if (call) {
402
- call.name = event.name || call.name;
403
- }
404
- }
405
- break;
406
- }
407
- // 函数参数增量
408
- case "response.function_call_arguments.delta": {
409
- if (currentFunctionCallId) {
410
- const call = pendingFunctionCalls.get(currentFunctionCallId);
411
- if (call) {
412
- call.arguments += event.delta || "";
413
- }
414
- }
415
- break;
416
- }
417
- // 函数调用完成
418
- case "response.function_call_arguments.done":
419
- case "response.output_item.done": {
420
- const item = event.item;
421
- if (item?.type === "function_call" && item.name) {
422
- const callId = item.id || currentFunctionCallId || `call_${Date.now()}`;
423
- pendingFunctionCalls.set(callId, {
424
- name: item.name,
425
- arguments: item.arguments || pendingFunctionCalls.get(callId)?.arguments || "{}"
426
- });
427
- needsFunctionExecution = true;
428
- }
429
- if (item?.type === "web_search_call" && item.action?.query) {
430
- }
431
- break;
432
- }
433
- // 搜索进行中(官方事件类型)
434
- case "response.web_search_call.in_progress": {
435
- break;
436
- }
437
- // 搜索完成(官方事件类型)
438
- case "response.web_search_call.completed": {
439
- break;
440
- }
441
- // 搜索引用(流式收集,最后统一发送)
442
- case "response.output_text.annotation.added": {
443
- const annotation = event.annotation;
444
- const isUrlCitation = annotation?.type === "url_citation" || annotation?.type === "citation" || annotation?.url;
445
- if (isUrlCitation && annotation?.url) {
446
- const exists = searchResults.some((r) => r.url === annotation.url);
447
- if (!exists) {
448
- searchResults.push({
449
- title: annotation.title || annotation.text || "",
450
- url: annotation.url,
451
- snippet: annotation.summary || annotation.snippet || ""
452
- });
453
- console.log("[ARK] \u6536\u96C6\u5230\u641C\u7D22\u5F15\u7528:", annotation.url);
454
- }
455
- }
456
- break;
457
- }
458
- // 文本输出增量
459
- case "response.output_text.delta": {
460
- if (!thinkingComplete) {
461
- thinkingComplete = true;
462
- yield { type: "thinking", data: { content: "", isComplete: true } };
463
- }
464
- const delta = event.delta || "";
465
- finalText += delta;
466
- yield { type: "text_delta", data: delta };
467
- break;
468
- }
469
- // 深度思考内容增量
470
- case "response.reasoning_summary_text.delta": {
471
- yield {
472
- type: "thinking",
473
- data: { content: event.delta || "", isComplete: false }
474
- };
475
- break;
476
- }
477
- // 深度思考完成
478
- case "response.reasoning_summary_text.done": {
606
+ for await (const chunk of stream) {
607
+ switch (chunk.type) {
608
+ case "text":
609
+ if (chunk.text) {
610
+ if (!thinkingComplete) {
479
611
  thinkingComplete = true;
480
- yield {
481
- type: "thinking",
482
- data: { content: "", isComplete: true }
483
- };
484
- break;
612
+ yield createThinkingEnd(thinkingStartedAt);
485
613
  }
486
- // 响应完成
487
- case "response.done": {
488
- if (searchStarted && !searchComplete) {
489
- searchComplete = true;
490
- console.log("[ARK] \u53D1\u9001\u641C\u7D22\u7ED3\u679C:", searchResults.length, "\u6761");
491
- yield { type: "search_result", data: { results: searchResults, isComplete: true } };
492
- }
493
- break;
494
- }
495
- default: {
496
- if (event.type && !event.type.startsWith("response.created") && !event.type.startsWith("rate_limits")) {
497
- }
614
+ finalText += chunk.text;
615
+ yield createTextDelta(chunk.text);
616
+ }
617
+ break;
618
+ case "thinking":
619
+ if (chunk.thinking) {
620
+ thinkingText += chunk.thinking;
621
+ yield createThinkingDelta(chunk.thinking);
622
+ }
623
+ break;
624
+ case "thinking_done":
625
+ if (!thinkingComplete) {
626
+ thinkingComplete = true;
627
+ yield createThinkingEnd(thinkingStartedAt);
628
+ }
629
+ break;
630
+ case "tool_call":
631
+ if (chunk.toolCall) {
632
+ pendingToolCalls.push(chunk.toolCall);
633
+ hasToolCalls = true;
634
+ }
635
+ break;
636
+ case "search_result":
637
+ if (chunk.searchResults) {
638
+ if (!searchStarted) {
639
+ searchStarted = true;
640
+ searchStartedAt = Date.now();
641
+ yield createSearchStart(message);
498
642
  }
643
+ searchResults = chunk.searchResults;
644
+ yield createSearchResult(searchResults, searchStartedAt);
499
645
  }
500
- } catch {
501
- }
646
+ break;
647
+ case "done":
648
+ if (chunk.finishReason === "tool_calls") {
649
+ hasToolCalls = true;
650
+ }
651
+ break;
652
+ case "error":
653
+ yield createApiError(chunk.error ?? "\u672A\u77E5\u9519\u8BEF");
654
+ return;
502
655
  }
503
656
  }
504
- if (needsFunctionExecution && pendingFunctionCalls.size > 0) {
505
- for (const [callId, call] of pendingFunctionCalls) {
506
- if (!call.name) continue;
657
+ if (!thinkingComplete) {
658
+ thinkingComplete = true;
659
+ yield createThinkingEnd(thinkingStartedAt);
660
+ }
661
+ if (hasToolCalls && pendingToolCalls.length > 0) {
662
+ const assistantMessage = {
663
+ role: "assistant",
664
+ content: finalText,
665
+ toolCalls: pendingToolCalls
666
+ };
667
+ messages.push(assistantMessage);
668
+ for (const toolCall of pendingToolCalls) {
669
+ const toolStartedAt = Date.now();
507
670
  let args;
508
671
  try {
509
- args = JSON.parse(call.arguments || "{}");
672
+ args = JSON.parse(toolCall.arguments || "{}");
510
673
  } catch {
511
- yield { type: "error", data: `\u5DE5\u5177\u53C2\u6570\u89E3\u6790\u5931\u8D25: ${call.arguments}` };
512
- input.push({
513
- type: "function_call_output",
514
- call_id: callId,
515
- output: "\u53C2\u6570\u89E3\u6790\u9519\u8BEF"
674
+ messages.push({
675
+ role: "tool",
676
+ content: "\u53C2\u6570\u89E3\u6790\u9519\u8BEF",
677
+ toolCallId: toolCall.id
516
678
  });
517
679
  continue;
518
680
  }
519
- yield { type: "tool_call", data: { name: call.name, args } };
520
- const result = await context.executeTool(call.name, args);
521
- yield { type: "tool_result", data: { name: call.name, result } };
522
- input.push({
523
- type: "function_call_output",
524
- call_id: callId,
525
- output: result
681
+ const autoRunConfig = this.config.getAutoRunConfig ? await this.config.getAutoRunConfig() : options.autoRunConfig || this.config.autoRunConfig;
682
+ console.log("[Orchestrator] \u68C0\u67E5\u5DE5\u5177\u6279\u51C6:", {
683
+ toolName: toolCall.name,
684
+ autoRunConfigMode: autoRunConfig?.mode,
685
+ hasCallback: !!this.config.onToolApprovalRequest
526
686
  });
527
- hasExecutedTools = true;
687
+ if (autoRunConfig?.mode === "manual" && this.config.onToolApprovalRequest) {
688
+ console.log("[Orchestrator] \u53D1\u9001\u5DE5\u5177\u6279\u51C6\u8BF7\u6C42:", toolCall.name);
689
+ const approvalRequest = {
690
+ type: "tool_approval_request",
691
+ data: {
692
+ id: toolCall.id,
693
+ name: toolCall.name,
694
+ args,
695
+ requestedAt: Date.now()
696
+ }
697
+ };
698
+ yield approvalRequest;
699
+ const approved = await this.config.onToolApprovalRequest({
700
+ id: toolCall.id,
701
+ name: toolCall.name,
702
+ args
703
+ });
704
+ if (!approved) {
705
+ const result2 = JSON.stringify({ skipped: true, message: "\u7528\u6237\u8DF3\u8FC7\u4E86\u6B64\u5DE5\u5177" });
706
+ yield createToolCallResult(toolCall.id, toolCall.name, result2, false, toolStartedAt);
707
+ messages.push({
708
+ role: "tool",
709
+ content: result2,
710
+ toolCallId: toolCall.id,
711
+ toolName: toolCall.name
712
+ });
713
+ continue;
714
+ }
715
+ }
716
+ yield createToolCallStart(toolCall.id, toolCall.name, args);
717
+ let result;
718
+ let dynamicSideEffects;
719
+ let success = true;
720
+ try {
721
+ const executeResult = await this.config.executeTool(toolCall.name, args, context.signal);
722
+ if (typeof executeResult === "string") {
723
+ result = executeResult;
724
+ } else {
725
+ result = executeResult.result;
726
+ dynamicSideEffects = executeResult.sideEffects;
727
+ }
728
+ } catch (error) {
729
+ success = false;
730
+ const errorMessage = error instanceof Error ? error.message : String(error);
731
+ result = `\u9519\u8BEF: ${errorMessage}`;
732
+ }
733
+ const tool2 = this.config.tools?.get(toolCall.name);
734
+ const sideEffects = dynamicSideEffects ?? tool2?.sideEffects;
735
+ yield createToolCallResult(toolCall.id, toolCall.name, result, success, toolStartedAt, void 0, sideEffects);
736
+ messages.push({
737
+ role: "tool",
738
+ content: result,
739
+ toolCallId: toolCall.id,
740
+ toolName: toolCall.name
741
+ // Gemini 需要工具名称
742
+ });
743
+ if (context.signal.aborted) {
744
+ yield createAbort("\u8BF7\u6C42\u5DF2\u53D6\u6D88");
745
+ return;
746
+ }
528
747
  }
529
- pendingFunctionCalls.clear();
748
+ finalText = "";
530
749
  continue;
531
750
  }
532
751
  break;
533
752
  } catch (error) {
534
753
  if (context.signal.aborted) {
535
- yield { type: "error", data: "\u8BF7\u6C42\u5DF2\u53D6\u6D88" };
754
+ yield createAbort("\u8BF7\u6C42\u5DF2\u53D6\u6D88");
536
755
  } else {
537
- yield { type: "error", data: String(error) };
756
+ yield createApiError(error instanceof Error ? error.message : String(error));
538
757
  }
539
- break;
758
+ return;
540
759
  }
541
760
  }
542
- if (!thinkingComplete) {
543
- yield { type: "thinking", data: { content: "", isComplete: true } };
761
+ if (searchStarted) {
762
+ yield createSearchEnd(true, searchStartedAt);
763
+ }
764
+ const duration = Date.now() - startedAt;
765
+ yield createDone(finalText, void 0, duration);
766
+ }
767
+ /**
768
+ * 构建标准化消息列表
769
+ */
770
+ buildMessages(context, message) {
771
+ const messages = [];
772
+ if (context.systemPrompt) {
773
+ messages.push({
774
+ role: "system",
775
+ content: context.systemPrompt
776
+ });
544
777
  }
545
- if (searchStarted && !searchComplete) {
546
- yield { type: "search_result", data: { results: searchResults, isComplete: true } };
778
+ for (const msg of context.history) {
779
+ const standardMsg = {
780
+ role: msg.role,
781
+ content: msg.content
782
+ };
783
+ if (msg.tool_calls) {
784
+ standardMsg.toolCalls = msg.tool_calls.map((tc) => ({
785
+ id: tc.id,
786
+ name: tc.function.name,
787
+ arguments: tc.function.arguments
788
+ }));
789
+ }
790
+ messages.push(standardMsg);
547
791
  }
548
- context.history.push({ role: "user", content: message });
549
- context.history.push({ role: "assistant", content: finalText || "\u5B8C\u6210" });
550
- yield { type: "text", data: finalText };
551
- yield { type: "done", data: finalText };
792
+ messages.push({
793
+ role: "user",
794
+ content: message,
795
+ images: context.images
796
+ });
797
+ return messages;
552
798
  }
553
799
  };
800
+ function createOrchestrator(config) {
801
+ return new ChatOrchestrator(config);
802
+ }
554
803
 
555
- // src/providers/qwen.ts
556
- var QWEN_MODELS = [
557
- "qwen3-max-preview",
558
- "qwen-plus"
804
+ // src/providers/adapters/ark.ts
805
+ var ARK_MODELS = [
806
+ DOUBAO_SEED_1_8.id,
807
+ DOUBAO_SEED_1_6.id,
808
+ DOUBAO_SEED_1_6_LATEST.id,
809
+ DOUBAO_SEED_1_6_FLASH.id,
810
+ "doubao-1-5-pro-32k-250115",
811
+ "doubao-1-5-pro-256k-250115",
812
+ "doubao-1-5-lite-32k-250115"
559
813
  ];
560
- var MODEL_CAPABILITIES2 = {
561
- "qwen3-max-preview": {
562
- supportsTools: true,
563
- supportsWebSearch: true,
564
- supportsThinking: true,
565
- supportedThinkingModes: ["enabled", "disabled"]
566
- },
567
- "qwen-plus": {
568
- supportsTools: true,
569
- supportsWebSearch: true,
570
- supportsThinking: true,
571
- supportedThinkingModes: ["enabled", "disabled"]
814
+ var ArkAdapter = class {
815
+ name = "ark";
816
+ supportedModels = ARK_MODELS;
817
+ apiKey;
818
+ apiUrl;
819
+ constructor(config) {
820
+ this.apiKey = config.apiKey;
821
+ this.apiUrl = config.apiUrl ?? DEFAULT_ARK_URL;
822
+ }
823
+ supportsModel(model) {
824
+ return this.supportedModels.some((m) => model.includes(m) || m.includes(model));
825
+ }
826
+ /**
827
+ * 单次流式调用
828
+ */
829
+ async *streamOnce(messages, tools2, options) {
830
+ const input = this.convertMessages(messages);
831
+ const requestBody = {
832
+ model: options.model,
833
+ stream: true,
834
+ max_output_tokens: 32768,
835
+ input
836
+ };
837
+ const apiTools = [];
838
+ if (options.enableSearch) {
839
+ apiTools.push({ type: "web_search", max_keyword: 5, limit: 20 });
840
+ }
841
+ if (tools2.length > 0) {
842
+ for (const t of tools2) {
843
+ apiTools.push({
844
+ type: "function",
845
+ name: t.name,
846
+ description: t.description,
847
+ parameters: t.parameters
848
+ });
849
+ }
850
+ }
851
+ if (apiTools.length > 0) {
852
+ requestBody.tools = apiTools;
853
+ }
854
+ if (options.enableThinking) {
855
+ requestBody.thinking = { type: "enabled" };
856
+ }
857
+ const response = await fetch(`${this.apiUrl}/responses`, {
858
+ method: "POST",
859
+ headers: {
860
+ "Authorization": `Bearer ${this.apiKey}`,
861
+ "Content-Type": "application/json"
862
+ },
863
+ body: JSON.stringify(requestBody),
864
+ signal: options.signal
865
+ });
866
+ if (!response.ok) {
867
+ const errorText = await response.text();
868
+ yield { type: "error", error: `ARK API \u9519\u8BEF: ${response.status} - ${errorText}` };
869
+ return;
870
+ }
871
+ const reader = response.body?.getReader();
872
+ if (!reader) {
873
+ yield { type: "error", error: "\u65E0\u6CD5\u83B7\u53D6\u54CD\u5E94\u6D41" };
874
+ return;
875
+ }
876
+ yield* this.parseStream(reader);
877
+ }
878
+ /**
879
+ * 转换标准消息为 ARK Responses API 格式
880
+ */
881
+ convertMessages(messages) {
882
+ const input = [];
883
+ for (const msg of messages) {
884
+ switch (msg.role) {
885
+ case "system":
886
+ input.push({ role: "system", content: msg.content });
887
+ break;
888
+ case "user": {
889
+ const content = [{ type: "input_text", text: msg.content }];
890
+ if (msg.images?.length) {
891
+ for (const img of msg.images) {
892
+ content.push({
893
+ type: "input_image",
894
+ image_url: img.startsWith("data:") ? img : `data:image/jpeg;base64,${img}`
895
+ });
896
+ }
897
+ }
898
+ input.push({ role: "user", content });
899
+ break;
900
+ }
901
+ case "assistant":
902
+ if (msg.toolCalls?.length) {
903
+ for (const tc of msg.toolCalls) {
904
+ input.push({
905
+ type: "function_call",
906
+ call_id: tc.id,
907
+ name: tc.name,
908
+ arguments: tc.arguments
909
+ });
910
+ }
911
+ } else {
912
+ input.push({
913
+ role: "developer",
914
+ content: `[\u4E0A\u4E00\u8F6EAI\u56DE\u590D]: ${msg.content}`
915
+ });
916
+ }
917
+ break;
918
+ case "tool":
919
+ input.push({
920
+ type: "function_call_output",
921
+ call_id: msg.toolCallId,
922
+ output: msg.content
923
+ });
924
+ break;
925
+ }
926
+ }
927
+ return input;
928
+ }
929
+ /**
930
+ * 解析 ARK 流式响应
931
+ */
932
+ async *parseStream(reader) {
933
+ const decoder = new TextDecoder();
934
+ let buffer = "";
935
+ const pendingToolCalls = /* @__PURE__ */ new Map();
936
+ let currentFunctionCallId = null;
937
+ const searchResults = [];
938
+ while (true) {
939
+ const { done, value } = await reader.read();
940
+ if (done) break;
941
+ buffer += decoder.decode(value, { stream: true });
942
+ const lines = buffer.split("\n");
943
+ buffer = lines.pop() || "";
944
+ for (const line of lines) {
945
+ if (!line.startsWith("data:")) continue;
946
+ const data = line.slice(5).trim();
947
+ if (data === "[DONE]") {
948
+ if (pendingToolCalls.size > 0) {
949
+ for (const tc of pendingToolCalls.values()) {
950
+ yield { type: "tool_call", toolCall: tc };
951
+ }
952
+ yield { type: "done", finishReason: "tool_calls" };
953
+ } else {
954
+ yield { type: "done", finishReason: "stop" };
955
+ }
956
+ return;
957
+ }
958
+ try {
959
+ const event = JSON.parse(data);
960
+ switch (event.type) {
961
+ case "response.output_item.added": {
962
+ const item = event.item;
963
+ if (item?.type === "function_call") {
964
+ const callId = item.call_id;
965
+ if (!callId) {
966
+ console.warn("[ARK] function_call \u7F3A\u5C11 call_id");
967
+ break;
968
+ }
969
+ currentFunctionCallId = callId;
970
+ pendingToolCalls.set(callId, {
971
+ id: callId,
972
+ name: item.name || "",
973
+ arguments: item.arguments || ""
974
+ });
975
+ }
976
+ break;
977
+ }
978
+ case "response.function_call_arguments.delta": {
979
+ if (currentFunctionCallId) {
980
+ const call = pendingToolCalls.get(currentFunctionCallId);
981
+ if (call) {
982
+ call.arguments += event.delta || "";
983
+ }
984
+ }
985
+ break;
986
+ }
987
+ case "response.function_call_arguments.done":
988
+ case "response.output_item.done": {
989
+ const item = event.item;
990
+ if (item?.type === "function_call" && item.call_id) {
991
+ const callId = item.call_id;
992
+ const existing = pendingToolCalls.get(callId);
993
+ pendingToolCalls.set(callId, {
994
+ id: callId,
995
+ name: item.name || existing?.name || "",
996
+ arguments: item.arguments || existing?.arguments || "{}"
997
+ });
998
+ }
999
+ break;
1000
+ }
1001
+ case "response.output_text.annotation.added": {
1002
+ const annotation = event.annotation;
1003
+ const isUrlCitation = annotation?.type === "url_citation" || annotation?.type === "citation" || annotation?.url;
1004
+ if (isUrlCitation && annotation?.url) {
1005
+ const exists = searchResults.some((r) => r.url === annotation.url);
1006
+ if (!exists) {
1007
+ searchResults.push({
1008
+ title: annotation.title || annotation.text || "",
1009
+ url: annotation.url,
1010
+ snippet: annotation.summary || annotation.snippet || ""
1011
+ });
1012
+ yield { type: "search_result", searchResults: [...searchResults] };
1013
+ }
1014
+ }
1015
+ break;
1016
+ }
1017
+ case "response.output_text.delta":
1018
+ if (event.delta) {
1019
+ yield { type: "text", text: event.delta };
1020
+ }
1021
+ break;
1022
+ case "response.reasoning_summary_text.delta":
1023
+ if (event.delta) {
1024
+ yield { type: "thinking", thinking: event.delta };
1025
+ }
1026
+ break;
1027
+ case "response.reasoning_summary_text.done":
1028
+ yield { type: "thinking_done" };
1029
+ break;
1030
+ }
1031
+ } catch {
1032
+ }
1033
+ }
1034
+ }
1035
+ if (pendingToolCalls.size > 0) {
1036
+ for (const tc of pendingToolCalls.values()) {
1037
+ yield { type: "tool_call", toolCall: tc };
1038
+ }
1039
+ yield { type: "done", finishReason: "tool_calls" };
1040
+ } else {
1041
+ yield { type: "done", finishReason: "stop" };
1042
+ }
572
1043
  }
573
1044
  };
574
- var DEFAULT_CAPABILITIES2 = {
575
- supportsTools: true,
576
- supportsWebSearch: true,
577
- supportsThinking: true,
578
- supportedThinkingModes: ["enabled", "disabled"]
579
- };
580
- var QwenProvider = class {
581
- name = "qwen";
582
- supportedModels = QWEN_MODELS;
1045
+ function createArkAdapter(config) {
1046
+ return new ArkAdapter(config);
1047
+ }
1048
+
1049
+ // src/providers/adapters/openrouter.ts
1050
+ var OPENROUTER_MODELS = [
1051
+ OR_GEMINI_2_5_FLASH.id,
1052
+ OR_GEMINI_2_5_PRO.id,
1053
+ OR_GEMINI_2_0_FLASH.id,
1054
+ "anthropic/claude-sonnet-4",
1055
+ "anthropic/claude-3.5-sonnet",
1056
+ "openai/gpt-4o",
1057
+ "openai/gpt-4o-mini",
1058
+ OR_DEEPSEEK_R1.id,
1059
+ OR_DEEPSEEK_CHAT_V3.id
1060
+ ];
1061
+ var OpenRouterAdapter = class {
1062
+ name = "openrouter";
1063
+ supportedModels = OPENROUTER_MODELS;
583
1064
  apiKey;
584
1065
  apiUrl;
585
1066
  constructor(config) {
586
1067
  this.apiKey = config.apiKey;
587
- this.apiUrl = config.apiUrl || DEFAULT_QWEN_NATIVE_URL;
1068
+ this.apiUrl = config.apiUrl ?? DEFAULT_OPENROUTER_URL;
588
1069
  }
589
1070
  supportsModel(model) {
590
- return QWEN_MODELS.includes(model) || model.startsWith("qwen");
1071
+ return this.supportedModels.some((m) => model.includes(m) || m.includes(model));
591
1072
  }
592
- getModelCapabilities(model) {
593
- return MODEL_CAPABILITIES2[model] || DEFAULT_CAPABILITIES2;
1073
+ /**
1074
+ * 单次流式调用
1075
+ */
1076
+ async *streamOnce(messages, tools2, options) {
1077
+ const apiMessages = this.convertMessages(messages);
1078
+ const requestBody = {
1079
+ model: options.model,
1080
+ messages: apiMessages,
1081
+ stream: true,
1082
+ max_tokens: 32768
1083
+ };
1084
+ if (tools2.length > 0) {
1085
+ requestBody.tools = tools2.map((t) => ({
1086
+ type: "function",
1087
+ function: {
1088
+ name: t.name,
1089
+ description: t.description,
1090
+ parameters: t.parameters
1091
+ }
1092
+ }));
1093
+ }
1094
+ if (options.model.includes("gemini") && options.enableThinking) {
1095
+ requestBody.reasoning = { effort: "high" };
1096
+ }
1097
+ if (options.model.includes("claude") && options.enableThinking) {
1098
+ requestBody.reasoning = { effort: "high", exclude: false };
1099
+ }
1100
+ if (options.enableSearch) {
1101
+ const isSmallContext = options.model.includes("deepseek") || options.model.includes("qwen");
1102
+ const maxResults = isSmallContext ? 2 : 3;
1103
+ requestBody.plugins = [{ id: "web", max_results: maxResults }];
1104
+ }
1105
+ const response = await fetch(`${this.apiUrl}/chat/completions`, {
1106
+ method: "POST",
1107
+ headers: {
1108
+ "Authorization": `Bearer ${this.apiKey}`,
1109
+ "Content-Type": "application/json",
1110
+ "HTTP-Referer": "https://ai-chat.local",
1111
+ "X-Title": "AI Chat"
1112
+ },
1113
+ body: JSON.stringify(requestBody),
1114
+ signal: options.signal
1115
+ });
1116
+ if (!response.ok) {
1117
+ const errorText = await response.text();
1118
+ yield { type: "error", error: `OpenRouter API \u9519\u8BEF: ${response.status} - ${errorText}` };
1119
+ return;
1120
+ }
1121
+ const reader = response.body?.getReader();
1122
+ if (!reader) {
1123
+ yield { type: "error", error: "\u65E0\u6CD5\u83B7\u53D6\u54CD\u5E94\u6D41" };
1124
+ return;
1125
+ }
1126
+ yield* this.parseStream(reader);
594
1127
  }
595
- async *chat(message, model, options, context) {
596
- const showThinking = options.thinkingMode === "enabled";
597
- const enableSearch = options.enableWebSearch === true;
598
- if (showThinking) {
599
- yield { type: "thinking", data: { content: "\u6B63\u5728\u601D\u8003...", isComplete: false } };
600
- }
601
- if (enableSearch) {
602
- yield { type: "search_start", data: { query: message } };
603
- }
604
- const tools = options.mode === "ask" ? [] : createToolDefinitions(context.workingDir).map((t) => ({
605
- type: "function",
606
- function: {
607
- name: t.function.name,
608
- description: t.function.description,
609
- parameters: t.function.parameters
610
- }
611
- }));
612
- const messages = [
613
- { role: "system", content: context.systemPrompt },
614
- ...context.history,
615
- { role: "user", content: message }
616
- ];
617
- let iterations = 0;
618
- let finalText = "";
619
- let thinkingContent = "";
620
- let thinkingComplete = !showThinking;
621
- let searchComplete = !enableSearch;
622
- let searchResults = [];
623
- let hasExecutedTools = false;
624
- while (iterations < MAX_ITERATIONS) {
625
- if (context.signal.aborted) {
626
- yield { type: "error", data: "\u8BF7\u6C42\u5DF2\u53D6\u6D88" };
627
- return;
1128
+ /**
1129
+ * 转换标准消息为 OpenAI 格式
1130
+ */
1131
+ convertMessages(messages) {
1132
+ const apiMessages = [];
1133
+ for (const msg of messages) {
1134
+ switch (msg.role) {
1135
+ case "system":
1136
+ apiMessages.push({ role: "system", content: msg.content });
1137
+ break;
1138
+ case "user": {
1139
+ if (msg.images?.length) {
1140
+ const content = [{ type: "text", text: msg.content }];
1141
+ for (const img of msg.images) {
1142
+ content.push({
1143
+ type: "image_url",
1144
+ image_url: { url: img.startsWith("data:") ? img : `data:image/jpeg;base64,${img}` }
1145
+ });
1146
+ }
1147
+ apiMessages.push({ role: "user", content });
1148
+ } else {
1149
+ apiMessages.push({ role: "user", content: msg.content });
1150
+ }
1151
+ break;
1152
+ }
1153
+ case "assistant":
1154
+ if (msg.toolCalls?.length) {
1155
+ apiMessages.push({
1156
+ role: "assistant",
1157
+ content: msg.content || null,
1158
+ tool_calls: msg.toolCalls.map((tc) => ({
1159
+ id: tc.id,
1160
+ type: "function",
1161
+ function: {
1162
+ name: tc.name,
1163
+ arguments: tc.arguments
1164
+ }
1165
+ }))
1166
+ });
1167
+ } else {
1168
+ apiMessages.push({ role: "assistant", content: msg.content });
1169
+ }
1170
+ break;
1171
+ case "tool":
1172
+ apiMessages.push({
1173
+ role: "tool",
1174
+ tool_call_id: msg.toolCallId,
1175
+ content: msg.content
1176
+ });
1177
+ break;
628
1178
  }
629
- iterations++;
630
- try {
631
- const toolCallsMap = /* @__PURE__ */ new Map();
632
- const effectiveEnableThinking = hasExecutedTools ? false : showThinking;
633
- for await (const event of this.callDashScopeStream(
634
- model,
635
- messages,
636
- tools,
637
- { enableSearch, enableThinking: effectiveEnableThinking, thinkingBudget: options.thinkingBudget },
638
- context.signal
639
- )) {
640
- const choice = event.output?.choices?.[0];
641
- if (event.output?.search_info?.search_results && !searchComplete) {
642
- searchResults = event.output.search_info.search_results;
643
- searchComplete = true;
644
- yield {
645
- type: "search_result",
646
- data: {
647
- results: searchResults.map((r) => ({
648
- title: r.title,
649
- url: r.url,
650
- snippet: r.snippet || ""
651
- })),
652
- isComplete: true
1179
+ }
1180
+ return apiMessages;
1181
+ }
1182
+ /**
1183
+ * 解析 OpenAI 兼容流式响应
1184
+ */
1185
+ async *parseStream(reader) {
1186
+ const decoder = new TextDecoder();
1187
+ let buffer = "";
1188
+ const toolCallsMap = /* @__PURE__ */ new Map();
1189
+ let toolCallCounter = 0;
1190
+ let finishReason = null;
1191
+ const searchResults = [];
1192
+ let hasThinking = false;
1193
+ let thinkingDone = false;
1194
+ while (true) {
1195
+ const { done, value } = await reader.read();
1196
+ if (done) break;
1197
+ buffer += decoder.decode(value, { stream: true });
1198
+ const lines = buffer.split("\n");
1199
+ buffer = lines.pop() || "";
1200
+ for (const line of lines) {
1201
+ if (!line.startsWith("data:")) continue;
1202
+ const data = line.slice(5).trim();
1203
+ if (data === "[DONE]") {
1204
+ if (searchResults.length > 0) {
1205
+ yield { type: "search_result", searchResults: [...searchResults] };
1206
+ }
1207
+ if (toolCallsMap.size > 0) {
1208
+ for (const tc of toolCallsMap.values()) {
1209
+ if (tc.name) {
1210
+ yield { type: "tool_call", toolCall: tc };
653
1211
  }
654
- };
1212
+ }
1213
+ yield { type: "done", finishReason: "tool_calls" };
1214
+ } else {
1215
+ yield { type: "done", finishReason: "stop" };
1216
+ }
1217
+ return;
1218
+ }
1219
+ try {
1220
+ const json = JSON.parse(data);
1221
+ const choice = json.choices?.[0];
1222
+ const annotations = json.annotations;
1223
+ if (annotations?.length) {
1224
+ for (const annotation of annotations) {
1225
+ if (annotation.type === "url_citation" && annotation.url_citation?.url) {
1226
+ const exists = searchResults.some((r) => r.url === annotation.url_citation.url);
1227
+ if (!exists) {
1228
+ searchResults.push({
1229
+ url: annotation.url_citation.url,
1230
+ title: annotation.url_citation.title || "",
1231
+ snippet: annotation.url_citation.content || ""
1232
+ });
1233
+ yield { type: "search_result", searchResults: [...searchResults] };
1234
+ }
1235
+ }
1236
+ }
655
1237
  }
656
1238
  if (!choice) continue;
657
- const msg = choice.message;
658
- if (msg.reasoning_content && !thinkingComplete) {
659
- thinkingContent += msg.reasoning_content;
660
- yield { type: "thinking", data: { content: msg.reasoning_content, isComplete: false } };
1239
+ const delta = choice.delta || {};
1240
+ finishReason = choice.finish_reason;
1241
+ if (delta.reasoning) {
1242
+ hasThinking = true;
1243
+ yield { type: "thinking", thinking: delta.reasoning };
661
1244
  }
662
- if (msg.content) {
663
- if (!thinkingComplete) {
664
- thinkingComplete = true;
665
- yield { type: "thinking", data: { content: "", isComplete: true } };
1245
+ if (delta.content) {
1246
+ if (hasThinking && !thinkingDone) {
1247
+ thinkingDone = true;
1248
+ yield { type: "thinking_done" };
666
1249
  }
667
- finalText += msg.content;
668
- yield { type: "text_delta", data: msg.content };
1250
+ yield { type: "text", text: delta.content };
669
1251
  }
670
- if (msg.tool_calls?.length) {
671
- for (const tc of msg.tool_calls) {
672
- const index = tc.index ?? 0;
1252
+ if (delta.tool_calls?.length) {
1253
+ for (const tc of delta.tool_calls) {
1254
+ const index = tc.index;
673
1255
  const existing = toolCallsMap.get(index);
674
1256
  if (existing) {
675
1257
  if (tc.function?.arguments) {
@@ -683,126 +1265,99 @@ var QwenProvider = class {
683
1265
  }
684
1266
  } else {
685
1267
  toolCallsMap.set(index, {
686
- id: tc.id || `call_${Date.now()}_${index}`,
1268
+ id: tc.id || `call_${Date.now()}_${toolCallCounter++}`,
687
1269
  name: tc.function?.name || "",
688
1270
  arguments: tc.function?.arguments || ""
689
1271
  });
690
1272
  }
691
1273
  }
692
1274
  }
693
- if (choice.finish_reason === "stop" || choice.finish_reason === "length" || choice.finish_reason === "tool_calls") {
694
- break;
695
- }
696
- }
697
- const currentToolCalls = Array.from(toolCallsMap.values()).filter((tc) => tc.name);
698
- if (!thinkingComplete) {
699
- thinkingComplete = true;
700
- yield { type: "thinking", data: { content: "", isComplete: true } };
701
- }
702
- if (currentToolCalls.length > 0) {
703
- messages.push({
704
- role: "assistant",
705
- content: finalText || "",
706
- tool_calls: currentToolCalls.map((tc) => ({
707
- id: tc.id,
708
- type: "function",
709
- function: {
710
- name: tc.name,
711
- arguments: tc.arguments
1275
+ if (finishReason === "tool_calls") {
1276
+ for (const tc of toolCallsMap.values()) {
1277
+ if (tc.name) {
1278
+ yield { type: "tool_call", toolCall: tc };
712
1279
  }
713
- }))
714
- });
715
- for (const tc of currentToolCalls) {
716
- const name = tc.name;
717
- let args;
718
- try {
719
- args = JSON.parse(tc.arguments);
720
- } catch {
721
- yield { type: "error", data: `\u5DE5\u5177\u53C2\u6570\u89E3\u6790\u5931\u8D25: ${tc.arguments}` };
722
- messages.push({
723
- role: "tool",
724
- tool_call_id: tc.id,
725
- content: `\u53C2\u6570\u89E3\u6790\u9519\u8BEF`
726
- });
727
- continue;
728
1280
  }
729
- yield { type: "tool_call", data: { name, args } };
730
- const result = await context.executeTool(name, args);
731
- yield { type: "tool_result", data: { name, result } };
732
- messages.push({
733
- role: "tool",
734
- tool_call_id: tc.id,
735
- content: result
736
- });
737
- hasExecutedTools = true;
1281
+ yield { type: "done", finishReason: "tool_calls" };
1282
+ return;
738
1283
  }
739
- finalText = "";
740
- continue;
741
- }
742
- break;
743
- } catch (error) {
744
- if (context.signal.aborted) {
745
- yield { type: "error", data: "\u8BF7\u6C42\u5DF2\u53D6\u6D88" };
746
- } else {
747
- yield { type: "error", data: String(error) };
1284
+ } catch {
748
1285
  }
749
- break;
750
1286
  }
751
1287
  }
752
- if (!searchComplete) {
753
- yield { type: "search_result", data: { results: [], isComplete: true } };
1288
+ if (searchResults.length > 0) {
1289
+ yield { type: "search_result", searchResults: [...searchResults] };
754
1290
  }
755
- context.history.push({ role: "user", content: message });
756
- context.history.push({
757
- role: "assistant",
758
- content: finalText || "\u5B8C\u6210",
759
- reasoning_content: thinkingContent || void 0
760
- });
761
- yield { type: "text", data: finalText };
762
- yield { type: "done", data: finalText };
1291
+ if (toolCallsMap.size > 0) {
1292
+ for (const tc of toolCallsMap.values()) {
1293
+ if (tc.name) {
1294
+ yield { type: "tool_call", toolCall: tc };
1295
+ }
1296
+ }
1297
+ yield { type: "done", finishReason: "tool_calls" };
1298
+ } else {
1299
+ yield { type: "done", finishReason: "stop" };
1300
+ }
1301
+ }
1302
+ };
1303
+ function createOpenRouterAdapter(config) {
1304
+ return new OpenRouterAdapter(config);
1305
+ }
1306
+
1307
+ // src/providers/adapters/qwen.ts
1308
+ var QWEN_MODELS = [
1309
+ QWEN_MAX.id,
1310
+ QWEN_MAX_LATEST.id,
1311
+ QWEN_PLUS.id,
1312
+ QWEN_PLUS_LATEST.id,
1313
+ QWEN_TURBO.id,
1314
+ QWEN_TURBO_LATEST.id,
1315
+ QWEN3_235B.id
1316
+ ];
1317
+ var QwenAdapter = class {
1318
+ name = "qwen";
1319
+ supportedModels = QWEN_MODELS;
1320
+ apiKey;
1321
+ apiUrl;
1322
+ constructor(config) {
1323
+ this.apiKey = config.apiKey;
1324
+ this.apiUrl = config.apiUrl ?? DEFAULT_QWEN_URL;
763
1325
  }
764
- /** 调用 DashScope API (流式) */
765
- async *callDashScopeStream(model, messages, tools, options, signal) {
766
- const formattedMessages = messages.map((m) => {
767
- const msg = {
768
- role: m.role,
769
- content: m.content
770
- };
771
- if (m.tool_call_id) {
772
- msg.tool_call_id = m.tool_call_id;
773
- }
774
- if (m.tool_calls?.length) {
775
- msg.tool_calls = m.tool_calls;
776
- }
777
- return msg;
778
- });
1326
+ supportsModel(model) {
1327
+ return this.supportedModels.some((m) => model.includes(m) || m.includes(model));
1328
+ }
1329
+ /**
1330
+ * 单次流式调用
1331
+ */
1332
+ async *streamOnce(messages, tools2, options) {
1333
+ const apiMessages = this.convertMessages(messages);
779
1334
  const requestBody = {
780
- model,
781
- input: { messages: formattedMessages },
1335
+ model: options.model,
1336
+ input: { messages: apiMessages },
782
1337
  parameters: {
783
1338
  result_format: "message",
784
1339
  incremental_output: true,
785
- max_tokens: 32768
1340
+ enable_search: options.enableSearch,
1341
+ // 深度思考支持
1342
+ ...options.enableThinking && {
1343
+ enable_thinking: true,
1344
+ thinking_budget: 38400
1345
+ }
786
1346
  }
787
1347
  };
788
- const params = requestBody.parameters;
789
- if (options.enableThinking) {
790
- params.enable_thinking = true;
791
- if (options.thinkingBudget) {
792
- params.thinking_budget = options.thinkingBudget;
793
- }
794
- }
795
- if (options.enableSearch) {
796
- params.enable_search = true;
797
- params.search_options = {
798
- enable_source: true,
799
- enable_citation: true,
800
- search_strategy: "turbo"
1348
+ if (tools2.length > 0) {
1349
+ requestBody.parameters = {
1350
+ ...requestBody.parameters,
1351
+ tools: tools2.map((t) => ({
1352
+ type: "function",
1353
+ function: {
1354
+ name: t.name,
1355
+ description: t.description,
1356
+ parameters: t.parameters
1357
+ }
1358
+ }))
801
1359
  };
802
1360
  }
803
- if (tools.length > 0) {
804
- params.tools = tools;
805
- }
806
1361
  const response = await fetch(`${this.apiUrl}/services/aigc/text-generation/generation`, {
807
1362
  method: "POST",
808
1363
  headers: {
@@ -811,18 +1366,85 @@ var QwenProvider = class {
811
1366
  "X-DashScope-SSE": "enable"
812
1367
  },
813
1368
  body: JSON.stringify(requestBody),
814
- signal
1369
+ signal: options.signal
815
1370
  });
816
1371
  if (!response.ok) {
817
1372
  const errorText = await response.text();
818
- throw new Error(`DashScope API \u9519\u8BEF: ${response.status} - ${errorText}`);
1373
+ yield { type: "error", error: `Qwen API \u9519\u8BEF: ${response.status} - ${errorText}` };
1374
+ return;
819
1375
  }
820
1376
  const reader = response.body?.getReader();
821
1377
  if (!reader) {
822
- throw new Error("\u65E0\u6CD5\u83B7\u53D6\u54CD\u5E94\u6D41");
1378
+ yield { type: "error", error: "\u65E0\u6CD5\u83B7\u53D6\u54CD\u5E94\u6D41" };
1379
+ return;
1380
+ }
1381
+ yield* this.parseStream(reader);
1382
+ }
1383
+ /**
1384
+ * 转换标准消息为 Qwen 格式
1385
+ */
1386
+ convertMessages(messages) {
1387
+ const apiMessages = [];
1388
+ for (const msg of messages) {
1389
+ switch (msg.role) {
1390
+ case "system":
1391
+ apiMessages.push({ role: "system", content: msg.content });
1392
+ break;
1393
+ case "user": {
1394
+ if (msg.images?.length) {
1395
+ const content = [{ type: "text", text: msg.content }];
1396
+ for (const img of msg.images) {
1397
+ content.push({
1398
+ type: "image_url",
1399
+ image_url: { url: img.startsWith("data:") ? img : `data:image/jpeg;base64,${img}` }
1400
+ });
1401
+ }
1402
+ apiMessages.push({ role: "user", content });
1403
+ } else {
1404
+ apiMessages.push({ role: "user", content: msg.content });
1405
+ }
1406
+ break;
1407
+ }
1408
+ case "assistant":
1409
+ if (msg.toolCalls?.length) {
1410
+ apiMessages.push({
1411
+ role: "assistant",
1412
+ content: msg.content || "",
1413
+ tool_calls: msg.toolCalls.map((tc) => ({
1414
+ id: tc.id,
1415
+ type: "function",
1416
+ function: {
1417
+ name: tc.name,
1418
+ arguments: tc.arguments
1419
+ }
1420
+ }))
1421
+ });
1422
+ } else {
1423
+ apiMessages.push({ role: "assistant", content: msg.content });
1424
+ }
1425
+ break;
1426
+ case "tool":
1427
+ apiMessages.push({
1428
+ role: "tool",
1429
+ tool_call_id: msg.toolCallId,
1430
+ content: msg.content
1431
+ });
1432
+ break;
1433
+ }
823
1434
  }
1435
+ return apiMessages;
1436
+ }
1437
+ /**
1438
+ * 解析 Qwen 流式响应
1439
+ */
1440
+ async *parseStream(reader) {
824
1441
  const decoder = new TextDecoder();
825
1442
  let buffer = "";
1443
+ const toolCallsMap = /* @__PURE__ */ new Map();
1444
+ let toolCallCounter = 0;
1445
+ const searchResults = [];
1446
+ let hasThinking = false;
1447
+ let thinkingDone = false;
826
1448
  while (true) {
827
1449
  const { done, value } = await reader.read();
828
1450
  if (done) break;
@@ -830,422 +1452,533 @@ var QwenProvider = class {
830
1452
  const lines = buffer.split("\n");
831
1453
  buffer = lines.pop() || "";
832
1454
  for (const line of lines) {
833
- if (line.startsWith("data:")) {
834
- const data = line.slice(5).trim();
835
- if (data && data !== "[DONE]") {
836
- try {
837
- yield JSON.parse(data);
838
- } catch {
1455
+ if (!line.startsWith("data:")) continue;
1456
+ const data = line.slice(5).trim();
1457
+ if (!data || data === "[DONE]") continue;
1458
+ try {
1459
+ const json = JSON.parse(data);
1460
+ const output = json.output;
1461
+ if (!output) continue;
1462
+ const choice = output.choices?.[0];
1463
+ const message = choice?.message || {};
1464
+ const finishReason = choice?.finish_reason;
1465
+ if (message.reasoning_content) {
1466
+ hasThinking = true;
1467
+ yield { type: "thinking", thinking: message.reasoning_content };
1468
+ }
1469
+ if (message.content) {
1470
+ if (!thinkingDone && hasThinking) {
1471
+ thinkingDone = true;
1472
+ yield { type: "thinking_done" };
1473
+ }
1474
+ yield { type: "text", text: message.content };
1475
+ }
1476
+ const searchInfo = json.output?.search_info;
1477
+ if (searchInfo?.search_results?.length) {
1478
+ for (const result of searchInfo.search_results) {
1479
+ const exists = searchResults.some((r) => r.url === result.url);
1480
+ if (!exists) {
1481
+ searchResults.push({
1482
+ title: result.title || "",
1483
+ url: result.url || "",
1484
+ snippet: result.snippet || ""
1485
+ });
1486
+ }
1487
+ }
1488
+ yield { type: "search_result", searchResults: [...searchResults] };
1489
+ }
1490
+ if (message.tool_calls?.length) {
1491
+ for (const tc of message.tool_calls) {
1492
+ const index = toolCallsMap.size;
1493
+ const existing = toolCallsMap.get(index);
1494
+ if (existing) {
1495
+ if (tc.function?.arguments) {
1496
+ existing.arguments += tc.function.arguments;
1497
+ }
1498
+ } else {
1499
+ toolCallsMap.set(index, {
1500
+ id: tc.id || `call_${Date.now()}_${toolCallCounter++}`,
1501
+ name: tc.function.name,
1502
+ arguments: tc.function.arguments
1503
+ });
1504
+ }
1505
+ }
1506
+ }
1507
+ if (finishReason === "stop" || finishReason === "length" || finishReason === "tool_calls") {
1508
+ if (toolCallsMap.size > 0) {
1509
+ for (const tc of toolCallsMap.values()) {
1510
+ yield { type: "tool_call", toolCall: tc };
1511
+ }
1512
+ yield { type: "done", finishReason: "tool_calls" };
1513
+ } else {
1514
+ yield { type: "done", finishReason: "stop" };
839
1515
  }
1516
+ return;
840
1517
  }
1518
+ } catch {
841
1519
  }
842
1520
  }
843
1521
  }
1522
+ if (toolCallsMap.size > 0) {
1523
+ for (const tc of toolCallsMap.values()) {
1524
+ yield { type: "tool_call", toolCall: tc };
1525
+ }
1526
+ yield { type: "done", finishReason: "tool_calls" };
1527
+ } else {
1528
+ yield { type: "done", finishReason: "stop" };
1529
+ }
844
1530
  }
845
1531
  };
1532
+ function createQwenAdapter(config) {
1533
+ return new QwenAdapter(config);
1534
+ }
846
1535
 
847
- // src/providers/gemini.ts
1536
+ // src/providers/adapters/gemini.ts
848
1537
  var GEMINI_MODELS = [
849
- "gemini-3-pro-preview"
1538
+ GEMINI_2_5_FLASH.id,
1539
+ GEMINI_2_5_PRO.id,
1540
+ GEMINI_2_0_FLASH.id,
1541
+ GEMINI_2_0_FLASH_LITE.id,
1542
+ GEMINI_3_PRO.id
850
1543
  ];
851
- var MODEL_CAPABILITIES3 = {
852
- "gemini-3-pro-preview": {
853
- supportsTools: true,
854
- supportsWebSearch: true,
855
- supportsThinking: true,
856
- supportedThinkingModes: ["enabled", "disabled"]
857
- }
858
- };
859
- var DEFAULT_CAPABILITIES3 = {
860
- supportsTools: true,
861
- supportsWebSearch: true,
862
- supportsThinking: true,
863
- supportedThinkingModes: ["enabled", "disabled"]
864
- };
865
- var GeminiProvider = class {
1544
+ var GeminiAdapter = class {
866
1545
  name = "gemini";
867
1546
  supportedModels = GEMINI_MODELS;
868
- geminiClient = null;
1547
+ client = null;
869
1548
  apiKey;
870
1549
  constructor(config) {
871
1550
  this.apiKey = config.apiKey;
872
1551
  }
873
- /** 懒加载 Gemini 客户端 */
1552
+ /** 懒加载客户端 */
874
1553
  async getClient() {
875
- if (!this.geminiClient) {
1554
+ if (!this.client) {
876
1555
  const { GoogleGenAI: GoogleGenAI2 } = await import('@google/genai');
877
- this.geminiClient = new GoogleGenAI2({ apiKey: this.apiKey });
1556
+ this.client = new GoogleGenAI2({ apiKey: this.apiKey });
878
1557
  }
879
- return this.geminiClient;
1558
+ return this.client;
880
1559
  }
881
1560
  supportsModel(model) {
882
- return GEMINI_MODELS.includes(model) || model.startsWith("gemini-");
1561
+ return this.supportedModels.some((m) => model.includes(m) || m.includes(model)) || model.startsWith("gemini-");
883
1562
  }
884
- getModelCapabilities(model) {
885
- return MODEL_CAPABILITIES3[model] || DEFAULT_CAPABILITIES3;
886
- }
887
- async *chat(message, model, options, context) {
888
- const geminiClient = await this.getClient();
889
- const showThinking = options.thinkingMode === "enabled";
890
- const tools = options.mode === "ask" ? [] : createToolDefinitions(context.workingDir);
891
- const enableWebSearch = options.enableWebSearch === true && tools.length === 0;
892
- if (showThinking) {
893
- yield { type: "thinking", data: { content: "\u6B63\u5728\u601D\u8003...", isComplete: false } };
894
- }
895
- if (enableWebSearch) {
896
- yield { type: "search_start", data: { query: message } };
897
- }
898
- const geminiMessages = [
899
- { role: "user", parts: [{ text: context.systemPrompt }] },
900
- { role: "model", parts: [{ text: "\u597D\u7684\uFF0C\u6211\u4F1A\u6309\u7167\u4F60\u7684\u6307\u793A\u884C\u4E8B\u3002" }] },
901
- ...this.convertToGeminiMessages(context.history),
902
- { role: "user", parts: [{ text: message }] }
903
- ];
904
- let iterations = 0;
905
- let finalText = "";
906
- let thinkingComplete = !showThinking;
907
- let searchComplete = !enableWebSearch;
908
- while (iterations < MAX_ITERATIONS) {
909
- if (context.signal.aborted) {
910
- yield { type: "error", data: "\u8BF7\u6C42\u5DF2\u53D6\u6D88" };
911
- return;
912
- }
913
- iterations++;
914
- try {
915
- const generateConfig = {
916
- thinkingConfig: {
917
- thinkingLevel: options.thinkingMode === "enabled" ? "high" : "low",
918
- // 启用思考摘要返回(Gemini 3 支持)
919
- includeThoughts: showThinking
920
- }
921
- };
922
- if (tools.length > 0) {
923
- generateConfig.tools = [{
924
- functionDeclarations: this.convertToGeminiFunctions(tools)
925
- }];
926
- } else if (enableWebSearch) {
927
- generateConfig.tools = [{ googleSearch: {} }];
928
- }
929
- const response = await geminiClient.models.generateContent({
930
- model,
931
- contents: geminiMessages,
932
- config: generateConfig
933
- });
934
- const candidate = response.candidates?.[0];
935
- if (!candidate) {
936
- yield { type: "error", data: "\u65E0\u54CD\u5E94" };
937
- break;
938
- }
939
- const rawCandidate = response.candidates?.[0];
940
- const groundingMetadata = rawCandidate?.groundingMetadata;
941
- if (groundingMetadata && !searchComplete) {
942
- searchComplete = true;
943
- const searchResults = (groundingMetadata.groundingChunks || []).filter((chunk) => chunk.web).map((chunk) => ({
944
- url: chunk.web.uri,
945
- title: chunk.web.title,
946
- snippet: ""
947
- }));
948
- yield {
949
- type: "search_result",
950
- data: {
951
- results: searchResults,
952
- queries: groundingMetadata.webSearchQueries,
953
- isComplete: true
954
- }
955
- };
1563
+ /**
1564
+ * 单次流式调用
1565
+ */
1566
+ async *streamOnce(messages, tools2, options) {
1567
+ const client = await this.getClient();
1568
+ const geminiMessages = this.convertMessages(messages);
1569
+ const generateConfig = {};
1570
+ const toolsConfig = [];
1571
+ if (tools2.length > 0) {
1572
+ toolsConfig.push({
1573
+ functionDeclarations: tools2.map((t) => ({
1574
+ name: t.name,
1575
+ description: t.description,
1576
+ parameters: t.parameters
1577
+ }))
1578
+ });
1579
+ }
1580
+ if (options.enableSearch) {
1581
+ toolsConfig.push({ googleSearch: {} });
1582
+ }
1583
+ if (toolsConfig.length > 0) {
1584
+ generateConfig.tools = toolsConfig;
1585
+ }
1586
+ if (options.enableThinking) {
1587
+ generateConfig.thinkingConfig = {
1588
+ thinkingLevel: "high",
1589
+ includeThoughts: true
1590
+ };
1591
+ }
1592
+ try {
1593
+ const streamResponse = await client.models.generateContentStream({
1594
+ model: options.model,
1595
+ contents: geminiMessages,
1596
+ config: generateConfig
1597
+ });
1598
+ const pendingToolCalls = [];
1599
+ let toolCallCounter = 0;
1600
+ let hasThinking = false;
1601
+ let thinkingDone = false;
1602
+ for await (const chunk of streamResponse) {
1603
+ if (options.signal.aborted) {
1604
+ yield { type: "error", error: "\u8BF7\u6C42\u5DF2\u53D6\u6D88" };
1605
+ return;
956
1606
  }
1607
+ const candidate = chunk.candidates?.[0];
1608
+ if (!candidate) continue;
957
1609
  const parts = candidate.content?.parts || [];
958
- let hasToolCall = false;
959
1610
  for (const part of parts) {
960
- const extendedPart = part;
961
- if (extendedPart.thought && part.text) {
962
- yield { type: "thinking", data: { content: part.text, isComplete: false } };
1611
+ if (part.thought && part.text) {
1612
+ hasThinking = true;
1613
+ yield { type: "thinking", thinking: part.text };
963
1614
  continue;
964
1615
  }
965
- if (part.text && !extendedPart.thought) {
966
- if (!thinkingComplete) {
967
- thinkingComplete = true;
968
- yield { type: "thinking", data: { content: "", isComplete: true } };
1616
+ if (part.text && !part.thought) {
1617
+ if (hasThinking && !thinkingDone) {
1618
+ thinkingDone = true;
1619
+ yield { type: "thinking_done" };
969
1620
  }
970
- finalText += part.text;
971
- yield { type: "text_delta", data: part.text };
1621
+ yield { type: "text", text: part.text };
972
1622
  }
973
1623
  if (part.functionCall && part.functionCall.name) {
974
- hasToolCall = true;
975
- const name = part.functionCall.name;
976
- const args = part.functionCall.args || {};
977
- const thoughtSignature = part.thoughtSignature;
978
- yield { type: "tool_call", data: { name, args } };
979
- const result = await context.executeTool(name, args);
980
- yield { type: "tool_result", data: { name, result } };
981
- const modelPart = { functionCall: { name, args } };
982
- if (thoughtSignature) {
983
- modelPart.thoughtSignature = thoughtSignature;
1624
+ pendingToolCalls.push({
1625
+ id: `call_${Date.now()}_${toolCallCounter++}`,
1626
+ name: part.functionCall.name,
1627
+ arguments: JSON.stringify(part.functionCall.args || {})
1628
+ });
1629
+ }
1630
+ }
1631
+ }
1632
+ if (pendingToolCalls.length > 0) {
1633
+ for (const tc of pendingToolCalls) {
1634
+ yield { type: "tool_call", toolCall: tc };
1635
+ }
1636
+ yield { type: "done", finishReason: "tool_calls" };
1637
+ } else {
1638
+ yield { type: "done", finishReason: "stop" };
1639
+ }
1640
+ } catch (error) {
1641
+ const errorStr = String(error);
1642
+ if (errorStr.includes("User location is not supported") || errorStr.includes("FAILED_PRECONDITION")) {
1643
+ yield {
1644
+ type: "error",
1645
+ error: "Gemini API \u4E0D\u652F\u6301\u5F53\u524D\u5730\u533A\uFF08\u4E2D\u56FD\u5927\u9646\u65E0\u6CD5\u8BBF\u95EE\uFF09\u3002\u8BF7\u4F7F\u7528\u5176\u4ED6\u6A21\u578B\u3002"
1646
+ };
1647
+ } else {
1648
+ yield { type: "error", error: errorStr };
1649
+ }
1650
+ }
1651
+ }
1652
+ /**
1653
+ * 转换标准消息为 Gemini 格式
1654
+ */
1655
+ convertMessages(messages) {
1656
+ const geminiMessages = [];
1657
+ for (const msg of messages) {
1658
+ switch (msg.role) {
1659
+ case "system":
1660
+ geminiMessages.push(
1661
+ { role: "user", parts: [{ text: msg.content }] },
1662
+ { role: "model", parts: [{ text: "OK" }] }
1663
+ );
1664
+ break;
1665
+ case "user": {
1666
+ const parts = [{ text: msg.content }];
1667
+ if (msg.images?.length) {
1668
+ for (const img of msg.images) {
1669
+ const base64 = img.startsWith("data:") ? img.split(",")[1] : img;
1670
+ parts.push({
1671
+ inlineData: {
1672
+ mimeType: "image/jpeg",
1673
+ data: base64
1674
+ }
1675
+ });
1676
+ }
1677
+ }
1678
+ geminiMessages.push({ role: "user", parts });
1679
+ break;
1680
+ }
1681
+ case "assistant":
1682
+ if (msg.toolCalls?.length) {
1683
+ const parts = [];
1684
+ if (msg.content) {
1685
+ parts.push({ text: msg.content });
1686
+ }
1687
+ for (const tc of msg.toolCalls) {
1688
+ parts.push({
1689
+ functionCall: {
1690
+ name: tc.name,
1691
+ args: JSON.parse(tc.arguments || "{}")
1692
+ }
1693
+ });
984
1694
  }
1695
+ geminiMessages.push({ role: "model", parts });
1696
+ } else {
985
1697
  geminiMessages.push({
986
1698
  role: "model",
987
- parts: [modelPart]
988
- });
989
- geminiMessages.push({
990
- role: "user",
991
- parts: [{ functionResponse: { name, response: { result } } }]
1699
+ parts: [{ text: msg.content }]
992
1700
  });
993
1701
  }
994
- }
995
- if (!hasToolCall) {
996
1702
  break;
997
- }
998
- } catch (error) {
999
- if (context.signal.aborted) {
1000
- yield { type: "error", data: "\u8BF7\u6C42\u5DF2\u53D6\u6D88" };
1001
- } else {
1002
- yield { type: "error", data: String(error) };
1003
- }
1004
- break;
1703
+ case "tool":
1704
+ geminiMessages.push({
1705
+ role: "user",
1706
+ parts: [{
1707
+ functionResponse: {
1708
+ name: msg.toolName || "unknown_tool",
1709
+ response: { result: msg.content }
1710
+ }
1711
+ }]
1712
+ });
1713
+ break;
1005
1714
  }
1006
1715
  }
1007
- if (!thinkingComplete) {
1008
- yield { type: "thinking", data: { content: "", isComplete: true } };
1009
- }
1010
- if (!searchComplete) {
1011
- yield { type: "search_result", data: { results: [], isComplete: true } };
1716
+ return geminiMessages;
1717
+ }
1718
+ };
1719
+ function createGeminiAdapter(config) {
1720
+ return new GeminiAdapter(config);
1721
+ }
1722
+
1723
+ // src/agent.ts
1724
+ var HybridAgent = class {
1725
+ config;
1726
+ adapters = /* @__PURE__ */ new Map();
1727
+ orchestrator;
1728
+ geminiClient;
1729
+ toolExecutor;
1730
+ abortController = null;
1731
+ /** 已注册的工具 */
1732
+ tools = /* @__PURE__ */ new Map();
1733
+ /** 工具配置(用于异步初始化) */
1734
+ toolConfig;
1735
+ constructor(config, toolExecutor) {
1736
+ this.config = {
1737
+ arkApiKey: config.arkApiKey,
1738
+ arkApiUrl: config.arkApiUrl || DEFAULT_ARK_URL,
1739
+ qwenApiKey: config.qwenApiKey || "",
1740
+ qwenApiUrl: config.qwenApiUrl || DEFAULT_QWEN_NATIVE_URL,
1741
+ openrouterApiKey: config.openrouterApiKey || "",
1742
+ openrouterApiUrl: config.openrouterApiUrl || DEFAULT_OPENROUTER_URL,
1743
+ geminiApiKey: config.geminiApiKey,
1744
+ cwd: config.cwd || process.cwd(),
1745
+ planPrompt: config.planPrompt || PLAN_MODE_PROMPT
1746
+ };
1747
+ this.geminiClient = new genai.GoogleGenAI({ apiKey: this.config.geminiApiKey });
1748
+ this.toolExecutor = toolExecutor || createDefaultToolExecutor(this.config.cwd);
1749
+ this.toolConfig = config.tools;
1750
+ this.tools = /* @__PURE__ */ new Map();
1751
+ this.initializeAdapters();
1752
+ this.orchestrator = new ChatOrchestrator({
1753
+ maxIterations: 10,
1754
+ executeTool: this.executeTool.bind(this),
1755
+ tools: this.tools,
1756
+ // 传入工具列表,用于获取 sideEffects
1757
+ // autoRunConfig 在每次 chat 调用时通过 options 传递
1758
+ onToolApprovalRequest: config.onToolApprovalRequest,
1759
+ // 传递工具批准回调
1760
+ getAutoRunConfig: config.getAutoRunConfig
1761
+ // 传递动态获取配置回调
1762
+ });
1763
+ }
1764
+ /** 异步初始化工具(在第一次 chat 前调用) */
1765
+ async asyncInit() {
1766
+ if (this.toolConfig && this.tools.size === 0) {
1767
+ const resolvedTools = await resolveTools(this.toolConfig);
1768
+ for (const tool2 of resolvedTools) {
1769
+ this.tools.set(tool2.name, tool2);
1770
+ }
1012
1771
  }
1013
- context.history.push({ role: "user", content: message });
1014
- context.history.push({ role: "assistant", content: finalText || "\u5B8C\u6210" });
1015
- yield { type: "text", data: finalText };
1016
- yield { type: "done", data: finalText };
1017
1772
  }
1018
- /** 转换消息格式为 Gemini 格式 */
1019
- convertToGeminiMessages(messages) {
1020
- return messages.map((msg) => ({
1021
- role: msg.role === "assistant" ? "model" : msg.role,
1022
- parts: [{ text: msg.content }]
1023
- }));
1773
+ /** 初始化所有 Adapter */
1774
+ initializeAdapters() {
1775
+ if (this.config.arkApiKey) {
1776
+ this.adapters.set("ark", new ArkAdapter({
1777
+ apiKey: this.config.arkApiKey,
1778
+ apiUrl: this.config.arkApiUrl
1779
+ }));
1780
+ }
1781
+ if (this.config.qwenApiKey) {
1782
+ this.adapters.set("qwen", new QwenAdapter({
1783
+ apiKey: this.config.qwenApiKey,
1784
+ apiUrl: this.config.qwenApiUrl
1785
+ }));
1786
+ }
1787
+ if (this.config.geminiApiKey) {
1788
+ this.adapters.set("gemini", new GeminiAdapter({
1789
+ apiKey: this.config.geminiApiKey
1790
+ }));
1791
+ }
1792
+ if (this.config.openrouterApiKey) {
1793
+ this.adapters.set("openrouter", new OpenRouterAdapter({
1794
+ apiKey: this.config.openrouterApiKey,
1795
+ apiUrl: this.config.openrouterApiUrl
1796
+ }));
1797
+ }
1024
1798
  }
1025
- /** 转换工具定义为 Gemini functionDeclarations 格式 */
1026
- convertToGeminiFunctions(tools) {
1027
- return tools.map((tool) => {
1028
- const t = tool;
1029
- return {
1030
- name: t.function.name,
1031
- description: t.function.description,
1032
- parameters: t.function.parameters
1033
- };
1034
- });
1799
+ /**
1800
+ * 判断模型提供商
1801
+ */
1802
+ getModelProvider(model) {
1803
+ return routeModelToProvider(model);
1035
1804
  }
1036
- };
1037
-
1038
- // src/providers/openrouter.ts
1039
- var OPENROUTER_MODELS = [
1040
- "anthropic/claude-opus-4.5"
1041
- ];
1042
- var MODEL_CAPABILITIES4 = {
1043
- "anthropic/claude-opus-4.5": {
1044
- supportsTools: true,
1045
- supportsWebSearch: true,
1046
- supportsThinking: true,
1047
- supportedThinkingModes: ["enabled", "disabled"]
1805
+ /** 获取 Adapter */
1806
+ getAdapter(model) {
1807
+ const providerName = this.getModelProvider(model);
1808
+ return this.adapters.get(providerName);
1048
1809
  }
1049
- };
1050
- var DEFAULT_CAPABILITIES4 = {
1051
- supportsTools: true,
1052
- supportsWebSearch: true,
1053
- supportsThinking: false,
1054
- supportedThinkingModes: ["disabled"]
1055
- };
1056
- var OpenRouterProvider = class {
1057
- name = "openrouter";
1058
- supportedModels = OPENROUTER_MODELS;
1059
- apiKey;
1060
- apiUrl;
1061
- constructor(config) {
1062
- this.apiKey = config.apiKey;
1063
- this.apiUrl = config.apiUrl || DEFAULT_OPENROUTER_URL;
1810
+ /**
1811
+ * 调试:获取模型路由信息
1812
+ */
1813
+ getModelRouteInfo(model) {
1814
+ const result = routeModelWithDetails(model);
1815
+ return {
1816
+ ...result,
1817
+ available: this.adapters.has(result.provider)
1818
+ };
1064
1819
  }
1065
- supportsModel(model) {
1066
- return OPENROUTER_MODELS.includes(model) || model.includes("/");
1820
+ /** 构建系统提示词 */
1821
+ buildSystemPrompt(options) {
1822
+ if (options.mode === "plan") {
1823
+ return this.config.planPrompt;
1824
+ }
1825
+ return AGENT_MODE_PROMPT;
1067
1826
  }
1068
- getModelCapabilities(model) {
1069
- return MODEL_CAPABILITIES4[model] || DEFAULT_CAPABILITIES4;
1827
+ /** 获取默认深度思考模式 */
1828
+ getDefaultThinkingMode() {
1829
+ return "disabled";
1070
1830
  }
1071
- /** 检查模型是否支持 Extended Thinking */
1072
- supportsThinking(model) {
1073
- return model.includes("claude-opus-4.5") || model === "anthropic/claude-opus-4.5";
1831
+ /** 创建工具执行上下文 */
1832
+ createToolContext(signal) {
1833
+ return {
1834
+ cwd: this.config.cwd,
1835
+ geminiClient: this.geminiClient,
1836
+ executeCommand: (command, cwd) => this.toolExecutor.executeCommand(command, cwd || this.config.cwd, signal),
1837
+ signal
1838
+ };
1074
1839
  }
1075
- async *chat(message, model, options, context) {
1076
- const enableThinking = options.thinkingMode === "enabled" && this.supportsThinking(model);
1077
- const enableWebSearch = options.enableWebSearch === true;
1078
- if (enableThinking) {
1079
- yield { type: "thinking", data: { content: "", isComplete: false } };
1080
- }
1081
- if (enableWebSearch) {
1082
- yield { type: "search_start", data: { query: message } };
1083
- }
1084
- const tools = options.mode === "ask" ? [] : createToolDefinitions(context.workingDir);
1085
- const messages = [
1086
- { role: "system", content: context.systemPrompt },
1087
- ...context.history,
1088
- { role: "user", content: message }
1089
- ];
1090
- let iterations = 0;
1091
- let finalText = "";
1092
- let thinkingContent = "";
1093
- let thinkingComplete = !enableThinking;
1094
- let searchComplete = !enableWebSearch;
1095
- while (iterations < MAX_ITERATIONS) {
1096
- if (context.signal.aborted) {
1097
- yield { type: "error", data: "\u8BF7\u6C42\u5DF2\u53D6\u6D88" };
1098
- return;
1099
- }
1100
- iterations++;
1101
- try {
1102
- const response = await this.callOpenRouter(
1103
- model,
1104
- messages,
1105
- tools,
1106
- enableWebSearch,
1107
- enableThinking,
1108
- context.signal
1109
- );
1110
- const choice = response.choices?.[0];
1111
- if (!choice) {
1112
- yield { type: "error", data: "\u65E0\u54CD\u5E94" };
1113
- break;
1114
- }
1115
- const responseMessage = choice.message;
1116
- const finishReason = choice.finish_reason;
1117
- if (responseMessage.reasoning && !thinkingComplete) {
1118
- thinkingContent = responseMessage.reasoning;
1119
- yield { type: "thinking", data: { content: responseMessage.reasoning, isComplete: false } };
1120
- }
1121
- if (enableThinking && !thinkingComplete && responseMessage.content) {
1122
- thinkingComplete = true;
1123
- yield { type: "thinking", data: { content: "", isComplete: true } };
1124
- }
1125
- if (enableWebSearch && !searchComplete && responseMessage.annotations?.length) {
1126
- searchComplete = true;
1127
- const searchResults = responseMessage.annotations.filter((a) => a.type === "url_citation").map((a) => ({
1128
- url: a.url_citation.url,
1129
- title: a.url_citation.title || "",
1130
- snippet: a.url_citation.content || ""
1131
- }));
1132
- yield {
1133
- type: "search_result",
1134
- data: {
1135
- results: searchResults,
1136
- isComplete: true
1137
- }
1138
- };
1139
- } else if (enableWebSearch && !searchComplete && iterations === 1) {
1140
- searchComplete = true;
1141
- yield { type: "search_result", data: { results: [], isComplete: true } };
1142
- }
1143
- if (finishReason === "tool_calls" && responseMessage.tool_calls?.length) {
1144
- messages.push(responseMessage);
1145
- for (const toolCall of responseMessage.tool_calls) {
1146
- const name = toolCall.function.name;
1147
- let args;
1148
- try {
1149
- args = JSON.parse(toolCall.function.arguments);
1150
- } catch {
1151
- yield { type: "error", data: `\u5DE5\u5177\u53C2\u6570\u89E3\u6790\u5931\u8D25` };
1152
- messages.push({
1153
- role: "tool",
1154
- tool_call_id: toolCall.id,
1155
- content: `\u53C2\u6570\u89E3\u6790\u9519\u8BEF: ${toolCall.function.arguments}`
1156
- });
1157
- continue;
1158
- }
1159
- yield { type: "tool_call", data: { name, args } };
1160
- const result = await context.executeTool(name, args);
1161
- yield { type: "tool_result", data: { name, result } };
1162
- messages.push({
1163
- role: "tool",
1164
- tool_call_id: toolCall.id,
1165
- content: result
1166
- });
1167
- }
1168
- if (responseMessage.content) {
1169
- finalText += responseMessage.content + "\n";
1170
- yield { type: "text_delta", data: responseMessage.content };
1171
- }
1172
- continue;
1173
- }
1174
- if (responseMessage.content || finishReason === "stop" || finishReason === "end_turn") {
1175
- if (responseMessage.content) {
1176
- finalText += responseMessage.content;
1177
- yield { type: "text_delta", data: responseMessage.content };
1178
- }
1179
- break;
1180
- }
1181
- break;
1182
- } catch (error) {
1183
- if (context.signal.aborted) {
1184
- yield { type: "error", data: "\u8BF7\u6C42\u5DF2\u53D6\u6D88" };
1185
- } else {
1186
- yield { type: "error", data: String(error) };
1187
- }
1188
- break;
1189
- }
1840
+ /** 执行工具 */
1841
+ async executeTool(name, args, signal) {
1842
+ const tool2 = this.tools.get(name);
1843
+ if (!tool2) {
1844
+ return `\u672A\u77E5\u5DE5\u5177: ${name}`;
1190
1845
  }
1191
- if (!thinkingComplete) {
1192
- yield { type: "thinking", data: { content: "", isComplete: true } };
1846
+ return await tool2.execute(args, this.createToolContext(signal));
1847
+ }
1848
+ /** 获取工具定义列表 */
1849
+ getToolDefinitions() {
1850
+ return Array.from(this.tools.values()).map((tool2) => ({
1851
+ name: tool2.name,
1852
+ description: tool2.description,
1853
+ parameters: tool2.parameters
1854
+ }));
1855
+ }
1856
+ /**
1857
+ * 聊天入口
1858
+ *
1859
+ * 使用 ChatOrchestrator 统一处理所有 Provider 的工具调用循环
1860
+ */
1861
+ async *chat(message, options = {}, images) {
1862
+ await this.asyncInit();
1863
+ this.abortController = new AbortController();
1864
+ const signal = this.abortController.signal;
1865
+ const model = options.model || DEFAULT_MODEL;
1866
+ const adapter = this.getAdapter(model);
1867
+ if (!adapter) {
1868
+ const providerName = this.getModelProvider(model);
1869
+ yield createApiError(`\u7F3A\u5C11 ${providerName} Provider \u7684 API Key`, { code: "MISSING_API_KEY" });
1870
+ return;
1193
1871
  }
1194
- if (!searchComplete) {
1195
- yield { type: "search_result", data: { results: [], isComplete: true } };
1872
+ const thinkingMode = options.thinkingMode ?? this.getDefaultThinkingMode();
1873
+ const systemPrompt = this.buildSystemPrompt(options);
1874
+ const tools2 = options.mode === "ask" ? [] : this.getToolDefinitions();
1875
+ const history = options.history || [];
1876
+ const context = {
1877
+ systemPrompt,
1878
+ history,
1879
+ tools: tools2,
1880
+ signal,
1881
+ images
1882
+ };
1883
+ try {
1884
+ yield* this.orchestrator.chat(adapter, message, context, {
1885
+ model,
1886
+ enableThinking: thinkingMode === "enabled",
1887
+ enableSearch: options.enableWebSearch,
1888
+ autoRunConfig: options.autoRunConfig
1889
+ });
1890
+ } finally {
1891
+ this.abortController = null;
1196
1892
  }
1197
- context.history.push({ role: "user", content: message });
1198
- context.history.push({
1199
- role: "assistant",
1200
- content: finalText || "\u5B8C\u6210",
1201
- reasoning_content: thinkingContent || void 0
1202
- });
1203
- yield { type: "text", data: finalText };
1204
- yield { type: "done", data: finalText };
1205
1893
  }
1206
- /** 调用 OpenRouter API */
1207
- async callOpenRouter(model, messages, tools, enableWebSearch, enableThinking, signal) {
1208
- const requestBody = {
1209
- model,
1210
- messages,
1211
- max_tokens: 32768
1212
- };
1213
- if (enableThinking && this.supportsThinking(model)) {
1214
- requestBody.reasoning = {
1215
- effort: "high",
1216
- exclude: false
1217
- // 包含推理过程
1218
- };
1894
+ /** 中断当前请求 */
1895
+ abort() {
1896
+ if (this.abortController) {
1897
+ this.abortController.abort();
1219
1898
  }
1220
- if (tools.length > 0) {
1221
- requestBody.tools = tools;
1899
+ }
1900
+ /** 设置当前工作目录 */
1901
+ setCwd(dir) {
1902
+ this.config.cwd = dir;
1903
+ }
1904
+ /** 获取当前配置 */
1905
+ getConfig() {
1906
+ return { ...this.config };
1907
+ }
1908
+ /** 获取所有支持的模型 */
1909
+ getSupportedModels() {
1910
+ return MODELS;
1911
+ }
1912
+ };
1913
+
1914
+ // src/builtin-tools/get-cwd.ts
1915
+ function getCwdTool() {
1916
+ return tool({
1917
+ name: "get_cwd",
1918
+ description: "\u83B7\u53D6\u7528\u6237\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u7684\u7EDD\u5BF9\u8DEF\u5F84\u3002\u5F53\u4F60\u9700\u8981\u77E5\u9053\u7528\u6237\u5728\u54EA\u4E2A\u76EE\u5F55\u4E0B\u5DE5\u4F5C\u65F6\u4F7F\u7528\u3002",
1919
+ parameters: {
1920
+ type: "object",
1921
+ properties: {},
1922
+ required: []
1923
+ },
1924
+ execute: async (_args, context) => {
1925
+ return JSON.stringify({ cwd: context.cwd });
1222
1926
  }
1223
- if (enableWebSearch) {
1224
- requestBody.plugins = [
1225
- {
1226
- id: "web",
1227
- max_results: 5
1228
- }
1229
- ];
1927
+ });
1928
+ }
1929
+
1930
+ // src/builtin-tools/get-platform.ts
1931
+ function getPlatformTool() {
1932
+ return tool({
1933
+ name: "get_platform",
1934
+ description: "\u83B7\u53D6\u64CD\u4F5C\u7CFB\u7EDF\u4FE1\u606F\uFF08macOS/Windows/Linux\uFF09\u3002\u5F53\u4F60\u9700\u8981\u786E\u5B9A\u4F7F\u7528\u54EA\u79CD\u547D\u4EE4\u8BED\u6CD5\u65F6\u8C03\u7528\uFF08\u5982 ls vs dir\uFF09\u3002",
1935
+ parameters: {
1936
+ type: "object",
1937
+ properties: {},
1938
+ required: []
1939
+ },
1940
+ execute: async () => {
1941
+ const os = await import('os');
1942
+ const platform = os.default.platform();
1943
+ const platformName = platform === "darwin" ? "macOS" : platform === "win32" ? "Windows" : "Linux";
1944
+ const shell = platform === "win32" ? "powershell" : "bash/zsh";
1945
+ const pathSeparator = platform === "win32" ? "\\" : "/";
1946
+ return JSON.stringify({
1947
+ platform,
1948
+ platformName,
1949
+ shell,
1950
+ pathSeparator
1951
+ });
1230
1952
  }
1231
- const response = await fetch(`${this.apiUrl}/chat/completions`, {
1232
- method: "POST",
1233
- headers: {
1234
- "Authorization": `Bearer ${this.apiKey}`,
1235
- "Content-Type": "application/json",
1236
- "HTTP-Referer": "https://github.com/huyooo/ai-chat",
1237
- "X-Title": "AI Chat Agent"
1953
+ });
1954
+ }
1955
+
1956
+ // src/builtin-tools/execute-command.ts
1957
+ function executeCommandTool() {
1958
+ return tool({
1959
+ name: "execute_command",
1960
+ description: "\u6267\u884C shell \u547D\u4EE4\u3002\u6267\u884C\u524D\u5EFA\u8BAE\u5148\u7528 get_cwd \u83B7\u53D6\u5F53\u524D\u76EE\u5F55\u3001get_platform \u83B7\u53D6\u7CFB\u7EDF\u7C7B\u578B\uFF0C\u4EE5\u786E\u4FDD\u4F7F\u7528\u6B63\u786E\u7684\u8DEF\u5F84\u548C\u547D\u4EE4\u8BED\u6CD5\u3002",
1961
+ parameters: {
1962
+ type: "object",
1963
+ properties: {
1964
+ command: { type: "string", description: "\u8981\u6267\u884C\u7684\u547D\u4EE4" },
1965
+ cwd: { type: "string", description: "\u6267\u884C\u547D\u4EE4\u7684\u76EE\u5F55\uFF08\u4E0D\u4F20\u5219\u4F7F\u7528\u7528\u6237\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF09" }
1238
1966
  },
1239
- body: JSON.stringify(requestBody),
1240
- signal
1241
- });
1242
- if (!response.ok) {
1243
- const errorText = await response.text();
1244
- throw new Error(`OpenRouter API \u9519\u8BEF: ${response.status} - ${errorText}`);
1967
+ required: ["command"]
1968
+ },
1969
+ execute: async (args, context) => {
1970
+ const cwd = args.cwd || context.cwd;
1971
+ const result = await context.executeCommand(args.command, cwd);
1972
+ if (result.success) {
1973
+ return result.output || "\u6267\u884C\u6210\u529F";
1974
+ }
1975
+ if (result.error === "\u64CD\u4F5C\u5DF2\u53D6\u6D88") {
1976
+ return JSON.stringify({ success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88", cancelled: true });
1977
+ }
1978
+ return `\u9519\u8BEF: ${result.error}`;
1245
1979
  }
1246
- return response.json();
1247
- }
1248
- };
1980
+ });
1981
+ }
1249
1982
 
1250
1983
  // src/gemini.ts
1251
1984
  function getMimeType(ext) {
@@ -1267,8 +2000,11 @@ function getVideoMimeType(ext) {
1267
2000
  };
1268
2001
  return types[ext] || "video/mp4";
1269
2002
  }
1270
- async function analyzeImage(geminiClient, imagePath, question) {
2003
+ async function analyzeImage(geminiClient, imagePath, question, signal) {
1271
2004
  try {
2005
+ if (signal?.aborted) {
2006
+ return JSON.stringify({ success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88", cancelled: true });
2007
+ }
1272
2008
  const fs = await import('fs/promises');
1273
2009
  const path = await import('path');
1274
2010
  const imageData = await fs.readFile(imagePath);
@@ -1283,43 +2019,53 @@ async function analyzeImage(geminiClient, imagePath, question) {
1283
2019
  { text: question || "\u8BF7\u8BE6\u7EC6\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9" },
1284
2020
  { inlineData: { mimeType, data: base64 } }
1285
2021
  ]
1286
- }]
2022
+ }],
2023
+ config: { abortSignal: signal }
1287
2024
  });
1288
2025
  return response.text || "\u65E0\u6CD5\u5206\u6790\u56FE\u7247";
1289
2026
  } catch (error) {
2027
+ if (signal?.aborted) return JSON.stringify({ success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88", cancelled: true });
1290
2028
  return `\u56FE\u7247\u5206\u6790\u5931\u8D25: ${error}`;
1291
2029
  }
1292
2030
  }
1293
- async function generateImage(geminiClient, workingDir, prompt, outputPath) {
2031
+ async function generateImage(geminiClient, cwd, prompt, outputPath, signal) {
1294
2032
  try {
2033
+ if (signal?.aborted) {
2034
+ return JSON.stringify({ success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88" });
2035
+ }
1295
2036
  const response = await geminiClient.models.generateContent({
1296
2037
  model: GEMINI_IMAGE_GEN_MODEL,
1297
- contents: [{ role: "user", parts: [{ text: prompt }] }]
2038
+ contents: [{ role: "user", parts: [{ text: prompt }] }],
2039
+ config: { abortSignal: signal }
1298
2040
  });
1299
2041
  for (const part of response.candidates?.[0]?.content?.parts || []) {
1300
2042
  if (part.inlineData) {
1301
2043
  const imageData = Buffer.from(part.inlineData.data, "base64");
1302
2044
  const mimeType = part.inlineData.mimeType || "image/png";
1303
2045
  const ext = mimeType === "image/jpeg" ? ".jpg" : mimeType === "image/webp" ? ".webp" : ".png";
1304
- if (outputPath) {
1305
- const fs = await import('fs/promises');
1306
- await fs.writeFile(outputPath, imageData);
1307
- return `\u56FE\u7247\u5DF2\u751F\u6210\u5E76\u4FDD\u5B58\u5230: ${outputPath}`;
1308
- } else {
1309
- const savePath = `${workingDir}/generated_${Date.now()}${ext}`;
1310
- const fs = await import('fs/promises');
1311
- await fs.writeFile(savePath, imageData);
1312
- return `\u56FE\u7247\u5DF2\u751F\u6210\u5E76\u4FDD\u5B58\u5230: ${savePath}`;
1313
- }
2046
+ const fs = await import('fs/promises');
2047
+ const savePath = outputPath || `${cwd}/generated_${Date.now()}${ext}`;
2048
+ await fs.writeFile(savePath, imageData);
2049
+ return JSON.stringify({
2050
+ success: true,
2051
+ path: savePath,
2052
+ format: mimeType
2053
+ });
1314
2054
  }
1315
2055
  }
1316
- return "\u56FE\u7247\u751F\u6210\u5931\u8D25";
2056
+ return JSON.stringify({ success: false, error: "\u56FE\u7247\u751F\u6210\u5931\u8D25" });
1317
2057
  } catch (error) {
1318
- return `\u56FE\u7247\u751F\u6210\u5931\u8D25: ${error}`;
2058
+ if (signal?.aborted) {
2059
+ return JSON.stringify({ success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88", cancelled: true });
2060
+ }
2061
+ return JSON.stringify({ success: false, error: `\u56FE\u7247\u751F\u6210\u5931\u8D25: ${error}` });
1319
2062
  }
1320
2063
  }
1321
- async function analyzeVideo(geminiClient, videoPath, question) {
2064
+ async function analyzeVideo(geminiClient, videoPath, question, signal) {
1322
2065
  try {
2066
+ if (signal?.aborted) {
2067
+ return JSON.stringify({ success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88", cancelled: true });
2068
+ }
1323
2069
  const fs = await import('fs/promises');
1324
2070
  const path = await import('path');
1325
2071
  const videoData = await fs.readFile(videoPath);
@@ -1334,230 +2080,790 @@ async function analyzeVideo(geminiClient, videoPath, question) {
1334
2080
  { text: question || "\u8BF7\u8BE6\u7EC6\u63CF\u8FF0\u8FD9\u4E2A\u89C6\u9891\u7684\u5185\u5BB9" },
1335
2081
  { inlineData: { mimeType, data: base64 } }
1336
2082
  ]
1337
- }]
2083
+ }],
2084
+ config: { abortSignal: signal }
1338
2085
  });
1339
2086
  return response.text || "\u65E0\u6CD5\u5206\u6790\u89C6\u9891";
1340
2087
  } catch (error) {
2088
+ if (signal?.aborted) return JSON.stringify({ success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88", cancelled: true });
1341
2089
  return `\u89C6\u9891\u5206\u6790\u5931\u8D25: ${error}`;
1342
2090
  }
1343
2091
  }
1344
2092
 
1345
- // src/agent.ts
1346
- var HybridAgent = class {
1347
- config;
1348
- providers = /* @__PURE__ */ new Map();
1349
- geminiClient;
1350
- toolExecutor;
1351
- conversationHistory = [];
1352
- abortController = null;
1353
- constructor(config, toolExecutor) {
1354
- this.config = {
1355
- arkApiKey: config.arkApiKey,
1356
- arkApiUrl: config.arkApiUrl || DEFAULT_ARK_URL,
1357
- qwenApiKey: config.qwenApiKey || "",
1358
- qwenApiUrl: config.qwenApiUrl || DEFAULT_QWEN_NATIVE_URL,
1359
- openrouterApiKey: config.openrouterApiKey || "",
1360
- openrouterApiUrl: config.openrouterApiUrl || DEFAULT_OPENROUTER_URL,
1361
- geminiApiKey: config.geminiApiKey,
1362
- workingDir: config.workingDir || process.cwd()
1363
- };
1364
- this.geminiClient = new genai.GoogleGenAI({ apiKey: this.config.geminiApiKey });
1365
- this.toolExecutor = toolExecutor || createDefaultToolExecutor(this.config.workingDir);
1366
- this.initializeProviders();
1367
- }
1368
- /**
1369
- * 初始化所有 Provider
1370
- */
1371
- initializeProviders() {
1372
- if (this.config.arkApiKey) {
1373
- this.providers.set("ark", new ArkProvider({
1374
- apiKey: this.config.arkApiKey,
1375
- apiUrl: this.config.arkApiUrl
1376
- }));
1377
- }
1378
- if (this.config.qwenApiKey) {
1379
- this.providers.set("qwen", new QwenProvider({
1380
- apiKey: this.config.qwenApiKey,
1381
- apiUrl: this.config.qwenApiUrl
1382
- }));
2093
+ // src/builtin-tools/analyze-image.ts
2094
+ function analyzeImageTool() {
2095
+ return tool({
2096
+ name: "analyze_image",
2097
+ description: "\u5206\u6790\u56FE\u7247\u5185\u5BB9\uFF08\u8BC6\u522B\u7269\u4F53\u3001\u573A\u666F\u3001\u6587\u5B57\u7B49\uFF09",
2098
+ parameters: {
2099
+ type: "object",
2100
+ properties: {
2101
+ imagePath: { type: "string", description: "\u56FE\u7247\u6587\u4EF6\u8DEF\u5F84" },
2102
+ question: { type: "string", description: "\u5173\u4E8E\u56FE\u7247\u7684\u95EE\u9898\uFF08\u53EF\u9009\uFF09" }
2103
+ },
2104
+ required: ["imagePath"]
2105
+ },
2106
+ execute: async (args, context) => {
2107
+ if (!context.geminiClient) {
2108
+ return "\u9519\u8BEF: \u672A\u914D\u7F6E Gemini API Key\uFF0C\u65E0\u6CD5\u4F7F\u7528\u56FE\u7247\u5206\u6790\u529F\u80FD";
2109
+ }
2110
+ return await analyzeImage(
2111
+ context.geminiClient,
2112
+ args.imagePath,
2113
+ args.question,
2114
+ context.signal
2115
+ );
1383
2116
  }
1384
- if (this.config.geminiApiKey) {
1385
- this.providers.set("gemini", new GeminiProvider({
1386
- apiKey: this.config.geminiApiKey
1387
- }));
2117
+ });
2118
+ }
2119
+
2120
+ // src/builtin-tools/generate-image.ts
2121
+ function generateImageTool() {
2122
+ return tool({
2123
+ name: "generate_image",
2124
+ description: "\u6839\u636E\u6587\u5B57\u63CF\u8FF0\u751F\u6210\u56FE\u7247\u5E76\u4FDD\u5B58\u5230\u672C\u5730\u3002\u8FD4\u56DE\u4FDD\u5B58\u8DEF\u5F84\u3002",
2125
+ parameters: {
2126
+ type: "object",
2127
+ properties: {
2128
+ prompt: { type: "string", description: "\u56FE\u7247\u63CF\u8FF0\uFF0C\u4F7F\u7528\u8BE6\u7EC6\u7684\u82F1\u6587\u63CF\u8FF0\u6548\u679C\u66F4\u597D" },
2129
+ outputPath: { type: "string", description: "\u4FDD\u5B58\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09" }
2130
+ },
2131
+ required: ["prompt"]
2132
+ },
2133
+ // 图片生成完成后通知用户
2134
+ sideEffects: [{ type: "notification", success: true, message: "\u{1F3A8} \u56FE\u7247\u751F\u6210\u5B8C\u6210" }],
2135
+ execute: async (args, context) => {
2136
+ if (!context.geminiClient) {
2137
+ return "\u9519\u8BEF: \u672A\u914D\u7F6E Gemini API Key\uFF0C\u65E0\u6CD5\u4F7F\u7528\u56FE\u7247\u751F\u6210\u529F\u80FD";
2138
+ }
2139
+ return await generateImage(
2140
+ context.geminiClient,
2141
+ context.cwd,
2142
+ args.prompt,
2143
+ args.outputPath,
2144
+ context.signal
2145
+ );
1388
2146
  }
1389
- if (this.config.openrouterApiKey) {
1390
- this.providers.set("openrouter", new OpenRouterProvider({
1391
- apiKey: this.config.openrouterApiKey,
1392
- apiUrl: this.config.openrouterApiUrl
1393
- }));
2147
+ });
2148
+ }
2149
+
2150
+ // src/builtin-tools/analyze-video.ts
2151
+ function analyzeVideoTool() {
2152
+ return tool({
2153
+ name: "analyze_video",
2154
+ description: "\u5206\u6790\u89C6\u9891\u5185\u5BB9\uFF08\u8BC6\u522B\u573A\u666F\u3001\u52A8\u4F5C\u3001\u7269\u4F53\u7B49\uFF09",
2155
+ parameters: {
2156
+ type: "object",
2157
+ properties: {
2158
+ videoPath: { type: "string", description: "\u89C6\u9891\u6587\u4EF6\u8DEF\u5F84" },
2159
+ question: { type: "string", description: "\u5173\u4E8E\u89C6\u9891\u7684\u95EE\u9898\uFF08\u53EF\u9009\uFF09" }
2160
+ },
2161
+ required: ["videoPath"]
2162
+ },
2163
+ execute: async (args, context) => {
2164
+ if (!context.geminiClient) {
2165
+ return "\u9519\u8BEF: \u672A\u914D\u7F6E Gemini API Key\uFF0C\u65E0\u6CD5\u4F7F\u7528\u89C6\u9891\u5206\u6790\u529F\u80FD";
2166
+ }
2167
+ return await analyzeVideo(
2168
+ context.geminiClient,
2169
+ args.videoPath,
2170
+ args.question,
2171
+ context.signal
2172
+ );
1394
2173
  }
2174
+ });
2175
+ }
2176
+
2177
+ // src/builtin-tools/weather/city-adcodes.ts
2178
+ var CITY_ADCODE_MAP = {
2179
+ // 直辖市
2180
+ "\u5317\u4EAC": "110000",
2181
+ "\u5929\u6D25": "120000",
2182
+ "\u4E0A\u6D77": "310000",
2183
+ "\u91CD\u5E86": "500000",
2184
+ // 河北省
2185
+ "\u77F3\u5BB6\u5E84": "130100",
2186
+ "\u5510\u5C71": "130200",
2187
+ "\u79E6\u7687\u5C9B": "130300",
2188
+ "\u90AF\u90F8": "130400",
2189
+ "\u90A2\u53F0": "130500",
2190
+ "\u4FDD\u5B9A": "130600",
2191
+ "\u5F20\u5BB6\u53E3": "130700",
2192
+ "\u627F\u5FB7": "130800",
2193
+ "\u6CA7\u5DDE": "130900",
2194
+ "\u5ECA\u574A": "131000",
2195
+ "\u8861\u6C34": "131100",
2196
+ // 山西省
2197
+ "\u592A\u539F": "140100",
2198
+ "\u5927\u540C": "140200",
2199
+ "\u9633\u6CC9": "140300",
2200
+ "\u957F\u6CBB": "140400",
2201
+ "\u664B\u57CE": "140500",
2202
+ "\u6714\u5DDE": "140600",
2203
+ "\u664B\u4E2D": "140700",
2204
+ "\u8FD0\u57CE": "140800",
2205
+ "\u5FFB\u5DDE": "140900",
2206
+ "\u4E34\u6C7E": "141000",
2207
+ "\u5415\u6881": "141100",
2208
+ // 内蒙古自治区
2209
+ "\u547C\u548C\u6D69\u7279": "150100",
2210
+ "\u5305\u5934": "150200",
2211
+ "\u4E4C\u6D77": "150300",
2212
+ "\u8D64\u5CF0": "150400",
2213
+ "\u901A\u8FBD": "150500",
2214
+ "\u9102\u5C14\u591A\u65AF": "150600",
2215
+ "\u547C\u4F26\u8D1D\u5C14": "150700",
2216
+ "\u5DF4\u5F66\u6DD6\u5C14": "150800",
2217
+ "\u4E4C\u5170\u5BDF\u5E03": "150900",
2218
+ "\u5174\u5B89\u76DF": "152200",
2219
+ "\u9521\u6797\u90ED\u52D2\u76DF": "152500",
2220
+ "\u963F\u62C9\u5584\u76DF": "152900",
2221
+ // 辽宁省
2222
+ "\u6C88\u9633": "210100",
2223
+ "\u5927\u8FDE": "210200",
2224
+ "\u978D\u5C71": "210300",
2225
+ "\u629A\u987A": "210400",
2226
+ "\u672C\u6EAA": "210500",
2227
+ "\u4E39\u4E1C": "210600",
2228
+ "\u9526\u5DDE": "210700",
2229
+ "\u8425\u53E3": "210800",
2230
+ "\u961C\u65B0": "210900",
2231
+ "\u8FBD\u9633": "211000",
2232
+ "\u76D8\u9526": "211100",
2233
+ "\u94C1\u5CAD": "211200",
2234
+ "\u671D\u9633": "211300",
2235
+ "\u846B\u82A6\u5C9B": "211400",
2236
+ // 吉林省
2237
+ "\u957F\u6625": "220100",
2238
+ "\u5409\u6797": "220200",
2239
+ "\u56DB\u5E73": "220300",
2240
+ "\u8FBD\u6E90": "220400",
2241
+ "\u901A\u5316": "220500",
2242
+ "\u767D\u5C71": "220600",
2243
+ "\u677E\u539F": "220700",
2244
+ "\u767D\u57CE": "220800",
2245
+ "\u5EF6\u8FB9\u671D\u9C9C\u65CF\u81EA\u6CBB\u5DDE": "222400",
2246
+ // 黑龙江省
2247
+ "\u54C8\u5C14\u6EE8": "230100",
2248
+ "\u9F50\u9F50\u54C8\u5C14": "230200",
2249
+ "\u9E21\u897F": "230300",
2250
+ "\u9E64\u5C97": "230400",
2251
+ "\u53CC\u9E2D\u5C71": "230500",
2252
+ "\u5927\u5E86": "230600",
2253
+ "\u4F0A\u6625": "230700",
2254
+ "\u4F73\u6728\u65AF": "230800",
2255
+ "\u4E03\u53F0\u6CB3": "230900",
2256
+ "\u7261\u4E39\u6C5F": "231000",
2257
+ "\u9ED1\u6CB3": "231100",
2258
+ "\u7EE5\u5316": "231200",
2259
+ "\u5927\u5174\u5B89\u5CAD\u5730\u533A": "232700",
2260
+ // 江苏省
2261
+ "\u5357\u4EAC": "320100",
2262
+ "\u65E0\u9521": "320200",
2263
+ "\u5F90\u5DDE": "320300",
2264
+ "\u5E38\u5DDE": "320400",
2265
+ "\u82CF\u5DDE": "320500",
2266
+ "\u5357\u901A": "320600",
2267
+ "\u8FDE\u4E91\u6E2F": "320700",
2268
+ "\u6DEE\u5B89": "320800",
2269
+ "\u76D0\u57CE": "320900",
2270
+ "\u626C\u5DDE": "321000",
2271
+ "\u9547\u6C5F": "321100",
2272
+ "\u6CF0\u5DDE": "321200",
2273
+ "\u5BBF\u8FC1": "321300",
2274
+ // 浙江省
2275
+ "\u676D\u5DDE": "330100",
2276
+ "\u5B81\u6CE2": "330200",
2277
+ "\u6E29\u5DDE": "330300",
2278
+ "\u5609\u5174": "330400",
2279
+ "\u6E56\u5DDE": "330500",
2280
+ "\u7ECD\u5174": "330600",
2281
+ "\u91D1\u534E": "330700",
2282
+ "\u8862\u5DDE": "330800",
2283
+ "\u821F\u5C71": "330900",
2284
+ "\u53F0\u5DDE": "331000",
2285
+ "\u4E3D\u6C34": "331100",
2286
+ // 安徽省
2287
+ "\u5408\u80A5": "340100",
2288
+ "\u829C\u6E56": "340200",
2289
+ "\u868C\u57E0": "340300",
2290
+ "\u6DEE\u5357": "340400",
2291
+ "\u9A6C\u978D\u5C71": "340500",
2292
+ "\u6DEE\u5317": "340600",
2293
+ "\u94DC\u9675": "340700",
2294
+ "\u5B89\u5E86": "340800",
2295
+ "\u9EC4\u5C71": "341000",
2296
+ "\u6EC1\u5DDE": "341100",
2297
+ "\u961C\u9633": "341200",
2298
+ "\u5BBF\u5DDE": "341300",
2299
+ "\u516D\u5B89": "341500",
2300
+ "\u4EB3\u5DDE": "341600",
2301
+ "\u6C60\u5DDE": "341700",
2302
+ "\u5BA3\u57CE": "341800",
2303
+ // 福建省
2304
+ "\u798F\u5DDE": "350100",
2305
+ "\u53A6\u95E8": "350200",
2306
+ "\u8386\u7530": "350300",
2307
+ "\u4E09\u660E": "350400",
2308
+ "\u6CC9\u5DDE": "350500",
2309
+ "\u6F33\u5DDE": "350600",
2310
+ "\u5357\u5E73": "350700",
2311
+ "\u9F99\u5CA9": "350800",
2312
+ "\u5B81\u5FB7": "350900",
2313
+ // 江西省
2314
+ "\u5357\u660C": "360100",
2315
+ "\u666F\u5FB7\u9547": "360200",
2316
+ "\u840D\u4E61": "360300",
2317
+ "\u4E5D\u6C5F": "360400",
2318
+ "\u65B0\u4F59": "360500",
2319
+ "\u9E70\u6F6D": "360600",
2320
+ "\u8D63\u5DDE": "360700",
2321
+ "\u5409\u5B89": "360800",
2322
+ "\u5B9C\u6625": "360900",
2323
+ "\u629A\u5DDE": "361000",
2324
+ "\u4E0A\u9976": "361100",
2325
+ // 山东省
2326
+ "\u6D4E\u5357": "370100",
2327
+ "\u9752\u5C9B": "370200",
2328
+ "\u6DC4\u535A": "370300",
2329
+ "\u67A3\u5E84": "370400",
2330
+ "\u4E1C\u8425": "370500",
2331
+ "\u70DF\u53F0": "370600",
2332
+ "\u6F4D\u574A": "370700",
2333
+ "\u6D4E\u5B81": "370800",
2334
+ "\u6CF0\u5B89": "370900",
2335
+ "\u5A01\u6D77": "371000",
2336
+ "\u65E5\u7167": "371100",
2337
+ "\u4E34\u6C82": "371300",
2338
+ "\u5FB7\u5DDE": "371400",
2339
+ "\u804A\u57CE": "371500",
2340
+ "\u6EE8\u5DDE": "371600",
2341
+ "\u83CF\u6CFD": "371700",
2342
+ // 河南省
2343
+ "\u90D1\u5DDE": "410100",
2344
+ "\u5F00\u5C01": "410200",
2345
+ "\u6D1B\u9633": "410300",
2346
+ "\u5E73\u9876\u5C71": "410400",
2347
+ "\u5B89\u9633": "410500",
2348
+ "\u9E64\u58C1": "410600",
2349
+ "\u65B0\u4E61": "410700",
2350
+ "\u7126\u4F5C": "410800",
2351
+ "\u6FEE\u9633": "410900",
2352
+ "\u8BB8\u660C": "411000",
2353
+ "\u6F2F\u6CB3": "411100",
2354
+ "\u4E09\u95E8\u5CE1": "411200",
2355
+ "\u5357\u9633": "411300",
2356
+ "\u5546\u4E18": "411400",
2357
+ "\u4FE1\u9633": "411500",
2358
+ "\u5468\u53E3": "411600",
2359
+ "\u9A7B\u9A6C\u5E97": "411700",
2360
+ // 湖北省
2361
+ "\u6B66\u6C49": "420100",
2362
+ "\u9EC4\u77F3": "420200",
2363
+ "\u5341\u5830": "420300",
2364
+ "\u5B9C\u660C": "420500",
2365
+ "\u8944\u9633": "420600",
2366
+ "\u9102\u5DDE": "420700",
2367
+ "\u8346\u95E8": "420800",
2368
+ "\u5B5D\u611F": "420900",
2369
+ "\u8346\u5DDE": "421000",
2370
+ "\u9EC4\u5188": "421100",
2371
+ "\u54B8\u5B81": "421200",
2372
+ "\u968F\u5DDE": "421300",
2373
+ "\u6069\u65BD\u571F\u5BB6\u65CF\u82D7\u65CF\u81EA\u6CBB\u5DDE": "422800",
2374
+ // 湖南省
2375
+ "\u957F\u6C99": "430100",
2376
+ "\u682A\u6D32": "430200",
2377
+ "\u6E58\u6F6D": "430300",
2378
+ "\u8861\u9633": "430400",
2379
+ "\u90B5\u9633": "430500",
2380
+ "\u5CB3\u9633": "430600",
2381
+ "\u5E38\u5FB7": "430700",
2382
+ "\u5F20\u5BB6\u754C": "430800",
2383
+ "\u76CA\u9633": "430900",
2384
+ "\u90F4\u5DDE": "431000",
2385
+ "\u6C38\u5DDE": "431100",
2386
+ "\u6000\u5316": "431200",
2387
+ "\u5A04\u5E95": "431300",
2388
+ "\u6E58\u897F\u571F\u5BB6\u65CF\u82D7\u65CF\u81EA\u6CBB\u5DDE": "433100",
2389
+ // 广东省
2390
+ "\u5E7F\u5DDE": "440100",
2391
+ "\u97F6\u5173": "440200",
2392
+ "\u6DF1\u5733": "440300",
2393
+ "\u73E0\u6D77": "440400",
2394
+ "\u6C55\u5934": "440500",
2395
+ "\u4F5B\u5C71": "440600",
2396
+ "\u6C5F\u95E8": "440700",
2397
+ "\u6E5B\u6C5F": "440800",
2398
+ "\u8302\u540D": "440900",
2399
+ "\u8087\u5E86": "441200",
2400
+ "\u60E0\u5DDE": "441300",
2401
+ "\u6885\u5DDE": "441400",
2402
+ "\u6C55\u5C3E": "441500",
2403
+ "\u6CB3\u6E90": "441600",
2404
+ "\u9633\u6C5F": "441700",
2405
+ "\u6E05\u8FDC": "441800",
2406
+ "\u4E1C\u839E": "441900",
2407
+ "\u4E2D\u5C71": "442000",
2408
+ "\u6F6E\u5DDE": "445100",
2409
+ "\u63ED\u9633": "445200",
2410
+ "\u4E91\u6D6E": "445300",
2411
+ // 广西壮族自治区
2412
+ "\u5357\u5B81": "450100",
2413
+ "\u67F3\u5DDE": "450200",
2414
+ "\u6842\u6797": "450300",
2415
+ "\u68A7\u5DDE": "450400",
2416
+ "\u5317\u6D77": "450500",
2417
+ "\u9632\u57CE\u6E2F": "450600",
2418
+ "\u94A6\u5DDE": "450700",
2419
+ "\u8D35\u6E2F": "450800",
2420
+ "\u7389\u6797": "450900",
2421
+ "\u767E\u8272": "451000",
2422
+ "\u8D3A\u5DDE": "451100",
2423
+ "\u6CB3\u6C60": "451200",
2424
+ "\u6765\u5BBE": "451300",
2425
+ "\u5D07\u5DE6": "451400",
2426
+ // 海南省
2427
+ "\u6D77\u53E3": "460100",
2428
+ "\u4E09\u4E9A": "460200",
2429
+ "\u4E09\u6C99": "460300",
2430
+ "\u510B\u5DDE": "460400",
2431
+ // 四川省
2432
+ "\u6210\u90FD": "510100",
2433
+ "\u81EA\u8D21": "510300",
2434
+ "\u6500\u679D\u82B1": "510400",
2435
+ "\u6CF8\u5DDE": "510500",
2436
+ "\u5FB7\u9633": "510600",
2437
+ "\u7EF5\u9633": "510700",
2438
+ "\u5E7F\u5143": "510800",
2439
+ "\u9042\u5B81": "510900",
2440
+ "\u5185\u6C5F": "511000",
2441
+ "\u4E50\u5C71": "511100",
2442
+ "\u5357\u5145": "511300",
2443
+ "\u7709\u5C71": "511400",
2444
+ "\u5B9C\u5BBE": "511500",
2445
+ "\u5E7F\u5B89": "511600",
2446
+ "\u8FBE\u5DDE": "511700",
2447
+ "\u96C5\u5B89": "511800",
2448
+ "\u5DF4\u4E2D": "511900",
2449
+ "\u8D44\u9633": "512000",
2450
+ "\u963F\u575D\u85CF\u65CF\u7F8C\u65CF\u81EA\u6CBB\u5DDE": "513200",
2451
+ "\u7518\u5B5C\u85CF\u65CF\u81EA\u6CBB\u5DDE": "513300",
2452
+ "\u51C9\u5C71\u5F5D\u65CF\u81EA\u6CBB\u5DDE": "513400",
2453
+ // 贵州省
2454
+ "\u8D35\u9633": "520100",
2455
+ "\u516D\u76D8\u6C34": "520200",
2456
+ "\u9075\u4E49": "520300",
2457
+ "\u5B89\u987A": "520400",
2458
+ "\u6BD5\u8282": "520500",
2459
+ "\u94DC\u4EC1": "520600",
2460
+ "\u9ED4\u897F\u5357\u5E03\u4F9D\u65CF\u82D7\u65CF\u81EA\u6CBB\u5DDE": "522300",
2461
+ "\u9ED4\u4E1C\u5357\u82D7\u65CF\u4F97\u65CF\u81EA\u6CBB\u5DDE": "522600",
2462
+ "\u9ED4\u5357\u5E03\u4F9D\u65CF\u82D7\u65CF\u81EA\u6CBB\u5DDE": "522700",
2463
+ // 云南省
2464
+ "\u6606\u660E": "530100",
2465
+ "\u66F2\u9756": "530300",
2466
+ "\u7389\u6EAA": "530400",
2467
+ "\u4FDD\u5C71": "530500",
2468
+ "\u662D\u901A": "530600",
2469
+ "\u4E3D\u6C5F": "530700",
2470
+ "\u666E\u6D31": "530800",
2471
+ "\u4E34\u6CA7": "530900",
2472
+ "\u695A\u96C4\u5F5D\u65CF\u81EA\u6CBB\u5DDE": "532300",
2473
+ "\u7EA2\u6CB3\u54C8\u5C3C\u65CF\u5F5D\u65CF\u81EA\u6CBB\u5DDE": "532500",
2474
+ "\u6587\u5C71\u58EE\u65CF\u82D7\u65CF\u81EA\u6CBB\u5DDE": "532600",
2475
+ "\u897F\u53CC\u7248\u7EB3\u50A3\u65CF\u81EA\u6CBB\u5DDE": "532800",
2476
+ "\u5927\u7406\u767D\u65CF\u81EA\u6CBB\u5DDE": "532900",
2477
+ "\u5FB7\u5B8F\u50A3\u65CF\u666F\u9887\u65CF\u81EA\u6CBB\u5DDE": "533100",
2478
+ "\u6012\u6C5F\u5088\u50F3\u65CF\u81EA\u6CBB\u5DDE": "533300",
2479
+ "\u8FEA\u5E86\u85CF\u65CF\u81EA\u6CBB\u5DDE": "533400",
2480
+ // 西藏自治区
2481
+ "\u62C9\u8428": "540100",
2482
+ "\u65E5\u5580\u5219": "540200",
2483
+ "\u660C\u90FD": "540300",
2484
+ "\u6797\u829D": "540400",
2485
+ "\u5C71\u5357": "540500",
2486
+ "\u90A3\u66F2": "540600",
2487
+ "\u963F\u91CC\u5730\u533A": "542500",
2488
+ // 陕西省
2489
+ "\u897F\u5B89": "610100",
2490
+ "\u94DC\u5DDD": "610200",
2491
+ "\u5B9D\u9E21": "610300",
2492
+ "\u54B8\u9633": "610400",
2493
+ "\u6E2D\u5357": "610500",
2494
+ "\u5EF6\u5B89": "610600",
2495
+ "\u6C49\u4E2D": "610700",
2496
+ "\u6986\u6797": "610800",
2497
+ "\u5B89\u5EB7": "610900",
2498
+ "\u5546\u6D1B": "611000",
2499
+ // 甘肃省
2500
+ "\u5170\u5DDE": "620100",
2501
+ "\u5609\u5CEA\u5173": "620200",
2502
+ "\u91D1\u660C": "620300",
2503
+ "\u767D\u94F6": "620400",
2504
+ "\u5929\u6C34": "620500",
2505
+ "\u6B66\u5A01": "620600",
2506
+ "\u5F20\u6396": "620700",
2507
+ "\u5E73\u51C9": "620800",
2508
+ "\u9152\u6CC9": "620900",
2509
+ "\u5E86\u9633": "621000",
2510
+ "\u5B9A\u897F": "621100",
2511
+ "\u9647\u5357": "621200",
2512
+ "\u4E34\u590F\u56DE\u65CF\u81EA\u6CBB\u5DDE": "622900",
2513
+ "\u7518\u5357\u85CF\u65CF\u81EA\u6CBB\u5DDE": "623000",
2514
+ // 青海省
2515
+ "\u897F\u5B81": "630100",
2516
+ "\u6D77\u4E1C": "630200",
2517
+ "\u6D77\u5317\u85CF\u65CF\u81EA\u6CBB\u5DDE": "632200",
2518
+ "\u9EC4\u5357\u85CF\u65CF\u81EA\u6CBB\u5DDE": "632300",
2519
+ "\u6D77\u5357\u85CF\u65CF\u81EA\u6CBB\u5DDE": "632500",
2520
+ "\u679C\u6D1B\u85CF\u65CF\u81EA\u6CBB\u5DDE": "632600",
2521
+ "\u7389\u6811\u85CF\u65CF\u81EA\u6CBB\u5DDE": "632700",
2522
+ "\u6D77\u897F\u8499\u53E4\u65CF\u85CF\u65CF\u81EA\u6CBB\u5DDE": "632800",
2523
+ // 宁夏回族自治区
2524
+ "\u94F6\u5DDD": "640100",
2525
+ "\u77F3\u5634\u5C71": "640200",
2526
+ "\u5434\u5FE0": "640300",
2527
+ "\u56FA\u539F": "640400",
2528
+ "\u4E2D\u536B": "640500",
2529
+ // 新疆维吾尔自治区
2530
+ "\u4E4C\u9C81\u6728\u9F50": "650100",
2531
+ "\u514B\u62C9\u739B\u4F9D": "650200",
2532
+ "\u5410\u9C81\u756A": "650400",
2533
+ "\u54C8\u5BC6": "650500",
2534
+ "\u660C\u5409\u56DE\u65CF\u81EA\u6CBB\u5DDE": "652300",
2535
+ "\u535A\u5C14\u5854\u62C9\u8499\u53E4\u81EA\u6CBB\u5DDE": "652700",
2536
+ "\u5DF4\u97F3\u90ED\u695E\u8499\u53E4\u81EA\u6CBB\u5DDE": "652800",
2537
+ "\u963F\u514B\u82CF\u5730\u533A": "652900",
2538
+ "\u514B\u5B5C\u52D2\u82CF\u67EF\u5C14\u514B\u5B5C\u81EA\u6CBB\u5DDE": "653000",
2539
+ "\u5580\u4EC0\u5730\u533A": "653100",
2540
+ "\u548C\u7530\u5730\u533A": "653200",
2541
+ "\u4F0A\u7281\u54C8\u8428\u514B\u81EA\u6CBB\u5DDE": "654000",
2542
+ "\u5854\u57CE\u5730\u533A": "654200",
2543
+ "\u963F\u52D2\u6CF0\u5730\u533A": "654300"
2544
+ };
2545
+ function getCityAdcode(cityName) {
2546
+ if (CITY_ADCODE_MAP[cityName]) {
2547
+ return CITY_ADCODE_MAP[cityName];
1395
2548
  }
1396
- /**
1397
- * 判断模型提供商
1398
- */
1399
- getModelProvider(model) {
1400
- const lowerModel = model.toLowerCase();
1401
- if (lowerModel.startsWith("qwen")) {
1402
- return "qwen";
1403
- }
1404
- if (lowerModel.startsWith("gemini")) {
1405
- return "gemini";
1406
- }
1407
- if (lowerModel.includes("/")) {
1408
- return "openrouter";
1409
- }
1410
- return "ark";
2549
+ const normalized = cityName.replace(/[市省区]$/, "");
2550
+ if (CITY_ADCODE_MAP[normalized]) {
2551
+ return CITY_ADCODE_MAP[normalized];
1411
2552
  }
1412
- /**
1413
- * 获取 Provider
1414
- */
1415
- getProvider(model) {
1416
- const providerName = this.getModelProvider(model);
1417
- return this.providers.get(providerName);
2553
+ for (const [name, code] of Object.entries(CITY_ADCODE_MAP)) {
2554
+ if (name.startsWith(cityName) || cityName.startsWith(name.slice(0, 2))) {
2555
+ return code;
2556
+ }
1418
2557
  }
1419
- /**
1420
- * 构建系统提示词
1421
- */
1422
- buildSystemPrompt(options) {
1423
- const modePrompt = MODE_PROMPTS[options.mode || "agent"];
1424
- const toolsInfo = options.mode === "ask" ? "" : `
1425
- \u53EF\u7528\u5DE5\u5177\uFF1A
1426
- 1. execute_command - \u6267\u884C Shell \u547D\u4EE4
1427
- 2. analyze_image - \u5206\u6790\u56FE\u7247\u5185\u5BB9
1428
- 3. generate_image - \u751F\u6210\u56FE\u7247
1429
- 4. analyze_video - \u5206\u6790\u89C6\u9891\u5185\u5BB9`;
1430
- return `${modePrompt}
2558
+ return null;
2559
+ }
1431
2560
 
1432
- \u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF1A${this.config.workingDir}
1433
- ${toolsInfo}`;
1434
- }
1435
- /**
1436
- * 获取默认深度思考模式(统一为 disabled)
1437
- */
1438
- getDefaultThinkingMode() {
1439
- return "disabled";
1440
- }
1441
- /**
1442
- * 执行工具
1443
- */
1444
- async executeTool(name, args) {
1445
- switch (name) {
1446
- case "execute_command": {
1447
- const result = await this.toolExecutor.executeCommand(
1448
- args.command,
1449
- args.workingDir
1450
- );
1451
- return result.success ? result.output || "\u6267\u884C\u6210\u529F" : `\u9519\u8BEF: ${result.error}`;
2561
+ // src/builtin-tools/weather/get-weather.ts
2562
+ var AMAP_KEY = "16924e39cfd78c645f0035c1bc290961";
2563
+ function getWeatherTool() {
2564
+ return tool({
2565
+ name: "get_weather",
2566
+ description: "\u83B7\u53D6\u6307\u5B9A\u57CE\u5E02\u7684\u5B9E\u65F6\u5929\u6C14\u4FE1\u606F\u3002\u652F\u6301\u5168\u56FD 337 \u4E2A\u5730\u7EA7\u5E02\uFF0C\u5305\u62EC\uFF1A\u5317\u4EAC\u3001\u4E0A\u6D77\u3001\u5E7F\u5DDE\u3001\u6DF1\u5733\u3001\u676D\u5DDE\u3001\u6210\u90FD\u3001\u6B66\u6C49\u3001\u897F\u5B89\u3001\u5357\u4EAC\u3001\u82CF\u5DDE\u3001\u91CD\u5E86\u3001\u5929\u6D25\u7B49\u3002\u8FD4\u56DE\u6E29\u5EA6\u3001\u5929\u6C14\u72B6\u51B5\u3001\u6E7F\u5EA6\u3001\u98CE\u5411\u98CE\u529B\u7B49\u4FE1\u606F\u3002",
2567
+ parameters: {
2568
+ type: "object",
2569
+ properties: {
2570
+ city: {
2571
+ type: "string",
2572
+ description: '\u57CE\u5E02\u540D\u79F0\uFF0C\u5982 "\u5317\u4EAC"\u3001"\u4E0A\u6D77"\u3001"\u6DF1\u5733"\u3001"\u676D\u5DDE"'
2573
+ }
2574
+ },
2575
+ required: ["city"]
2576
+ },
2577
+ execute: async (args, context) => {
2578
+ const city = args.city;
2579
+ if (context.signal?.aborted) {
2580
+ return JSON.stringify({ success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88", cancelled: true });
2581
+ }
2582
+ const adcode = getCityAdcode(city);
2583
+ if (!adcode) {
2584
+ return JSON.stringify({
2585
+ error: `\u4E0D\u652F\u6301\u7684\u57CE\u5E02: ${city}`,
2586
+ tip: "\u8BF7\u8F93\u5165\u5730\u7EA7\u5E02\u540D\u79F0\uFF0C\u5982\uFF1A\u5317\u4EAC\u3001\u4E0A\u6D77\u3001\u5E7F\u5DDE\u3001\u6DF1\u5733"
2587
+ });
2588
+ }
2589
+ try {
2590
+ const url = `https://restapi.amap.com/v3/weather/weatherInfo?key=${AMAP_KEY}&city=${adcode}&extensions=base&output=JSON`;
2591
+ const response = await fetch(url, { signal: context.signal });
2592
+ if (!response.ok) {
2593
+ return JSON.stringify({ error: `\u5929\u6C14\u670D\u52A1\u8BF7\u6C42\u5931\u8D25: ${response.status}` });
2594
+ }
2595
+ const data = await response.json();
2596
+ if (data.status !== "1" || data.infocode !== "10000") {
2597
+ return JSON.stringify({ error: `\u5929\u6C14\u670D\u52A1\u8FD4\u56DE\u9519\u8BEF: ${data.info}` });
2598
+ }
2599
+ if (!data.lives?.length) {
2600
+ return JSON.stringify({ error: "\u672A\u83B7\u53D6\u5230\u5929\u6C14\u6570\u636E" });
2601
+ }
2602
+ const live = data.lives[0];
2603
+ return JSON.stringify({
2604
+ city: live.city,
2605
+ temperature: parseInt(live.temperature, 10),
2606
+ condition: live.weather,
2607
+ humidity: parseInt(live.humidity, 10),
2608
+ wind: `${live.winddirection}\u98CE ${live.windpower}\u7EA7`,
2609
+ reportTime: live.reporttime,
2610
+ province: live.province
2611
+ });
2612
+ } catch (error) {
2613
+ if (context.signal?.aborted) {
2614
+ return JSON.stringify({ success: false, error: "\u64CD\u4F5C\u5DF2\u53D6\u6D88", cancelled: true });
2615
+ }
2616
+ return JSON.stringify({
2617
+ success: false,
2618
+ error: `\u83B7\u53D6\u5929\u6C14\u5931\u8D25: ${error instanceof Error ? error.message : "\u7F51\u7EDC\u9519\u8BEF"}`
2619
+ });
1452
2620
  }
1453
- case "analyze_image":
1454
- return await analyzeImage(
1455
- this.geminiClient,
1456
- args.imagePath,
1457
- args.question
1458
- );
1459
- case "generate_image":
1460
- return await generateImage(
1461
- this.geminiClient,
1462
- this.config.workingDir,
1463
- args.prompt,
1464
- args.outputPath
1465
- );
1466
- case "analyze_video":
1467
- return await analyzeVideo(
1468
- this.geminiClient,
1469
- args.videoPath,
1470
- args.question
1471
- );
1472
- default:
1473
- return `\u672A\u77E5\u5DE5\u5177: ${name}`;
1474
2621
  }
1475
- }
1476
- /**
1477
- * 聊天入口
1478
- *
1479
- * 使用 Provider 模式路由请求到对应的模型提供商
1480
- */
1481
- async *chat(message, options = {}, images) {
1482
- this.abortController = new AbortController();
1483
- const signal = this.abortController.signal;
1484
- const model = options.model || DEFAULT_MODEL;
1485
- const provider = this.getProvider(model);
1486
- if (!provider) {
1487
- const providerName = this.getModelProvider(model);
1488
- yield { type: "error", data: `\u7F3A\u5C11 ${providerName} Provider \u7684 API Key` };
1489
- return;
2622
+ });
2623
+ }
2624
+
2625
+ // src/builtin-tools/ui-actions.ts
2626
+ function showToastTool() {
2627
+ return tool({
2628
+ name: "show_toast",
2629
+ description: "\u5728\u7528\u6237\u754C\u9762\u4E0A\u663E\u793A\u4E00\u6761Toast \u901A\u77E5\u6D88\u606F\uFF0C\u7528\u4E8E\u544A\u77E5\u7528\u6237\u64CD\u4F5C\u7ED3\u679C\u6216\u91CD\u8981\u63D0\u793A\u4FE1\u606F\uFF0C\u6D88\u606F\u4F1A\u5728\u51E0\u79D2\u540E\u81EA\u52A8\u6D88\u5931",
2630
+ parameters: {
2631
+ type: "object",
2632
+ properties: {
2633
+ message: { type: "string", description: "\u8981\u663E\u793A\u7ED9\u7528\u6237\u7684\u901A\u77E5\u5185\u5BB9" }
2634
+ },
2635
+ required: ["message"]
2636
+ },
2637
+ execute: async (args) => {
2638
+ const message = args.message;
2639
+ return {
2640
+ result: JSON.stringify({ success: true }),
2641
+ sideEffects: [{ type: "notification", success: true, message }]
2642
+ };
1490
2643
  }
1491
- const thinkingMode = options.thinkingMode ?? this.getDefaultThinkingMode();
1492
- const optionsWithThinking = { ...options, thinkingMode };
1493
- const systemPrompt = this.buildSystemPrompt(optionsWithThinking);
1494
- const context = {
1495
- history: this.conversationHistory,
1496
- systemPrompt,
1497
- workingDir: this.config.workingDir,
1498
- executeTool: this.executeTool.bind(this),
1499
- signal
1500
- };
1501
- try {
1502
- yield* provider.chat(message, model, optionsWithThinking, context);
1503
- } finally {
1504
- this.abortController = null;
2644
+ });
2645
+ }
2646
+ function openConfirmDialogTool() {
2647
+ return tool({
2648
+ name: "open_confirm_dialog",
2649
+ description: "\u5728\u7528\u6237\u754C\u9762\u4E0A\u5F39\u51FA\u4E00\u4E2A\u6A21\u6001\u786E\u8BA4\u5BF9\u8BDD\u6846\u3002",
2650
+ parameters: {
2651
+ type: "object",
2652
+ properties: {
2653
+ title: { type: "string", description: "\u5BF9\u8BDD\u6846\u6807\u9898\uFF0C\u7B80\u8981\u8BF4\u660E\u786E\u8BA4\u7684\u64CD\u4F5C" },
2654
+ message: { type: "string", description: "\u8BE6\u7EC6\u63CF\u8FF0\u9700\u8981\u7528\u6237\u786E\u8BA4\u7684\u5185\u5BB9" },
2655
+ confirmText: { type: "string", description: '\u786E\u8BA4\u6309\u94AE\u6587\u5B57\uFF0C\u9ED8\u8BA4\u4E3A"\u786E\u8BA4"' },
2656
+ cancelText: { type: "string", description: '\u53D6\u6D88\u6309\u94AE\u6587\u5B57\uFF0C\u9ED8\u8BA4\u4E3A"\u53D6\u6D88"' }
2657
+ },
2658
+ required: ["title", "message"]
2659
+ },
2660
+ execute: async (args) => {
2661
+ return {
2662
+ result: JSON.stringify({ success: true }),
2663
+ sideEffects: [{
2664
+ type: "ui:confirm_dialog",
2665
+ success: true,
2666
+ data: {
2667
+ title: args.title,
2668
+ message: args.message,
2669
+ confirmText: args.confirmText || "\u786E\u8BA4",
2670
+ cancelText: args.cancelText || "\u53D6\u6D88"
2671
+ }
2672
+ }]
2673
+ };
1505
2674
  }
1506
- }
1507
- /**
1508
- * 中断当前请求
1509
- */
1510
- abort() {
1511
- if (this.abortController) {
1512
- this.abortController.abort();
2675
+ });
2676
+ }
2677
+ function openFilePreviewTool() {
2678
+ return tool({
2679
+ name: "open_file_preview",
2680
+ description: "\u5728\u7528\u6237\u754C\u9762\u4E0A\u6253\u5F00\u6587\u4EF6\u9884\u89C8\u7A97\u53E3\uFF0C\u8BA9\u7528\u6237\u67E5\u770B\u6307\u5B9A\u6587\u4EF6\u7684\u5185\u5BB9\uFF0C\u652F\u6301\u6587\u672C\u3001\u56FE\u7247\u7B49\u5E38\u89C1\u683C\u5F0F",
2681
+ parameters: {
2682
+ type: "object",
2683
+ properties: {
2684
+ path: { type: "string", description: "\u8981\u9884\u89C8\u7684\u6587\u4EF6\u7684\u5B8C\u6574\u8DEF\u5F84" }
2685
+ },
2686
+ required: ["path"]
2687
+ },
2688
+ execute: async (args) => {
2689
+ return {
2690
+ result: JSON.stringify({ success: true, path: args.path }),
2691
+ sideEffects: [{
2692
+ type: "ui:file_preview",
2693
+ success: true,
2694
+ data: { path: args.path }
2695
+ }]
2696
+ };
1513
2697
  }
2698
+ });
2699
+ }
2700
+ function navigateToDirectoryTool() {
2701
+ return tool({
2702
+ name: "navigate_to_directory",
2703
+ description: "\u63A7\u5236\u7528\u6237\u754C\u9762\u5DE6\u4FA7\u7684\u6587\u4EF6\u6D4F\u89C8\u5668\u8DF3\u8F6C\u5230\u6307\u5B9A\u76EE\u5F55\uFF0C\u8BA9\u7528\u6237\u53EF\u4EE5\u76F4\u63A5\u770B\u5230\u8BE5\u76EE\u5F55\u4E0B\u7684\u6587\u4EF6\u5217\u8868",
2704
+ parameters: {
2705
+ type: "object",
2706
+ properties: {
2707
+ path: { type: "string", description: "\u8981\u8DF3\u8F6C\u5230\u7684\u76EE\u5F55\u7684\u5B8C\u6574\u8DEF\u5F84" }
2708
+ },
2709
+ required: ["path"]
2710
+ },
2711
+ execute: async (args) => {
2712
+ return {
2713
+ result: JSON.stringify({ success: true, path: args.path }),
2714
+ sideEffects: [{
2715
+ type: "ui:navigate",
2716
+ success: true,
2717
+ data: { path: args.path }
2718
+ }]
2719
+ };
2720
+ }
2721
+ });
2722
+ }
2723
+ var uiActionTools = [
2724
+ showToastTool(),
2725
+ openConfirmDialogTool(),
2726
+ openFilePreviewTool(),
2727
+ navigateToDirectoryTool()
2728
+ ];
2729
+
2730
+ // src/builtin-tools/search-documents.ts
2731
+ var aiSearchModule = null;
2732
+ async function loadAiSearch() {
2733
+ if (aiSearchModule) return aiSearchModule;
2734
+ try {
2735
+ const dynamicRequire = new Function(
2736
+ "moduleName",
2737
+ "return import(moduleName)"
2738
+ );
2739
+ const mod = await dynamicRequire("@huyooo/ai-search");
2740
+ aiSearchModule = mod;
2741
+ return aiSearchModule;
2742
+ } catch {
2743
+ return null;
1514
2744
  }
1515
- /**
1516
- * 清空对话历史
1517
- */
1518
- clearHistory() {
1519
- this.conversationHistory = [];
1520
- }
1521
- /**
1522
- * 获取对话历史
1523
- */
1524
- getHistory() {
1525
- return [...this.conversationHistory];
1526
- }
1527
- /**
1528
- * 设置工作目录
1529
- */
1530
- setWorkingDir(dir) {
1531
- this.config.workingDir = dir;
1532
- }
1533
- /**
1534
- * 获取当前配置
1535
- */
1536
- getConfig() {
1537
- return { ...this.config };
1538
- }
1539
- /**
1540
- * 获取模型能力
1541
- */
1542
- getModelCapabilities(model) {
1543
- const provider = this.getProvider(model);
1544
- return provider?.getModelCapabilities(model);
2745
+ }
2746
+ async function createDocumentSearchTools(options) {
2747
+ const mod = await loadAiSearch();
2748
+ if (!mod) {
2749
+ return [createPlaceholderTool()];
1545
2750
  }
1546
- /**
1547
- * 获取所有支持的模型
1548
- */
1549
- getSupportedModels() {
1550
- return AVAILABLE_MODELS;
2751
+ const dataDir = options?.dataDir || "./.search-data";
2752
+ const plugin = await mod.searchPlugin({
2753
+ dataDir,
2754
+ // 如果配置了默认目录且自动索引,使用第一个目录作为工作空间
2755
+ workspace: options?.autoIndex && options?.defaultDirectories?.length ? expandPath(options.defaultDirectories[0]) : void 0
2756
+ });
2757
+ return plugin.tools;
2758
+ }
2759
+ function createPlaceholderTool() {
2760
+ return {
2761
+ name: "search_local_documents",
2762
+ description: "\u641C\u7D22\u672C\u5730\u6587\u6863\uFF08\u9700\u8981\u5B89\u88C5 @huyooo/ai-search\uFF09",
2763
+ parameters: {
2764
+ type: "object",
2765
+ properties: {
2766
+ query: {
2767
+ type: "string",
2768
+ description: "\u641C\u7D22\u5185\u5BB9"
2769
+ }
2770
+ },
2771
+ required: ["query"]
2772
+ },
2773
+ execute: async () => {
2774
+ return JSON.stringify({
2775
+ error: "\u6587\u6863\u641C\u7D22\u529F\u80FD\u672A\u542F\u7528",
2776
+ solution: "\u8BF7\u5B89\u88C5 @huyooo/ai-search: npm install @huyooo/ai-search"
2777
+ });
2778
+ }
2779
+ };
2780
+ }
2781
+ function expandPath(p) {
2782
+ if (p.startsWith("~")) {
2783
+ return p.replace("~", process.env.HOME || "");
1551
2784
  }
1552
- };
2785
+ return p;
2786
+ }
2787
+ async function getDocumentSearchInstance(dataDir = "./.search-data", workspace) {
2788
+ const mod = await loadAiSearch();
2789
+ if (!mod) return null;
2790
+ return await mod.searchPlugin({ dataDir, workspace });
2791
+ }
1553
2792
 
1554
- exports.AVAILABLE_MODELS = AVAILABLE_MODELS;
1555
- exports.ArkProvider = ArkProvider;
1556
- exports.GeminiProvider = GeminiProvider;
2793
+ exports.ARK_MODELS = ARK_MODELS;
2794
+ exports.ArkAdapter = ArkAdapter;
2795
+ exports.ChatOrchestrator = ChatOrchestrator;
2796
+ exports.DEFAULT_MODEL = DEFAULT_MODEL;
2797
+ exports.GEMINI_MODELS = GEMINI_MODELS;
2798
+ exports.GeminiAdapter = GeminiAdapter;
1557
2799
  exports.HybridAgent = HybridAgent;
1558
- exports.OpenRouterProvider = OpenRouterProvider;
1559
- exports.QwenProvider = QwenProvider;
2800
+ exports.MODELS = MODELS;
2801
+ exports.OPENROUTER_MODELS = OPENROUTER_MODELS;
2802
+ exports.OpenRouterAdapter = OpenRouterAdapter;
2803
+ exports.QWEN_MODELS = QWEN_MODELS;
2804
+ exports.QwenAdapter = QwenAdapter;
2805
+ exports.analyzeImageTool = analyzeImageTool;
2806
+ exports.analyzeVideoTool = analyzeVideoTool;
2807
+ exports.createAbort = createAbort;
2808
+ exports.createApiError = createApiError;
2809
+ exports.createArkAdapter = createArkAdapter;
1560
2810
  exports.createDefaultToolExecutor = createDefaultToolExecutor;
1561
- exports.createToolDefinitions = createToolDefinitions;
2811
+ exports.createDocumentSearchTools = createDocumentSearchTools;
2812
+ exports.createDone = createDone;
2813
+ exports.createError = createError;
2814
+ exports.createGeminiAdapter = createGeminiAdapter;
2815
+ exports.createImage = createImage;
2816
+ exports.createOpenRouterAdapter = createOpenRouterAdapter;
2817
+ exports.createOrchestrator = createOrchestrator;
2818
+ exports.createParseError = createParseError;
2819
+ exports.createQwenAdapter = createQwenAdapter;
2820
+ exports.createRateLimitError = createRateLimitError;
2821
+ exports.createSearchEnd = createSearchEnd;
2822
+ exports.createSearchResult = createSearchResult;
2823
+ exports.createSearchStart = createSearchStart;
2824
+ exports.createStepEnd = createStepEnd;
2825
+ exports.createStepStart = createStepStart;
2826
+ exports.createStreamStart = createStreamStart;
2827
+ exports.createTextDelta = createTextDelta;
2828
+ exports.createThinkingDelta = createThinkingDelta;
2829
+ exports.createThinkingEnd = createThinkingEnd;
2830
+ exports.createThinkingStart = createThinkingStart;
2831
+ exports.createTimeoutError = createTimeoutError;
2832
+ exports.createToolCallResult = createToolCallResult;
2833
+ exports.createToolCallStart = createToolCallStart;
2834
+ exports.createToolError = createToolError;
2835
+ exports.createVideo = createVideo;
2836
+ exports.executeCommandTool = executeCommandTool;
2837
+ exports.generateImageTool = generateImageTool;
2838
+ exports.getCwdTool = getCwdTool;
2839
+ exports.getDefaultProvider = getDefaultProvider;
2840
+ exports.getDocumentSearchInstance = getDocumentSearchInstance;
2841
+ exports.getModelByModelId = getModelByModelId;
2842
+ exports.getNativeModels = getNativeModels;
2843
+ exports.getOpenRouterModels = getOpenRouterModels;
2844
+ exports.getPlatformTool = getPlatformTool;
2845
+ exports.getRouteRules = getRouteRules;
2846
+ exports.getWeatherTool = getWeatherTool;
2847
+ exports.isAbortEvent = isAbortEvent;
2848
+ exports.isErrorEvent = isErrorEvent;
2849
+ exports.isMediaEvent = isMediaEvent;
2850
+ exports.isModelForProvider = isModelForProvider;
2851
+ exports.isRetryableError = isRetryableError;
2852
+ exports.isSearchEvent = isSearchEvent;
2853
+ exports.isStatusEvent = isStatusEvent;
2854
+ exports.isStepEvent = isStepEvent;
2855
+ exports.isTextEvent = isTextEvent;
2856
+ exports.isThinkingEvent = isThinkingEvent;
2857
+ exports.isToolEvent = isToolEvent;
2858
+ exports.navigateToDirectoryTool = navigateToDirectoryTool;
2859
+ exports.openConfirmDialogTool = openConfirmDialogTool;
2860
+ exports.openFilePreviewTool = openFilePreviewTool;
2861
+ exports.resolveTools = resolveTools;
2862
+ exports.routeModelToProvider = routeModelToProvider;
2863
+ exports.routeModelWithDetails = routeModelWithDetails;
2864
+ exports.showToastTool = showToastTool;
2865
+ exports.tool = tool;
2866
+ exports.tools = tools;
2867
+ exports.uiActionTools = uiActionTools;
1562
2868
  //# sourceMappingURL=index.cjs.map
1563
2869
  //# sourceMappingURL=index.cjs.map