@ddlqhd/agent-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +53 -0
  2. package/dist/chunk-5QMA2YBY.cjs +2880 -0
  3. package/dist/chunk-5QMA2YBY.cjs.map +1 -0
  4. package/dist/chunk-5Y56A64C.cjs +5 -0
  5. package/dist/chunk-5Y56A64C.cjs.map +1 -0
  6. package/dist/chunk-A3S3AGE3.js +3 -0
  7. package/dist/chunk-A3S3AGE3.js.map +1 -0
  8. package/dist/chunk-CNSGZVRN.cjs +152 -0
  9. package/dist/chunk-CNSGZVRN.cjs.map +1 -0
  10. package/dist/chunk-JF5AJQMU.cjs +2788 -0
  11. package/dist/chunk-JF5AJQMU.cjs.map +1 -0
  12. package/dist/chunk-NDSL7NPN.js +807 -0
  13. package/dist/chunk-NDSL7NPN.js.map +1 -0
  14. package/dist/chunk-OHXW2YM6.js +2708 -0
  15. package/dist/chunk-OHXW2YM6.js.map +1 -0
  16. package/dist/chunk-Q3SOMX26.js +2854 -0
  17. package/dist/chunk-Q3SOMX26.js.map +1 -0
  18. package/dist/chunk-WH3APNQ5.js +147 -0
  19. package/dist/chunk-WH3APNQ5.js.map +1 -0
  20. package/dist/chunk-X35MHWXE.cjs +817 -0
  21. package/dist/chunk-X35MHWXE.cjs.map +1 -0
  22. package/dist/cli/index.cjs +926 -0
  23. package/dist/cli/index.cjs.map +1 -0
  24. package/dist/cli/index.d.cts +24 -0
  25. package/dist/cli/index.d.ts +24 -0
  26. package/dist/cli/index.js +916 -0
  27. package/dist/cli/index.js.map +1 -0
  28. package/dist/index-DPsZ1zat.d.ts +447 -0
  29. package/dist/index-RTPmFjMp.d.cts +447 -0
  30. package/dist/index.cjs +508 -0
  31. package/dist/index.cjs.map +1 -0
  32. package/dist/index.d.cts +664 -0
  33. package/dist/index.d.ts +664 -0
  34. package/dist/index.js +204 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/models/index.cjs +62 -0
  37. package/dist/models/index.cjs.map +1 -0
  38. package/dist/models/index.d.cts +165 -0
  39. package/dist/models/index.d.ts +165 -0
  40. package/dist/models/index.js +5 -0
  41. package/dist/models/index.js.map +1 -0
  42. package/dist/tools/index.cjs +207 -0
  43. package/dist/tools/index.cjs.map +1 -0
  44. package/dist/tools/index.d.cts +108 -0
  45. package/dist/tools/index.d.ts +108 -0
  46. package/dist/tools/index.js +6 -0
  47. package/dist/tools/index.js.map +1 -0
  48. package/dist/types-C0aX_Qdp.d.cts +917 -0
  49. package/dist/types-C0aX_Qdp.d.ts +917 -0
  50. package/package.json +80 -0
