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