@ddlqhd/agent-sdk 0.1.1 → 0.2.1

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 (40) hide show
  1. package/README.md +2 -1
  2. package/dist/{chunk-742JTNYI.js → chunk-D3UZNLZO.js} +570 -77
  3. package/dist/chunk-D3UZNLZO.js.map +1 -0
  4. package/dist/{chunk-AJD3DTL7.cjs → chunk-JXAJQGV5.cjs} +756 -107
  5. package/dist/chunk-JXAJQGV5.cjs.map +1 -0
  6. package/dist/{chunk-DQFTAD3I.cjs → chunk-NYZD3THB.cjs} +573 -76
  7. package/dist/chunk-NYZD3THB.cjs.map +1 -0
  8. package/dist/{chunk-Q3L4GIBG.cjs → chunk-P2X3AMDK.cjs} +115 -117
  9. package/dist/chunk-P2X3AMDK.cjs.map +1 -0
  10. package/dist/{chunk-THKEF32L.js → chunk-TKUPLTGJ.js} +110 -114
  11. package/dist/chunk-TKUPLTGJ.js.map +1 -0
  12. package/dist/{chunk-DXMVWGLJ.js → chunk-UHENMHUS.js} +744 -100
  13. package/dist/chunk-UHENMHUS.js.map +1 -0
  14. package/dist/cli/index.cjs +40 -31
  15. package/dist/cli/index.cjs.map +1 -1
  16. package/dist/cli/index.js +14 -5
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/{index-DGPDMbW5.d.cts → index-CnrY1ZA2.d.ts} +36 -14
  19. package/dist/{index-nEfayAzD.d.ts → index-DzBt4ewK.d.cts} +36 -14
  20. package/dist/index.cjs +128 -88
  21. package/dist/index.d.cts +42 -22
  22. package/dist/index.d.ts +42 -22
  23. package/dist/index.js +3 -3
  24. package/dist/models/index.cjs +14 -10
  25. package/dist/models/index.d.cts +7 -2
  26. package/dist/models/index.d.ts +7 -2
  27. package/dist/models/index.js +1 -1
  28. package/dist/tools/index.cjs +65 -57
  29. package/dist/tools/index.d.cts +3 -3
  30. package/dist/tools/index.d.ts +3 -3
  31. package/dist/tools/index.js +1 -1
  32. package/dist/{types-BLf9IqRs.d.cts → types-BUwjMwNH.d.cts} +279 -13
  33. package/dist/{types-BLf9IqRs.d.ts → types-BUwjMwNH.d.ts} +279 -13
  34. package/package.json +11 -11
  35. package/dist/chunk-742JTNYI.js.map +0 -1
  36. package/dist/chunk-AJD3DTL7.cjs.map +0 -1
  37. package/dist/chunk-DQFTAD3I.cjs.map +0 -1
  38. package/dist/chunk-DXMVWGLJ.js.map +0 -1
  39. package/dist/chunk-Q3L4GIBG.cjs.map +0 -1
  40. package/dist/chunk-THKEF32L.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { createModel, mergeMcpStdioEnv } from './chunk-742JTNYI.js';
3
- import { ToolRegistry, createAgentTool, HookManager, getAllBuiltinTools, getEnvironmentInfo, formatEnvironmentSection } from './chunk-THKEF32L.js';
2
+ import { createModel, emitSDKLog, mergeMcpStdioEnv } from './chunk-D3UZNLZO.js';
3
+ import { ToolRegistry, createAgentTool, HookManager, getAllBuiltinTools, getEnvironmentInfo, formatEnvironmentSection, SUBAGENT_EXPLORE_DEFAULT_TOOL_NAMES, subagentExploreDefaultsUnavailableMessage, resolveSubagentTypeAppend } from './chunk-TKUPLTGJ.js';
4
4
  import { readFileSync, promises, existsSync } from 'fs';
5
5
  import { dirname, join, resolve } from 'path';
6
6
  import { fileURLToPath } from 'url';
@@ -16,6 +16,25 @@ import { promisify } from 'util';
16
16
  var pkgDir = dirname(fileURLToPath(import.meta.url));
17
17
  var pkgPath = join(pkgDir, "..", "package.json");
