@ddlqhd/agent-sdk 0.1.0 → 0.2.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 (49) hide show
  1. package/README.md +4 -2
  2. package/dist/{chunk-5QMA2YBY.cjs → chunk-6X7EYQLS.cjs} +782 -114
  3. package/dist/chunk-6X7EYQLS.cjs.map +1 -0
  4. package/dist/{chunk-NDSL7NPN.js → chunk-D3UZNLZO.js} +769 -71
  5. package/dist/chunk-D3UZNLZO.js.map +1 -0
  6. package/dist/{chunk-Q3SOMX26.js → chunk-EQ5CXH44.js} +772 -111
  7. package/dist/chunk-EQ5CXH44.js.map +1 -0
  8. package/dist/chunk-LOYIGOBZ.js +54 -0
  9. package/dist/chunk-LOYIGOBZ.js.map +1 -0
  10. package/dist/{chunk-OHXW2YM6.js → chunk-MEJHTQJM.js} +289 -166
  11. package/dist/chunk-MEJHTQJM.js.map +1 -0
  12. package/dist/chunk-NYZD3THB.cjs +1521 -0
  13. package/dist/chunk-NYZD3THB.cjs.map +1 -0
  14. package/dist/chunk-OZO7D77N.cjs +59 -0
  15. package/dist/chunk-OZO7D77N.cjs.map +1 -0
  16. package/dist/{chunk-JF5AJQMU.cjs → chunk-Z45DHTDX.cjs} +291 -170
  17. package/dist/chunk-Z45DHTDX.cjs.map +1 -0
  18. package/dist/cli/index.cjs +47 -39
  19. package/dist/cli/index.cjs.map +1 -1
  20. package/dist/cli/index.js +22 -14
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/{index-DPsZ1zat.d.ts → index-Cw3SfEAB.d.ts} +20 -34
  23. package/dist/{index-RTPmFjMp.d.cts → index-D2Qntkn_.d.cts} +20 -34
  24. package/dist/index.cjs +125 -89
  25. package/dist/index.d.cts +62 -22
  26. package/dist/index.d.ts +62 -22
  27. package/dist/index.js +4 -4
  28. package/dist/models/index.cjs +19 -15
  29. package/dist/models/index.d.cts +55 -6
  30. package/dist/models/index.d.ts +55 -6
  31. package/dist/models/index.js +2 -2
  32. package/dist/tools/index.cjs +53 -61
  33. package/dist/tools/index.d.cts +3 -3
  34. package/dist/tools/index.d.ts +3 -3
  35. package/dist/tools/index.js +2 -2
  36. package/dist/{types-C0aX_Qdp.d.cts → types-CWPAYWzr.d.cts} +307 -61
  37. package/dist/{types-C0aX_Qdp.d.ts → types-CWPAYWzr.d.ts} +307 -61
  38. package/package.json +25 -14
  39. package/dist/chunk-5QMA2YBY.cjs.map +0 -1
  40. package/dist/chunk-CNSGZVRN.cjs +0 -152
  41. package/dist/chunk-CNSGZVRN.cjs.map +0 -1
  42. package/dist/chunk-JF5AJQMU.cjs.map +0 -1
  43. package/dist/chunk-NDSL7NPN.js.map +0 -1
  44. package/dist/chunk-OHXW2YM6.js.map +0 -1
  45. package/dist/chunk-Q3SOMX26.js.map +0 -1
  46. package/dist/chunk-WH3APNQ5.js +0 -147
  47. package/dist/chunk-WH3APNQ5.js.map +0 -1
  48. package/dist/chunk-X35MHWXE.cjs +0 -817
  49. package/dist/chunk-X35MHWXE.cjs.map +0 -1
@@ -1,7 +1,9 @@
1
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';
2
+ import { createModel, emitSDKLog, mergeMcpStdioEnv } from './chunk-D3UZNLZO.js';
3
+ import { ToolRegistry, createAgentTool, HookManager, getAllBuiltinTools, getEnvironmentInfo, formatEnvironmentSection } from './chunk-MEJHTQJM.js';
4
+ import { readFileSync, promises, existsSync } from 'fs';
5
+ import { dirname, join, resolve } from 'path';
6
+ import { fileURLToPath } from 'url';
5
7
  import { randomUUID } from 'crypto';
6
8
  import { homedir } from 'os';
7
9
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
@@ -11,6 +13,28 @@ import { z } from 'zod';
11
13
  import { exec } from 'child_process';
12
14
  import { promisify } from 'util';
13
15
 