@@ -0,0 +1,2854 @@
1
+ #!/usr/bin/env node
2
+ import { ToolRegistry, createAgentTool, HookManager, getAllBuiltinTools, getEnvironmentInfo, formatEnvironmentSection } from './chunk-OHXW2YM6.js';
3
+ import { promises, existsSync, readFileSync } from 'fs';
4
+ import { join, resolve } from 'path';
5
+ import { randomUUID } from 'crypto';
6
+ import { homedir } from 'os';
7
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
8
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
9
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
10
+ import { z } from 'zod';
11
+ import { exec } from 'child_process';
12
+ import { promisify } from 'util';
13
+
14
+ var JsonlStorage = class {
15
+ basePath;
16
+ constructor(config = {}) {
17
+ this.basePath = config.basePath || "./sessions";
18
+ }
19
+ /**
20
+ * 获取会话文件路径
21
+ */
22
+ getFilePath(sessionId) {
23
+ const safeId = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
24
+ return join(this.basePath, `${safeId}.jsonl`);
25
+ }
26
+ /**
27
+ * 获取元数据文件路径
28
+ */
29
+ getMetaFilePath(sessionId) {
30
+ const safeId = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
31
+ return join(this.basePath, `${safeId}.meta.json`);
32
+ }
33
+ /**
34
+ * 确保目录存在
35
+ */
36
+ async ensureDir() {
37
+ await promises.mkdir(this.basePath, { recursive: true });
38
+ }
39
+ /**
40
+ * 保存消息(追加模式)
41
+ */
42
+ async save(sessionId, messages) {
43
+ await this.ensureDir();
44
+ const filePath = this.getFilePath(sessionId);
45
+ const metaPath = this.getMetaFilePath(sessionId);
46
+ let existingCount = 0;
47
+ try {
48
+ const content = await promises.readFile(filePath, "utf-8");
49
+ existingCount = content.split("\n").filter(Boolean).length;
50
+ } catch {
51
+ }
52
+ const newMessages = messages.slice(existingCount);
53
+ if (newMessages.length > 0) {
54
+ const lines = newMessages.map((msg) => {
55
+ const record = {
56
+ ...msg,
57
+ timestamp: msg.timestamp || Date.now()
58
+ };
59
+ return JSON.stringify(record);
60
+ }).join("\n") + "\n";
61
+ await promises.appendFile(filePath, lines, "utf-8");
62
+ }
63
+ const meta = {
64
+ id: sessionId,
65
+ createdAt: Date.now(),
66
+ updatedAt: Date.now(),
67
+ messageCount: messages.length
68
+ };
69
+ try {
70
+ const existingMeta = JSON.parse(await promises.readFile(metaPath, "utf-8"));
71
+ meta.createdAt = existingMeta.createdAt;
72
+ } catch {
73
+ }
74
+ await promises.writeFile(metaPath, JSON.stringify(meta, null, 2), "utf-8");
75
+ }
76
+ /**
77
+ * 加载消息
78
+ */
79
+ async load(sessionId) {
80
+ const filePath = this.getFilePath(sessionId);
81
+ try {
82
+ const content = await promises.readFile(filePath, "utf-8");
83
+ const lines = content.split("\n").filter(Boolean);
84
+ return lines.map((line) => {
85
+ const parsed = JSON.parse(line);
86
+ const { timestamp, ...message } = parsed;
87
+ return message;
88
+ });
89
+ } catch (error) {
90
+ if (error.code === "ENOENT") {
91
+ return [];
92
+ }
93
+ throw error;
94
+ }
95
+ }
96
+ /**
97
+ * 列出所有会话
98
+ */
99
+ async list() {
100
+ await this.ensureDir();
101
+ try {
102
+ const files = await promises.readdir(this.basePath);
103
+ const metaFiles = files.filter((f) => f.endsWith(".meta.json"));
104
+ const sessions = [];
105
+ for (const metaFile of metaFiles) {
106
+ try {
107
+ const metaPath = join(this.basePath, metaFile);
108
+ const meta = JSON.parse(await promises.readFile(metaPath, "utf-8"));
109
+ sessions.push(meta);
110
+ } catch {
111
+ }
112
+ }
113
+ return sessions.sort((a, b) => b.updatedAt - a.updatedAt);
114
+ } catch {
115
+ return [];
116
+ }
117
+ }
118
+ /**
119
+ * 删除会话
120
+ */
121
+ async delete(sessionId) {
122
+ const filePath = this.getFilePath(sessionId);
123
+ const metaPath = this.getMetaFilePath(sessionId);
124
+ await Promise.all([
125
+ promises.unlink(filePath).catch(() => {
126
+ }),
127
+ promises.unlink(metaPath).catch(() => {
128
+ })
129
+ ]);
130
+ }
131
+ /**
132
+ * 检查会话是否存在
133
+ */
134
+ async exists(sessionId) {
135
+ const filePath = this.getFilePath(sessionId);
136
+ try {
137
+ await promises.access(filePath);
138
+ return true;
139
+ } catch {
140
+ return false;
141
+ }
142
+ }
143
+ /**
144
+ * 清空所有会话
145
+ */
146
+ async clear() {
147
+ await this.ensureDir();
148
+ try {
149
+ const files = await promises.readdir(this.basePath);
150
+ await Promise.all(
151
+ files.map((file) => promises.unlink(join(this.basePath, file)).catch(() => {
152
+ }))
153
+ );
154
+ } catch {
155
+ }
156
+ }
157
+ /**
158
+ * 获取会话统计
159
+ */
160
+ async getStats(sessionId) {
161
+ const filePath = this.getFilePath(sessionId);
162
+ const metaPath = this.getMetaFilePath(sessionId);
163
+ try {
164
+ const [metaContent, fileStat] = await Promise.all([
165
+ promises.readFile(metaPath, "utf-8"),
166
+ promises.stat(filePath)
167
+ ]);
168
+ const meta = JSON.parse(metaContent);
169
+ return {
170
+ messageCount: meta.messageCount,
171
+ createdAt: meta.createdAt,
172
+ updatedAt: meta.updatedAt,
173
+ size: fileStat.size
174
+ };
175
+ } catch {
176
+ return null;
177
+ }
178
+ }
179
+ };
180
+ function createJsonlStorage(config) {
181
+ return new JsonlStorage(config);
182
+ }
183
+
184
+ // src/storage/memory.ts
185
+ var MemoryStorage = class {
186
+ sessions = /* @__PURE__ */ new Map();
187
+ metadata = /* @__PURE__ */ new Map();
188
+ /**
189
+ * 保存消息
190
+ */
191
+ async save(sessionId, messages) {
192
+ this.sessions.set(sessionId, [...messages]);
193
+ const existing = this.metadata.get(sessionId);
194
+ const now = Date.now();
195
+ this.metadata.set(sessionId, {
196
+ id: sessionId,
197
+ createdAt: existing?.createdAt || now,
198
+ updatedAt: now,
199
+ messageCount: messages.length
200
+ });
201
+ }
202
+ /**
203
+ * 加载消息
204
+ */
205
+ async load(sessionId) {
206
+ return this.sessions.get(sessionId) || [];
207
+ }
208
+ /**
209
+ * 列出所有会话
210
+ */
211
+ async list() {
212
+ return Array.from(this.metadata.values()).sort((a, b) => b.updatedAt - a.updatedAt);
213
+ }
214
+ /**
215
+ * 删除会话
216
+ */
217
+ async delete(sessionId) {
218
+ this.sessions.delete(sessionId);
219
+ this.metadata.delete(sessionId);
220
+ }
221
+ /**
222
+ * 检查会话是否存在
223
+ */
224
+ async exists(sessionId) {
225
+ return this.sessions.has(sessionId);
226
+ }
227
+ /**
228
+ * 清空所有会话
229
+ */
230
+ async clear() {
231
+ this.sessions.clear();
232
+ this.metadata.clear();
233
+ }
234
+ /**
235
+ * 获取会话数量
236
+ */
237
+ get size() {
238
+ return this.sessions.size;
239
+ }
240
+ /**
241
+ * 导出所有数据
242
+ */
243
+ export() {
244
+ const result = {};
245
+ for (const [key, value] of this.sessions) {
246
+ result[key] = [...value];
247
+ }
248
+ return result;
249
+ }
250
+ /**
251
+ * 导入数据
252
+ */
253
+ import(data) {
254
+ for (const [sessionId, messages] of Object.entries(data)) {
255
+ this.save(sessionId, messages);
256
+ }
257
+ }
258
+ };
259
+ function createMemoryStorage() {
260
+ return new MemoryStorage();
261
+ }
262
+ var SessionManager = class {
263
+ storage;
264
+ currentSessionId = null;
265
+ constructor(config) {
266
+ this.storage = createStorage(config);
267
+ }
268
+ /**
269
+ * 获取当前会话 ID
270
+ */
271
+ get sessionId() {
272
+ return this.currentSessionId;
273
+ }
274
+ /**
275
+ * 创建新会话
276
+ */
277
+ createSession(sessionId) {
278
+ this.currentSessionId = sessionId || randomUUID();
279
+ return this.currentSessionId;
280
+ }
281
+ /**
282
+ * 恢复会话
283
+ */
284
+ async resumeSession(sessionId) {
285
+ const exists = await this.storage.exists(sessionId);
286
+ if (!exists) {
287
+ throw new Error(`Session "${sessionId}" not found`);
288
+ }
289
+ this.currentSessionId = sessionId;
290
+ return this.storage.load(sessionId);
291
+ }
292
+ /**
293
+ * 保存消息到当前会话
294
+ */
295
+ async saveMessages(messages) {
296
+ if (!this.currentSessionId) {
297
+ this.createSession();
298
+ }
299
+ await this.storage.save(this.currentSessionId, messages);
300
+ }
301
+ /**
302
+ * 加载当前会话消息
303
+ */
304
+ async loadMessages() {
305
+ if (!this.currentSessionId) {
306
+ return [];
307
+ }
308
+ return this.storage.load(this.currentSessionId);
309
+ }
310
+ /**
311
+ * 追加消息
312
+ */
313
+ async appendMessage(message) {
314
+ if (!this.currentSessionId) {
315
+ this.createSession();
316
+ }
317
+ const messages = await this.loadMessages();
318
+ messages.push(message);
319
+ await this.saveMessages(messages);
320
+ }
321
+ /**
322
+ * 列出所有会话
323
+ */
324
+ async listSessions() {
325
+ return this.storage.list();
326
+ }
327
+ /**
328
+ * 删除会话
329
+ */
330
+ async deleteSession(sessionId) {
331
+ await this.storage.delete(sessionId);
332
+ if (this.currentSessionId === sessionId) {
333
+ this.currentSessionId = null;
334
+ }
335
+ }
336
+ /**
337
+ * 检查会话是否存在
338
+ */
339
+ async sessionExists(sessionId) {
340
+ return this.storage.exists(sessionId);
341
+ }
342
+ /**
343
+ * 获取会话信息
344
+ */
345
+ async getSessionInfo(sessionId) {
346
+ const sessions = await this.storage.list();
347
+ return sessions.find((s) => s.id === sessionId) || null;
348
+ }
349
+ /**
350
+ * 清空当前会话
351
+ */
352
+ async clearCurrentSession() {
353
+ if (this.currentSessionId) {
354
+ await this.storage.delete(this.currentSessionId);
355
+ this.currentSessionId = null;
356
+ }
357
+ }
358
+ /**
359
+ * 获取底层存储适配器
360
+ */
361
+ getStorage() {
362
+ return this.storage;
363
+ }
364
+ };
365
+ function createSessionManager(config) {
366
+ return new SessionManager(config);
367
+ }
368
+
369
+ // src/storage/interface.ts
370
+ function createStorage(config) {
371
+ switch (config?.type) {
372
+ case "memory":
373
+ return new MemoryStorage();
374
+ case "jsonl":
375
+ default:
376
+ return new JsonlStorage({ basePath: config?.basePath });
377
+ }
378
+ }
379
+ function getSessionStoragePath(userBasePath) {
380
+ return join(userBasePath || homedir(), ".claude", "sessions");
381
+ }
382
+ async function getLatestSessionId(userBasePath) {
383
+ const sm = new SessionManager({
384
+ type: "jsonl",
385
+ basePath: getSessionStoragePath(userBasePath)
386
+ });
387
+ const list = await sm.listSessions();
388
+ return list[0]?.id;
389
+ }
390
+
391
+ // src/core/prompts.ts
392
+ var DEFAULT_SYSTEM_PROMPT = `You are an AI assistant powered by the Agent SDK. You can help users with various tasks by using your built-in tools and capabilities.
393
+
394
+ ## Core Capabilities
395
+
396
+ ### Tools
397
+ You have access to a set of tools that allow you to:
398
+ - **File Operations**: read, write, list, delete files and directories
399
+ - **Code Execution**: run shell commands, Python scripts, Node.js code
400
+ - **Web Access**: make HTTP requests, fetch webpages, download files
401
+ - **Custom Tools**: additional tools registered by the user or skills
402
+
403
+ When to use tools:
404
+ - Use tools when the task requires real-world actions (file I/O, computation, API calls)
405
+ - Prefer reading files before modifying them
406
+ - Use the simplest tool that gets the job done
407
+ - Run multiple independent tool calls in parallel when possible
408
+
409
+ **Prefer dedicated tools over Bash:** Do not use \`Bash\` to do work that has a first-class tool. This keeps actions reviewable and consistent.
410
+ - **Read** for file contents \u2014 not \`cat\`, \`head\`, \`tail\`, or \`sed\` to print files
411
+ - **Write** to create or overwrite files \u2014 not shell redirection or heredocs
412
+ - **Edit** for targeted file changes \u2014 not \`sed\`, \`awk\`, or ad-hoc scripts to patch files
413
+ - **Glob** to find paths by pattern \u2014 not \`find\` or \`ls\` for discovery
414
+ - **Grep** to search file contents \u2014 not \`grep\` or \`rg\` in the shell (built-in line-by-line regex search; correct integration)
415
+ - **WebFetch** / **WebSearch** when the task needs HTTP or web search (when configured)
416
+
417
+ Reserve **Bash** for real shell needs: \`git\`, package managers, build commands, compilers, and other operations that require a shell or are not covered above.
418
+
419
+ ### Skills
420
+ Skills are instruction guides for specialized tasks. When activated, you receive the skill's full content including any referenced file paths.
421
+
422
+ {{SKILL_LIST}}
423
+
424
+ **Usage:**
425
+ - **Listing skills**: When the user asks about available skills (e.g., "what skills do you have", "\u4F60\u6709\u54EA\u4E9B\u6280\u80FD", "list your skills") \u2192 Simply describe the skills listed above. Do NOT activate any skill.
426
+ - **Activating skills**: When the user has a specific task that matches a skill's purpose \u2192 Call \`Skill\` with the skill name, then follow the returned instructions.
427
+ - After activation, use the provided Base Path to read any referenced files.
428
+
429
+ ### Sessions
430
+ - Conversations are persisted in sessions
431
+ - Use session IDs to maintain context across multiple interactions
432
+ - Previous messages provide important context for current tasks
433
+
434
+ ## Task Execution Principles
435
+
436
+ 1. **Plan First for Complex Tasks**: For multi-step tasks, you MUST call \`TaskCreate\` BEFORE any other tool. Do NOT skip this step.
437
+ 2. **Be Direct**: Go straight to the point. Try the simplest approach first.
438
+ 3. **Be Concise**: If you can say it in one sentence, don't use three.
439
+ 4. **Read Before Modify**: Always understand existing code before changing it.
440
+ 5. **No Over-Engineering**: Only make changes directly requested or clearly necessary.
441
+ 6. **Prefer Edit Over Create**: Modify existing files rather than creating new ones when appropriate.
442
+ 7. **Handle Errors Gracefully**: Report errors clearly with actionable suggestions.
443
+
444
+ ## Task Management with Todo List
445
+
446
+ **MANDATORY**: For multi-step tasks, call \`TaskCreate\` FIRST.
447
+
448
+ **Workflow:**
449
+ 1. Receive complex task -> call \`TaskCreate\` immediately
450
+ 2. Start first task (in_progress) -> complete -> mark completed
451
+ 3. Move to next task -> repeat
452
+ 4. Cancel tasks that become irrelevant
453
+
454
+ **Example:**
455
+ User: "Open Google, search X, summarize results, open first link, extract info"
456
+ -> Multi-step task detected -> call \`TaskCreate\` FIRST, then execute.
457
+
458
+ ## Output Format
459
+
460
+ - Lead with the answer or action, not the reasoning
461
+ - Skip filler words and unnecessary preamble
462
+ - Use code blocks with language hints for code
463
+ - Structure longer responses with headers and lists
464
+ - Reference file paths with line numbers when relevant (e.g., \`src/index.ts:42\`)
465
+
466
+ ## Security Guidelines
467
+
468
+ - Do not introduce security vulnerabilities (injection, XSS, etc.)
469
+ - Validate user inputs at boundaries
470
+ - Do not execute untrusted code without sandboxing
471
+ - Respect file system permissions and access controls
472
+
473
+ ### High-risk actions (confirm with the user first)
474
+
475
+ There is no automatic approval UI: **ask in the conversation** (or use \`AskUserQuestion\`) before proceeding when an action is destructive, hard to reverse, or affects others. Examples:
476
+ - Deleting files or branches, dropping data, \`rm -rf\`, overwriting uncommitted work
477
+ - Hard-to-reverse git: force-push, \`reset --hard\`, rewriting published history, amending shared commits
478
+ - Actions visible outside this machine: pushing code, opening/closing/commenting on PRs or issues, sending messages, posting to external services, changing shared CI/CD or cloud permissions
479
+ - Broad dependency or infrastructure changes (e.g. major version bumps, lockfile rewrites) when impact is unclear
480
+
481
+ Default to explaining what you intend and getting explicit agreement unless the user already directed that exact action.
482
+
483
+ ## Tool hooks
484
+
485
+ When hooks are configured (e.g. PreToolUse), a tool call may be **blocked** or its **inputs adjusted** before execution. If a tool fails with a message indicating a hook blocked or rejected the call, **do not** retry the identical tool call unchanged \u2014 read the reason, change your approach, or ask the user. Treat hook feedback as binding policy from the environment.
486
+
487
+ ## Interaction Style
488
+
489
+ - Be helpful and proactive
490
+ - Ask clarifying questions when instructions are ambiguous
491
+ - Provide suggestions when you see opportunities for improvement
492
+ - Acknowledge limitations honestly
493
+ - Maintain a professional, friendly tone`;
494
+ var MemoryManager = class {
495
+ workspaceRoot;
496
+ userBasePath;
497
+ config;
498
+ constructor(workspaceRoot, config, userBasePath) {
499
+ this.workspaceRoot = workspaceRoot || process.cwd();
500
+ this.userBasePath = userBasePath || homedir();
501
+ this.config = config || {};
502
+ }
503
+ /**
504
+ * Loads memory content from both user home ({userBasePath}/.claude/CLAUDE.md)
505
+ * and workspace root (./CLAUDE.md).
506
+ * @returns Combined memory content wrapped in system-minder tags
507
+ */
508
+ loadMemory() {
509
+ const memories = [];
510
+ const userPath = join(this.userBasePath, ".claude", "CLAUDE.md");
511
+ if (existsSync(userPath)) {
512
+ try {
513
+ const content = readFileSync(userPath, "utf-8");
514
+ if (content.trim()) {
515
+ memories.push(`# User Memory
516
+
517
+ ${content}`);
518
+ }
519
+ } catch (error) {
520
+ console.error(`Error reading user memory file: ${error}`);
521
+ }
522
+ }
523
+ const workspacePath = this.config.workspacePath || join(this.workspaceRoot, "CLAUDE.md");
524
+ if (existsSync(workspacePath)) {
525
+ try {
526
+ const content = readFileSync(workspacePath, "utf-8");
527
+ if (content.trim()) {
528
+ memories.push(`# Workspace Memory
529
+
530
+ ${content}`);
531
+ }
532
+ } catch (error) {
533
+ console.error(`Error reading workspace memory file: ${error}`);
534
+ }
535
+ }
536
+ if (memories.length === 0) {
537
+ return "";
538
+ }
539
+ const combinedContent = memories.join("\n\n");
540
+ return `<system-minder>
541
+ ${combinedContent}
542
+ </system-minder>`;
543
+ }
544
+ /**
545
+ * Checks if memory files exist.
546
+ * @returns Object indicating existence of each memory file type
547
+ */
548
+ checkMemoryFiles() {
549
+ const userPath = join(this.userBasePath, ".claude", "CLAUDE.md");
550
+ const workspacePath = this.config.workspacePath || join(this.workspaceRoot, "CLAUDE.md");
551
+ return {
552
+ userHome: existsSync(userPath),
553
+ workspace: existsSync(workspacePath)
554
+ };
555
+ }
556
+ };
557
+ function isStdioConfig(config) {
558
+ return "command" in config;
559
+ }
560
+ var MCPClient = class {
561
+ client;
562
+ transport;
563
+ _name;
564
+ _connected = false;
565
+ _tools = [];
566
+ _serverInfo;
567
+ constructor(config) {
568
+ this._name = config.name;
569
+ this.client = new Client(
570
+ { name: "agent-sdk-client", version: "0.1.0" },
571
+ { capabilities: {} }
572
+ );
573
+ if (isStdioConfig(config)) {
574
+ this.transport = new StdioClientTransport({
575
+ command: config.command,
576
+ args: config.args,
577
+ env: config.env
578
+ });
579
+ } else {
580
+ this.transport = new StreamableHTTPClientTransport(
581
+ new URL(config.url),
582
+ { requestInit: { headers: config.headers } }
583
+ );
584
+ }
585
+ }
586
+ async connect() {
587
+ if (this._connected) return;
588
+ await this.client.connect(this.transport);
589
+ this._connected = true;
590
+ const serverInfo = this.client.getServerVersion();
591
+ if (serverInfo) {
592
+ this._serverInfo = {
593
+ name: serverInfo.name,
594
+ version: serverInfo.version
595
+ };
596
+ }
597
+ await this.listTools();
598
+ }
599
+ async disconnect() {
600
+ if (!this._connected) return;
601
+ await this.client.close();
602
+ this._connected = false;
603
+ }
604
+ async listTools() {
605
+ const result = await this.client.listTools();
606
+ this._tools = result.tools.map((tool) => ({
607
+ name: tool.name,
608
+ description: tool.description,
609
+ inputSchema: tool.inputSchema
610
+ }));
611
+ return this._tools;
612
+ }
613
+ async callTool(name, args) {
614
+ try {
615
+ const result = await this.client.callTool({
616
+ name,
617
+ arguments: args
618
+ });
619
+ if ("toolResult" in result) {
620
+ return {
621
+ content: JSON.stringify(result.toolResult),
622
+ isError: false
623
+ };
624
+ }
625
+ const content = result.content.map((c) => {
626
+ if (c.type === "text") return c.text;
627
+ if (c.type === "image") return `[Image: ${c.mimeType}]`;
628
+ if (c.type === "resource") {
629
+ const res = c.resource;
630
+ if ("text" in res) return res.text;
631
+ if ("blob" in res) return `[Blob: ${res.mimeType}]`;
632
+ return "";
633
+ }
634
+ return JSON.stringify(c);
635
+ }).join("\n");
636
+ return {
637
+ content,
638
+ isError: result.isError ?? false
639
+ };
640
+ } catch (error) {
641
+ return {
642
+ content: `MCP tool error: ${error instanceof Error ? error.message : String(error)}`,
643
+ isError: true
644
+ };
645
+ }
646
+ }
647
+ async listResources() {
648
+ const result = await this.client.listResources();
649
+ return result.resources.map((r) => ({
650
+ uri: r.uri,
651
+ name: r.name,
652
+ description: r.description,
653
+ mimeType: r.mimeType
654
+ }));
655
+ }
656
+ async readResource(uri) {
657
+ const result = await this.client.readResource({ uri });
658
+ const content = result.contents[0];
659
+ if (!content) return "";
660
+ if ("text" in content) return content.text;
661
+ if ("blob" in content) return content.blob;
662
+ return "";
663
+ }
664
+ async listPrompts() {
665
+ const result = await this.client.listPrompts();
666
+ return result.prompts.map((p) => ({
667
+ name: p.name,
668
+ description: p.description,
669
+ arguments: p.arguments?.map((a) => ({
670
+ name: a.name,
671
+ description: a.description,
672
+ required: a.required
673
+ }))
674
+ }));
675
+ }
676
+ async getPrompt(name, args) {
677
+ const result = await this.client.getPrompt({
678
+ name,
679
+ arguments: args
680
+ });
681
+ return result.messages.map((m) => ({
682
+ role: m.role,
683
+ content: m.content.type === "text" ? m.content.text : JSON.stringify(m.content)
684
+ }));
685
+ }
686
+ toToolDefinitions() {
687
+ return this._tools.map((tool) => ({
688
+ name: `mcp_${this._name}__${tool.name}`,
689
+ description: tool.description || `MCP tool: ${tool.name}`,
690
+ parameters: this.convertSchema(tool.inputSchema),
691
+ handler: async (args) => this.callTool(tool.name, args),
692
+ category: "mcp"
693
+ // 标记为 MCP 类别,用于输出处理策略选择
694
+ }));
695
+ }
696
+ convertSchema(schema) {
697
+ if (!schema || !schema.properties) {
698
+ return z.object({}).passthrough();
699
+ }
700
+ const shape = {};
701
+ for (const [key, value] of Object.entries(schema.properties)) {
702
+ const field = value;
703
+ let zodField;
704
+ switch (field.type) {
705
+ case "string":
706
+ zodField = z.string();
707
+ break;
708
+ case "number":
709
+ case "integer":
710
+ zodField = z.number();
711
+ break;
712
+ case "boolean":
713
+ zodField = z.boolean();
714
+ break;
715
+ case "array":
716
+ zodField = z.array(z.any());
717
+ break;
718
+ case "object":
719
+ zodField = z.object({}).passthrough();
720
+ break;
721
+ default:
722
+ zodField = z.any();
723
+ }
724
+ if (field.description) {
725
+ zodField = zodField.describe(field.description);
726
+ }
727
+ if (!schema.required?.includes(key)) {
728
+ zodField = zodField.optional();
729
+ }
730
+ shape[key] = zodField;
731
+ }
732
+ return z.object(shape);
733
+ }
734
+ get name() {
735
+ return this._name;
736
+ }
737
+ get connected() {
738
+ return this._connected;
739
+ }
740
+ get serverInfo() {
741
+ return this._serverInfo;
742
+ }
743
+ get tools() {
744
+ return this._tools;
745
+ }
746
+ };
747
+ function createMCPClient(config) {
748
+ return new MCPClient(config);
749
+ }
750
+
751
+ // src/mcp/adapter.ts
752
+ var MCPAdapter = class {
753
+ clients = /* @__PURE__ */ new Map();
754
+ toolMap = /* @__PURE__ */ new Map();
755
+ async addServer(config) {
756
+ if (this.clients.has(config.name)) {
757
+ throw new Error(`MCP server "${config.name}" already exists`);
758
+ }
759
+ const client = new MCPClient(config);
760
+ await client.connect();
761
+ this.clients.set(config.name, client);
762
+ for (const tool of client.tools) {
763
+ const fullName = `mcp_${config.name}__${tool.name}`;
764
+ this.toolMap.set(fullName, { client, toolName: tool.name });
765
+ }
766
+ }
767
+ async removeServer(name) {
768
+ const client = this.clients.get(name);
769
+ if (!client) return;
770
+ for (const [fullName, { client: c }] of this.toolMap.entries()) {
771
+ if (c === client) {
772
+ this.toolMap.delete(fullName);
773
+ }
774
+ }
775
+ await client.disconnect();
776
+ this.clients.delete(name);
777
+ }
778
+ getToolDefinitions() {
779
+ const tools = [];
780
+ for (const client of this.clients.values()) {
781
+ tools.push(...client.toToolDefinitions());
782
+ }
783
+ return tools;
784
+ }
785
+ async executeTool(fullName, args) {
786
+ const mapping = this.toolMap.get(fullName);
787
+ if (!mapping) {
788
+ return {
789
+ content: `MCP tool "${fullName}" not found`,
790
+ isError: true
791
+ };
792
+ }
793
+ return mapping.client.callTool(mapping.toolName, args);
794
+ }
795
+ getClient(name) {
796
+ return this.clients.get(name);
797
+ }
798
+ getServerNames() {
799
+ return Array.from(this.clients.keys());
800
+ }
801
+ isConnected(name) {
802
+ const client = this.clients.get(name);
803
+ return client?.connected ?? false;
804
+ }
805
+ async disconnectAll() {
806
+ for (const client of this.clients.values()) {
807
+ await client.disconnect();
808
+ }
809
+ this.clients.clear();
810
+ this.toolMap.clear();
811
+ }
812
+ async listAllTools() {
813
+ const result = /* @__PURE__ */ new Map();
814
+ for (const [name, client] of this.clients) {
815
+ const tools = await client.listTools();
816
+ result.set(name, tools);
817
+ }
818
+ return result;
819
+ }
820
+ async listAllResources() {
821
+ const result = /* @__PURE__ */ new Map();
822
+ for (const [name, client] of this.clients) {
823
+ const resources = await client.listResources();
824
+ result.set(name, resources);
825
+ }
826
+ return result;
827
+ }
828
+ get size() {
829
+ return this.clients.size;
830
+ }
831
+ };
832
+ function createMCPAdapter() {
833
+ return new MCPAdapter();
834
+ }
835
+
836
+ // src/skills/parser.ts
837
+ function parseSkillMd(content) {
838
+ const lines = content.split("\n");
839
+ let metadataEndIndex = -1;
840
+ let metadataStartIndex = -1;
841
+ for (let i = 0; i < lines.length; i++) {
842
+ const line = lines[i].trim();
843
+ if (line === "---") {
844
+ if (metadataStartIndex === -1) {
845
+ metadataStartIndex = i;
846
+ } else {
847
+ metadataEndIndex = i;
848
+ break;
849
+ }
850
+ }
851
+ }
852
+ let metadata = {
853
+ name: "unknown",
854
+ description: ""
855
+ };
856
+ let bodyContent = content;
857
+ if (metadataStartIndex !== -1 && metadataEndIndex !== -1) {
858
+ const yamlContent = lines.slice(metadataStartIndex + 1, metadataEndIndex).join("\n");
859
+ metadata = parseSimpleYaml(yamlContent);
860
+ bodyContent = lines.slice(metadataEndIndex + 1).join("\n").trim();
861
+ }
862
+ if (metadata.name === "unknown") {
863
+ const titleMatch = bodyContent.match(/^#\s+(.+)$/m);
864
+ if (titleMatch) {
865
+ metadata.name = titleMatch[1].toLowerCase().replace(/\s+/g, "-");
866
+ }
867
+ }
868
+ return {
869
+ metadata,
870
+ content: bodyContent
871
+ };
872
+ }
873
+ function parseSimpleYaml(yaml) {
874
+ const metadata = {
875
+ name: "unknown",
876
+ description: ""
877
+ };
878
+ const lines = yaml.split("\n");
879
+ let currentKey = null;
880
+ let currentArray = [];
881
+ for (const line of lines) {
882
+ const trimmed = line.trim();
883
+ if (!trimmed || trimmed.startsWith("#")) continue;
884
+ if (trimmed.startsWith("- ")) {
885
+ if (currentKey && currentArray) {
886
+ currentArray.push(trimmed.slice(2).replace(/^["']|["']$/g, ""));
887
+ }
888
+ continue;
889
+ }
890
+ if (currentKey && currentArray.length > 0) {
891
+ metadata[currentKey] = currentArray;
892
+ currentArray = [];
893
+ currentKey = null;
894
+ }
895
+ const match = trimmed.match(/^(\w+):\s*(.*)$/);
896
+ if (match) {
897
+ const [, key, value] = match;
898
+ if (value === "" || value === "[]") {
899
+ currentKey = key;
900
+ currentArray = [];
901
+ if (value === "[]") {
902
+ metadata[key] = [];
903
+ currentKey = null;
904
+ }
905
+ } else {
906
+ let parsedValue = value.replace(/^["']|["']$/g, "");
907
+ if (parsedValue === "true") {
908
+ parsedValue = true;
909
+ } else if (parsedValue === "false") {
910
+ parsedValue = false;
911
+ } else if (parsedValue.startsWith("[") && parsedValue.endsWith("]")) {
912
+ parsedValue = parsedValue.slice(1, -1).split(",").map((s) => s.trim().replace(/^["']|["']$/g, ""));
913
+ }
914
+ metadata[key] = parsedValue;
915
+ }
916
+ }
917
+ }
918
+ if (currentKey && currentArray.length > 0) {
919
+ metadata[currentKey] = currentArray;
920
+ }
921
+ return metadata;
922
+ }
923
+ function inferMetadataFromPath(skillPath) {
924
+ const pathParts = skillPath.replace(/\\/g, "/").split("/");
925
+ const dirName = pathParts[pathParts.length - 1] || pathParts[pathParts.length - 2];
926
+ return {
927
+ name: dirName
928
+ };
929
+ }
930
+ var SkillLoader = class {
931
+ config;
932
+ constructor(config = {}) {
933
+ this.config = {
934
+ cwd: process.cwd(),
935
+ ...config
936
+ };
937
+ }
938
+ /**
939
+ * 加载单个 Skill
940
+ */
941
+ async load(skillPath) {
942
+ const resolvedPath = resolve(this.config.cwd, skillPath);
943
+ const stat = await this.getPathType(resolvedPath);
944
+ if (stat === "file") {
945
+ return this.loadFromFile(resolvedPath);
946
+ } else if (stat === "directory") {
947
+ return this.loadFromDirectory(resolvedPath);
948
+ }
949
+ throw new Error(`Skill path not found: ${resolvedPath}`);
950
+ }
951
+ /**
952
+ * 从文件加载 Skill
953
+ */
954
+ async loadFromFile(filePath) {
955
+ const content = await promises.readFile(filePath, "utf-8");
956
+ const parsed = parseSkillMd(content);
957
+ const metadata = parsed.metadata;
958
+ if (!metadata.name || metadata.name === "unknown") {
959
+ const inferred = inferMetadataFromPath(filePath);
960
+ if (inferred.name) {
961
+ parsed.metadata.name = inferred.name;
962
+ }
963
+ }
964
+ return {
965
+ metadata: parsed.metadata,
966
+ path: filePath,
967
+ instructions: parsed.content
968
+ };
969
+ }
970
+ /**
971
+ * 从目录加载 Skill
972
+ */
973
+ async loadFromDirectory(dirPath) {
974
+ const skillMdPath = join(dirPath, "SKILL.md");
975
+ try {
976
+ await promises.access(skillMdPath);
977
+ } catch {
978
+ throw new Error(`SKILL.md not found in ${dirPath}`);
979
+ }
980
+ const content = await promises.readFile(skillMdPath, "utf-8");
981
+ const parsed = parseSkillMd(content);
982
+ const dirMetadata = parsed.metadata;
983
+ if (!dirMetadata.name || dirMetadata.name === "unknown") {
984
+ const inferred = inferMetadataFromPath(dirPath);
985
+ if (inferred.name) {
986
+ parsed.metadata.name = inferred.name;
987
+ }
988
+ }
989
+ return {
990
+ metadata: parsed.metadata,
991
+ path: dirPath,
992
+ instructions: parsed.content
993
+ };
994
+ }
995
+ /**
996
+ * 加载目录下的所有 Skills
997
+ */
998
+ async loadAll(dirPath) {
999
+ const resolvedPath = resolve(this.config.cwd, dirPath);
1000
+ const skills = [];
1001
+ try {
1002
+ const entries = await promises.readdir(resolvedPath, { withFileTypes: true });
1003
+ for (const entry of entries) {
1004
+ const entryPath = join(resolvedPath, entry.name);
1005
+ if (this.config.filter && !this.config.filter(entryPath)) {
1006
+ continue;
1007
+ }
1008
+ try {
1009
+ if (entry.isDirectory()) {
1010
+ const hasSkillMd = await this.hasFile(entryPath, "SKILL.md");
1011
+ if (hasSkillMd) {
1012
+ const skill = await this.loadFromDirectory(entryPath);
1013
+ skills.push(skill);
1014
+ }
1015
+ } else if (entry.name.endsWith(".md") && entry.name !== "SKILL.md") {
1016
+ const skill = await this.loadFromFile(entryPath);
1017
+ skills.push(skill);
1018
+ }
1019
+ } catch (error) {
1020
+ console.warn(`Failed to load skill from ${entryPath}:`, error);
1021
+ }
1022
+ }
1023
+ } catch {
1024
+ }
1025
+ return skills;
1026
+ }
1027
+ /**
1028
+ * 检查文件是否存在
1029
+ */
1030
+ async hasFile(dirPath, fileName) {
1031
+ try {
1032
+ await promises.access(join(dirPath, fileName));
1033
+ return true;
1034
+ } catch {
1035
+ return false;
1036
+ }
1037
+ }
1038
+ /**
1039
+ * 获取路径类型
1040
+ */
1041
+ async getPathType(path) {
1042
+ try {
1043
+ const stat = await promises.stat(path);
1044
+ if (stat.isFile()) return "file";
1045
+ if (stat.isDirectory()) return "directory";
1046
+ return null;
1047
+ } catch {
1048
+ return null;
1049
+ }
1050
+ }
1051
+ };
1052
+ function createSkillLoader(config) {
1053
+ return new SkillLoader(config);
1054
+ }
1055
+ var SkillRegistry = class {
1056
+ skills = /* @__PURE__ */ new Map();
1057
+ loader;
1058
+ workspaceRoot;
1059
+ userBasePath;
1060
+ skillConfig;
1061
+ constructor(config) {
1062
+ this.loader = new SkillLoader(config);
1063
+ this.workspaceRoot = config?.cwd || process.cwd();
1064
+ this.userBasePath = config?.userBasePath || homedir();
1065
+ }
1066
+ /**
1067
+ * 注册 Skill
1068
+ */
1069
+ register(skill) {
1070
+ if (this.skills.has(skill.metadata.name)) {
1071
+ throw new Error(`Skill "${skill.metadata.name}" is already registered`);
1072
+ }
1073
+ this.skills.set(skill.metadata.name, skill);
1074
+ }
1075
+ /**
1076
+ * 加载并注册 Skill
1077
+ */
1078
+ async load(path) {
1079
+ const skill = await this.loader.load(path);
1080
+ this.register(skill);
1081
+ }
1082
+ /**
1083
+ * 加载目录下的所有 Skills
1084
+ */
1085
+ async loadAll(dirPath) {
1086
+ const skills = await this.loader.loadAll(dirPath);
1087
+ for (const skill of skills) {
1088
+ try {
1089
+ this.register(skill);
1090
+ } catch (error) {
1091
+ console.warn(`Failed to register skill "${skill.metadata.name}":`, error);
1092
+ }
1093
+ }
1094
+ }
1095
+ /**
1096
+ * 注销 Skill
1097
+ */
1098
+ unregister(name) {
1099
+ return this.skills.delete(name);
1100
+ }
1101
+ /**
1102
+ * 获取 Skill
1103
+ */
1104
+ get(name) {
1105
+ return this.skills.get(name);
1106
+ }
1107
+ /**
1108
+ * 获取所有 Skill
1109
+ */
1110
+ getAll() {
1111
+ return Array.from(this.skills.values());
1112
+ }
1113
+ /**
1114
+ * 获取 Skill 名称列表
1115
+ */
1116
+ getNames() {
1117
+ return Array.from(this.skills.keys());
1118
+ }
1119
+ /**
1120
+ * 检查 Skill 是否存在
1121
+ */
1122
+ has(name) {
1123
+ return this.skills.has(name);
1124
+ }
1125
+ /**
1126
+ * 搜索 Skill
1127
+ */
1128
+ search(query) {
1129
+ const lowerQuery = query.toLowerCase();
1130
+ return this.getAll().filter(
1131
+ (skill) => skill.metadata.name.toLowerCase().includes(lowerQuery) || skill.metadata.description.toLowerCase().includes(lowerQuery) || skill.metadata.tags?.some((tag) => tag.toLowerCase().includes(lowerQuery))
1132
+ );
1133
+ }
1134
+ /**
1135
+ * 按标签过滤
1136
+ */
1137
+ filterByTag(tag) {
1138
+ return this.getAll().filter(
1139
+ (skill) => skill.metadata.tags?.includes(tag)
1140
+ );
1141
+ }
1142
+ /**
1143
+ * 获取 Skill 数量
1144
+ */
1145
+ get size() {
1146
+ return this.skills.size;
1147
+ }
1148
+ /**
1149
+ * 清空所有 Skill
1150
+ */
1151
+ clear() {
1152
+ this.skills.clear();
1153
+ }
1154
+ /**
1155
+ * 导出 Skill 信息
1156
+ */
1157
+ export() {
1158
+ return this.getAll().map((skill) => ({
1159
+ name: skill.metadata.name,
1160
+ description: skill.metadata.description,
1161
+ version: skill.metadata.version,
1162
+ path: skill.path
1163
+ }));
1164
+ }
1165
+ /**
1166
+ * 获取所有 Skill 的元数据列表(用于 System Prompt)
1167
+ */
1168
+ getMetadataList() {
1169
+ return this.getAll().map((skill) => ({
1170
+ name: skill.metadata.name,
1171
+ description: skill.metadata.description,
1172
+ argumentHint: skill.metadata.argumentHint
1173
+ }));
1174
+ }
1175
+ /**
1176
+ * 获取用户可调用的 Skills
1177
+ */
1178
+ getUserInvocableSkills() {
1179
+ return this.getAll().filter((skill) => skill.metadata.userInvocable !== false).map((skill) => ({
1180
+ name: skill.metadata.name,
1181
+ description: skill.metadata.description,
1182
+ argumentHint: skill.metadata.argumentHint
1183
+ }));
1184
+ }
1185
+ /**
1186
+ * 获取模型可自动调用的 Skills(用于注入到 system prompt)
1187
+ */
1188
+ getModelInvocableSkills() {
1189
+ return this.getAll().filter((skill) => skill.metadata.disableModelInvocation !== true).map((skill) => ({
1190
+ name: skill.metadata.name,
1191
+ description: skill.metadata.description
1192
+ }));
1193
+ }
1194
+ /**
1195
+ * 获取格式化的 Skill 列表文本(用于 System Prompt)
1196
+ */
1197
+ getFormattedList() {
1198
+ const userInvocable = this.getUserInvocableSkills();
1199
+ const modelInvocable = this.getModelInvocableSkills();
1200
+ if (userInvocable.length === 0 && modelInvocable.length === 0) {
1201
+ return "No skills are currently available.";
1202
+ }
1203
+ const sections = [];
1204
+ if (userInvocable.length > 0) {
1205
+ const userSkillsText = userInvocable.map((s) => {
1206
+ const hint = s.argumentHint ? ` ${s.argumentHint}` : "";
1207
+ return `- **/${s.name}**${hint}: ${s.description}`;
1208
+ }).join("\n");
1209
+ sections.push(`**User-invocable Skills** (type /skill-name to invoke):
1210
+ ${userSkillsText}`);
1211
+ }
1212
+ if (modelInvocable.length > 0) {
1213
+ const modelSkillsText = modelInvocable.map((s) => `- **${s.name}**: ${s.description}`).join("\n");
1214
+ sections.push(`**Auto-loadable Skills** (Claude can invoke when relevant):
1215
+ ${modelSkillsText}`);
1216
+ }
1217
+ return sections.join("\n\n") + `
1218
+
1219
+ **Note:** Only activate a skill when you need to perform its specific task. For questions about your capabilities, simply describe the available skills.`;
1220
+ }
1221
+ /**
1222
+ * 根据名称获取 Skill 路径
1223
+ */
1224
+ getSkillPath(name) {
1225
+ const skill = this.skills.get(name);
1226
+ return skill?.path;
1227
+ }
1228
+ /**
1229
+ * 加载 Skill 全量内容
1230
+ */
1231
+ async loadFullContent(name) {
1232
+ const skill = this.skills.get(name);
1233
+ if (!skill) {
1234
+ throw new Error(`Skill "${name}" not found`);
1235
+ }
1236
+ if (skill.path) {
1237
+ try {
1238
+ const pathStat = await promises.stat(skill.path);
1239
+ let skillMdPath;
1240
+ if (pathStat.isDirectory()) {
1241
+ skillMdPath = join(skill.path, "SKILL.md");
1242
+ } else {
1243
+ skillMdPath = skill.path;
1244
+ }
1245
+ const content = await promises.readFile(skillMdPath, "utf-8");
1246
+ return content;
1247
+ } catch (error) {
1248
+ throw new Error(`Failed to read skill file: ${error instanceof Error ? error.message : String(error)}`);
1249
+ }
1250
+ }
1251
+ if (skill.instructions) {
1252
+ return skill.instructions;
1253
+ }
1254
+ throw new Error(`No content available for skill "${name}"`);
1255
+ }
1256
+ /**
1257
+ * 获取默认 skill 路径
1258
+ */
1259
+ getDefaultPaths() {
1260
+ const paths = [];
1261
+ const userPath = join(this.userBasePath, ".claude", "skills");
1262
+ if (existsSync(userPath)) {
1263
+ paths.push(userPath);
1264
+ }
1265
+ const workspacePath = this.skillConfig?.workspacePath || join(this.workspaceRoot, ".claude", "skills");
1266
+ if (existsSync(workspacePath)) {
1267
+ paths.push(workspacePath);
1268
+ }
1269
+ return paths;
1270
+ }
1271
+ /**
1272
+ * 初始化加载所有 Skills
1273
+ * @param config Skill 配置
1274
+ * @param additionalPaths 额外的 skill 路径(来自 AgentConfig.skills)
1275
+ */
1276
+ async initialize(config, additionalPaths) {
1277
+ this.skillConfig = config;
1278
+ if (config?.autoLoad !== false) {
1279
+ const defaultPaths = this.getDefaultPaths();
1280
+ for (const dirPath of defaultPaths) {
1281
+ try {
1282
+ const beforeCount = this.skills.size;
1283
+ await this.loadAll(dirPath);
1284
+ const loaded = this.skills.size - beforeCount;
1285
+ if (loaded > 0) {
1286
+ console.log(`Loaded ${loaded} skill(s) from: ${dirPath}`);
1287
+ }
1288
+ } catch (err) {
1289
+ console.error(`Failed to load skills from "${dirPath}":`, err);
1290
+ }
1291
+ }
1292
+ }
1293
+ const allPaths = [...config?.additionalPaths || [], ...additionalPaths || []];
1294
+ for (const path of allPaths) {
1295
+ try {
1296
+ await this.load(path);
1297
+ } catch (err) {
1298
+ console.error(`Failed to load skill from "${path}":`, err);
1299
+ }
1300
+ }
1301
+ if (this.skills.size > 0) {
1302
+ console.log(`Skills initialized: ${this.getNames().join(", ")}`);
1303
+ }
1304
+ }
1305
+ };
1306
+ function createSkillRegistry(config) {
1307
+ return new SkillRegistry(config);
1308
+ }
1309
+
1310
+ // src/streaming/chunk-processor.ts
1311
+ var StreamChunkProcessor = class {
1312
+ currentToolCall = null;
1313
+ lastUsage;
1314
+ inTextBlock = false;
1315
+ emitTextBoundaries;
1316
+ constructor(options) {
1317
+ this.emitTextBoundaries = options?.emitTextBoundaries ?? true;
1318
+ }
1319
+ processChunk(chunk) {
1320
+ const events = [];
1321
+ const endTextBlockIfNeeded = () => {
1322
+ if (this.emitTextBoundaries && this.inTextBlock) {
1323
+ events.push({ type: "text_end" });
1324
+ this.inTextBlock = false;
1325
+ }
1326
+ };
1327
+ switch (chunk.type) {
1328
+ case "text":
1329
+ if (chunk.content) {
1330
+ if (this.emitTextBoundaries && !this.inTextBlock) {
1331
+ events.push({ type: "text_start" });
1332
+ this.inTextBlock = true;
1333
+ }
1334
+ events.push({ type: "text_delta", content: chunk.content });
1335
+ }
1336
+ break;
1337
+ case "tool_call_start":
1338
+ endTextBlockIfNeeded();
1339
+ if (this.currentToolCall) {
1340
+ events.push(...this.finalizeStreamingToolCall());
1341
+ }
1342
+ if (chunk.toolCall) {
1343
+ this.currentToolCall = {
1344
+ id: chunk.toolCall.id,
1345
+ name: chunk.toolCall.name,
1346
+ arguments: ""
1347
+ };
1348
+ events.push({
1349
+ type: "tool_call_start",
1350
+ id: chunk.toolCall.id,
1351
+ name: chunk.toolCall.name
1352
+ });
1353
+ } else if (chunk.toolCallId && chunk.content) {
1354
+ this.currentToolCall = {
1355
+ id: chunk.toolCallId,
1356
+ name: chunk.content,
1357
+ arguments: ""
1358
+ };
1359
+ events.push({
1360
+ type: "tool_call_start",
1361
+ id: chunk.toolCallId,
1362
+ name: chunk.content
1363
+ });
1364
+ }
1365
+ break;
1366
+ case "tool_call_delta":
1367
+ if (this.currentToolCall && chunk.toolCallId === this.currentToolCall.id && chunk.content) {
1368
+ this.currentToolCall.arguments += chunk.content;
1369
+ events.push({
1370
+ type: "tool_call_delta",
1371
+ id: this.currentToolCall.id,
1372
+ arguments: chunk.content
1373
+ });
1374
+ } else if (chunk.toolCallId && chunk.content) {
1375
+ events.push({
1376
+ type: "tool_call_delta",
1377
+ id: chunk.toolCallId,
1378
+ arguments: chunk.content
1379
+ });
1380
+ }
1381
+ break;
1382
+ case "tool_call": {
1383
+ endTextBlockIfNeeded();
1384
+ if (!chunk.toolCall) break;
1385
+ const tc = chunk.toolCall;
1386
+ if (this.currentToolCall?.id === tc.id) {
1387
+ this.currentToolCall = null;
1388
+ } else if (this.currentToolCall) {
1389
+ events.push(...this.finalizeStreamingToolCall());
1390
+ }
1391
+ events.push({ type: "tool_call_end", id: tc.id });
1392
+ events.push({
1393
+ type: "tool_call",
1394
+ id: tc.id,
1395
+ name: tc.name,
1396
+ arguments: tc.arguments
1397
+ });
1398
+ break;
1399
+ }
1400
+ case "tool_call_end":
1401
+ endTextBlockIfNeeded();
1402
+ if (this.currentToolCall) {
1403
+ events.push(...this.finalizeStreamingToolCall());
1404
+ }
1405
+ break;
1406
+ case "thinking":
1407
+ endTextBlockIfNeeded();
1408
+ if (chunk.content !== void 0) {
1409
+ events.push({
1410
+ type: "thinking",
1411
+ content: chunk.content,
1412
+ signature: chunk.signature
1413
+ });
1414
+ }
1415
+ break;
1416
+ case "error":
1417
+ endTextBlockIfNeeded();
1418
+ if (chunk.error) {
1419
+ events.push({
1420
+ type: "end",
1421
+ timestamp: Date.now(),
1422
+ reason: "error",
1423
+ error: chunk.error
1424
+ });
1425
+ }
1426
+ break;
1427
+ case "metadata":
1428
+ if (chunk.metadata?.usage) {
1429
+ const usage = chunk.metadata.usage;
1430
+ this.lastUsage = usage;
1431
+ events.push({
1432
+ type: "model_usage",
1433
+ usage,
1434
+ ...chunk.usagePhase !== void 0 ? { phase: chunk.usagePhase } : {}
1435
+ });
1436
+ }
1437
+ break;
1438
+ }
1439
+ return events;
1440
+ }
1441
+ /** End open text block and finalize any in-progress streamed tool call. */
1442
+ flush() {
1443
+ const events = [];
1444
+ if (this.emitTextBoundaries && this.inTextBlock) {
1445
+ events.push({ type: "text_end" });
1446
+ this.inTextBlock = false;
1447
+ }
1448
+ if (this.currentToolCall) {
1449
+ events.push(...this.finalizeStreamingToolCall());
1450
+ }
1451
+ return events;
1452
+ }
1453
+ getUsage() {
1454
+ return this.lastUsage;
1455
+ }
1456
+ finalizeStreamingToolCall() {
1457
+ if (!this.currentToolCall) return [];
1458
+ const { id, name, arguments: argStr } = this.currentToolCall;
1459
+ const parsed = this.safeParseJSON(argStr);
1460
+ this.currentToolCall = null;
1461
+ return [
1462
+ { type: "tool_call_end", id },
1463
+ { type: "tool_call", id, name, arguments: parsed }
1464
+ ];
1465
+ }
1466
+ safeParseJSON(str) {
1467
+ try {
1468
+ return JSON.parse(str);
1469
+ } catch {
1470
+ return str;
1471
+ }
1472
+ }
1473
+ };
1474
+ var execAsync = promisify(exec);
1475
+ var SkillTemplateProcessor = class {
1476
+ context;
1477
+ constructor(context) {
1478
+ this.context = {
1479
+ cwd: process.cwd(),
1480
+ ...context
1481
+ };
1482
+ }
1483
+ /**
1484
+ * 处理模板内容
1485
+ * @param content SKILL.md 内容
1486
+ * @param args 用户传入的参数字符串
1487
+ * @returns 处理后的内容
1488
+ */
1489
+ async process(content, args) {
1490
+ let result = content;
1491
+ result = await this.processShellCommands(result);
1492
+ result = this.processVariables(result, args);
1493
+ return result;
1494
+ }
1495
+ /**
1496
+ * 处理 shell 命令注入
1497
+ * 格式: !`command`
1498
+ * 命令在工作目录中执行,输出替换占位符
1499
+ */
1500
+ async processShellCommands(content) {
1501
+ const shellCommandRegex = /!`([^`]+)`/g;
1502
+ const matches = [...content.matchAll(shellCommandRegex)];
1503
+ for (const match of matches) {
1504
+ const command = match[1];
1505
+ try {
1506
+ const { stdout } = await execAsync(command, {
1507
+ cwd: this.context.cwd,
1508
+ timeout: 3e4
1509
+ });
1510
+ content = content.replace(match[0], stdout.trim());
1511
+ } catch (error) {
1512
+ const errorMsg = error instanceof Error ? error.message : String(error);
1513
+ content = content.replace(match[0], `[Error executing: ${command}
1514
+ ${errorMsg}]`);
1515
+ }
1516
+ }
1517
+ return content;
1518
+ }
1519
+ /**
1520
+ * 处理变量替换
1521
+ */
1522
+ processVariables(content, args) {
1523
+ const argsArray = this.parseArguments(args);
1524
+ if (content.includes("$ARGUMENTS")) {
1525
+ content = content.replace(/\$ARGUMENTS/g, args);
1526
+ }
1527
+ content = content.replace(/\$ARGUMENTS\[(\d+)\]/g, (_, index) => {
1528
+ const i = parseInt(index, 10);
1529
+ return argsArray[i] || "";
1530
+ });
1531
+ content = content.replace(/\$(\d+)/g, (_, index) => {
1532
+ const i = parseInt(index, 10);
1533
+ return argsArray[i] || "";
1534
+ });
1535
+ if (this.context.sessionId) {
1536
+ content = content.replace(/\$\{CLAUDE_SESSION_ID\}/g, this.context.sessionId);
1537
+ }
1538
+ content = content.replace(/\$\{CLAUDE_SKILL_DIR\}/g, this.context.skillDir);
1539
+ return content;
1540
+ }
1541
+ /**
1542
+ * 解析参数字符串为数组
1543
+ * 支持引号包裹的参数
1544
+ */
1545
+ parseArguments(args) {
1546
+ if (!args.trim()) return [];
1547
+ const result = [];
1548
+ let current = "";
1549
+ let inQuotes = false;
1550
+ let quoteChar = "";
1551
+ for (let i = 0; i < args.length; i++) {
1552
+ const char = args[i];
1553
+ if (inQuotes) {
1554
+ if (char === quoteChar) {
1555
+ inQuotes = false;
1556
+ } else {
1557
+ current += char;
1558
+ }
1559
+ } else if (char === '"' || char === "'") {
1560
+ inQuotes = true;
1561
+ quoteChar = char;
1562
+ } else if (char === " ") {
1563
+ if (current) {
1564
+ result.push(current);
1565
+ current = "";
1566
+ }
1567
+ } else {
1568
+ current += char;
1569
+ }
1570
+ }
1571
+ if (current) {
1572
+ result.push(current);
1573
+ }
1574
+ return result;
1575
+ }
1576
+ };
1577
+ function createSkillTemplateProcessor(context) {
1578
+ return new SkillTemplateProcessor(context);
1579
+ }
1580
+
1581
+ // src/core/compressor.ts
1582
+ var SummarizationCompressor = class {
1583
+ constructor(model, options = {}) {
1584
+ this.model = model;
1585
+ this.options = options;
1586
+ }
1587
+ name = "summarization";
1588
+ async compress(messages, targetTokens) {
1589
+ const preserveRecent = this.options.preserveRecent ?? 6;
1590
+ const systemMessages = messages.filter((m) => m.role === "system");
1591
+ const nonSystemMessages = messages.filter((m) => m.role !== "system");
1592
+ if (nonSystemMessages.length <= preserveRecent) {
1593
+ return messages;
1594
+ }
1595
+ const recentMessages = nonSystemMessages.slice(-preserveRecent);
1596
+ const messagesToSummarize = nonSystemMessages.slice(0, -preserveRecent);
1597
+ const summaryPrompt = this.options.summaryPrompt ?? this.buildDefaultPrompt();
1598
+ const maxTokens = Math.min(
1599
+ this.options.maxSummaryTokens ?? 4e3,
1600
+ Math.floor(targetTokens * 0.3)
1601
+ );
1602
+ const summaryResponse = await this.model.complete({
1603
+ messages: [
1604
+ { role: "system", content: summaryPrompt },
1605
+ ...messagesToSummarize
1606
+ ],
1607
+ maxTokens
1608
+ });
1609
+ return [
1610
+ ...systemMessages,
1611
+ {
1612
+ role: "system",
1613
+ content: this.wrapSummary(summaryResponse.content)
1614
+ },
1615
+ ...recentMessages
1616
+ ];
1617
+ }
1618
+ /**
1619
+ * 构建默认摘要提示 (借鉴 Opencode 模板)
1620
+ */
1621
+ buildDefaultPrompt() {
1622
+ return `Provide a detailed prompt for continuing our conversation above.
1623
+ Focus on information that would be helpful for continuing the conversation, including what we did, what we're doing, which files we're working on, and what we're going to do next.
1624
+ The summary that you construct will be used so that another agent can read it and continue the work.
1625
+
1626
+ When constructing the summary, try to stick to this template:
1627
+ ---
1628
+ ## Goal
1629
+
1630
+ [What goal(s) is the user trying to accomplish?]
1631
+
1632
+ ## Instructions
1633
+
1634
+ - [What important instructions did the user give you that are relevant]
1635
+ - [If there is a plan or spec, include information about it so next agent can continue using it]
1636
+
1637
+ ## Discoveries
1638
+
1639
+ [What notable things were learned during this conversation that would be useful for the next agent to know when continuing the work]
1640
+
1641
+ ## Accomplished
1642
+
1643
+ [What work has been completed, what work is still in progress, and what work is left?]
1644
+
1645
+ ## Relevant files / directories
1646
+
1647
+ [Construct a structured list of relevant files that have been read, edited, or created that pertain to the task at hand. If all the files in a directory are relevant, include the path to the directory.]
1648
+ ---`;
1649
+ }
1650
+ /**
1651
+ * 包装摘要为 continuation 格式 (借鉴 Opencode)
1652
+ */
1653
+ wrapSummary(summary) {
1654
+ return `This session is being continued from a previous conversation that ran out of context.
1655
+ The summary below covers the earlier portion of the conversation.
1656
+
1657
+ ${summary}
1658
+
1659
+ Continue if you have next steps, or stop and ask for clarification if you are unsure how to proceed.`;
1660
+ }
1661
+ };
1662
+
1663
+ // src/core/context-manager.ts
1664
+ var ContextManager = class {
1665
+ compressor;
1666
+ reserved;
1667
+ pruneEnabled;
1668
+ pruneMinimum;
1669
+ pruneProtect;
1670
+ compressCount = 0;
1671
+ _contextLength;
1672
+ _maxOutputTokens;
1673
+ constructor(model, options = {}) {
1674
+ const contextLength = options.contextLength ?? model.capabilities?.contextLength ?? 128e3;
1675
+ const maxOutputTokens = options.maxOutputTokens ?? model.capabilities?.maxOutputTokens ?? 32768;
1676
+ this._contextLength = contextLength;
1677
+ this._maxOutputTokens = maxOutputTokens;
1678
+ this.reserved = options.reserved ?? Math.min(2e4, maxOutputTokens);
1679
+ this.pruneEnabled = options.prune !== false;
1680
+ this.pruneMinimum = options.pruneMinimum ?? 2e4;
1681
+ this.pruneProtect = options.pruneProtect ?? 4e4;
1682
+ this.compressor = options.compressor ?? new SummarizationCompressor(model);
1683
+ }
1684
+ /**
1685
+ * 计算可用空间
1686
+ *
1687
+ * usable = contextLength - maxOutputTokens - reserved
1688
+ */
1689
+ get usable() {
1690
+ return this._contextLength - this._maxOutputTokens - this.reserved;
1691
+ }
1692
+ /**
1693
+ * 判断是否需要压缩
1694
+ *
1695
+ * 使用当前上下文大小 (contextTokens) 判断,而非累计值
1696
+ *
1697
+ * @param usage 会话 token 使用量
1698
+ */
1699
+ shouldCompress(usage) {
1700
+ return usage.contextTokens >= this.usable;
1701
+ }
1702
+ /**
1703
+ * 执行压缩
1704
+ */
1705
+ async compress(messages, targetTokens) {
1706
+ const startTime = Date.now();
1707
+ const originalCount = messages.length;
1708
+ const target = targetTokens ?? Math.floor(this.usable * 0.6);
1709
+ const compressedMessages = await this.compressor.compress(messages, target);
1710
+ this.compressCount++;
1711
+ return {
1712
+ messages: compressedMessages,
1713
+ stats: {
1714
+ originalMessageCount: originalCount,
1715
+ compressedMessageCount: compressedMessages.length,
1716
+ durationMs: Date.now() - startTime
1717
+ }
1718
+ };
1719
+ }
1720
+ /**
1721
+ * Prune: 清理旧的工具输出
1722
+ *
1723
+ * 借鉴 Opencode 的 prune 策略:
1724
+ * - 从后往前遍历消息
1725
+ * - 保留最近 PRUNE_PROTECT tokens 的工具输出
1726
+ * - 清理更早的工具输出
1727
+ *
1728
+ * @param messages 消息列表
1729
+ * @returns 处理后的消息列表
1730
+ */
1731
+ prune(messages) {
1732
+ if (!this.pruneEnabled) return messages;
1733
+ let total = 0;
1734
+ const toPrune = [];
1735
+ let turns = 0;
1736
+ for (let i = messages.length - 1; i >= 0; i--) {
1737
+ const msg = messages[i];
1738
+ if (msg.role === "user") turns++;
1739
+ if (turns < 2) continue;
1740
+ if (msg.role === "tool") {
1741
+ const estimate = this.estimateTokens(typeof msg.content === "string" ? msg.content : "");
1742
+ total += estimate;
1743
+ if (total > this.pruneProtect) {
1744
+ toPrune.push(i);
1745
+ }
1746
+ }
1747
+ }
1748
+ if (toPrune.length > 0) {
1749
+ const prunedTokens = toPrune.reduce((sum, idx) => {
1750
+ const content = messages[idx].content;
1751
+ return sum + this.estimateTokens(typeof content === "string" ? content : "");
1752
+ }, 0);
1753
+ if (prunedTokens >= this.pruneMinimum) {
1754
+ return messages.map((msg, idx) => {
1755
+ if (toPrune.includes(idx) && msg.role === "tool") {
1756
+ return {
1757
+ ...msg,
1758
+ content: "[Tool output pruned to save context]"
1759
+ };
1760
+ }
1761
+ return msg;
1762
+ });
1763
+ }
1764
+ }
1765
+ return messages;
1766
+ }
1767
+ /**
1768
+ * 获取上下文状态
1769
+ */
1770
+ getStatus(usage) {
1771
+ return {
1772
+ used: usage.contextTokens,
1773
+ usable: this.usable,
1774
+ needsCompaction: usage.contextTokens >= this.usable,
1775
+ compressCount: this.compressCount
1776
+ };
1777
+ }
1778
+ /**
1779
+ * 重置 token 使用量 (压缩后调用)
1780
+ */
1781
+ resetUsage() {
1782
+ return {
1783
+ contextTokens: 0,
1784
+ inputTokens: 0,
1785
+ outputTokens: 0,
1786
+ cacheReadTokens: 0,
1787
+ cacheWriteTokens: 0,
1788
+ totalTokens: 0
1789
+ };
1790
+ }
1791
+ /**
1792
+ * Token 估算 (仅用于 prune,不做压缩判断)
1793
+ *
1794
+ * 借鉴 Opencode: CHARS_PER_TOKEN = 4
1795
+ */
1796
+ estimateTokens(text) {
1797
+ return Math.max(0, Math.round((text || "").length / 4));
1798
+ }
1799
+ };
1800
+
1801
+ // src/core/agent.ts
1802
+ function toMCPClientConfig(config) {
1803
+ if (config.transport === "http") {
1804
+ return {
1805
+ name: config.name,
1806
+ url: config.url,
1807
+ headers: config.headers
1808
+ };
1809
+ }
1810
+ return {
1811
+ name: config.name,
1812
+ command: config.command,
1813
+ args: config.args,
1814
+ env: config.env
1815
+ };
1816
+ }
1817
+ var Agent = class _Agent {
1818
+ config;
1819
+ toolRegistry;
1820
+ sessionManager;
1821
+ messages = [];
1822
+ mcpAdapter = null;
1823
+ skillRegistry;
1824
+ initPromise;
1825
+ contextManager = null;
1826
+ hookDiscoverPromise = null;
1827
+ agentDepth = 0;
1828
+ activeSubagentRuns = 0;
1829
+ // Token 使用量统计
1830
+ // contextTokens: 当前上下文大小 (用于压缩判断)
1831
+ // inputTokens/outputTokens: 累计消耗
1832
+ // totalTokens: 累计总消耗 (inputTokens + outputTokens)
1833
+ sessionUsage = _Agent.createEmptySessionUsage();
1834
+ constructor(config) {
1835
+ this.config = {
1836
+ maxIterations: 200,
1837
+ streaming: true,
1838
+ ...config
1839
+ };
1840
+ this.skillRegistry = createSkillRegistry({
1841
+ cwd: config.cwd,
1842
+ userBasePath: config.userBasePath
1843
+ });
1844
+ this.toolRegistry = new ToolRegistry({
1845
+ executionPolicy: {
1846
+ disallowedTools: this.config.disallowedTools,
1847
+ allowedTools: this.config.allowedTools,
1848
+ canUseTool: this.config.canUseTool
1849
+ }
1850
+ });
1851
+ this.registerInitialTools();
1852
+ const subagentEnabled = this.config.subagent?.enabled !== false;
1853
+ if (subagentEnabled) {
1854
+ if (this.toolRegistry.has("Agent")) {
1855
+ this.toolRegistry.unregister("Agent");
1856
+ }
1857
+ this.toolRegistry.register(createAgentTool({
1858
+ runner: (request, context) => this.runSubagent(request, context)
1859
+ }));
1860
+ } else if (this.toolRegistry.has("Agent")) {
1861
+ this.toolRegistry.unregister("Agent");
1862
+ }
1863
+ if (config.hookManager) {
1864
+ this.toolRegistry.setHookManager(config.hookManager);
1865
+ } else if (config.hookConfigDir !== void 0) {
1866
+ const hm = HookManager.create();
1867
+ this.toolRegistry.setHookManager(hm);
1868
+ this.hookDiscoverPromise = hm.discoverAndLoad(config.hookConfigDir);
1869
+ }
1870
+ this.sessionManager = new SessionManager({
1871
+ type: config.storage?.type || "jsonl",
1872
+ basePath: getSessionStoragePath(config.userBasePath)
1873
+ });
1874
+ if (config.contextManagement !== false) {
1875
+ const cmConfig = config.contextManagement === true ? {} : config.contextManagement ?? {};
1876
+ this.contextManager = new ContextManager(config.model, cmConfig);
1877
+ }
1878
+ this.initPromise = this.initializeAsync();
1879
+ }
1880
+ /**
1881
+ * 注册内置 + 自定义工具,或仅 {@link AgentConfig.exclusiveTools}。
1882
+ */
1883
+ registerInitialTools() {
1884
+ if (this.config.exclusiveTools !== void 0) {
1885
+ for (const tool of this.config.exclusiveTools) {
1886
+ if (this.toolRegistry.isDisallowed(tool.name)) {
1887
+ continue;
1888
+ }
1889
+ this.toolRegistry.register(tool);
1890
+ }
1891
+ return;
1892
+ }
1893
+ const builtins = getAllBuiltinTools(this.skillRegistry, {
1894
+ resolve: this.config.askUserQuestion
1895
+ }).filter((t) => !this.toolRegistry.isDisallowed(t.name));
1896
+ this.toolRegistry.registerMany(builtins);
1897
+ for (const tool of this.config.tools ?? []) {
1898
+ if (this.toolRegistry.isDisallowed(tool.name)) {
1899
+ continue;
1900
+ }
1901
+ if (this.toolRegistry.has(tool.name)) {
1902
+ this.toolRegistry.unregister(tool.name);
1903
+ }
1904
+ this.toolRegistry.register(tool);
1905
+ }
1906
+ }
1907
+ /**
1908
+ * 异步初始化(skills 和 MCP)
1909
+ */
1910
+ async initializeAsync() {
1911
+ try {
1912
+ if (this.hookDiscoverPromise) {
1913
+ await this.hookDiscoverPromise;
1914
+ }
1915
+ await this.skillRegistry.initialize(
1916
+ this.config.skillConfig,
1917
+ this.config.skills
1918
+ );
1919
+ if (this.config.mcpServers && this.config.mcpServers.length > 0) {
1920
+ this.mcpAdapter = new MCPAdapter();
1921
+ await this.initializeMCP(this.config.mcpServers);
1922
+ }
1923
+ } catch (err) {
1924
+ console.error("Failed to initialize:", err);
1925
+ }
1926
+ }
1927
+ /**
1928
+ * 等待初始化完成
1929
+ * CLI 应在开始交互前调用此方法
1930
+ */
1931
+ async waitForInit() {
1932
+ await this.initPromise;
1933
+ }
1934
+ /**
1935
+ * 初始化 MCP 服务器
1936
+ */
1937
+ async initializeMCP(servers) {
1938
+ if (!this.mcpAdapter) return;
1939
+ for (const serverConfig of servers) {
1940
+ try {
1941
+ await this.connectMCP(serverConfig);
1942
+ } catch (err) {
1943
+ console.error(`Failed to connect MCP server "${serverConfig.name}":`, err);
1944
+ }
1945
+ }
1946
+ }
1947
+ annotateStreamEvent(event, iteration) {
1948
+ return {
1949
+ ...event,
1950
+ streamEventId: randomUUID(),
1951
+ ...iteration !== void 0 ? { iteration } : {},
1952
+ sessionId: this.sessionManager.sessionId ?? void 0
1953
+ };
1954
+ }
1955
+ static createEmptySessionUsage() {
1956
+ return {
1957
+ contextTokens: 0,
1958
+ inputTokens: 0,
1959
+ outputTokens: 0,
1960
+ cacheReadTokens: 0,
1961
+ cacheWriteTokens: 0,
1962
+ totalTokens: 0
1963
+ };
1964
+ }
1965
+ resetSessionState() {
1966
+ this.messages = [];
1967
+ this.sessionUsage = this.contextManager ? this.contextManager.resetUsage() : _Agent.createEmptySessionUsage();
1968
+ }
1969
+ /**
1970
+ * 构建系统提示词
1971
+ * 处理默认提示词、替换模式、追加模式
1972
+ */
1973
+ buildSystemPrompt(customPrompt) {
1974
+ const shouldIncludeEnv = typeof customPrompt === "object" ? customPrompt.includeEnvironment !== false : this.config.includeEnvironment !== false;
1975
+ let envSection = "";
1976
+ if (shouldIncludeEnv) {
1977
+ const cwd = this.config.cwd || process.cwd();
1978
+ const envInfo = getEnvironmentInfo(cwd);
1979
+ envSection = formatEnvironmentSection(envInfo);
1980
+ }
1981
+ if (!customPrompt) {
1982
+ let basePrompt = DEFAULT_SYSTEM_PROMPT;
1983
+ basePrompt = basePrompt.replace("{{SKILL_LIST}}", this.skillRegistry.getFormattedList());
1984
+ return basePrompt + envSection;
1985
+ }
1986
+ if (typeof customPrompt === "string") {
1987
+ let basePrompt = DEFAULT_SYSTEM_PROMPT;
1988
+ basePrompt = basePrompt.replace("{{SKILL_LIST}}", this.skillRegistry.getFormattedList());
1989
+ return `${basePrompt}${envSection}
1990
+
1991
+ ${customPrompt}`;
1992
+ }
1993
+ const { content, mode = "append" } = customPrompt;
1994
+ if (mode === "replace") {
1995
+ return content + envSection;
1996
+ } else {
1997
+ let basePrompt = DEFAULT_SYSTEM_PROMPT;
1998
+ basePrompt = basePrompt.replace("{{SKILL_LIST}}", this.skillRegistry.getFormattedList());
1999
+ return `${basePrompt}${envSection}
2000
+
2001
+ ${content}`;
2002
+ }
2003
+ }
2004
+ /**
2005
+ * 流式执行
2006
+ */
2007
+ async *stream(input, options) {
2008
+ const signal = options?.signal;
2009
+ if (options?.sessionId) {
2010
+ const isSwitchingSession = this.sessionManager.sessionId !== options.sessionId;
2011
+ if (isSwitchingSession) {
2012
+ this.resetSessionState();
2013
+ }
2014
+ try {
2015
+ this.messages = await this.sessionManager.resumeSession(options.sessionId);
2016
+ } catch {
2017
+ this.sessionManager.createSession(options.sessionId);
2018
+ }
2019
+ } else if (!this.sessionManager.sessionId) {
2020
+ this.resetSessionState();
2021
+ this.sessionManager.createSession();
2022
+ }
2023
+ if (this.messages.length === 0) {
2024
+ const systemPrompt = this.buildSystemPrompt(
2025
+ options?.systemPrompt || this.config.systemPrompt
2026
+ );
2027
+ this.messages.push({
2028
+ role: "system",
2029
+ content: systemPrompt
2030
+ });
2031
+ }
2032
+ if (this.config.memory !== false) {
2033
+ const hasUserMessages = this.messages.some((m) => m.role === "user");
2034
+ if (!hasUserMessages) {
2035
+ const memoryManager = new MemoryManager(this.config.cwd, this.config.memoryConfig, this.config.userBasePath);
2036
+ const memoryContent = memoryManager.loadMemory();
2037
+ if (memoryContent) {
2038
+ this.messages.push({
2039
+ role: "system",
2040
+ content: memoryContent
2041
+ });
2042
+ }
2043
+ }
2044
+ }
2045
+ let processedInput = input;
2046
+ const processed = await this.processInput(input);
2047
+ if (processed.invoked) {
2048
+ processedInput = processed.prompt;
2049
+ }
2050
+ this.messages.push({
2051
+ role: "user",
2052
+ content: processedInput
2053
+ });
2054
+ yield this.annotateStreamEvent({ type: "start", timestamp: Date.now() });
2055
+ try {
2056
+ const maxIterations = this.config.maxIterations || 10;
2057
+ let totalUsage = {
2058
+ promptTokens: 0,
2059
+ completionTokens: 0,
2060
+ totalTokens: 0
2061
+ };
2062
+ for (let iteration = 0; iteration < maxIterations; iteration++) {
2063
+ if (signal?.aborted) {
2064
+ yield this.annotateStreamEvent(
2065
+ {
2066
+ type: "end",
2067
+ usage: totalUsage,
2068
+ timestamp: Date.now(),
2069
+ reason: "aborted"
2070
+ },
2071
+ iteration
2072
+ );
2073
+ return;
2074
+ }
2075
+ const contextEvents = await this.checkContextCompression();
2076
+ for (const event of contextEvents) {
2077
+ yield this.annotateStreamEvent(event, iteration);
2078
+ }
2079
+ const modelParams = {
2080
+ messages: this.messages,
2081
+ tools: this.toolRegistry.getAll(),
2082
+ temperature: this.config.temperature,
2083
+ maxTokens: this.config.maxTokens,
2084
+ signal,
2085
+ includeRawStreamEvents: options?.includeRawStreamEvents
2086
+ };
2087
+ const stream = this.config.model.stream(modelParams);
2088
+ let hasToolCalls = false;
2089
+ const toolCalls = [];
2090
+ let assistantContent = "";
2091
+ let thinkingContent = "";
2092
+ let thinkingSignature;
2093
+ const chunkProcessor = new StreamChunkProcessor({ emitTextBoundaries: true });
2094
+ const applyStreamOut = (out) => {
2095
+ if (out.type === "text_delta") {
2096
+ assistantContent += out.content;
2097
+ }
2098
+ if (out.type === "thinking") {
2099
+ thinkingContent += out.content;
2100
+ if (out.signature !== void 0 && !thinkingSignature) {
2101
+ thinkingSignature = out.signature;
2102
+ }
2103
+ }
2104
+ if (out.type === "tool_call") {
2105
+ hasToolCalls = true;
2106
+ toolCalls.push({
2107
+ id: out.id,
2108
+ name: out.name,
2109
+ arguments: out.arguments
2110
+ });
2111
+ }
2112
+ if (out.type === "model_usage") {
2113
+ const usage = out.usage;
2114
+ if (usage.promptTokens > 0) {
2115
+ totalUsage.promptTokens = usage.promptTokens;
2116
+ this.sessionUsage.contextTokens = usage.promptTokens;
2117
+ this.sessionUsage.inputTokens += usage.promptTokens;
2118
+ }
2119
+ totalUsage.completionTokens += usage.completionTokens;
2120
+ totalUsage.totalTokens = totalUsage.promptTokens + totalUsage.completionTokens;
2121
+ this.sessionUsage.outputTokens += usage.completionTokens;
2122
+ }
2123
+ };
2124
+ let fatalModelError = false;
2125
+ for await (const chunk of stream) {
2126
+ if (signal?.aborted) {
2127
+ for (const event of chunkProcessor.flush()) {
2128
+ const out = this.annotateStreamEvent(event, iteration);
2129
+ yield out;
2130
+ applyStreamOut(out);
2131
+ }
2132
+ if (assistantContent) {
2133
+ const assistantMessage2 = {
2134
+ role: "assistant",
2135
+ content: assistantContent
2136
+ };
2137
+ if (thinkingContent) {
2138
+ assistantMessage2.content = [
2139
+ { type: "thinking", thinking: thinkingContent, signature: thinkingSignature || "" },
2140
+ { type: "text", text: assistantContent }
2141
+ ];
2142
+ }
2143
+ this.messages.push(assistantMessage2);
2144
+ }
2145
+ this.messages.push({
2146
+ role: "user",
2147
+ content: "[User interrupted the response]"
2148
+ });
2149
+ await this.sessionManager.saveMessages(this.messages);
2150
+ yield this.annotateStreamEvent(
2151
+ {
2152
+ type: "end",
2153
+ usage: totalUsage,
2154
+ timestamp: Date.now(),
2155
+ reason: "aborted",
2156
+ partialContent: assistantContent
2157
+ },
2158
+ iteration
2159
+ );
2160
+ return;
2161
+ }
2162
+ const events = chunkProcessor.processChunk(chunk);
2163
+ for (const event of events) {
2164
+ const out = this.annotateStreamEvent(event, iteration);
2165
+ yield out;
2166
+ applyStreamOut(out);
2167
+ if (out.type === "end" && out.reason === "error") {
2168
+ fatalModelError = true;
2169
+ break;
2170
+ }
2171
+ }
2172
+ if (fatalModelError) {
2173
+ break;
2174
+ }
2175
+ }
2176
+ if (fatalModelError) {
2177
+ return;
2178
+ }
2179
+ for (const event of chunkProcessor.flush()) {
2180
+ const out = this.annotateStreamEvent(event, iteration);
2181
+ yield out;
2182
+ applyStreamOut(out);
2183
+ }
2184
+ const assistantMessage = {
2185
+ role: "assistant",
2186
+ content: assistantContent
2187
+ };
2188
+ if (thinkingContent) {
2189
+ const contentParts = [
2190
+ {
2191
+ type: "thinking",
2192
+ thinking: thinkingContent,
2193
+ signature: thinkingSignature
2194
+ }
2195
+ ];
2196
+ if (assistantContent.trim()) {
2197
+ contentParts.push({ type: "text", text: assistantContent });
2198
+ }
2199
+ assistantMessage.content = contentParts;
2200
+ }
2201
+ if (toolCalls.length > 0) {
2202
+ assistantMessage.toolCalls = toolCalls;
2203
+ }
2204
+ this.messages.push(assistantMessage);
2205
+ if (!hasToolCalls) {
2206
+ break;
2207
+ }
2208
+ const toolResults = await this.executeTools(toolCalls);
2209
+ for (const result of toolResults) {
2210
+ if (result.isError && result.error) {
2211
+ yield this.annotateStreamEvent(
2212
+ {
2213
+ type: "tool_error",
2214
+ toolCallId: result.toolCallId,
2215
+ error: result.error
2216
+ },
2217
+ iteration
2218
+ );
2219
+ }
2220
+ yield this.annotateStreamEvent(
2221
+ {
2222
+ type: "tool_result",
2223
+ toolCallId: result.toolCallId,
2224
+ result: result.content
2225
+ },
2226
+ iteration
2227
+ );
2228
+ this.messages.push({
2229
+ role: "tool",
2230
+ toolCallId: result.toolCallId,
2231
+ content: result.content
2232
+ });
2233
+ }
2234
+ }
2235
+ await this.sessionManager.saveMessages(this.messages);
2236
+ yield this.annotateStreamEvent({
2237
+ type: "session_summary",
2238
+ usage: totalUsage,
2239
+ iterations: Math.min(maxIterations, this.messages.length)
2240
+ });
2241
+ yield this.annotateStreamEvent({
2242
+ type: "end",
2243
+ timestamp: Date.now(),
2244
+ reason: "complete"
2245
+ });
2246
+ } catch (error) {
2247
+ if (error.name === "AbortError") {
2248
+ yield this.annotateStreamEvent({
2249
+ type: "end",
2250
+ timestamp: Date.now(),
2251
+ reason: "aborted"
2252
+ });
2253
+ return;
2254
+ }
2255
+ yield this.annotateStreamEvent({
2256
+ type: "end",
2257
+ timestamp: Date.now(),
2258
+ reason: "error",
2259
+ error
2260
+ });
2261
+ }
2262
+ }
2263
+ /**
2264
+ * 非流式执行
2265
+ */
2266
+ async run(input, options) {
2267
+ let content = "";
2268
+ const toolCalls = [];
2269
+ let usage;
2270
+ let iterations = 0;
2271
+ let streamError;
2272
+ for await (const event of this.stream(input, options)) {
2273
+ if (event.type === "text_delta") {
2274
+ content += event.content;
2275
+ }
2276
+ if (event.type === "tool_result") {
2277
+ const matchingCall = this.messages.filter((m) => m.role === "assistant" && m.toolCalls).flatMap((m) => m.toolCalls).find((tc) => tc.id === event.toolCallId);
2278
+ if (matchingCall) {
2279
+ toolCalls.push({
2280
+ name: matchingCall.name,
2281
+ arguments: matchingCall.arguments,
2282
+ result: event.result
2283
+ });
2284
+ }
2285
+ }
2286
+ if (event.type === "model_usage") {
2287
+ usage = event.usage;
2288
+ }
2289
+ if (event.type === "session_summary") {
2290
+ usage = event.usage;
2291
+ iterations = event.iterations;
2292
+ }
2293
+ if (event.type === "end") {
2294
+ if (event.usage !== void 0) {
2295
+ usage = event.usage;
2296
+ }
2297
+ if (event.reason === "error" && event.error) {
2298
+ streamError = event.error;
2299
+ }
2300
+ }
2301
+ }
2302
+ if (streamError) {
2303
+ throw streamError;
2304
+ }
2305
+ return {
2306
+ content,
2307
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
2308
+ usage,
2309
+ sessionId: this.sessionManager.sessionId,
2310
+ iterations
2311
+ };
2312
+ }
2313
+ /**
2314
+ * 注册工具
2315
+ */
2316
+ registerTool(tool) {
2317
+ this.toolRegistry.register(tool);
2318
+ }
2319
+ /**
2320
+ * 注册多个工具
2321
+ */
2322
+ registerTools(tools) {
2323
+ for (const t of tools) {
2324
+ this.registerTool(t);
2325
+ }
2326
+ }
2327
+ /**
2328
+ * 获取工具注册中心
2329
+ */
2330
+ getToolRegistry() {
2331
+ return this.toolRegistry;
2332
+ }
2333
+ /**
2334
+ * 获取会话管理器
2335
+ */
2336
+ getSessionManager() {
2337
+ return this.sessionManager;
2338
+ }
2339
+ /**
2340
+ * 加载 Skill
2341
+ */
2342
+ async loadSkill(path) {
2343
+ await this.skillRegistry.load(path);
2344
+ }
2345
+ /**
2346
+ * 获取 Skill 注册中心
2347
+ */
2348
+ getSkillRegistry() {
2349
+ return this.skillRegistry;
2350
+ }
2351
+ /**
2352
+ * 处理用户输入,检测并处理 skill 调用
2353
+ * @param input 用户输入
2354
+ * @returns 处理结果
2355
+ */
2356
+ async processInput(input) {
2357
+ const invocation = this.parseSkillInvocation(input);
2358
+ if (!invocation) {
2359
+ return { invoked: false, prompt: input };
2360
+ }
2361
+ const { name, args } = invocation;
2362
+ try {
2363
+ const prompt = await this.invokeSkill(name, args);
2364
+ return { invoked: true, skillName: name, prompt };
2365
+ } catch (error) {
2366
+ const errorMsg = error instanceof Error ? error.message : String(error);
2367
+ return {
2368
+ invoked: false,
2369
+ prompt: `Error invoking skill "${name}": ${errorMsg}
2370
+
2371
+ Original input: ${input}`
2372
+ };
2373
+ }
2374
+ }
2375
+ /**
2376
+ * 调用 skill 并返回处理后的 prompt
2377
+ * @param name Skill 名称
2378
+ * @param args 参数字符串
2379
+ * @returns 处理后的 prompt
2380
+ */
2381
+ async invokeSkill(name, args = "") {
2382
+ const skill = this.skillRegistry.get(name);
2383
+ if (!skill) {
2384
+ const available = this.skillRegistry.getNames();
2385
+ throw new Error(
2386
+ `Skill "${name}" not found. Available skills: ${available.join(", ") || "none"}`
2387
+ );
2388
+ }
2389
+ if (skill.metadata.userInvocable === false) {
2390
+ throw new Error(`Skill "${name}" is not user-invocable`);
2391
+ }
2392
+ const content = await this.skillRegistry.loadFullContent(name);
2393
+ const context = {
2394
+ skillDir: skill.path || "",
2395
+ sessionId: this.sessionManager.sessionId || void 0,
2396
+ cwd: this.config.cwd
2397
+ };
2398
+ const processor = createSkillTemplateProcessor(context);
2399
+ let processedContent = await processor.process(content, args);
2400
+ if (args && !content.includes("$ARGUMENTS") && !content.includes("$0")) {
2401
+ processedContent += `
2402
+
2403
+ ARGUMENTS: ${args}`;
2404
+ }
2405
+ return processedContent;
2406
+ }
2407
+ /**
2408
+ * 解析 skill 调用格式
2409
+ * 格式: /skill-name [args]
2410
+ * @param input 用户输入
2411
+ * @returns 解析结果或 null
2412
+ */
2413
+ parseSkillInvocation(input) {
2414
+ const trimmed = input.trim();
2415
+ if (!trimmed.startsWith("/")) {
2416
+ return null;
2417
+ }
2418
+ const match = trimmed.match(/^\/([^\s\/]+)(?:\s+(.*))?$/);
2419
+ if (!match) {
2420
+ return null;
2421
+ }
2422
+ const name = match[1];
2423
+ const args = match[2] || "";
2424
+ if (!this.skillRegistry.has(name)) {
2425
+ return null;
2426
+ }
2427
+ return { name, args };
2428
+ }
2429
+ /**
2430
+ * 连接 MCP 服务器
2431
+ */
2432
+ async connectMCP(config) {
2433
+ if (!this.mcpAdapter) {
2434
+ this.mcpAdapter = new MCPAdapter();
2435
+ }
2436
+ await this.mcpAdapter.addServer(toMCPClientConfig(config));
2437
+ const mcpTools = this.mcpAdapter.getToolDefinitions();
2438
+ for (const tool of mcpTools) {
2439
+ if (!tool.name.startsWith(`mcp_${config.name}__`)) {
2440
+ continue;
2441
+ }
2442
+ if (this.toolRegistry.isDisallowed(tool.name)) {
2443
+ continue;
2444
+ }
2445
+ this.toolRegistry.register(tool);
2446
+ }
2447
+ }
2448
+ /**
2449
+ * 断开指定 MCP 服务器
2450
+ */
2451
+ async disconnectMCP(name) {
2452
+ if (!this.mcpAdapter) return;
2453
+ const tools = this.toolRegistry.getAll();
2454
+ for (const tool of tools) {
2455
+ if (tool.name.startsWith(`mcp_${name}__`)) {
2456
+ this.toolRegistry.unregister(tool.name);
2457
+ }
2458
+ }
2459
+ await this.mcpAdapter.removeServer(name);
2460
+ }
2461
+ /**
2462
+ * 断开所有 MCP 服务器
2463
+ */
2464
+ async disconnectAllMCP() {
2465
+ if (!this.mcpAdapter) return;
2466
+ const tools = this.toolRegistry.getAll();
2467
+ for (const tool of tools) {
2468
+ if (tool.name.startsWith("mcp_") && tool.name.includes("__")) {
2469
+ this.toolRegistry.unregister(tool.name);
2470
+ }
2471
+ }
2472
+ await this.mcpAdapter.disconnectAll();
2473
+ this.mcpAdapter = null;
2474
+ }
2475
+ /**
2476
+ * 获取 MCP 适配器
2477
+ */
2478
+ getMCPAdapter() {
2479
+ return this.mcpAdapter;
2480
+ }
2481
+ /**
2482
+ * 销毁 Agent,清理资源
2483
+ */
2484
+ async destroy() {
2485
+ await this.disconnectAllMCP();
2486
+ this.messages = [];
2487
+ }
2488
+ /**
2489
+ * 获取消息历史
2490
+ */
2491
+ getMessages() {
2492
+ return [...this.messages];
2493
+ }
2494
+ /**
2495
+ * 清空消息历史
2496
+ */
2497
+ clearMessages() {
2498
+ this.resetSessionState();
2499
+ }
2500
+ /**
2501
+ * 设置系统提示 (运行时替换)
2502
+ */
2503
+ setSystemPrompt(prompt) {
2504
+ this.messages = this.messages.filter((m) => m.role !== "system");
2505
+ const systemPrompt = this.buildSystemPrompt(prompt);
2506
+ if (this.messages.length > 0) {
2507
+ this.messages.unshift({
2508
+ role: "system",
2509
+ content: systemPrompt
2510
+ });
2511
+ }
2512
+ }
2513
+ /**
2514
+ * 追加系统提示内容
2515
+ */
2516
+ appendSystemPrompt(additionalContent) {
2517
+ const systemMessageIndex = this.messages.findIndex((m) => m.role === "system");
2518
+ if (systemMessageIndex >= 0) {
2519
+ this.messages[systemMessageIndex].content += `
2520
+
2521
+ ${additionalContent}`;
2522
+ } else {
2523
+ const systemPrompt = this.buildSystemPrompt(additionalContent);
2524
+ this.messages.unshift({
2525
+ role: "system",
2526
+ content: systemPrompt
2527
+ });
2528
+ }
2529
+ }
2530
+ /**
2531
+ * 获取当前系统提示内容
2532
+ */
2533
+ getSystemPrompt() {
2534
+ const systemMessage = this.messages.find((m) => m.role === "system");
2535
+ if (!systemMessage) return void 0;
2536
+ return typeof systemMessage.content === "string" ? systemMessage.content : void 0;
2537
+ }
2538
+ /**
2539
+ * 手动触发上下文压缩
2540
+ */
2541
+ async compressContext() {
2542
+ if (!this.contextManager) {
2543
+ throw new Error("Context management is disabled");
2544
+ }
2545
+ const result = await this.contextManager.compress(this.messages);
2546
+ this.messages = result.messages;
2547
+ this.sessionUsage = this.contextManager.resetUsage();
2548
+ await this.sessionManager.saveMessages(this.messages);
2549
+ return {
2550
+ messageCount: this.messages.length,
2551
+ stats: result.stats
2552
+ };
2553
+ }
2554
+ /**
2555
+ * 获取上下文状态
2556
+ */
2557
+ getContextStatus() {
2558
+ if (!this.contextManager) {
2559
+ return null;
2560
+ }
2561
+ return this.contextManager.getStatus(this.sessionUsage);
2562
+ }
2563
+ /**
2564
+ * 获取会话累计 Token 使用量
2565
+ */
2566
+ getSessionUsage() {
2567
+ return {
2568
+ ...this.sessionUsage,
2569
+ totalTokens: this.sessionUsage.inputTokens + this.sessionUsage.outputTokens
2570
+ };
2571
+ }
2572
+ /**
2573
+ * 检查并执行上下文压缩
2574
+ * @returns 压缩事件数组(可能为空)
2575
+ */
2576
+ async checkContextCompression() {
2577
+ if (!this.contextManager) {
2578
+ return [];
2579
+ }
2580
+ this.messages = this.contextManager.prune(this.messages);
2581
+ if (!this.contextManager.shouldCompress(this.sessionUsage)) {
2582
+ return [];
2583
+ }
2584
+ const result = await this.contextManager.compress(this.messages);
2585
+ this.messages = result.messages;
2586
+ this.sessionUsage = this.contextManager.resetUsage();
2587
+ return [
2588
+ {
2589
+ type: "context_compressed",
2590
+ stats: result.stats
2591
+ }
2592
+ ];
2593
+ }
2594
+ getSubagentConfig() {
2595
+ return {
2596
+ enabled: this.config.subagent?.enabled !== false,
2597
+ maxDepth: this.config.subagent?.maxDepth ?? 1,
2598
+ maxParallel: this.config.subagent?.maxParallel ?? 5,
2599
+ timeoutMs: this.config.subagent?.timeoutMs ?? 12e4,
2600
+ allowDangerousTools: this.config.subagent?.allowDangerousTools ?? false,
2601
+ defaultAllowedTools: this.config.subagent?.defaultAllowedTools
2602
+ };
2603
+ }
2604
+ resolveSubagentTools(request) {
2605
+ const subagentConfig = this.getSubagentConfig();
2606
+ const parentTools = this.toolRegistry.getAll();
2607
+ const byName = new Map(parentTools.map((tool) => [tool.name, tool]));
2608
+ const requestedNames = request.allowed_tools ?? subagentConfig.defaultAllowedTools;
2609
+ let selected = requestedNames ? requestedNames.map((name) => byName.get(name)).filter((tool) => tool !== void 0) : parentTools.filter((tool) => !tool.isDangerous);
2610
+ selected = selected.filter((tool) => tool.name !== "Agent");
2611
+ selected = selected.filter((tool) => tool.name !== "AskUserQuestion");
2612
+ if (!subagentConfig.allowDangerousTools) {
2613
+ const requestedDangerous = request.allowed_tools?.some((name) => byName.get(name)?.isDangerous);
2614
+ if (requestedDangerous) {
2615
+ return {
2616
+ error: "Subagent dangerous tools are disabled by configuration"
2617
+ };
2618
+ }
2619
+ selected = selected.filter((tool) => !tool.isDangerous);
2620
+ }
2621
+ if (selected.length === 0) {
2622
+ return { error: "No tools available for subagent after filtering" };
2623
+ }
2624
+ return { tools: selected };
2625
+ }
2626
+ async runSubagent(request, context) {
2627
+ const subagentConfig = this.getSubagentConfig();
2628
+ const currentDepth = context?.agentDepth ?? this.agentDepth;
2629
+ if (!subagentConfig.enabled) {
2630
+ return { content: "Subagent is disabled by configuration", isError: true };
2631
+ }
2632
+ if (currentDepth >= subagentConfig.maxDepth) {
2633
+ return { content: "Subagent cannot spawn subagents", isError: true };
2634
+ }
2635
+ if (this.activeSubagentRuns >= subagentConfig.maxParallel) {
2636
+ return { content: "Subagent concurrency limit reached", isError: true };
2637
+ }
2638
+ const normalizedType = request.subagent_type ?? "general-purpose";
2639
+ const requestedTimeout = request.timeout_ms ?? subagentConfig.timeoutMs;
2640
+ const timeoutMs = Math.min(requestedTimeout, subagentConfig.timeoutMs);
2641
+ const maxIterations = Math.max(1, request.max_iterations ?? this.config.maxIterations ?? 50);
2642
+ const resolved = this.resolveSubagentTools(request);
2643
+ if (!resolved.tools) {
2644
+ return {
2645
+ content: resolved.error ?? "Unable to resolve subagent tools",
2646
+ isError: true
2647
+ };
2648
+ }
2649
+ const childConfig = {
2650
+ ...this.config,
2651
+ exclusiveTools: resolved.tools,
2652
+ tools: void 0,
2653
+ mcpServers: void 0,
2654
+ maxIterations,
2655
+ subagent: {
2656
+ ...this.config.subagent,
2657
+ enabled: false
2658
+ }
2659
+ };
2660
+ const child = new _Agent(childConfig);
2661
+ child.agentDepth = currentDepth + 1;
2662
+ const startedAt = Date.now();
2663
+ this.activeSubagentRuns += 1;
2664
+ try {
2665
+ await child.waitForInit();
2666
+ const runPromise = child.run(request.prompt, {
2667
+ systemPrompt: request.system_prompt
2668
+ });
2669
+ const timeoutPromise = new Promise((_, reject) => {
2670
+ const timer = setTimeout(() => {
2671
+ reject(new Error(`Subagent timed out after ${timeoutMs}ms`));
2672
+ }, timeoutMs);
2673
+ runPromise.finally(() => clearTimeout(timer)).catch(() => {
2674
+ });
2675
+ });
2676
+ const result = await Promise.race([runPromise, timeoutPromise]);
2677
+ return {
2678
+ content: result.content,
2679
+ metadata: {
2680
+ sessionId: result.sessionId,
2681
+ subagentType: normalizedType,
2682
+ durationMs: Date.now() - startedAt,
2683
+ usage: result.usage,
2684
+ toolNames: resolved.tools.map((tool) => tool.name),
2685
+ description: request.description
2686
+ }
2687
+ };
2688
+ } catch (error) {
2689
+ return {
2690
+ content: error instanceof Error ? error.message : String(error),
2691
+ isError: true,
2692
+ metadata: {
2693
+ subagentType: normalizedType,
2694
+ durationMs: Date.now() - startedAt,
2695
+ description: request.description,
2696
+ error: error instanceof Error ? error.message : String(error)
2697
+ }
2698
+ };
2699
+ } finally {
2700
+ this.activeSubagentRuns -= 1;
2701
+ await child.destroy();
2702
+ }
2703
+ }
2704
+ /**
2705
+ * 获取默认系统提示词
2706
+ */
2707
+ static getDefaultSystemPrompt() {
2708
+ return DEFAULT_SYSTEM_PROMPT;
2709
+ }
2710
+ /**
2711
+ * 执行工具调用
2712
+ */
2713
+ async executeTools(toolCalls) {
2714
+ const results = await Promise.all(
2715
+ toolCalls.map(async (tc) => {
2716
+ const result = await this.toolRegistry.execute(tc.name, tc.arguments, {
2717
+ toolCallId: tc.id,
2718
+ projectDir: this.config.cwd || process.cwd(),
2719
+ agentDepth: this.agentDepth
2720
+ });
2721
+ const isError = Boolean(result.isError);
2722
+ return {
2723
+ toolCallId: tc.id,
2724
+ content: isError ? `Error: ${result.content}` : result.content,
2725
+ isError,
2726
+ error: isError ? new Error(result.content) : void 0
2727
+ };
2728
+ })
2729
+ );
2730
+ return results;
2731
+ }
2732
+ };
2733
+ function createAgent(config) {
2734
+ return new Agent(config);
2735
+ }
2736
+ function expandEnvVars(value) {
2737
+ let result = value.replace(/\$\{([^}]+)\}/g, (_, varName) => {
2738
+ return process.env[varName] || "";
2739
+ });
2740
+ result = result.replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, varName) => {
2741
+ return process.env[varName] || "";
2742
+ });
2743
+ return result;
2744
+ }
2745
+ function expandEnvVarsInObject(obj) {
2746
+ if (typeof obj === "string") {
2747
+ return expandEnvVars(obj);
2748
+ }
2749
+ if (Array.isArray(obj)) {
2750
+ return obj.map((item) => expandEnvVarsInObject(item));
2751
+ }
2752
+ if (obj && typeof obj === "object") {
2753
+ const result = {};
2754
+ for (const [key, value] of Object.entries(obj)) {
2755
+ result[key] = expandEnvVarsInObject(value);
2756
+ }
2757
+ return result;
2758
+ }
2759
+ return obj;
2760
+ }
2761
+ function transformConfig(config) {
2762
+ const servers = [];
2763
+ for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
2764
+ const transport = serverConfig.url ? "http" : "stdio";
2765
+ const server = {
2766
+ name,
2767
+ transport,
2768
+ ...transport === "stdio" ? {
2769
+ command: serverConfig.command,
2770
+ args: serverConfig.args,
2771
+ env: serverConfig.env
2772
+ } : {
2773
+ url: serverConfig.url,
2774
+ headers: serverConfig.headers
2775
+ }
2776
+ };
2777
+ servers.push(server);
2778
+ }
2779
+ return servers;
2780
+ }
2781
+ function findConfigFiles(startDir = process.cwd(), userBasePath) {
2782
+ const paths = [];
2783
+ const base = userBasePath || homedir();
2784
+ const userConfig = join(base, ".claude", "mcp_config.json");
2785
+ if (existsSync(userConfig)) {
2786
+ paths.push(userConfig);
2787
+ }
2788
+ const workspaceConfig = join(startDir, ".claude", "mcp_config.json");
2789
+ if (existsSync(workspaceConfig)) {
2790
+ paths.push(workspaceConfig);
2791
+ }
2792
+ return paths;
2793
+ }
2794
+ function loadSingleConfig(filePath) {
2795
+ const content = readFileSync(filePath, "utf-8");
2796
+ const rawConfig = JSON.parse(content);
2797
+ const expandedConfig = expandEnvVarsInObject(rawConfig);
2798
+ return transformConfig(expandedConfig);
2799
+ }
2800
+ function loadMCPConfig(configPath, startDir = process.cwd(), userBasePath) {
2801
+ if (configPath) {
2802
+ if (!existsSync(configPath)) {
2803
+ return { servers: [] };
2804
+ }
2805
+ try {
2806
+ const servers = loadSingleConfig(configPath);
2807
+ return { servers, configPath };
2808
+ } catch (error) {
2809
+ console.error(`Failed to load MCP config from ${configPath}:`, error);
2810
+ return { servers: [] };
2811
+ }
2812
+ }
2813
+ const configPaths = findConfigFiles(startDir, userBasePath);
2814
+ if (configPaths.length === 0) {
2815
+ return { servers: [] };
2816
+ }
2817
+ const mergedServers = /* @__PURE__ */ new Map();
2818
+ for (const path of configPaths) {
2819
+ try {
2820
+ const servers = loadSingleConfig(path);
2821
+ for (const server of servers) {
2822
+ mergedServers.set(server.name, server);
2823
+ }
2824
+ } catch (error) {
2825
+ console.error(`Failed to load MCP config from ${path}:`, error);
2826
+ }
2827
+ }
2828
+ return {
2829
+ servers: Array.from(mergedServers.values()),
2830
+ configPath: configPaths[configPaths.length - 1],
2831
+ // 主配置(工作目录)
2832
+ configPaths
2833
+ };
2834
+ }
2835
+ function validateMCPConfig(config) {
2836
+ const errors = [];
2837
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
2838
+ errors.push("mcpServers must be an object");
2839
+ return errors;
2840
+ }
2841
+ for (const [name, server] of Object.entries(config.mcpServers)) {
2842
+ if (!server.command && !server.url) {
2843
+ errors.push(`Server "${name}": must have either "command" or "url"`);
2844
+ }
2845
+ if (server.command && server.url) {
2846
+ errors.push(`Server "${name}": cannot have both "command" and "url"`);
2847
+ }
2848
+ }
2849
+ return errors;
2850
+ }
2851
+
2852
+ export { Agent, DEFAULT_SYSTEM_PROMPT, JsonlStorage, MCPAdapter, MCPClient, MemoryManager, MemoryStorage, SessionManager, SkillLoader, SkillRegistry, StreamChunkProcessor, createAgent, createJsonlStorage, createMCPAdapter, createMCPClient, createMemoryStorage, createSessionManager, createSkillLoader, createSkillRegistry, createStorage, getLatestSessionId, getSessionStoragePath, loadMCPConfig, parseSkillMd, validateMCPConfig };
2853
+ //# sourceMappingURL=chunk-Q3SOMX26.js.map
2854
+ //# sourceMappingURL=chunk-Q3SOMX26.js.map