18
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
+ }
19
38
  var JsonlStorage = class {
20
39
  basePath;
21
40
  constructor(config = {}) {
@@ -418,6 +437,7 @@ When to use tools:
418
437
  - **Glob** to find paths by pattern \u2014 not \`find\` or \`ls\` for discovery
419
438
  - **Grep** to search file contents \u2014 not \`grep\` or \`rg\` in the shell (built-in line-by-line regex search; correct integration)
420
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)
421
441
 
422
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.
423
443
 
@@ -438,7 +458,7 @@ Skills are instruction guides for specialized tasks. When activated, you receive
438
458
 
439
459
  ## Task Execution Principles
440
460
 
441
- 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.
442
462
  2. **Be Direct**: Go straight to the point. Try the simplest approach first.
443
463
  3. **Be Concise**: If you can say it in one sentence, don't use three.
444
464
  4. **Read Before Modify**: Always understand existing code before changing it.
@@ -448,17 +468,17 @@ Skills are instruction guides for specialized tasks. When activated, you receive
448
468
 
449
469
  ## Task Management with Todo List
450
470
 
451
- **MANDATORY**: For multi-step tasks, call \`TaskCreate\` FIRST.
471
+ **MANDATORY**: For multi-step tasks, call \`TodoWrite\` FIRST.
452
472
 
453
473
  **Workflow:**
454
- 1. Receive complex task -> call \`TaskCreate\` immediately
455
- 2. Start first task (in_progress) -> complete -> mark completed
456
- 3. Move to next task -> repeat
457
- 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
458
478
 
459
479
  **Example:**
460
480
  User: "Open Google, search X, summarize results, open first link, extract info"
461
- -> Multi-step task detected -> call \`TaskCreate\` FIRST, then execute.
481
+ -> Multi-step task detected -> call \`TodoWrite\` FIRST, then execute.
462
482
 
463
483
  ## Output Format
464
484
 
@@ -559,8 +579,16 @@ ${combinedContent}
559
579
  };
560
580
  }
561
581
  };
562
- function isStdioConfig(config) {
563
- 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("__");
564
592
  }
565
593
  var MCPClient = class {
566
594
  client;
@@ -575,13 +603,21 @@ var MCPClient = class {
575
603
  { name: "agent-sdk-client", version: PACKAGE_VERSION },
576
604
  { capabilities: {} }
577
605
  );
578
- 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();
579
611
  this.transport = new StdioClientTransport({
580
612
  command: config.command,
581
613
  args: config.args,
582
- env: config.env
614
+ env: config.env,
615
+ ...cwd !== "" ? { cwd } : {}
583
616
  });
584
617
  } else {
618
+ if (!config.url) {
619
+ throw new Error(`MCP server "${config.name}": http transport requires url`);
620
+ }
585
621
  this.transport = new StreamableHTTPClientTransport(
586
622
  new URL(config.url),
587
623
  { requestInit: { headers: config.headers } }
@@ -690,7 +726,7 @@ var MCPClient = class {
690
726
  }
691
727
  toToolDefinitions() {
692
728
  return this._tools.map((tool) => ({
693
- name: `mcp_${this._name}__${tool.name}`,
729
+ name: formatMcpToolName(this._name, tool.name),
694
730
  description: tool.description || `MCP tool: ${tool.name}`,
695
731
  parameters: this.convertSchema(tool.inputSchema),
696
732
  handler: async (args) => this.callTool(tool.name, args),
@@ -765,7 +801,7 @@ var MCPAdapter = class {
765
801
  await client.connect();
766
802
  this.clients.set(config.name, client);
767
803
  for (const tool of client.tools) {
768
- const fullName = `mcp_${config.name}__${tool.name}`;
804
+ const fullName = formatMcpToolName(config.name, tool.name);
769
805
  this.toolMap.set(fullName, { client, toolName: tool.name });
770
806
  }
771
807
  }
@@ -1317,9 +1353,12 @@ var StreamChunkProcessor = class {
1317
1353
  currentToolCall = null;
1318
1354
  lastUsage;
1319
1355
  inTextBlock = false;
1356
+ inThinkingBlock = false;
1320
1357
  emitTextBoundaries;
1358
+ emitThinkingBoundaries;
1321
1359
  constructor(options) {
1322
1360
  this.emitTextBoundaries = options?.emitTextBoundaries ?? true;
1361
+ this.emitThinkingBoundaries = options?.emitThinkingBoundaries ?? true;
1323
1362
  }
1324
1363
  processChunk(chunk) {
1325
1364
  const events = [];
@@ -1329,8 +1368,15 @@ var StreamChunkProcessor = class {
1329
1368
  this.inTextBlock = false;
1330
1369
  }
1331
1370
  };
1371
+ const endThinkingBlockIfNeeded = () => {
1372
+ if (this.emitThinkingBoundaries && this.inThinkingBlock) {
1373
+ events.push({ type: "thinking_end" });
1374
+ this.inThinkingBlock = false;
1375
+ }
1376
+ };
1332
1377
  switch (chunk.type) {
1333
1378
  case "text":
1379
+ endThinkingBlockIfNeeded();
1334
1380
  if (chunk.content) {
1335
1381
  if (this.emitTextBoundaries && !this.inTextBlock) {
1336
1382
  events.push({ type: "text_start" });
@@ -1340,6 +1386,7 @@ var StreamChunkProcessor = class {
1340
1386
  }
1341
1387
  break;
1342
1388
  case "tool_call_start":
1389
+ endThinkingBlockIfNeeded();
1343
1390
  endTextBlockIfNeeded();
1344
1391
  if (this.currentToolCall) {
1345
1392
  events.push(...this.finalizeStreamingToolCall());
@@ -1385,6 +1432,7 @@ var StreamChunkProcessor = class {
1385
1432
  }
1386
1433
  break;
1387
1434
  case "tool_call": {
1435
+ endThinkingBlockIfNeeded();
1388
1436
  endTextBlockIfNeeded();
1389
1437
  if (!chunk.toolCall) break;
1390
1438
  const tc = chunk.toolCall;
@@ -1403,6 +1451,7 @@ var StreamChunkProcessor = class {
1403
1451
  break;
1404
1452
  }
1405
1453
  case "tool_call_end":
1454
+ endThinkingBlockIfNeeded();
1406
1455
  endTextBlockIfNeeded();
1407
1456
  if (this.currentToolCall) {
1408
1457
  events.push(...this.finalizeStreamingToolCall());
@@ -1410,6 +1459,16 @@ var StreamChunkProcessor = class {
1410
1459
  break;
1411
1460
  case "thinking":
1412
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
+ }
1413
1472
  if (chunk.content !== void 0) {
1414
1473
  events.push({
1415
1474
  type: "thinking",
@@ -1418,7 +1477,12 @@ var StreamChunkProcessor = class {
1418
1477
  });
1419
1478
  }
1420
1479
  break;
1480
+ case "thinking_block_end":
1481
+ endThinkingBlockIfNeeded();
1482
+ endTextBlockIfNeeded();
1483
+ break;
1421
1484
  case "error":
1485
+ endThinkingBlockIfNeeded();
1422
1486
  endTextBlockIfNeeded();
1423
1487
  if (chunk.error) {
1424
1488
  events.push({
@@ -1446,6 +1510,10 @@ var StreamChunkProcessor = class {
1446
1510
  /** End open text block and finalize any in-progress streamed tool call. */
1447
1511
  flush() {
1448
1512
  const events = [];
1513
+ if (this.emitThinkingBoundaries && this.inThinkingBlock) {
1514
+ events.push({ type: "thinking_end" });
1515
+ this.inThinkingBlock = false;
1516
+ }
1449
1517
  if (this.emitTextBoundaries && this.inTextBlock) {
1450
1518
  events.push({ type: "text_end" });
1451
1519
  this.inTextBlock = false;
@@ -1590,11 +1658,52 @@ var SummarizationCompressor = class {
1590
1658
  this.options = options;
1591
1659
  }
1592
1660
  name = "summarization";
1661
+ getSessionId() {
1662
+ return this.options.sessionIdProvider?.() ?? this.options.sessionId;
1663
+ }
1593
1664
  async compress(messages, targetTokens) {
1665
+ const startedAt = Date.now();
1594
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
+ });
1595
1686
  const systemMessages = messages.filter((m) => m.role === "system");
1596
1687
  const nonSystemMessages = messages.filter((m) => m.role !== "system");
1597
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
+ });
1598
1707
  return messages;
1599
1708
  }
1600
1709
  const recentMessages = nonSystemMessages.slice(-preserveRecent);
@@ -1604,21 +1713,68 @@ var SummarizationCompressor = class {
1604
1713
  this.options.maxSummaryTokens ?? 4e3,
1605
1714
  Math.floor(targetTokens * 0.3)
1606
1715
  );
1607
- const summaryResponse = await this.model.complete({
1608
- messages: [
1609
- { role: "system", content: summaryPrompt },
1610
- ...messagesToSummarize
1611
- ],
1612
- maxTokens
1613
- });
1614
- return [
1615
- ...systemMessages,
1616
- {
1617
- role: "system",
1618
- content: this.wrapSummary(summaryResponse.content)
1619
- },
1620
- ...recentMessages
1621
- ];
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
+ }
1622
1778
  }
1623
1779
  /**
1624
1780
  * 构建默认摘要提示 (借鉴 Opencode 模板)
@@ -1804,21 +1960,7 @@ var ContextManager = class {
1804
1960
  };
1805
1961
 
1806
1962
  // src/core/agent.ts
1807
- function toMCPClientConfig(config, agentEnv) {
1808
- if (config.transport === "http") {
1809
- return {
1810
- name: config.name,
1811
- url: config.url,
1812
- headers: config.headers
1813
- };
1814
- }
1815
- return {
1816
- name: config.name,
1817
- command: config.command,
1818
- args: config.args,
1819
- env: mergeMcpStdioEnv(agentEnv, config.env)
1820
- };
1821
- }
1963
+ var DEFAULT_MAX_ITERATIONS = 400;
1822
1964
  var Agent = class _Agent {
1823
1965
  config;
1824
1966
  toolRegistry;
@@ -1851,12 +1993,13 @@ var Agent = class _Agent {
1851
1993
  constructor(config) {
1852
1994
  const resolvedModel = _Agent.resolveModel(config);
1853
1995
  this.config = {
1854
- maxIterations: 200,
1996
+ maxIterations: DEFAULT_MAX_ITERATIONS,
1855
1997
  streaming: true,
1856
1998
  ...config,
1857
1999
  model: resolvedModel,
1858
2000
  modelConfig: void 0
1859
2001
  };
2002
+ this.config.maxIterations = this.config.maxIterations ?? DEFAULT_MAX_ITERATIONS;
1860
2003
  this.skillRegistry = createSkillRegistry({
1861
2004
  cwd: this.config.cwd,
1862
2005
  userBasePath: this.config.userBasePath
@@ -1866,7 +2009,8 @@ var Agent = class _Agent {
1866
2009
  disallowedTools: this.config.disallowedTools,
1867
2010
  allowedTools: this.config.allowedTools,
1868
2011
  canUseTool: this.config.canUseTool
1869
- }
2012
+ },
2013
+ hookObserver: this.config.callbacks?.lifecycle?.hooks
1870
2014
  });
1871
2015
  this.registerInitialTools();
1872
2016
  const subagentEnabled = this.config.subagent?.enabled !== false;
@@ -1893,10 +2037,27 @@ var Agent = class _Agent {
1893
2037
  });
1894
2038
  if (this.config.contextManagement !== false) {
1895
2039
  const cmConfig = this.config.contextManagement === true ? {} : this.config.contextManagement ?? {};
1896
- this.contextManager = new ContextManager(this.config.model, cmConfig);
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
+ });
1897
2050
  }
1898
2051
  this.initPromise = this.initializeAsync();
1899
2052
  }
2053
+ log(level, event) {
2054
+ emitSDKLog({
2055
+ logger: this.config.logger,
2056
+ logLevel: this.config.logLevel,
2057
+ level,
2058
+ event
2059
+ });
2060
+ }
1900
2061
  /**
1901
2062
  * 注册内置 + 自定义工具,或仅 {@link AgentConfig.exclusiveTools}。
1902
2063
  */
@@ -1941,7 +2102,14 @@ var Agent = class _Agent {
1941
2102
  await this.initializeMCP(this.config.mcpServers);
1942
2103
  }
1943
2104
  } catch (err) {
1944
- 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
+ });
1945
2113
  }
1946
2114
  }
1947
2115
  /**
@@ -1960,7 +2128,17 @@ var Agent = class _Agent {
1960
2128
  try {
1961
2129
  await this.connectMCP(serverConfig);
1962
2130
  } catch (err) {
1963
- 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
+ });
1964
2142
  }
1965
2143
  }
1966
2144
  }
@@ -1972,6 +2150,74 @@ var Agent = class _Agent {
1972
2150
  sessionId: this.sessionManager.sessionId ?? void 0
1973
2151
  };
1974
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
+ }
1975
2221
  static createEmptySessionUsage() {
1976
2222
  return {
1977
2223
  contextTokens: 0,
@@ -2033,20 +2279,45 @@ ${content}`;
2033
2279
  }
2034
2280
  try {
2035
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
+ });
2036
2288
  } catch {
2037
2289
  this.sessionManager.createSession(options.sessionId);
2290
+ this.safeLifecycleVoid(() => {
2291
+ this.config.callbacks?.lifecycle?.onSessionCreate?.({
2292
+ sessionId: this.sessionManager.sessionId ?? void 0
2293
+ });
2294
+ });
2038
2295
  }