16
+ var pkgDir = dirname(fileURLToPath(import.meta.url));
17
+ var pkgPath = join(pkgDir, "..", "package.json");
18
+ var PACKAGE_VERSION = JSON.parse(readFileSync(pkgPath, "utf-8")).version;
19
+
20
+ // src/core/types.ts
21
+ var MODEL_STREAM_EVENT_TYPES = [
22
+ "text_start",
23
+ "text_delta",
24
+ "text_end",
25
+ "tool_call_start",
26
+ "tool_call_delta",
27
+ "tool_call",
28
+ "tool_call_end",
29
+ "thinking_start",
30
+ "thinking",
31
+ "thinking_end",
32
+ "model_usage"
33
+ ];
34
+ var MODEL_STREAM_EVENT_TYPE_SET = new Set(MODEL_STREAM_EVENT_TYPES);
35
+ function isModelStreamEventType(type) {
36
+ return MODEL_STREAM_EVENT_TYPE_SET.has(type);
37
+ }
14
38
  var JsonlStorage = class {
15
39
  basePath;
16
40
  constructor(config = {}) {
@@ -413,6 +437,7 @@ When to use tools:
413
437
  - **Glob** to find paths by pattern \u2014 not \`find\` or \`ls\` for discovery
414
438
  - **Grep** to search file contents \u2014 not \`grep\` or \`rg\` in the shell (built-in line-by-line regex search; correct integration)
415
439
  - **WebFetch** / **WebSearch** when the task needs HTTP or web search (when configured)
440
+ - **TodoWrite** for structured multi-step task lists (see **Task Management with Todo List** below)
416
441
 
417
442
  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
443
 
@@ -433,7 +458,7 @@ Skills are instruction guides for specialized tasks. When activated, you receive
433
458
 
434
459
  ## Task Execution Principles
435
460
 
436
- 1. **Plan First for Complex Tasks**: For multi-step tasks, you MUST call \`TaskCreate\` BEFORE any other tool. Do NOT skip this step.
461
+ 1. **Plan First for Complex Tasks**: For multi-step tasks, you MUST call \`TodoWrite\` BEFORE any other tool. Do NOT skip this step.
437
462
  2. **Be Direct**: Go straight to the point. Try the simplest approach first.
438
463
  3. **Be Concise**: If you can say it in one sentence, don't use three.
439
464
  4. **Read Before Modify**: Always understand existing code before changing it.
@@ -443,17 +468,17 @@ Skills are instruction guides for specialized tasks. When activated, you receive
443
468
 
444
469
  ## Task Management with Todo List
445
470
 
446
- **MANDATORY**: For multi-step tasks, call \`TaskCreate\` FIRST.
471
+ **MANDATORY**: For multi-step tasks, call \`TodoWrite\` FIRST.
447
472
 
448
473
  **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
474
+ 1. Receive complex task -> call \`TodoWrite\` immediately
475
+ 2. Mark tasks \`in_progress\` / \`completed\` as you work; several items may be \`in_progress\` at once when work is parallel
476
+ 3. **Replan freely:** if the plan was wrong or incomplete, call \`TodoWrite\` again with a revised full list (add, remove, reorder, or rewrite steps)
477
+ 4. **Before you finish your response** for a multi-step request, ensure **every** todo is \`completed\` via \`TodoWrite\` (unless you intentionally pause for a follow-up turn). Do not leave \`pending\` / \`in_progress\` items when the work is done
453
478
 
454
479
  **Example:**
455
480
  User: "Open Google, search X, summarize results, open first link, extract info"
456
- -> Multi-step task detected -> call \`TaskCreate\` FIRST, then execute.
481
+ -> Multi-step task detected -> call \`TodoWrite\` FIRST, then execute.
457
482
 
458
483
  ## Output Format
459
484
 
@@ -554,8 +579,16 @@ ${combinedContent}
554
579
  };
555
580
  }
556
581
  };
557
- function isStdioConfig(config) {
558
- return "command" in config;
582
+
583
+ // src/mcp/mcp-tool-name.ts
584
+ function formatMcpToolName(serverName, toolName) {
585
+ return `mcp__${serverName}__${toolName}`;
586
+ }
587
+ function isMcpPrefixedToolName(name) {
588
+ if (!name.startsWith("mcp__")) {
589
+ return false;
590
+ }
591
+ return name.slice("mcp__".length).includes("__");
559
592
  }
560
593
  var MCPClient = class {
561
594
  client;
@@ -567,16 +600,24 @@ var MCPClient = class {
567
600
  constructor(config) {
568
601
  this._name = config.name;
569
602
  this.client = new Client(
570
- { name: "agent-sdk-client", version: "0.1.0" },
603
+ { name: "agent-sdk-client", version: PACKAGE_VERSION },
571
604
  { capabilities: {} }
572
605
  );
573
- if (isStdioConfig(config)) {
606
+ if (config.transport === "stdio") {
607
+ if (!config.command) {
608
+ throw new Error(`MCP server "${config.name}": stdio transport requires command`);
609
+ }
610
+ const cwd = (config.cwd ?? "").trim();
574
611
  this.transport = new StdioClientTransport({
575
612
  command: config.command,
576
613
  args: config.args,
577
- env: config.env
614
+ env: config.env,
615
+ ...cwd !== "" ? { cwd } : {}
578
616
  });
579
617
  } else {
618
+ if (!config.url) {
619
+ throw new Error(`MCP server "${config.name}": http transport requires url`);
620
+ }
580
621
  this.transport = new StreamableHTTPClientTransport(
581
622
  new URL(config.url),
582
623
  { requestInit: { headers: config.headers } }
@@ -685,7 +726,7 @@ var MCPClient = class {
685
726
  }
686
727
  toToolDefinitions() {
687
728
  return this._tools.map((tool) => ({
688
- name: `mcp_${this._name}__${tool.name}`,
729
+ name: formatMcpToolName(this._name, tool.name),
689
730
  description: tool.description || `MCP tool: ${tool.name}`,
690
731
  parameters: this.convertSchema(tool.inputSchema),
691
732
  handler: async (args) => this.callTool(tool.name, args),
@@ -695,7 +736,7 @@ var MCPClient = class {
695
736
  }
696
737
  convertSchema(schema) {
697
738
  if (!schema || !schema.properties) {
698
- return z.object({}).passthrough();
739
+ return z.looseObject({});
699
740
  }
700
741
  const shape = {};
701
742
  for (const [key, value] of Object.entries(schema.properties)) {
@@ -716,7 +757,7 @@ var MCPClient = class {
716
757
  zodField = z.array(z.any());
717
758
  break;
718
759
  case "object":
719
- zodField = z.object({}).passthrough();
760
+ zodField = z.looseObject({});
720
761
  break;
721
762
  default:
722
763
  zodField = z.any();
@@ -760,7 +801,7 @@ var MCPAdapter = class {
760
801
  await client.connect();
761
802
  this.clients.set(config.name, client);
762
803
  for (const tool of client.tools) {
763
- const fullName = `mcp_${config.name}__${tool.name}`;
804
+ const fullName = formatMcpToolName(config.name, tool.name);
764
805
  this.toolMap.set(fullName, { client, toolName: tool.name });
765
806
  }
766
807
  }
@@ -1312,9 +1353,12 @@ var StreamChunkProcessor = class {
1312
1353
  currentToolCall = null;
1313
1354
  lastUsage;
1314
1355
  inTextBlock = false;
1356
+ inThinkingBlock = false;
1315
1357
  emitTextBoundaries;
1358
+ emitThinkingBoundaries;
1316
1359
  constructor(options) {
1317
1360
  this.emitTextBoundaries = options?.emitTextBoundaries ?? true;
1361
+ this.emitThinkingBoundaries = options?.emitThinkingBoundaries ?? true;
1318
1362
  }
1319
1363
  processChunk(chunk) {
1320
1364
  const events = [];
@@ -1324,8 +1368,15 @@ var StreamChunkProcessor = class {
1324
1368
  this.inTextBlock = false;
1325
1369
  }
1326
1370
  };
1371
+ const endThinkingBlockIfNeeded = () => {
1372
+ if (this.emitThinkingBoundaries && this.inThinkingBlock) {
1373
+ events.push({ type: "thinking_end" });
1374
+ this.inThinkingBlock = false;
1375
+ }
1376
+ };
1327
1377
  switch (chunk.type) {
1328
1378
  case "text":
1379
+ endThinkingBlockIfNeeded();
1329
1380
  if (chunk.content) {
1330
1381
  if (this.emitTextBoundaries && !this.inTextBlock) {
1331
1382
  events.push({ type: "text_start" });
@@ -1335,6 +1386,7 @@ var StreamChunkProcessor = class {
1335
1386
  }
1336
1387
  break;
1337
1388
  case "tool_call_start":
1389
+ endThinkingBlockIfNeeded();
1338
1390
  endTextBlockIfNeeded();
1339
1391
  if (this.currentToolCall) {
1340
1392
  events.push(...this.finalizeStreamingToolCall());
@@ -1380,6 +1432,7 @@ var StreamChunkProcessor = class {
1380
1432
  }
1381
1433
  break;
1382
1434
  case "tool_call": {
1435
+ endThinkingBlockIfNeeded();
1383
1436
  endTextBlockIfNeeded();
1384
1437
  if (!chunk.toolCall) break;
1385
1438
  const tc = chunk.toolCall;
@@ -1398,6 +1451,7 @@ var StreamChunkProcessor = class {
1398
1451
  break;
1399
1452
  }
1400
1453
  case "tool_call_end":
1454
+ endThinkingBlockIfNeeded();
1401
1455
  endTextBlockIfNeeded();
1402
1456
  if (this.currentToolCall) {
1403
1457
  events.push(...this.finalizeStreamingToolCall());
@@ -1405,6 +1459,16 @@ var StreamChunkProcessor = class {
1405
1459
  break;
1406
1460
  case "thinking":
1407
1461
  endTextBlockIfNeeded();
1462
+ if (this.emitThinkingBoundaries) {
1463
+ const opensBlock = !this.inThinkingBlock && (chunk.content !== void 0 || chunk.signature !== void 0);
1464
+ if (opensBlock) {
1465
+ events.push({
1466
+ type: "thinking_start",
1467
+ ...chunk.signature !== void 0 ? { signature: chunk.signature } : {}
1468
+ });
1469
+ this.inThinkingBlock = true;
1470
+ }
1471
+ }
1408
1472
  if (chunk.content !== void 0) {
1409
1473
  events.push({
1410
1474
  type: "thinking",
@@ -1413,7 +1477,12 @@ var StreamChunkProcessor = class {
1413
1477
  });
1414
1478
  }
1415
1479
  break;
1480
+ case "thinking_block_end":
1481
+ endThinkingBlockIfNeeded();
1482
+ endTextBlockIfNeeded();
1483
+ break;
1416
1484
  case "error":
1485
+ endThinkingBlockIfNeeded();
1417
1486
  endTextBlockIfNeeded();
1418
1487
  if (chunk.error) {
1419
1488
  events.push({
@@ -1441,6 +1510,10 @@ var StreamChunkProcessor = class {
1441
1510
  /** End open text block and finalize any in-progress streamed tool call. */
1442
1511
  flush() {
1443
1512
  const events = [];
1513
+ if (this.emitThinkingBoundaries && this.inThinkingBlock) {
1514
+ events.push({ type: "thinking_end" });
1515
+ this.inThinkingBlock = false;
1516
+ }
1444
1517
  if (this.emitTextBoundaries && this.inTextBlock) {
1445
1518
  events.push({ type: "text_end" });
1446
1519
  this.inTextBlock = false;
@@ -1585,11 +1658,52 @@ var SummarizationCompressor = class {
1585
1658
  this.options = options;
1586
1659
  }
1587
1660
  name = "summarization";
1661
+ getSessionId() {
1662
+ return this.options.sessionIdProvider?.() ?? this.options.sessionId;
1663
+ }
1588
1664
  async compress(messages, targetTokens) {
1665
+ const startedAt = Date.now();
1589
1666
  const preserveRecent = this.options.preserveRecent ?? 6;
1667
+ const sessionId = this.getSessionId();
1668
+ emitSDKLog({
1669
+ logger: this.options.logger,
1670
+ logLevel: this.options.logLevel,
1671
+ level: "info",
1672
+ event: {
1673
+ component: "memory",
1674
+ event: "context.compress.start",
1675
+ message: "Starting context compression",
1676
+ operation: "compress",
1677
+ sessionId,
1678
+ metadata: {
1679
+ compressor: this.name,
1680
+ messageCount: messages.length,
1681
+ targetTokens,
1682
+ preserveRecent
1683
+ }
1684
+ }
1685
+ });
1590
1686
  const systemMessages = messages.filter((m) => m.role === "system");
1591
1687
  const nonSystemMessages = messages.filter((m) => m.role !== "system");
1592
1688
  if (nonSystemMessages.length <= preserveRecent) {
1689
+ emitSDKLog({
1690
+ logger: this.options.logger,
1691
+ logLevel: this.options.logLevel,
1692
+ level: "debug",
1693
+ event: {
1694
+ component: "memory",
1695
+ event: "context.compress.skipped",
1696
+ message: "Skipped compression because there are not enough messages",
1697
+ operation: "compress",
1698
+ sessionId,
1699
+ durationMs: Date.now() - startedAt,
1700
+ metadata: {
1701
+ compressor: this.name,
1702
+ messageCount: messages.length,
1703
+ preserveRecent
1704
+ }
1705
+ }
1706
+ });
1593
1707
  return messages;
1594
1708
  }
1595
1709
  const recentMessages = nonSystemMessages.slice(-preserveRecent);
@@ -1599,21 +1713,68 @@ var SummarizationCompressor = class {
1599
1713
  this.options.maxSummaryTokens ?? 4e3,
1600
1714
  Math.floor(targetTokens * 0.3)
1601
1715
  );
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
- ];
1716
+ try {
1717
+ const summaryResponse = await this.model.complete({
1718
+ messages: [
1719
+ { role: "system", content: summaryPrompt },
1720
+ ...messagesToSummarize
1721
+ ],
1722
+ maxTokens,
1723
+ logger: this.options.logger,
1724
+ logLevel: this.options.logLevel,
1725
+ redaction: this.options.redaction,
1726
+ sessionId
1727
+ });
1728
+ const compressedMessages = [
1729
+ ...systemMessages,
1730
+ {
1731
+ role: "system",
1732
+ content: this.wrapSummary(summaryResponse.content)
1733
+ },
1734
+ ...recentMessages
1735
+ ];
1736
+ emitSDKLog({
1737
+ logger: this.options.logger,
1738
+ logLevel: this.options.logLevel,
1739
+ level: "info",
1740
+ event: {
1741
+ component: "memory",
1742
+ event: "context.compress.end",
1743
+ message: "Context compression completed",
1744
+ operation: "compress",
1745
+ sessionId,
1746
+ durationMs: Date.now() - startedAt,
1747
+ metadata: {
1748
+ compressor: this.name,
1749
+ originalMessageCount: messages.length,
1750
+ compressedMessageCount: compressedMessages.length
1751
+ }
1752
+ }
1753
+ });
1754
+ return compressedMessages;
1755
+ } catch (error) {
1756
+ const err = error instanceof Error ? error : new Error(String(error));
1757
+ emitSDKLog({
1758
+ logger: this.options.logger,
1759
+ logLevel: this.options.logLevel,
1760
+ level: "error",
1761
+ event: {
1762
+ component: "memory",
1763
+ event: "context.compress.error",
1764
+ message: "Context compression failed",
1765
+ operation: "compress",
1766
+ sessionId,
1767
+ durationMs: Date.now() - startedAt,
1768
+ errorName: err.name,
1769
+ errorMessage: err.message,
1770
+ metadata: {
1771
+ compressor: this.name,
1772
+ messageCount: messages.length
1773
+ }
1774
+ }
1775
+ });
1776
+ throw err;
1777
+ }
1617
1778
  }
1618
1779
  /**
1619
1780
  * 构建默认摘要提示 (借鉴 Opencode 模板)
@@ -1799,21 +1960,7 @@ var ContextManager = class {
1799
1960
  };
1800
1961
 
1801
1962
  // 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
- }
1963
+ var DEFAULT_MAX_ITERATIONS = 400;
1817
1964
  var Agent = class _Agent {
1818
1965
  config;
1819
1966
  toolRegistry;
@@ -1831,22 +1978,39 @@ var Agent = class _Agent {
1831
1978
  // inputTokens/outputTokens: 累计消耗
1832
1979
  // totalTokens: 累计总消耗 (inputTokens + outputTokens)
1833
1980
  sessionUsage = _Agent.createEmptySessionUsage();
1981
+ static resolveModel(config) {
1982
+ if (config.model) {
1983
+ if (config.modelConfig) {
1984
+ throw new Error("AgentConfig: pass only one of `model` or `modelConfig`");
1985
+ }
1986
+ return config.model;
1987
+ }
1988
+ if (config.modelConfig) {
1989
+ return createModel(config.modelConfig, config.env);
1990
+ }
1991
+ throw new Error("AgentConfig: `model` or `modelConfig` is required");
1992
+ }
1834
1993
  constructor(config) {
1994
+ const resolvedModel = _Agent.resolveModel(config);
1835
1995
  this.config = {
1836
- maxIterations: 200,
1996
+ maxIterations: DEFAULT_MAX_ITERATIONS,
1837
1997
  streaming: true,
1838
- ...config
1998
+ ...config,
1999
+ model: resolvedModel,
2000
+ modelConfig: void 0
1839
2001
  };
2002
+ this.config.maxIterations = this.config.maxIterations ?? DEFAULT_MAX_ITERATIONS;
1840
2003
  this.skillRegistry = createSkillRegistry({
1841
- cwd: config.cwd,
1842
- userBasePath: config.userBasePath
2004
+ cwd: this.config.cwd,
2005
+ userBasePath: this.config.userBasePath
1843
2006
  });
1844
2007
  this.toolRegistry = new ToolRegistry({
1845
2008
  executionPolicy: {
1846
2009
  disallowedTools: this.config.disallowedTools,
1847
2010
  allowedTools: this.config.allowedTools,
1848
2011
  canUseTool: this.config.canUseTool
1849
- }
2012
+ },
2013
+ hookObserver: this.config.callbacks?.lifecycle?.hooks
1850
2014
  });
1851
2015
  this.registerInitialTools();
1852
2016
  const subagentEnabled = this.config.subagent?.enabled !== false;
@@ -1860,23 +2024,40 @@ var Agent = class _Agent {
1860
2024
  } else if (this.toolRegistry.has("Agent")) {
1861
2025
  this.toolRegistry.unregister("Agent");
1862
2026
  }
1863
- if (config.hookManager) {
1864
- this.toolRegistry.setHookManager(config.hookManager);
1865
- } else if (config.hookConfigDir !== void 0) {
2027
+ if (this.config.hookManager) {
2028
+ this.toolRegistry.setHookManager(this.config.hookManager);
2029
+ } else if (this.config.hookConfigDir !== void 0) {
1866
2030
  const hm = HookManager.create();
1867
2031
  this.toolRegistry.setHookManager(hm);
1868
- this.hookDiscoverPromise = hm.discoverAndLoad(config.hookConfigDir);
2032
+ this.hookDiscoverPromise = hm.discoverAndLoad(this.config.hookConfigDir);
1869
2033
  }
1870
2034
  this.sessionManager = new SessionManager({
1871
- type: config.storage?.type || "jsonl",
1872
- basePath: getSessionStoragePath(config.userBasePath)
2035
+ type: this.config.storage?.type || "jsonl",
2036
+ basePath: getSessionStoragePath(this.config.userBasePath)
1873
2037
  });
1874
- if (config.contextManagement !== false) {
1875
- const cmConfig = config.contextManagement === true ? {} : config.contextManagement ?? {};
1876
- this.contextManager = new ContextManager(config.model, cmConfig);
2038
+ if (this.config.contextManagement !== false) {
2039
+ const cmConfig = this.config.contextManagement === true ? {} : this.config.contextManagement ?? {};
2040
+ const compressor = cmConfig.compressor ?? new SummarizationCompressor(this.config.model, {
2041
+ logger: this.config.logger,
2042
+ logLevel: this.config.logLevel,
2043
+ redaction: this.config.redaction,
2044
+ sessionIdProvider: () => this.sessionManager.sessionId ?? void 0
2045
+ });
2046
+ this.contextManager = new ContextManager(this.config.model, {
2047
+ ...cmConfig,
2048
+ compressor
2049
+ });
1877
2050
  }
1878
2051
  this.initPromise = this.initializeAsync();
1879
2052
  }
2053
+ log(level, event) {
2054
+ emitSDKLog({
2055
+ logger: this.config.logger,
2056
+ logLevel: this.config.logLevel,
2057
+ level,
2058
+ event
2059
+ });
2060
+ }
1880
2061
  /**
1881
2062
  * 注册内置 + 自定义工具,或仅 {@link AgentConfig.exclusiveTools}。
1882
2063
  */
@@ -1921,7 +2102,14 @@ var Agent = class _Agent {
1921
2102
  await this.initializeMCP(this.config.mcpServers);
1922
2103
  }
1923
2104
  } catch (err) {
1924
- console.error("Failed to initialize:", err);
2105
+ const error = err instanceof Error ? err : new Error(String(err));
2106
+ this.log("error", {
2107
+ component: "agent",
2108
+ event: "agent.initialize.error",
2109
+ message: "Failed to initialize agent resources",
2110
+ errorName: error.name,
2111
+ errorMessage: error.message
2112
+ });
1925
2113
  }
1926
2114
  }
1927
2115
  /**
@@ -1940,7 +2128,17 @@ var Agent = class _Agent {
1940
2128
  try {
1941
2129
  await this.connectMCP(serverConfig);
1942
2130
  } catch (err) {
1943
- console.error(`Failed to connect MCP server "${serverConfig.name}":`, err);
2131
+ const error = err instanceof Error ? err : new Error(String(err));
2132
+ this.log("error", {
2133
+ component: "tooling",
2134
+ event: "mcp.connect.error",
2135
+ message: `Failed to connect MCP server "${serverConfig.name}"`,
2136
+ errorName: error.name,
2137
+ errorMessage: error.message,
2138
+ metadata: {
2139
+ serverName: serverConfig.name
2140
+ }
2141
+ });
1944
2142
  }
1945
2143
  }
1946
2144
  }
@@ -1952,6 +2150,74 @@ var Agent = class _Agent {
1952
2150
  sessionId: this.sessionManager.sessionId ?? void 0
1953
2151
  };
1954
2152
  }
2153
+ baseRunContext() {
2154
+ return {
2155
+ sessionId: this.sessionManager.sessionId ?? void 0,
2156
+ cwd: this.config.cwd
2157
+ };
2158
+ }
2159
+ /**
2160
+ * 分发流式事件到 `callbacks.onEvent` 与 `lifecycle.onModelEvent` / `onModelUsage`。
2161
+ */
2162
+ emitStreamEvent(event) {
2163
+ try {
2164
+ this.config.callbacks?.onEvent?.(event);
2165
+ const lifecycle = this.config.callbacks?.lifecycle;
2166
+ if (lifecycle?.onModelEvent && isModelStreamEventType(event.type)) {
2167
+ lifecycle.onModelEvent(event);
2168
+ }
2169
+ if (event.type === "model_usage" && lifecycle?.onModelUsage) {
2170
+ lifecycle.onModelUsage({
2171
+ ...this.baseRunContext(),
2172
+ usage: event.usage,
2173
+ iteration: event.iteration,
2174
+ phase: event.phase
2175
+ });
2176
+ }
2177
+ } catch (err) {
2178
+ const e = err instanceof Error ? err : new Error(String(err));
2179
+ this.emitAgentError(e, { phase: "lifecycle_callback" });
2180
+ }
2181
+ }
2182
+ /** 标注、触发观察回调并返回供 `yield` 的事件 */
2183
+ streamOut(event, iteration) {
2184
+ const out = iteration !== void 0 ? this.annotateStreamEvent(event, iteration) : this.annotateStreamEvent(event);
2185
+ this.emitStreamEvent(out);
2186
+ return out;
2187
+ }
2188
+ emitAgentError(error, ctx) {
2189
+ try {
2190
+ this.config.callbacks?.lifecycle?.onAgentError?.(error, ctx);
2191
+ this.config.callbacks?.onError?.(error, ctx);
2192
+ } catch (err) {
2193
+ this.log("error", {
2194
+ component: "agent",
2195
+ event: "agent.callback.error",
2196
+ message: "Agent error callback threw",
2197
+ errorName: err instanceof Error ? err.name : "Error",
2198
+ errorMessage: err instanceof Error ? err.message : String(err)
2199
+ });
2200
+ }
2201
+ }
2202
+ safeLifecycleVoid(fn) {
2203
+ try {
2204
+ fn();
2205
+ } catch (err) {
2206
+ const e = err instanceof Error ? err : new Error(String(err));
2207
+ this.emitAgentError(e, { phase: "lifecycle_callback" });
2208
+ }
2209
+ }
2210
+ emitRunEnd(args) {
2211
+ this.safeLifecycleVoid(() => {
2212
+ this.config.callbacks?.lifecycle?.onRunEnd?.({
2213
+ ...this.baseRunContext(),
2214
+ reason: args.reason,
2215
+ iterations: args.iterations,
2216
+ usage: args.usage,
2217
+ error: args.error
2218
+ });
2219
+ });
2220
+ }
1955
2221
  static createEmptySessionUsage() {
1956
2222
  return {
1957
2223
  contextTokens: 0,
@@ -2013,20 +2279,45 @@ ${content}`;
2013
2279
  }
2014
2280
  try {
2015
2281
  this.messages = await this.sessionManager.resumeSession(options.sessionId);
2282
+ this.safeLifecycleVoid(() => {
2283
+ this.config.callbacks?.lifecycle?.onSessionResume?.({
2284
+ sessionId: options.sessionId,
2285
+ messageCount: this.messages.length
2286
+ });
2287
+ });
2016
2288
  } catch {
2017
2289
  this.sessionManager.createSession(options.sessionId);
2290
+ this.safeLifecycleVoid(() => {
2291
+ this.config.callbacks?.lifecycle?.onSessionCreate?.({
2292
+ sessionId: this.sessionManager.sessionId ?? void 0
2293
+ });
2294
+ });
2018
2295
  }
2019
2296
  } else if (!this.sessionManager.sessionId) {
2020
2297
  this.resetSessionState();
2021
2298
  this.sessionManager.createSession();
2299
+ this.safeLifecycleVoid(() => {
2300
+ this.config.callbacks?.lifecycle?.onSessionCreate?.({
2301
+ sessionId: this.sessionManager.sessionId ?? void 0
2302
+ });
2303
+ });
2022
2304
  }
