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