2039
2296
  } else if (!this.sessionManager.sessionId) {
2040
2297
  this.resetSessionState();
2041
2298
  this.sessionManager.createSession();
2299
+ this.safeLifecycleVoid(() => {
2300
+ this.config.callbacks?.lifecycle?.onSessionCreate?.({
2301
+ sessionId: this.sessionManager.sessionId ?? void 0
2302
+ });
2303
+ });
2042
2304
  }
2043
2305
  if (this.messages.length === 0) {
2306
+ const usedRuntimePrompt = options?.systemPrompt !== void 0;
2044
2307
  const systemPrompt = this.buildSystemPrompt(
2045
2308
  options?.systemPrompt || this.config.systemPrompt
2046
2309
  );
2047
- this.messages.push({
2310
+ const sysMsg = {
2048
2311
  role: "system",
2049
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
+ );
2050
2321
  });
2051
2322
  }
2052
2323
  if (this.config.memory !== false) {
@@ -2055,9 +2326,13 @@ ${content}`;
2055
2326
  const memoryManager = new MemoryManager(this.config.cwd, this.config.memoryConfig, this.config.userBasePath);
2056
2327
  const memoryContent = memoryManager.loadMemory();
2057
2328
  if (memoryContent) {
2058
- this.messages.push({
2329
+ const memMsg = {
2059
2330
  role: "system",
2060
2331
  content: memoryContent
2332
+ };
2333
+ this.messages.push(memMsg);
2334
+ this.safeLifecycleVoid(() => {
2335
+ this.config.callbacks?.lifecycle?.onSystemMessage?.(memMsg, "memory", this.baseRunContext());
2061
2336
  });
2062
2337
  }
2063
2338
  }
@@ -2067,21 +2342,60 @@ ${content}`;
2067
2342
  if (processed.invoked) {
2068
2343
  processedInput = processed.prompt;
2069
2344
  }
2070
- this.messages.push({
2345
+ const userMsg = {
2071
2346
  role: "user",
2072
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
+ );
2356
+ });
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
+ });
2073
2375
  });