2023
2305
  if (this.messages.length === 0) {
2306
+ const usedRuntimePrompt = options?.systemPrompt !== void 0;
2024
2307
  const systemPrompt = this.buildSystemPrompt(
2025
2308
  options?.systemPrompt || this.config.systemPrompt
2026
2309
  );
2027
- this.messages.push({
2310
+ const sysMsg = {
2028
2311
  role: "system",
2029
2312
  content: systemPrompt
2313
+ };
2314
+ this.messages.push(sysMsg);
2315
+ this.safeLifecycleVoid(() => {
2316
+ this.config.callbacks?.lifecycle?.onSystemMessage?.(
2317
+ sysMsg,
2318
+ usedRuntimePrompt ? "runtime_prompt" : "default_prompt",
2319
+ this.baseRunContext()
2320
+ );
2030
2321
  });
2031
2322
  }
2032
2323
  if (this.config.memory !== false) {
@@ -2035,9 +2326,13 @@ ${content}`;
2035
2326
  const memoryManager = new MemoryManager(this.config.cwd, this.config.memoryConfig, this.config.userBasePath);
2036
2327
  const memoryContent = memoryManager.loadMemory();
2037
2328
  if (memoryContent) {
2038
- this.messages.push({
2329
+ const memMsg = {
2039
2330
  role: "system",
2040
2331
  content: memoryContent
2332
+ };
2333
+ this.messages.push(memMsg);
2334
+ this.safeLifecycleVoid(() => {
2335
+ this.config.callbacks?.lifecycle?.onSystemMessage?.(memMsg, "memory", this.baseRunContext());
2041
2336
  });
2042
2337
  }
2043
2338
  }
@@ -2047,21 +2342,60 @@ ${content}`;
2047
2342
  if (processed.invoked) {
2048
2343
  processedInput = processed.prompt;
2049
2344
  }
2050
- this.messages.push({
2345
+ const userMsg = {
2051
2346
  role: "user",
2052
2347
  content: processedInput
2348
+ };
2349
+ this.messages.push(userMsg);
2350
+ this.safeLifecycleVoid(() => {
2351
+ this.config.callbacks?.lifecycle?.onUserMessage?.(
2352
+ userMsg,
2353
+ processed.invoked ? "processed_input" : "raw_input",
2354
+ this.baseRunContext()
2355
+ );
2053
2356
  });
2054
- yield this.annotateStreamEvent({ type: "start", timestamp: Date.now() });
2357
+ this.log("info", {
2358
+ component: "agent",
2359
+ event: "agent.run.start",
2360
+ message: "Starting agent turn",
2361
+ sessionId: this.sessionManager.sessionId ?? void 0,
2362
+ metadata: {
2363
+ inputLength: input.length,
2364
+ processedInputLength: processedInput.length,
2365
+ includeRawStreamEvents: options?.includeRawStreamEvents === true
2366
+ }
2367
+ });
2368
+ this.safeLifecycleVoid(() => {
2369
+ this.config.callbacks?.lifecycle?.onRunStart?.({
2370
+ ...this.baseRunContext(),
2371
+ inputLength: input.length,
2372
+ processedInputLength: processedInput.length,
2373
+ resumeSessionId: options?.sessionId
2374
+ });
2375
+ });
2376
+ yield this.streamOut({ type: "start", timestamp: Date.now() });
2055
2377
  try {
2056
- const maxIterations = this.config.maxIterations || 10;
2378
+ const maxIterations = Math.max(1, this.config.maxIterations ?? DEFAULT_MAX_ITERATIONS);
2057
2379
  let totalUsage = {
2058
2380
  promptTokens: 0,
2059
2381
  completionTokens: 0,
2060
2382
  totalTokens: 0
2061
2383
  };
2062
- for (let iteration = 0; iteration < maxIterations; iteration++) {
2384
+ let iteration = 0;
2385
+ for (; iteration < maxIterations; iteration++) {
2063
2386
  if (signal?.aborted) {
2064
- yield this.annotateStreamEvent(
2387
+ this.log("info", {
2388
+ component: "agent",
2389
+ event: "agent.run.aborted",
2390
+ message: "Agent turn aborted before model request",
2391
+ sessionId: this.sessionManager.sessionId ?? void 0,
2392
+ iteration
2393
+ });
2394
+ this.emitRunEnd({ reason: "aborted", iterations: iteration, usage: totalUsage });
2395
+ this.safeLifecycleVoid(() => {
2396
+ this.config.callbacks?.lifecycle?.onRunAbort?.({ ...this.baseRunContext(), iteration });
2397
+ });
2398
+ yield this.streamOut(
2065
2399
  {
2066
2400
  type: "end",
2067
2401
  usage: totalUsage,
@@ -2072,17 +2406,60 @@ ${content}`;
2072
2406
  );
2073
2407
  return;
2074
2408
  }
2409
+ this.log("debug", {
2410
+ component: "agent",
2411
+ event: "agent.iteration.start",
2412
+ message: "Starting agent iteration",
2413
+ sessionId: this.sessionManager.sessionId ?? void 0,
2414
+ iteration,
2415
+ metadata: {
2416
+ messageCount: this.messages.length,
2417
+ toolCount: this.toolRegistry.getAll().length
2418
+ }
2419
+ });
2420
+ this.safeLifecycleVoid(() => {
2421
+ this.config.callbacks?.lifecycle?.onIterationStart?.({
2422
+ ...this.baseRunContext(),
2423
+ iteration,
2424
+ messageCount: this.messages.length,
2425
+ toolCount: this.toolRegistry.getAll().length
2426
+ });
2427
+ });
2075
2428
  const contextEvents = await this.checkContextCompression();
2076
2429
  for (const event of contextEvents) {
2077
- yield this.annotateStreamEvent(event, iteration);
2430
+ if (event.type === "context_compressed") {
2431
+ this.safeLifecycleVoid(() => {
2432
+ this.config.callbacks?.lifecycle?.onContextCompressed?.({
2433
+ ...this.baseRunContext(),
2434
+ iteration,
2435
+ stats: event.stats
2436
+ });
2437
+ });
2438
+ }
2439
+ yield this.streamOut(event, iteration);
2078
2440
  }
2441
+ this.safeLifecycleVoid(() => {
2442
+ this.config.callbacks?.lifecycle?.onModelRequestStart?.({
2443
+ ...this.baseRunContext(),
2444
+ iteration,
2445
+ messageCount: this.messages.length,
2446
+ toolCount: this.toolRegistry.getAll().length,
2447
+ temperature: this.config.temperature,
2448
+ maxTokens: this.config.maxTokens,
2449
+ includeRawStreamEvents: options?.includeRawStreamEvents
2450
+ });
2451
+ });
2079
2452
  const modelParams = {
2080
2453
  messages: this.messages,
2081
2454
  tools: this.toolRegistry.getAll(),
2082
2455
  temperature: this.config.temperature,
2083
2456
  maxTokens: this.config.maxTokens,
2084
2457
  signal,
2085
- includeRawStreamEvents: options?.includeRawStreamEvents
2458
+ includeRawStreamEvents: options?.includeRawStreamEvents,
2459
+ sessionId: this.sessionManager.sessionId ?? void 0,
2460
+ logger: this.config.logger,
2461
+ logLevel: this.config.logLevel,
2462
+ redaction: this.config.redaction
2086
2463
  };
2087
2464
  const stream = this.config.model.stream(modelParams);
2088
2465
  let hasToolCalls = false;
@@ -2125,7 +2502,7 @@ ${content}`;
2125
2502
  for await (const chunk of stream) {
2126
2503
  if (signal?.aborted) {
2127
2504
  for (const event of chunkProcessor.flush()) {
2128
- const out = this.annotateStreamEvent(event, iteration);
2505
+ const out = this.streamOut(event, iteration);
2129
2506
  yield out;
2130
2507
  applyStreamOut(out);
2131
2508
  }
@@ -2141,13 +2518,37 @@ ${content}`;
2141
2518
  ];
2142
2519
  }
2143
2520
  this.messages.push(assistantMessage2);
2521
+ this.safeLifecycleVoid(() => {
2522
+ this.config.callbacks?.lifecycle?.onAssistantMessage?.(assistantMessage2, {
2523
+ ...this.baseRunContext(),
2524
+ iteration
2525
+ });
2526
+ });
2144
2527
  }
2145
- this.messages.push({
2528
+ const interruptMsg = {
2146
2529
  role: "user",
2147
2530
  content: "[User interrupted the response]"
2531
+ };
2532
+ this.messages.push(interruptMsg);
2533
+ this.safeLifecycleVoid(() => {
2534
+ this.config.callbacks?.lifecycle?.onUserMessage?.(
2535
+ interruptMsg,
2536
+ "interruption_marker",
2537
+ this.baseRunContext()
2538
+ );
2148
2539
  });
2149
2540
  await this.sessionManager.saveMessages(this.messages);
2150
- yield this.annotateStreamEvent(
2541
+ this.safeLifecycleVoid(() => {
2542
+ this.config.callbacks?.lifecycle?.onMessagePersist?.({
2543
+ ...this.baseRunContext(),
2544
+ messageCount: this.messages.length
2545
+ });
2546
+ });
2547
+ this.emitRunEnd({ reason: "aborted", iterations: iteration + 1, usage: totalUsage });
2548
+ this.safeLifecycleVoid(() => {
2549
+ this.config.callbacks?.lifecycle?.onRunAbort?.({ ...this.baseRunContext(), iteration });
2550
+ });
2551
+ yield this.streamOut(
2151
2552
  {
2152
2553
  type: "end",
2153
2554
  usage: totalUsage,
@@ -2161,9 +2562,18 @@ ${content}`;
2161
2562
  }
2162
2563
  const events = chunkProcessor.processChunk(chunk);
2163
2564
  for (const event of events) {
2164
- const out = this.annotateStreamEvent(event, iteration);
2565
+ const out = this.streamOut(event, iteration);
2165
2566
  yield out;
2166
2567
  applyStreamOut(out);
2568
+ if (out.type === "end" && out.reason === "error" && out.error) {
2569
+ this.emitAgentError(out.error, { phase: "model", iteration });
2570
+ this.safeLifecycleVoid(() => {
2571
+ this.config.callbacks?.lifecycle?.onModelRequestError?.(out.error, {
2572
+ phase: "model",
2573
+ iteration
2574
+ });
2575
+ });
2576
+ }
2167
2577
  if (out.type === "end" && out.reason === "error") {
2168
2578
  fatalModelError = true;
2169
2579
  break;
@@ -2177,10 +2587,13 @@ ${content}`;
2177
2587
  return;
2178
2588
  }
2179
2589
  for (const event of chunkProcessor.flush()) {
2180
- const out = this.annotateStreamEvent(event, iteration);
2590
+ const out = this.streamOut(event, iteration);
2181
2591
  yield out;
2182
2592
  applyStreamOut(out);
2183
2593
  }
2594
+ this.safeLifecycleVoid(() => {
2595
+ this.config.callbacks?.lifecycle?.onModelRequestEnd?.({ ...this.baseRunContext(), iteration });
2596
+ });
2184
2597
  const assistantMessage = {
2185
2598
  role: "assistant",
2186
2599
  content: assistantContent
@@ -2202,13 +2615,36 @@ ${content}`;
2202
2615
  assistantMessage.toolCalls = toolCalls;
2203
2616
  }
2204
2617
  this.messages.push(assistantMessage);
2618
+ this.safeLifecycleVoid(() => {
2619
+ this.config.callbacks?.lifecycle?.onAssistantMessage?.(assistantMessage, {
2620
+ ...this.baseRunContext(),
2621
+ iteration
2622
+ });
2623
+ });
2205
2624
  if (!hasToolCalls) {
2625
+ this.log("debug", {
2626
+ component: "agent",
2627
+ event: "agent.iteration.end",
2628
+ message: "Iteration completed without tool calls",
2629
+ sessionId: this.sessionManager.sessionId ?? void 0,
2630
+ iteration,
2631
+ metadata: {
2632
+ assistantContentLength: assistantContent.length
2633
+ }
2634
+ });
2635
+ this.safeLifecycleVoid(() => {
2636
+ this.config.callbacks?.lifecycle?.onIterationEnd?.({
2637
+ ...this.baseRunContext(),
2638
+ iteration,
2639
+ hadToolCalls: false
2640
+ });
2641
+ });
2206
2642
  break;
2207
2643
  }
2208
- const toolResults = await this.executeTools(toolCalls);
2644
+ const toolResults = await this.executeTools(toolCalls, iteration);
2209
2645
  for (const result of toolResults) {
2210
2646
  if (result.isError && result.error) {
2211
- yield this.annotateStreamEvent(
2647
+ yield this.streamOut(
2212
2648
  {
2213
2649
  type: "tool_error",
2214
2650
  toolCallId: result.toolCallId,
@@ -2217,7 +2653,7 @@ ${content}`;
2217
2653
  iteration
2218
2654
  );
2219
2655
  }
2220
- yield this.annotateStreamEvent(
2656
+ yield this.streamOut(
2221
2657
  {
2222
2658
  type: "tool_result",
2223
2659
  toolCallId: result.toolCallId,
@@ -2225,38 +2661,110 @@ ${content}`;
2225
2661
  },
2226
2662
  iteration
2227
2663
  );
2228
- this.messages.push({
2664
+ const toolMsg = {
2229
2665
  role: "tool",
2230
2666
  toolCallId: result.toolCallId,
2231
2667
  content: result.content
2668
+ };
2669
+ this.messages.push(toolMsg);
2670
+ this.safeLifecycleVoid(() => {
2671
+ this.config.callbacks?.lifecycle?.onToolMessage?.(toolMsg, {
2672
+ ...this.baseRunContext(),
2673
+ iteration,
2674
+ toolCallId: result.toolCallId
2675
+ });
2232
2676
  });
2233
2677
  }
2678
+ this.log("debug", {
2679
+ component: "agent",
2680
+ event: "agent.iteration.end",
2681
+ message: "Iteration completed with tool calls",
2682
+ sessionId: this.sessionManager.sessionId ?? void 0,
2683
+ iteration,
2684
+ metadata: {
2685
+ toolCallCount: toolCalls.length,
2686
+ toolResultCount: toolResults.length
2687
+ }
2688
+ });
2689
+ this.safeLifecycleVoid(() => {
2690
+ this.config.callbacks?.lifecycle?.onIterationEnd?.({
2691
+ ...this.baseRunContext(),
2692
+ iteration,
2693
+ hadToolCalls: true
2694
+ });
2695
+ });
2234
2696
  }
2235
2697
  await this.sessionManager.saveMessages(this.messages);
2236
- yield this.annotateStreamEvent({
2698
+ this.safeLifecycleVoid(() => {
2699
+ this.config.callbacks?.lifecycle?.onMessagePersist?.({
2700
+ ...this.baseRunContext(),
2701
+ messageCount: this.messages.length
2702
+ });
2703
+ });
2704
+ const finishedByIterationCap = iteration >= maxIterations;
2705
+ const sessionIterations = finishedByIterationCap ? maxIterations : iteration + 1;
2706
+ yield this.streamOut({
2237
2707
  type: "session_summary",
2238
2708
  usage: totalUsage,
2239
- iterations: Math.min(maxIterations, this.messages.length)
2709
+ iterations: sessionIterations
2240
2710
  });
2241
- yield this.annotateStreamEvent({
2711
+ this.emitRunEnd({
2712
+ reason: finishedByIterationCap ? "max_iterations" : "complete",
2713
+ iterations: sessionIterations,
2714
+ usage: totalUsage
2715
+ });
2716
+ yield this.streamOut({
2242
2717
  type: "end",
2243
2718
  timestamp: Date.now(),
2244
- reason: "complete"
2719
+ reason: finishedByIterationCap ? "max_iterations" : "complete"
2720
+ });
2721
+ this.log("info", {
2722
+ component: "agent",
2723
+ event: "agent.run.end",
2724
+ message: finishedByIterationCap ? "Agent turn stopped at max iterations" : "Agent turn completed",
2725
+ sessionId: this.sessionManager.sessionId ?? void 0,
2726
+ metadata: {
2727
+ iterations: sessionIterations,
2728
+ promptTokens: totalUsage.promptTokens,
2729
+ completionTokens: totalUsage.completionTokens,
2730
+ totalTokens: totalUsage.totalTokens
2731
+ }
2245
2732
  });
2246
2733
  } catch (error) {
2247
2734
  if (error.name === "AbortError") {
2248
- yield this.annotateStreamEvent({
2735
+ this.log("info", {
2736
+ component: "agent",
2737
+ event: "agent.run.aborted",
2738
+ message: "Agent turn aborted",
2739
+ sessionId: this.sessionManager.sessionId ?? void 0
2740
+ });
2741
+ this.emitRunEnd({ reason: "aborted", iterations: 0 });
2742
+ this.safeLifecycleVoid(() => {
2743
+ this.config.callbacks?.lifecycle?.onRunAbort?.({ ...this.baseRunContext() });
2744
+ });
2745
+ yield this.streamOut({
2249
2746
  type: "end",
2250
2747
  timestamp: Date.now(),
2251
2748
  reason: "aborted"
2252
2749
  });
2253
2750
  return;
2254
2751
  }
2255
- yield this.annotateStreamEvent({
2752
+ const err = error instanceof Error ? error : new Error(String(error));
2753
+ this.log("error", {
2754
+ component: "agent",
2755
+ event: "agent.run.error",
2756
+ message: "Agent turn failed",
2757
+ sessionId: this.sessionManager.sessionId ?? void 0,
2758
+ errorName: err.name,
2759
+ errorMessage: err.message
2760
+ });
2761
+ this.emitAgentError(err, { phase: "run" });
2762
+ this.emitRunEnd({ reason: "error", iterations: 0, error: err });
2763
+ yield this.streamOut({
2256
2764
  type: "end",
2257
2765
  timestamp: Date.now(),
2258
2766
  reason: "error",
2259
- error
2767
+ error: err
2260
2768
  });
2261
2769
  }
2262
2770
  }
@@ -2348,6 +2856,12 @@ ${content}`;
2348
2856
  getSkillRegistry() {
2349
2857
  return this.skillRegistry;
2350
2858
  }
2859
+ /**
2860
+ * 解析后的模型适配器(`modelConfig` 已在构造时合并 `env` 并实例化)。
2861
+ */
2862
+ getModel() {
2863
+ return this.config.model;
2864
+ }
2351
2865
  /**
2352
2866
  * 处理用户输入,检测并处理 skill 调用
2353
2867
  * @param input 用户输入
@@ -2433,10 +2947,16 @@ ARGUMENTS: ${args}`;
2433
2947
  if (!this.mcpAdapter) {
2434
2948
  this.mcpAdapter = new MCPAdapter();
2435
2949
  }
2436
- await this.mcpAdapter.addServer(toMCPClientConfig(config));
2950
+ const resolved = config.transport === "stdio" ? {
2951
+ ...config,
2952
+ env: mergeMcpStdioEnv(this.config.env, config.env),
2953
+ cwd: (config.cwd ?? "").trim() || (this.config.cwd || process.cwd())
2954
+ } : config;
2955
+ await this.mcpAdapter.addServer(resolved);
2437
2956
  const mcpTools = this.mcpAdapter.getToolDefinitions();
2957
+ const serverPrefix = formatMcpToolName(config.name, "");
2438
2958
  for (const tool of mcpTools) {
2439
- if (!tool.name.startsWith(`mcp_${config.name}__`)) {
2959
+ if (!tool.name.startsWith(serverPrefix)) {
2440
2960
  continue;
2441
2961
  }
2442
2962
  if (this.toolRegistry.isDisallowed(tool.name)) {
@@ -2452,7 +2972,7 @@ ARGUMENTS: ${args}`;
2452
2972
  if (!this.mcpAdapter) return;
2453
2973
  const tools = this.toolRegistry.getAll();
2454
2974
  for (const tool of tools) {
2455
- if (tool.name.startsWith(`mcp_${name}__`)) {
2975
+ if (tool.name.startsWith(formatMcpToolName(name, ""))) {
2456
2976
  this.toolRegistry.unregister(tool.name);
2457
2977
  }
2458
2978
  }
@@ -2465,7 +2985,7 @@ ARGUMENTS: ${args}`;
2465
2985
  if (!this.mcpAdapter) return;
2466
2986
  const tools = this.toolRegistry.getAll();
2467
2987
  for (const tool of tools) {
2468
- if (tool.name.startsWith("mcp_") && tool.name.includes("__")) {
2988
+ if (isMcpPrefixedToolName(tool.name)) {
2469
2989
  this.toolRegistry.unregister(tool.name);
2470
2990
  }
2471
2991
  }
@@ -2638,7 +3158,10 @@ ${additionalContent}`;
2638
3158
  const normalizedType = request.subagent_type ?? "general-purpose";
2639
3159
  const requestedTimeout = request.timeout_ms ?? subagentConfig.timeoutMs;
2640
3160
  const timeoutMs = Math.min(requestedTimeout, subagentConfig.timeoutMs);
2641
- const maxIterations = Math.max(1, request.max_iterations ?? this.config.maxIterations ?? 50);
3161
+ const maxIterations = Math.max(
3162
+ 1,
3163
+ request.max_iterations ?? this.config.maxIterations ?? DEFAULT_MAX_ITERATIONS
3164
+ );
2642
3165
  const resolved = this.resolveSubagentTools(request);
2643
3166
  if (!resolved.tools) {
2644
3167
  return {
@@ -2710,21 +3233,158 @@ ${additionalContent}`;
2710
3233
  /**
2711
3234
  * 执行工具调用
2712
3235
  */
2713
- async executeTools(toolCalls) {
3236
+ async executeTools(toolCalls, iteration) {
2714
3237
  const results = await Promise.all(
2715
3238
  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
3239
+ this.safeLifecycleVoid(() => {
3240
+ this.config.callbacks?.lifecycle?.onToolCallPlanned?.(tc, {
3241
+ ...this.baseRunContext(),
3242
+ iteration
3243
+ });
2720
3244
  });
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
- };
3245
+ const startedAt = Date.now();
3246
+ this.safeLifecycleVoid(() => {
3247
+ this.config.callbacks?.lifecycle?.onToolExecutionStart?.({
3248
+ ...this.baseRunContext(),
3249
+ iteration,
3250
+ toolCallId: tc.id,
3251
+ toolName: tc.name,
3252
+ arguments: tc.arguments,
3253
+ projectDir: this.config.cwd || process.cwd(),
3254
+ agentDepth: this.agentDepth
3255
+ });
3256
+ });
3257
+ this.log("info", {
3258
+ component: "tooling",
3259
+ event: "tool.call.start",
3260
+ message: "Executing tool call",
3261
+ sessionId: this.sessionManager.sessionId ?? void 0,
3262
+ toolName: tc.name,
3263
+ toolCallId: tc.id
3264
+ });
3265
+ try {
3266
+ const result = await this.toolRegistry.execute(tc.name, tc.arguments, {
3267
+ toolCallId: tc.id,
3268
+ projectDir: this.config.cwd || process.cwd(),
3269
+ agentDepth: this.agentDepth
3270
+ });
3271
+ const durationMs = Date.now() - startedAt;
3272
+ const isError = Boolean(result.isError);
3273
+ const error = isError ? new Error(result.content) : void 0;
3274
+ this.safeLifecycleVoid(() => {
3275
+ this.config.callbacks?.lifecycle?.onToolExecutionEnd?.({
3276
+ ...this.baseRunContext(),
3277
+ iteration,
3278
+ toolCallId: tc.id,
3279
+ toolName: tc.name,
3280
+ arguments: tc.arguments,
3281
+ projectDir: this.config.cwd || process.cwd(),
3282
+ agentDepth: this.agentDepth,
3283
+ durationMs,
3284
+ isError,
3285
+ executionError: void 0
3286
+ });
3287
+ });
3288
+ this.safeLifecycleVoid(() => {
3289
+ this.config.callbacks?.lifecycle?.onToolResult?.({
3290
+ ...this.baseRunContext(),
3291
+ iteration,
3292
+ toolCallId: tc.id,
3293
+ toolName: tc.name,
3294
+ arguments: tc.arguments,
3295
+ projectDir: this.config.cwd || process.cwd(),
3296
+ agentDepth: this.agentDepth,
3297
+ durationMs,
3298
+ isError,
3299
+ result
3300
+ });
3301
+ });
3302
+ this.log(isError ? "warn" : "info", {
3303
+ component: "tooling",
3304
+ event: isError ? "tool.call.error" : "tool.call.end",
3305
+ message: isError ? "Tool call returned an error" : "Tool call completed",
3306
+ sessionId: this.sessionManager.sessionId ?? void 0,
3307
+ toolName: tc.name,
3308
+ toolCallId: tc.id,
3309
+ durationMs,
3310
+ ...error ? {
3311
+ errorName: error.name,
3312
+ errorMessage: error.message
3313
+ } : {},
3314
+ metadata: {
3315
+ resultLength: result.content.length
3316
+ }
3317
+ });
3318
+ return {
3319
+ toolCallId: tc.id,
3320
+ content: isError ? `Error: ${result.content}` : result.content,
3321
+ isError,
3322
+ error
3323
+ };
3324
+ } catch (error) {
3325
+ const err = error instanceof Error ? error : new Error(String(error));
3326
+ const durationMs = Date.now() - startedAt;
3327
+ const synthetic = { content: err.message, isError: true };
3328
+ this.emitAgentError(err, {
3329
+ phase: "tool",
3330
+ toolName: tc.name,
3331
+ toolCallId: tc.id,
3332
+ iteration
3333
+ });
3334
+ this.safeLifecycleVoid(() => {
3335
+ this.config.callbacks?.lifecycle?.onToolExecutionError?.(err, {
3336
+ phase: "tool",
3337
+ toolName: tc.name,
3338
+ toolCallId: tc.id,
3339
+ iteration
3340
+ });
3341
+ });
3342
+ this.safeLifecycleVoid(() => {
3343
+ this.config.callbacks?.lifecycle?.onToolExecutionEnd?.({
3344
+ ...this.baseRunContext(),
3345
+ iteration,
3346
+ toolCallId: tc.id,
3347
+ toolName: tc.name,
3348
+ arguments: tc.arguments,
3349
+ projectDir: this.config.cwd || process.cwd(),
3350
+ agentDepth: this.agentDepth,
3351
+ durationMs,
3352
+ isError: true,
3353
+ executionError: err
3354
+ });
3355
+ });
3356
+ this.safeLifecycleVoid(() => {
3357
+ this.config.callbacks?.lifecycle?.onToolResult?.({
3358
+ ...this.baseRunContext(),
3359
+ iteration,
3360
+ toolCallId: tc.id,
3361
+ toolName: tc.name,
3362
+ arguments: tc.arguments,
3363
+ projectDir: this.config.cwd || process.cwd(),
3364
+ agentDepth: this.agentDepth,
3365
+ durationMs,
3366
+ isError: true,
3367
+ result: synthetic
3368
+ });
3369
+ });
3370
+ this.log("error", {
3371
+ component: "tooling",
3372
+ event: "tool.call.error",
3373
+ message: "Tool call threw an exception",
3374
+ sessionId: this.sessionManager.sessionId ?? void 0,
3375
+ toolName: tc.name,
3376
+ toolCallId: tc.id,
3377
+ durationMs,
3378
+ errorName: err.name,
3379
+ errorMessage: err.message
3380
+ });
3381
+ return {
3382
+ toolCallId: tc.id,
3383
+ content: `Error: ${err.message}`,
3384
+ isError: true,
3385
+ error: err
3386
+ };
3387
+ }
2728
3388
  })
2729
3389
  );
2730
3390
  return results;
@@ -2768,7 +3428,8 @@ function transformConfig(config) {
2768
3428
  ...transport === "stdio" ? {
2769
3429
  command: serverConfig.command,
2770
3430
  args: serverConfig.args,
2771
- env: serverConfig.env
3431
+ env: serverConfig.env,
3432
+ cwd: serverConfig.cwd
2772
3433
  } : {
2773
3434
  url: serverConfig.url,
2774
3435
  headers: serverConfig.headers
@@ -2849,6 +3510,6 @@ function validateMCPConfig(config) {
2849
3510
  return errors;
2850
3511
  }
2851
3512
 
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
3513
+ export { Agent, DEFAULT_MAX_ITERATIONS, DEFAULT_SYSTEM_PROMPT, JsonlStorage, MCPAdapter, MCPClient, MODEL_STREAM_EVENT_TYPES, MemoryManager, MemoryStorage, PACKAGE_VERSION, SessionManager, SkillLoader, SkillRegistry, StreamChunkProcessor, createAgent, createJsonlStorage, createMCPAdapter, createMCPClient, createMemoryStorage, createSessionManager, createSkillLoader, createSkillRegistry, createStorage, formatMcpToolName, getLatestSessionId, getSessionStoragePath, isMcpPrefixedToolName, isModelStreamEventType, loadMCPConfig, parseSkillMd, validateMCPConfig };
3514
+ //# sourceMappingURL=chunk-EQ5CXH44.js.map
3515
+ //# sourceMappingURL=chunk-EQ5CXH44.js.map