2074
- yield this.annotateStreamEvent({ type: "start", timestamp: Date.now() });
2376
+ yield this.streamOut({ type: "start", timestamp: Date.now() });
2075
2377
  try {
2076
- const maxIterations = this.config.maxIterations || 10;
2378
+ const maxIterations = Math.max(1, this.config.maxIterations ?? DEFAULT_MAX_ITERATIONS);
2077
2379
  let totalUsage = {
2078
2380
  promptTokens: 0,
2079
2381
  completionTokens: 0,
2080
2382
  totalTokens: 0
2081
2383
  };
2082
- for (let iteration = 0; iteration < maxIterations; iteration++) {
2384
+ let iteration = 0;
2385
+ for (; iteration < maxIterations; iteration++) {
2083
2386
  if (signal?.aborted) {
2084
- 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(
2085
2399
  {
2086
2400
  type: "end",
2087
2401
  usage: totalUsage,
@@ -2092,10 +2406,49 @@ ${content}`;
2092
2406
  );
2093
2407
  return;
2094
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
+ });
2095
2428
  const contextEvents = await this.checkContextCompression();
2096
2429
  for (const event of contextEvents) {
2097
- 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);
2098
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
+ });
2099
2452
  const modelParams = {
2100
2453
  messages: this.messages,
2101
2454
  tools: this.toolRegistry.getAll(),
@@ -2103,7 +2456,10 @@ ${content}`;
2103
2456
  maxTokens: this.config.maxTokens,
2104
2457
  signal,
2105
2458
  includeRawStreamEvents: options?.includeRawStreamEvents,
2106
- sessionId: this.sessionManager.sessionId ?? void 0
2459
+ sessionId: this.sessionManager.sessionId ?? void 0,
2460
+ logger: this.config.logger,
2461
+ logLevel: this.config.logLevel,
2462
+ redaction: this.config.redaction
2107
2463
  };
2108
2464
  const stream = this.config.model.stream(modelParams);
2109
2465
  let hasToolCalls = false;
@@ -2146,7 +2502,7 @@ ${content}`;
2146
2502
  for await (const chunk of stream) {
2147
2503
  if (signal?.aborted) {
2148
2504
  for (const event of chunkProcessor.flush()) {
2149
- const out = this.annotateStreamEvent(event, iteration);
2505
+ const out = this.streamOut(event, iteration);
2150
2506
  yield out;
2151
2507
  applyStreamOut(out);
2152
2508
  }
@@ -2162,13 +2518,37 @@ ${content}`;
2162
2518
  ];
2163
2519
  }
2164
2520
  this.messages.push(assistantMessage2);
2521
+ this.safeLifecycleVoid(() => {
2522
+ this.config.callbacks?.lifecycle?.onAssistantMessage?.(assistantMessage2, {
2523
+ ...this.baseRunContext(),
2524
+ iteration
2525
+ });
2526
+ });
2165
2527
  }
2166
- this.messages.push({
2528
+ const interruptMsg = {
2167
2529
  role: "user",
2168
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
+ );
2169
2539
  });
2170
2540
  await this.sessionManager.saveMessages(this.messages);
2171
- 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(
2172
2552
  {
2173
2553
  type: "end",
2174
2554
  usage: totalUsage,
@@ -2182,9 +2562,18 @@ ${content}`;
2182
2562
  }
2183
2563
  const events = chunkProcessor.processChunk(chunk);
2184
2564
  for (const event of events) {
2185
- const out = this.annotateStreamEvent(event, iteration);
2565
+ const out = this.streamOut(event, iteration);
2186
2566
  yield out;
2187
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
+ }
2188
2577
  if (out.type === "end" && out.reason === "error") {
2189
2578
  fatalModelError = true;
2190
2579
  break;
@@ -2198,10 +2587,13 @@ ${content}`;
2198
2587
  return;
2199
2588
  }
2200
2589
  for (const event of chunkProcessor.flush()) {
2201
- const out = this.annotateStreamEvent(event, iteration);
2590
+ const out = this.streamOut(event, iteration);
2202
2591
  yield out;
2203
2592
  applyStreamOut(out);
2204
2593
  }
2594
+ this.safeLifecycleVoid(() => {
2595
+ this.config.callbacks?.lifecycle?.onModelRequestEnd?.({ ...this.baseRunContext(), iteration });
2596
+ });
2205
2597
  const assistantMessage = {
2206
2598
  role: "assistant",
2207
2599
  content: assistantContent
@@ -2223,13 +2615,36 @@ ${content}`;
2223
2615
  assistantMessage.toolCalls = toolCalls;
2224
2616
  }
2225
2617
  this.messages.push(assistantMessage);
2618
+ this.safeLifecycleVoid(() => {
2619
+ this.config.callbacks?.lifecycle?.onAssistantMessage?.(assistantMessage, {
2620
+ ...this.baseRunContext(),
2621
+ iteration
2622
+ });
2623
+ });
2226
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
+ });
2227
2642
  break;
2228
2643
  }
2229
- const toolResults = await this.executeTools(toolCalls);
2644
+ const toolResults = await this.executeTools(toolCalls, iteration);
2230
2645
  for (const result of toolResults) {
2231
2646
  if (result.isError && result.error) {
2232
- yield this.annotateStreamEvent(
2647
+ yield this.streamOut(
2233
2648
  {
2234
2649
  type: "tool_error",
2235
2650
  toolCallId: result.toolCallId,
@@ -2238,7 +2653,7 @@ ${content}`;
2238
2653
  iteration
2239
2654
  );
2240
2655
  }
2241
- yield this.annotateStreamEvent(
2656
+ yield this.streamOut(
2242
2657
  {
2243
2658
  type: "tool_result",
2244
2659
  toolCallId: result.toolCallId,
@@ -2246,38 +2661,110 @@ ${content}`;
2246
2661
  },
2247
2662
  iteration
2248
2663
  );
2249
- this.messages.push({
2664
+ const toolMsg = {
2250
2665
  role: "tool",
2251
2666
  toolCallId: result.toolCallId,
2252
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
+ });
2253
2676
  });
2254
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
+ });
2255
2696
  }
2256
2697
  await this.sessionManager.saveMessages(this.messages);
2257
- 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({
2258
2707
  type: "session_summary",
2259
2708
  usage: totalUsage,
2260
- iterations: Math.min(maxIterations, this.messages.length)
2709
+ iterations: sessionIterations
2710
+ });
2711
+ this.emitRunEnd({
2712
+ reason: finishedByIterationCap ? "max_iterations" : "complete",
2713
+ iterations: sessionIterations,
2714
+ usage: totalUsage
2261
2715
  });
2262
- yield this.annotateStreamEvent({
2716
+ yield this.streamOut({
2263
2717
  type: "end",
2264
2718
  timestamp: Date.now(),
2265
- 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
+ }
2266
2732
  });
2267
2733
  } catch (error) {
2268
2734
  if (error.name === "AbortError") {
2269
- 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({
2270
2746
  type: "end",
2271
2747
  timestamp: Date.now(),
2272
2748
  reason: "aborted"
2273
2749
  });
2274
2750
  return;
2275
2751
  }
2276
- 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({
2277
2764
  type: "end",
2278
2765
  timestamp: Date.now(),
2279
2766
  reason: "error",
2280
- error
2767
+ error: err
2281
2768
  });
2282
2769
  }
2283
2770
  }
@@ -2460,10 +2947,16 @@ ARGUMENTS: ${args}`;
2460
2947
  if (!this.mcpAdapter) {
2461
2948
  this.mcpAdapter = new MCPAdapter();
2462
2949
  }
2463
- await this.mcpAdapter.addServer(toMCPClientConfig(config, this.config.env));
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);
2464
2956
  const mcpTools = this.mcpAdapter.getToolDefinitions();
2957
+ const serverPrefix = formatMcpToolName(config.name, "");
2465
2958
  for (const tool of mcpTools) {
2466
- if (!tool.name.startsWith(`mcp_${config.name}__`)) {
2959
+ if (!tool.name.startsWith(serverPrefix)) {
2467
2960
  continue;
2468
2961
  }
2469
2962
  if (this.toolRegistry.isDisallowed(tool.name)) {
@@ -2479,7 +2972,7 @@ ARGUMENTS: ${args}`;
2479
2972
  if (!this.mcpAdapter) return;
2480
2973
  const tools = this.toolRegistry.getAll();
2481
2974
  for (const tool of tools) {
2482
- if (tool.name.startsWith(`mcp_${name}__`)) {
2975
+ if (tool.name.startsWith(formatMcpToolName(name, ""))) {
2483
2976
  this.toolRegistry.unregister(tool.name);
2484
2977
  }
2485
2978
  }
@@ -2492,7 +2985,7 @@ ARGUMENTS: ${args}`;
2492
2985
  if (!this.mcpAdapter) return;
2493
2986
  const tools = this.toolRegistry.getAll();
2494
2987
  for (const tool of tools) {
2495
- if (tool.name.startsWith("mcp_") && tool.name.includes("__")) {
2988
+ if (isMcpPrefixedToolName(tool.name)) {
2496
2989
  this.toolRegistry.unregister(tool.name);
2497
2990
  }
2498
2991
  }
@@ -2628,12 +3121,20 @@ ${additionalContent}`;
2628
3121
  defaultAllowedTools: this.config.subagent?.defaultAllowedTools
2629
3122
  };
2630
3123
  }
2631
- resolveSubagentTools(request) {
3124
+ resolveSubagentTools(request, subagentType) {
2632
3125
  const subagentConfig = this.getSubagentConfig();
2633
3126
  const parentTools = this.toolRegistry.getAll();
2634
3127
  const byName = new Map(parentTools.map((tool) => [tool.name, tool]));
2635
- const requestedNames = request.allowed_tools ?? subagentConfig.defaultAllowedTools;
3128
+ let requestedNames = request.allowed_tools ?? subagentConfig.defaultAllowedTools;
3129
+ let usedExploreDefaultNames = false;
3130
+ if (requestedNames === void 0 && subagentType === "explore") {
3131
+ requestedNames = [...SUBAGENT_EXPLORE_DEFAULT_TOOL_NAMES];
3132
+ usedExploreDefaultNames = true;
3133
+ }
2636
3134
  let selected = requestedNames ? requestedNames.map((name) => byName.get(name)).filter((tool) => tool !== void 0) : parentTools.filter((tool) => !tool.isDangerous);
3135
+ if (usedExploreDefaultNames && selected.length === 0) {
3136
+ return { error: subagentExploreDefaultsUnavailableMessage() };
3137
+ }
2637
3138
  selected = selected.filter((tool) => tool.name !== "Agent");
2638
3139
  selected = selected.filter((tool) => tool.name !== "AskUserQuestion");
2639
3140
  if (!subagentConfig.allowDangerousTools) {
@@ -2665,8 +3166,11 @@ ${additionalContent}`;
2665
3166
  const normalizedType = request.subagent_type ?? "general-purpose";
2666
3167
  const requestedTimeout = request.timeout_ms ?? subagentConfig.timeoutMs;
2667
3168
  const timeoutMs = Math.min(requestedTimeout, subagentConfig.timeoutMs);
2668
- const maxIterations = Math.max(1, request.max_iterations ?? this.config.maxIterations ?? 50);
2669
- const resolved = this.resolveSubagentTools(request);
3169
+ const maxIterations = Math.max(
3170
+ 1,
3171
+ request.max_iterations ?? this.config.maxIterations ?? DEFAULT_MAX_ITERATIONS
3172
+ );
3173
+ const resolved = this.resolveSubagentTools(request, normalizedType);
2670
3174
  if (!resolved.tools) {
2671
3175
  return {
2672
3176
  content: resolved.error ?? "Unable to resolve subagent tools",
@@ -2690,8 +3194,10 @@ ${additionalContent}`;
2690
3194
  this.activeSubagentRuns += 1;
2691
3195
  try {
2692
3196
  await child.waitForInit();
3197
+ const typeAppend = resolveSubagentTypeAppend(normalizedType, this.config.subagent);
3198
+ const mergedSystem = [typeAppend, request.system_prompt].filter((s) => typeof s === "string" && s.trim().length > 0).join("\n\n");
2693
3199
  const runPromise = child.run(request.prompt, {
2694
- systemPrompt: request.system_prompt
3200
+ systemPrompt: mergedSystem || void 0
2695
3201
  });
2696
3202
  const timeoutPromise = new Promise((_, reject) => {
2697
3203
  const timer = setTimeout(() => {
@@ -2737,21 +3243,158 @@ ${additionalContent}`;
2737
3243
  /**
2738
3244
  * 执行工具调用
2739
3245
  */
2740
- async executeTools(toolCalls) {
3246
+ async executeTools(toolCalls, iteration) {
2741
3247
  const results = await Promise.all(
2742
3248
  toolCalls.map(async (tc) => {
2743
- const result = await this.toolRegistry.execute(tc.name, tc.arguments, {
2744
- toolCallId: tc.id,
2745
- projectDir: this.config.cwd || process.cwd(),
2746
- agentDepth: this.agentDepth
3249
+ this.safeLifecycleVoid(() => {
3250
+ this.config.callbacks?.lifecycle?.onToolCallPlanned?.(tc, {
3251
+ ...this.baseRunContext(),
3252
+ iteration
3253
+ });
2747
3254
  });
2748
- const isError = Boolean(result.isError);
2749
- return {
2750
- toolCallId: tc.id,
2751
- content: isError ? `Error: ${result.content}` : result.content,
2752
- isError,
2753
- error: isError ? new Error(result.content) : void 0
2754
- };
3255
+ const startedAt = Date.now();
3256
+ this.safeLifecycleVoid(() => {
3257
+ this.config.callbacks?.lifecycle?.onToolExecutionStart?.({
3258
+ ...this.baseRunContext(),
3259
+ iteration,
3260
+ toolCallId: tc.id,
3261
+ toolName: tc.name,
3262
+ arguments: tc.arguments,
3263
+ projectDir: this.config.cwd || process.cwd(),
3264
+ agentDepth: this.agentDepth
3265
+ });
3266
+ });
3267
+ this.log("info", {
3268
+ component: "tooling",
3269
+ event: "tool.call.start",
3270
+ message: "Executing tool call",
3271
+ sessionId: this.sessionManager.sessionId ?? void 0,
3272
+ toolName: tc.name,
3273
+ toolCallId: tc.id
3274
+ });
3275
+ try {
3276
+ const result = await this.toolRegistry.execute(tc.name, tc.arguments, {
3277
+ toolCallId: tc.id,
3278
+ projectDir: this.config.cwd || process.cwd(),
3279
+ agentDepth: this.agentDepth
3280
+ });
3281
+ const durationMs = Date.now() - startedAt;
3282
+ const isError = Boolean(result.isError);
3283
+ const error = isError ? new Error(result.content) : void 0;
3284
+ this.safeLifecycleVoid(() => {
3285
+ this.config.callbacks?.lifecycle?.onToolExecutionEnd?.({
3286
+ ...this.baseRunContext(),
3287
+ iteration,
3288
+ toolCallId: tc.id,
3289
+ toolName: tc.name,
3290
+ arguments: tc.arguments,
3291
+ projectDir: this.config.cwd || process.cwd(),
3292
+ agentDepth: this.agentDepth,
3293
+ durationMs,
3294
+ isError,
3295
+ executionError: void 0
3296
+ });
3297
+ });
3298
+ this.safeLifecycleVoid(() => {
3299
+ this.config.callbacks?.lifecycle?.onToolResult?.({
3300
+ ...this.baseRunContext(),
3301
+ iteration,
3302
+ toolCallId: tc.id,
3303
+ toolName: tc.name,
3304
+ arguments: tc.arguments,
3305
+ projectDir: this.config.cwd || process.cwd(),
3306
+ agentDepth: this.agentDepth,
3307
+ durationMs,
3308
+ isError,
3309
+ result
3310
+ });
3311
+ });
3312
+ this.log(isError ? "warn" : "info", {
3313
+ component: "tooling",
3314
+ event: isError ? "tool.call.error" : "tool.call.end",
3315
+ message: isError ? "Tool call returned an error" : "Tool call completed",
3316
+ sessionId: this.sessionManager.sessionId ?? void 0,
3317
+ toolName: tc.name,
3318
+ toolCallId: tc.id,
3319
+ durationMs,
3320
+ ...error ? {
3321
+ errorName: error.name,
3322
+ errorMessage: error.message
3323
+ } : {},
3324
+ metadata: {
3325
+ resultLength: result.content.length
3326
+ }
3327
+ });
3328
+ return {
3329
+ toolCallId: tc.id,
3330
+ content: isError ? `Error: ${result.content}` : result.content,
3331
+ isError,
3332
+ error
3333
+ };
3334
+ } catch (error) {
3335
+ const err = error instanceof Error ? error : new Error(String(error));
3336
+ const durationMs = Date.now() - startedAt;
3337
+ const synthetic = { content: err.message, isError: true };
3338
+ this.emitAgentError(err, {
3339
+ phase: "tool",
3340
+ toolName: tc.name,
3341
+ toolCallId: tc.id,
3342
+ iteration
3343
+ });
3344
+ this.safeLifecycleVoid(() => {
3345
+ this.config.callbacks?.lifecycle?.onToolExecutionError?.(err, {
3346
+ phase: "tool",
3347
+ toolName: tc.name,
3348
+ toolCallId: tc.id,
3349
+ iteration
3350
+ });
3351
+ });
3352
+ this.safeLifecycleVoid(() => {
3353
+ this.config.callbacks?.lifecycle?.onToolExecutionEnd?.({
3354
+ ...this.baseRunContext(),
3355
+ iteration,
3356
+ toolCallId: tc.id,
3357
+ toolName: tc.name,
3358
+ arguments: tc.arguments,
3359
+ projectDir: this.config.cwd || process.cwd(),
3360
+ agentDepth: this.agentDepth,
3361
+ durationMs,
3362
+ isError: true,
3363
+ executionError: err
3364
+ });
3365
+ });
3366
+ this.safeLifecycleVoid(() => {
3367
+ this.config.callbacks?.lifecycle?.onToolResult?.({
3368
+ ...this.baseRunContext(),
3369
+ iteration,
3370
+ toolCallId: tc.id,
3371
+ toolName: tc.name,
3372
+ arguments: tc.arguments,
3373
+ projectDir: this.config.cwd || process.cwd(),
3374
+ agentDepth: this.agentDepth,
3375
+ durationMs,
3376
+ isError: true,
3377
+ result: synthetic
3378
+ });
3379
+ });
3380
+ this.log("error", {
3381
+ component: "tooling",
3382
+ event: "tool.call.error",
3383
+ message: "Tool call threw an exception",
3384
+ sessionId: this.sessionManager.sessionId ?? void 0,
3385
+ toolName: tc.name,
3386
+ toolCallId: tc.id,
3387
+ durationMs,
3388
+ errorName: err.name,
3389
+ errorMessage: err.message
3390
+ });
3391
+ return {
3392
+ toolCallId: tc.id,
3393
+ content: `Error: ${err.message}`,
3394
+ isError: true,
3395
+ error: err
3396
+ };
3397
+ }
2755
3398
  })
2756
3399
  );
2757
3400
  return results;
@@ -2795,7 +3438,8 @@ function transformConfig(config) {
2795
3438
  ...transport === "stdio" ? {
2796
3439
  command: serverConfig.command,
2797
3440
  args: serverConfig.args,
2798
- env: serverConfig.env
3441
+ env: serverConfig.env,
3442
+ cwd: serverConfig.cwd
2799
3443
  } : {
2800
3444
  url: serverConfig.url,
2801
3445
  headers: serverConfig.headers
@@ -2876,6 +3520,6 @@ function validateMCPConfig(config) {
2876
3520
  return errors;
2877
3521
  }
2878
3522
 
2879
- export { Agent, DEFAULT_SYSTEM_PROMPT, JsonlStorage, MCPAdapter, MCPClient, MemoryManager, MemoryStorage, PACKAGE_VERSION, SessionManager, SkillLoader, SkillRegistry, StreamChunkProcessor, createAgent, createJsonlStorage, createMCPAdapter, createMCPClient, createMemoryStorage, createSessionManager, createSkillLoader, createSkillRegistry, createStorage, getLatestSessionId, getSessionStoragePath, loadMCPConfig, parseSkillMd, validateMCPConfig };
2880
- //# sourceMappingURL=chunk-DXMVWGLJ.js.map
2881
- //# sourceMappingURL=chunk-DXMVWGLJ.js.map
3523
+ 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 };
3524
+ //# sourceMappingURL=chunk-UHENMHUS.js.map
3525
+ //# sourceMappingURL=chunk-UHENMHUS.js.map