yuangs 6.2.3 → 6.4.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 (166) hide show
  1. package/dist/agent/AgentRuntime.d.ts +7 -23
  2. package/dist/agent/AgentRuntime.js +35 -542
  3. package/dist/agent/AgentRuntime.js.map +1 -1
  4. package/dist/agent/DualAgentRuntime.js +16 -16
  5. package/dist/agent/DualAgentRuntime.js.map +1 -1
  6. package/dist/agent/ExecutionCompleter.d.ts +31 -0
  7. package/dist/agent/ExecutionCompleter.js +83 -0
  8. package/dist/agent/ExecutionCompleter.js.map +1 -0
  9. package/dist/agent/ExecutionHandler.d.ts +34 -0
  10. package/dist/agent/ExecutionHandler.js +227 -0
  11. package/dist/agent/ExecutionHandler.js.map +1 -0
  12. package/dist/agent/ExecutionLearning.d.ts +8 -0
  13. package/dist/agent/ExecutionLearning.js +31 -0
  14. package/dist/agent/ExecutionLearning.js.map +1 -0
  15. package/dist/agent/ExecutionRecovery.d.ts +26 -0
  16. package/dist/agent/ExecutionRecovery.js +99 -0
  17. package/dist/agent/ExecutionRecovery.js.map +1 -0
  18. package/dist/agent/ExecutionStabilizer.d.ts +20 -0
  19. package/dist/agent/ExecutionStabilizer.js +101 -0
  20. package/dist/agent/ExecutionStabilizer.js.map +1 -0
  21. package/dist/agent/ExecutionTypes.d.ts +13 -0
  22. package/dist/agent/ExecutionTypes.js +9 -0
  23. package/dist/agent/ExecutionTypes.js.map +1 -0
  24. package/dist/agent/LLMCaller.d.ts +19 -0
  25. package/dist/agent/LLMCaller.js +103 -0
  26. package/dist/agent/LLMCaller.js.map +1 -0
  27. package/dist/agent/PreFlightChecker.d.ts +51 -0
  28. package/dist/agent/PreFlightChecker.js +148 -0
  29. package/dist/agent/PreFlightChecker.js.map +1 -0
  30. package/dist/agent/commandSemantics.js +12 -0
  31. package/dist/agent/commandSemantics.js.map +1 -1
  32. package/dist/agent/errorTracker.d.ts +10 -0
  33. package/dist/agent/errorTracker.js +36 -0
  34. package/dist/agent/errorTracker.js.map +1 -1
  35. package/dist/agent/executor.d.ts +12 -90
  36. package/dist/agent/executor.js +59 -881
  37. package/dist/agent/executor.js.map +1 -1
  38. package/dist/agent/governance/core.js.map +1 -1
  39. package/dist/agent/governance/riskScoring.js +6 -3
  40. package/dist/agent/governance/riskScoring.js.map +1 -1
  41. package/dist/agent/governance.js +4 -36
  42. package/dist/agent/governance.js.map +1 -1
  43. package/dist/agent/llm.js +11 -17
  44. package/dist/agent/llm.js.map +1 -1
  45. package/dist/agent/policy/engine.d.ts +2 -5
  46. package/dist/agent/policy/engine.js.map +1 -1
  47. package/dist/agent/policy/index.d.ts +1 -0
  48. package/dist/agent/policy/index.js +1 -0
  49. package/dist/agent/policy/index.js.map +1 -1
  50. package/dist/agent/policy/policies/WorkdirWrite.d.ts +11 -0
  51. package/dist/agent/policy/policies/WorkdirWrite.js +65 -0
  52. package/dist/agent/policy/policies/WorkdirWrite.js.map +1 -0
  53. package/dist/agent/policy/policies/noDangerousShell.js +1 -1
  54. package/dist/agent/policy/policies/noDangerousShell.js.map +1 -1
  55. package/dist/agent/policy/types.d.ts +6 -4
  56. package/dist/agent/skills.d.ts +12 -17
  57. package/dist/agent/skills.js +45 -52
  58. package/dist/agent/skills.js.map +1 -1
  59. package/dist/agent/state.d.ts +33 -3
  60. package/dist/agent/state.js +14 -0
  61. package/dist/agent/state.js.map +1 -1
  62. package/dist/agent/tools/AnalyzeDependencies.d.ts +9 -0
  63. package/dist/agent/tools/AnalyzeDependencies.js +91 -0
  64. package/dist/agent/tools/AnalyzeDependencies.js.map +1 -0
  65. package/dist/agent/tools/AppendFile.d.ts +8 -0
  66. package/dist/agent/tools/AppendFile.js +36 -0
  67. package/dist/agent/tools/AppendFile.js.map +1 -0
  68. package/dist/agent/tools/CodeDiff.d.ts +8 -0
  69. package/dist/agent/tools/CodeDiff.js +36 -0
  70. package/dist/agent/tools/CodeDiff.js.map +1 -0
  71. package/dist/agent/tools/ContinueReading.d.ts +8 -0
  72. package/dist/agent/tools/ContinueReading.js +59 -0
  73. package/dist/agent/tools/ContinueReading.js.map +1 -0
  74. package/dist/agent/tools/FileInfo.d.ts +8 -0
  75. package/dist/agent/tools/FileInfo.js +43 -0
  76. package/dist/agent/tools/FileInfo.js.map +1 -0
  77. package/dist/agent/tools/GitDiff.d.ts +8 -0
  78. package/dist/agent/tools/GitDiff.js +40 -0
  79. package/dist/agent/tools/GitDiff.js.map +1 -0
  80. package/dist/agent/tools/GitLog.d.ts +8 -0
  81. package/dist/agent/tools/GitLog.js +36 -0
  82. package/dist/agent/tools/GitLog.js.map +1 -0
  83. package/dist/agent/tools/GitStatus.d.ts +8 -0
  84. package/dist/agent/tools/GitStatus.js +38 -0
  85. package/dist/agent/tools/GitStatus.js.map +1 -0
  86. package/dist/agent/tools/ListDirectoryTree.d.ts +9 -0
  87. package/dist/agent/tools/ListDirectoryTree.js +63 -0
  88. package/dist/agent/tools/ListDirectoryTree.js.map +1 -0
  89. package/dist/agent/tools/ListFiles.d.ts +9 -0
  90. package/dist/agent/tools/ListFiles.js +53 -0
  91. package/dist/agent/tools/ListFiles.js.map +1 -0
  92. package/dist/agent/tools/ReadFile.d.ts +8 -0
  93. package/dist/agent/tools/ReadFile.js +33 -0
  94. package/dist/agent/tools/ReadFile.js.map +1 -0
  95. package/dist/agent/tools/ReadFileLines.d.ts +8 -0
  96. package/dist/agent/tools/ReadFileLines.js +45 -0
  97. package/dist/agent/tools/ReadFileLines.js.map +1 -0
  98. package/dist/agent/tools/ReadFileLinesFromEnd.d.ts +8 -0
  99. package/dist/agent/tools/ReadFileLinesFromEnd.js +48 -0
  100. package/dist/agent/tools/ReadFileLinesFromEnd.js.map +1 -0
  101. package/dist/agent/tools/SearchInFiles.d.ts +8 -0
  102. package/dist/agent/tools/SearchInFiles.js +70 -0
  103. package/dist/agent/tools/SearchInFiles.js.map +1 -0
  104. package/dist/agent/tools/SearchSymbol.d.ts +8 -0
  105. package/dist/agent/tools/SearchSymbol.js +62 -0
  106. package/dist/agent/tools/SearchSymbol.js.map +1 -0
  107. package/dist/agent/tools/ShellCmd.d.ts +8 -0
  108. package/dist/agent/tools/ShellCmd.js +40 -0
  109. package/dist/agent/tools/ShellCmd.js.map +1 -0
  110. package/dist/agent/tools/WriteFile.d.ts +8 -0
  111. package/dist/agent/tools/WriteFile.js +35 -0
  112. package/dist/agent/tools/WriteFile.js.map +1 -0
  113. package/dist/agent/tools/index.d.ts +20 -0
  114. package/dist/agent/tools/index.js +50 -0
  115. package/dist/agent/tools/index.js.map +1 -0
  116. package/dist/agent/tools/pathSafety.d.ts +9 -0
  117. package/dist/agent/tools/pathSafety.js +85 -0
  118. package/dist/agent/tools/pathSafety.js.map +1 -0
  119. package/dist/agent/tools/registry.d.ts +40 -0
  120. package/dist/agent/tools/registry.js +77 -0
  121. package/dist/agent/tools/registry.js.map +1 -0
  122. package/dist/agent/tools/types.d.ts +21 -0
  123. package/dist/agent/tools/types.js +3 -0
  124. package/dist/agent/tools/types.js.map +1 -0
  125. package/dist/agent/tools/utils.d.ts +43 -0
  126. package/dist/agent/tools/utils.js +158 -0
  127. package/dist/agent/tools/utils.js.map +1 -0
  128. package/dist/agent/types.d.ts +5 -3
  129. package/dist/ai/client.d.ts +2 -2
  130. package/dist/ai/client.js +16 -23
  131. package/dist/ai/client.js.map +1 -1
  132. package/dist/audit/timeline.js +7 -5
  133. package/dist/audit/timeline.js.map +1 -1
  134. package/dist/cli.js +12 -6
  135. package/dist/cli.js.map +1 -1
  136. package/dist/commands/handleAIChat.js +1 -1
  137. package/dist/commands/handleAIChat.js.map +1 -1
  138. package/dist/commands/handleAICommand.js +3 -3
  139. package/dist/commands/handleAICommand.js.map +1 -1
  140. package/dist/commands/skillsCommands.js +4 -42
  141. package/dist/commands/skillsCommands.js.map +1 -1
  142. package/dist/core/ConfigService.d.ts +87 -0
  143. package/dist/core/ConfigService.js +259 -0
  144. package/dist/core/ConfigService.js.map +1 -0
  145. package/dist/core/executionRecord.d.ts +1 -1
  146. package/dist/core/explain.js +2 -2
  147. package/dist/core/explain.js.map +1 -1
  148. package/dist/core/git/BackupManager.d.ts +30 -0
  149. package/dist/core/git/BackupManager.js +153 -0
  150. package/dist/core/git/BackupManager.js.map +1 -0
  151. package/dist/core/git/ConflictResolver.js.map +1 -1
  152. package/dist/core/replayDiff.js +5 -5
  153. package/dist/core/replayDiff.js.map +1 -1
  154. package/dist/core/skillTypes.d.ts +22 -0
  155. package/dist/core/skillTypes.js +17 -0
  156. package/dist/core/skillTypes.js.map +1 -0
  157. package/dist/utils/Logger.d.ts +24 -5
  158. package/dist/utils/Logger.js +89 -63
  159. package/dist/utils/Logger.js.map +1 -1
  160. package/dist/utils/storage.d.ts +22 -0
  161. package/dist/utils/storage.js +62 -0
  162. package/dist/utils/storage.js.map +1 -0
  163. package/package.json +3 -1
  164. package/dist/core/ConfigManager.d.ts +0 -39
  165. package/dist/core/ConfigManager.js +0 -127
  166. package/dist/core/ConfigManager.js.map +0 -1
@@ -1,37 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
@@ -39,32 +6,30 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
6
  exports.AgentRuntime = void 0;
40
7
  const chalk_1 = __importDefault(require("chalk"));
41
8
  const crypto_1 = require("crypto");
42
- const marked = __importStar(require("marked"));
43
- const marked_terminal_1 = __importDefault(require("marked-terminal"));
44
- // Configure marked with TerminalRenderer
45
- const terminalRenderer = new marked_terminal_1.default();
46
- marked.setOptions({ renderer: terminalRenderer });
47
- const llmAdapter_1 = require("./llmAdapter");
48
- const llm_1 = require("./llm");
49
- const governance_1 = require("./governance");
50
- const executor_1 = require("./executor");
51
9
  const smartContextManager_1 = require("./smartContextManager");
52
- const core_1 = require("./governance/core");
53
- const errorTracker_1 = require("./errorTracker");
54
- const dynamicPrompt_1 = require("./dynamicPrompt");
55
10
  const renderer_1 = require("../utils/renderer");
11
+ const LLMCaller_1 = require("./LLMCaller");
12
+ const PreFlightChecker_1 = require("./PreFlightChecker");
13
+ const ExecutionHandler_1 = require("./ExecutionHandler");
14
+ const Logger_1 = require("../utils/Logger");
15
+ const log = Logger_1.logger.child('AgentRuntime');
16
+ /**
17
+ * Agent orchestration runtime — coordinates LLM calls, governance,
18
+ * pre-flight checks, and execution via delegated components.
19
+ */
56
20
  class AgentRuntime {
57
21
  context;
58
22
  executionId;
59
23
  maxTurns = 10;
60
- readOnlyTools = [
61
- 'read_file', 'list_files', 'read_file_lines', 'read_file_lines_from_end',
62
- 'file_info', 'git_status', 'git_log', 'git_diff', 'list_directory_tree',
63
- 'search_in_files', 'search_symbol', 'continue_reading', 'analyze_dependencies'
64
- ];
24
+ llmCaller;
25
+ preFlight;
26
+ execHandler;
65
27
  constructor(initialContext) {
66
28
  this.context = new smartContextManager_1.SmartContextManager(initialContext);
67
29
  this.executionId = (0, crypto_1.randomUUID)();
30
+ this.llmCaller = new LLMCaller_1.LLMCaller(this.context);
31
+ this.preFlight = new PreFlightChecker_1.PreFlightChecker(this.context);
32
+ this.execHandler = new ExecutionHandler_1.ExecutionHandler(this.context);
68
33
  }
69
34
  async run(userInput, mode = "chat", onChunk, model, renderer) {
70
35
  let turnCount = 0;
@@ -80,61 +45,63 @@ class AgentRuntime {
80
45
  console.log(chalk_1.default.blue(`\n--- Turn ${currentTurn} ---`));
81
46
  }
82
47
  const { agentRenderer, agentOnChunk } = this.prepareRenderer(renderer, onChunk);
83
- const { prompt: enhancedPrompt, userInput: query } = await this.buildPrompt(userInput, lastError);
48
+ const enhancedPrompt = await this.llmCaller.buildPrompt(userInput, lastError);
84
49
  // === Step 1: LLM Thinking ===
85
- const thought = await this.callLLM(enhancedPrompt, query, mode, agentOnChunk, model, agentRenderer);
50
+ const thought = await this.llmCaller.call(enhancedPrompt, userInput, mode, agentOnChunk, model, agentRenderer);
86
51
  if (!thought)
87
- break; // error or empty
52
+ break;
88
53
  // === Step 2: Build Action ===
89
54
  const action = this.buildAction(thought);
90
55
  if (action.reasoning && !onChunk) {
91
- console.log(chalk_1.default.gray(`\n🤔 Reasoning: ${action.reasoning}`));
56
+ log.debug('LLM reasoning', { reasoning: action.reasoning });
92
57
  }
93
58
  if (thought.usedRouter) {
94
- console.log(chalk_1.default.gray(`[Router] 🤖 Model: ${thought.modelName}`));
59
+ log.debug('Router model selected', { model: thought.modelName });
95
60
  }
96
61
  // === Step 3: Handle answer type ===
97
62
  if (thought.isDone || action.type === "answer") {
98
- // Command 模式下 AI 返回 answer:引导其使用工具
99
63
  if (mode === "command") {
100
64
  const content = action.payload.content || action.payload.text || '';
101
65
  this.context.addMessage('system', `在命令执行模式下,请直接使用工具执行命令,不要回复对话。请使用 list_directory_tree 查看文件列表,然后用 shell_cmd 执行命令来查找最大文件。任务: "${userInput}"`);
102
- continue; // 不 break,让 AI 重试使用工具
66
+ continue;
103
67
  }
104
- await this.handleAnswer(action, thought, userInput, mode, renderer, agentRenderer);
68
+ await this.execHandler.handleAnswer(action, thought, userInput, mode, renderer, agentRenderer);
105
69
  break;
106
70
  }
107
71
  // === Step 4: Causal ACK Check ===
108
- if (!this.verifyAckCausality(thought))
72
+ if (!this.preFlight.verifyAckCausality(thought))
109
73
  continue;
110
74
  // === Step 5: Governance ===
111
- if (!await this.passGovernance(action))
75
+ if (!await this.preFlight.passGovernance(action))
112
76
  continue;
113
77
  // === Step 6: Record KG Edge ===
114
- await this.recordKnowledgeGraphEdge(thought, action);
78
+ await this.preFlight.recordKnowledgeGraphEdge(thought, action);
115
79
  // === Step 7: Pre-execution Checks ===
116
- const cachedResult = await this.checkExecutionBlock(action, writeModeState);
117
- if (cachedResult === 'blocked')
80
+ const cachedResult = await this.preFlight.check(action, writeModeState, lastToolCall);
81
+ if (cachedResult === 'blocked' || cachedResult === 'force_break') {
82
+ if (cachedResult === 'force_break')
83
+ break;
118
84
  continue;
85
+ }
119
86
  // === Step 8: Execute ===
120
87
  const result = cachedResult && typeof cachedResult === 'object'
121
88
  ? cachedResult
122
- : await this.executeAction(action, writeModeState);
89
+ : await this.execHandler.execute(action);
123
90
  if (result.success) {
124
91
  lastError = undefined;
125
- lastToolCall = this.handleSuccessfulExecution(result, action, lastToolCall, userInput, writeModeState, mode, thought, agentRenderer);
92
+ lastToolCall = this.execHandler.handleSuccess(result, action, lastToolCall, userInput, writeModeState, mode, thought, agentRenderer);
126
93
  if (lastToolCall === null)
127
- break; // null signals break (stabilization or duplicate)
94
+ break;
128
95
  }
129
96
  else {
130
- lastError = await this.handleFailedExecution(result, action, mode, thought, userInput);
97
+ lastError = await this.execHandler.handleFailure(result, action, mode, thought, userInput);
131
98
  }
132
99
  }
133
100
  if (turnCount >= this.maxTurns) {
134
101
  console.log(chalk_1.default.red(`\n⚠️ Max turns (${this.maxTurns}) reached.`));
135
102
  }
136
103
  }
137
- // === Private Helper Methods ===
104
+ // --- Private helpers ---
138
105
  prepareRenderer(renderer, onChunk) {
139
106
  let agentRenderer = renderer;
140
107
  let agentOnChunk = onChunk;
@@ -144,83 +111,6 @@ class AgentRuntime {
144
111
  }
145
112
  return { agentRenderer, agentOnChunk };
146
113
  }
147
- async buildPrompt(userInput, lastError) {
148
- const dynamicContext = await (0, dynamicPrompt_1.buildDynamicContext)(lastError);
149
- const basePrompt = governance_1.GovernanceService.getPolicyManual();
150
- let prompt = (0, dynamicPrompt_1.injectDynamicContext)(basePrompt, dynamicContext);
151
- // 尝试匹配技能,注入技能 prompt
152
- const { matchSkill, generateSkillPrompt } = await Promise.resolve().then(() => __importStar(require('./promptSkills')));
153
- const skill = matchSkill(userInput);
154
- if (skill) {
155
- prompt += `\n\n[SKILL ACTIVE: ${skill.name}]\n${generateSkillPrompt(skill, userInput)}`;
156
- }
157
- return { prompt, userInput };
158
- }
159
- async callLLM(enhancedPrompt, userInput, mode, onChunk, model, renderer) {
160
- const messages = [];
161
- const enhancedContext = await this.context.getEnhancedContext({
162
- query: userInput || enhancedPrompt,
163
- minRelevance: 0.3,
164
- maxTokens: 8000,
165
- enableSmartSummary: true
166
- });
167
- if (enhancedContext.summary) {
168
- messages.push({ role: 'system', content: enhancedContext.summary });
169
- }
170
- for (const item of enhancedContext.rankedItems) {
171
- messages.push({
172
- role: 'user',
173
- content: `@${item.path} (相关度: ${(item.relevance * 100).toFixed(0)}%)\n${item.summary || item.content || ''}`
174
- });
175
- }
176
- // 关键:确保用户输入被包含为最后一条消息
177
- if (userInput) {
178
- messages.push({ role: 'user', content: userInput });
179
- }
180
- else {
181
- messages.push({ role: 'user', content: enhancedPrompt });
182
- }
183
- try {
184
- const thought = await llmAdapter_1.LLMAdapter.think(messages, mode, onChunk, model, enhancedPrompt, this.context);
185
- if (!thought.raw || thought.raw.trim() === '') {
186
- console.log(chalk_1.default.red('\n⚠️ AI 返回了空响应,请检查网络连接或模型配置。'));
187
- return null;
188
- }
189
- return thought;
190
- }
191
- catch (error) {
192
- this.handleLLMError(error);
193
- return null;
194
- }
195
- }
196
- handleLLMError(error) {
197
- let errorMessage = '未知内部错误';
198
- let statusCode = 0;
199
- if (error instanceof llm_1.AIError) {
200
- errorMessage = error.message;
201
- statusCode = error.statusCode;
202
- }
203
- else if (error instanceof Error) {
204
- errorMessage = error.message;
205
- statusCode = error.statusCode || 0;
206
- }
207
- else if (typeof error === 'string') {
208
- errorMessage = error;
209
- }
210
- const statusInfo = statusCode ? ` (状态码: ${statusCode})` : '';
211
- console.log(chalk_1.default.red(`\n❌ AI 思考过程发生错误: ${errorMessage}${statusInfo}`));
212
- const isTransient = statusCode === 429 || statusCode >= 500
213
- || errorMessage.includes('timeout') || errorMessage.includes('network') || errorMessage.includes('ETIMEDOUT');
214
- if (isTransient) {
215
- console.log(chalk_1.default.yellow('⚠️ 检测到瞬时错误,自动跳过此轮'));
216
- this.context.addMessage("system", `AI 调用失败${statusInfo},请稍后重试`);
217
- return;
218
- }
219
- this.context.addMessage("system", `思考过程中发生错误${statusInfo}: ${errorMessage}`);
220
- if (statusCode === 401 || statusCode === 403 || errorMessage.includes('401') || errorMessage.includes('403')) {
221
- console.log(chalk_1.default.yellow('💡 检测到权限或授权错误,请检查 API 配置。'));
222
- }
223
- }
224
114
  buildAction(thought) {
225
115
  return {
226
116
  id: (0, crypto_1.randomUUID)(),
@@ -230,403 +120,6 @@ class AgentRuntime {
230
120
  reasoning: thought.reasoning || "",
231
121
  };
232
122
  }
233
- async handleAnswer(action, thought, userInput, mode, externalRenderer, agentRenderer) {
234
- const result = await executor_1.ToolExecutor.execute(action);
235
- if (!externalRenderer && agentRenderer) {
236
- for (let i = 0; i < result.output.length; i += 10) {
237
- agentRenderer.onChunk(result.output.slice(i, i + 10));
238
- }
239
- agentRenderer.finish();
240
- }
241
- else if (!externalRenderer) {
242
- const rendered = marked.parse(result.output);
243
- console.log(chalk_1.default.green(`\n🤖 AI:\n`) + rendered);
244
- }
245
- this.context.addMessage("assistant", result.output);
246
- await this.learnFromExecution(userInput, mode, thought);
247
- }
248
- verifyAckCausality(thought) {
249
- const lastObs = this.context.getLastAckableObservation();
250
- const ackText = thought.parsedPlan?.acknowledged_observation;
251
- if (lastObs && ackText && ackText !== 'NONE') {
252
- if (lastObs.content.trim() !== ackText.trim()) {
253
- console.log(chalk_1.default.red(`[CAUSAL BREAK] ❌ ACK mismatch!`));
254
- console.log(chalk_1.default.red(` Expected: ${lastObs.content.trim().substring(0, 100)}...`));
255
- console.log(chalk_1.default.red(` Received: ${ackText.trim().substring(0, 100)}...`));
256
- this.context.addMessage("system", `CAUSAL BREAK: ACK does not match physical Observation. Cannot proceed without acknowledging reality.`);
257
- return false;
258
- }
259
- console.log(chalk_1.default.green(`[CAUSAL LOCK] ✅ ACK verified`));
260
- }
261
- return true;
262
- }
263
- async passGovernance(action) {
264
- const preCheck = (0, core_1.evaluateProposal)(action, governance_1.GovernanceService.getRules(), governance_1.GovernanceService.getLedgerSnapshot());
265
- if (preCheck.effect === "deny") {
266
- console.log(chalk_1.default.red(`[PRE-FLIGHT] 🛡️ Policy Blocked: ${preCheck.reason}`));
267
- this.context.addMessage("system", `POLICY DENIED: ${preCheck.reason}. Find a different way.`);
268
- return false;
269
- }
270
- const decision = await governance_1.GovernanceService.adjudicate(action);
271
- if (decision.status === "rejected") {
272
- console.log(chalk_1.default.red(`[GOVERNANCE] ❌ Rejected: ${decision.reason}`));
273
- this.context.addMessage("system", `Rejected by Governance: ${decision.reason}`);
274
- return false;
275
- }
276
- return true;
277
- }
278
- async recordKnowledgeGraphEdge(thought, action) {
279
- const lastObs = this.context.getLastAckableObservation();
280
- const ackText = thought.parsedPlan?.acknowledged_observation;
281
- if (lastObs && lastObs.metadata?.obsId && ackText && ackText !== 'NONE') {
282
- try {
283
- const { recordEdge } = await Promise.resolve().then(() => __importStar(require('../engine/agent/knowledgeGraph')));
284
- recordEdge({
285
- from: lastObs.metadata.obsId,
286
- to: action.id,
287
- type: 'ACKNOWLEDGED_BY',
288
- metadata: { verified: true, timestamp: Date.now() }
289
- });
290
- console.log(chalk_1.default.gray(`[KG] ⚓ Causal edge recorded`));
291
- }
292
- catch (error) {
293
- console.warn(chalk_1.default.yellow(`[KG] Warning: Failed to record causal edge: ${error.message}`));
294
- }
295
- }
296
- }
297
- async checkExecutionBlock(action, writeModeState) {
298
- if (action.type !== 'tool_call')
299
- return null;
300
- const toolName = action.payload.tool_name;
301
- // Write Mode cache
302
- if (writeModeState && toolName === 'read_file') {
303
- const filePath = action.payload.parameters.path;
304
- if (filePath === writeModeState.filePath && writeModeState.content) {
305
- console.log(chalk_1.default.yellow(`[CACHED] 📋 文件 ${filePath} 内容已缓存,直接返回`));
306
- return { success: true, output: writeModeState.content, artifacts: [filePath] };
307
- }
308
- }
309
- const blockCheck = errorTracker_1.ErrorTracker.shouldBlockExecution(toolName, action.payload.parameters);
310
- if (blockCheck.blocked) {
311
- console.log(chalk_1.default.red(`[BLOCKED] 🚫 ${blockCheck.reason}`));
312
- if (blockCheck.existingError) {
313
- console.log(chalk_1.default.yellow(`[Error History] 此错误已发生 ${blockCheck.existingError.attemptCount} 次`));
314
- console.log(chalk_1.default.gray(`上次错误: ${blockCheck.existingError.errorMessage}`));
315
- }
316
- this.context.addMessage("system", `BLOCKED: ${blockCheck.reason}. 建议尝试不同的方法。`);
317
- return 'blocked';
318
- }
319
- return null;
320
- }
321
- async executeAction(action, _writeModeState) {
322
- console.log(chalk_1.default.yellow(`[EXECUTING] ⚙️ ${action.type}...`));
323
- return await executor_1.ToolExecutor.execute(action);
324
- }
325
- handleSuccessfulExecution(result, action, lastToolCall, userInput, writeModeState, mode, thought, agentRenderer) {
326
- const actualToolName = action.payload?.tool_name || action.type;
327
- this.context.addToolResult(actualToolName, result.output);
328
- const preview = result.output.length > 300 ? result.output.substring(0, 300) + '...' : result.output;
329
- console.log(chalk_1.default.green(`[SUCCESS] Result:\n${preview}`));
330
- // Stabilization detection: 检查输出是否稳定(排除错误输出)
331
- const normalizedOutput = result.output?.trim()?.split('\n').filter(Boolean)[0] || '';
332
- const isErrorMessage = /invalid option|unknown|error|failed|no such|cannot|usage:|not found/i.test(normalizedOutput);
333
- if (!isErrorMessage) {
334
- // 方案3: 语义完成检测 — 输出已经是直接答案(适用于首轮查询,必须在 prevOutput 检测之前)
335
- // 只有当完整输出都很短时才触发,避免在截断的多行输出上误触发
336
- if (result.output.length < 100 && this.isSemanticComplete(normalizedOutput, userInput)) {
337
- console.log(chalk_1.default.yellow('[Semantic Complete] 输出已经是直接答案,自动完成'));
338
- console.log(chalk_1.default.cyan(`\n✓ 结果: ${normalizedOutput}\n`));
339
- this.context.addMessage("assistant", result.output);
340
- agentRenderer && (() => { agentRenderer.buffer = ''; agentRenderer.quietMode = true; agentRenderer.finish(); })();
341
- return null; // signal break
342
- }
343
- // 方案4: 截断输出检测 — 结果被截断但已包含数据,引导 AI 完成
344
- if (result.output.includes('[⚠️ OUTPUT TRUNCATED]') && normalizedOutput.length > 0) {
345
- this.context.addMessage('system', `输出已截断,但已包含足够的数据。请直接使用 answer 类型向用户呈现你看到的结果,不要再运行更多命令。`);
346
- }
347
- // 方案1: 连续两次输出完全相同
348
- const prevOutput = lastToolCall?.lastOutput;
349
- const isOutputStable = prevOutput && normalizedOutput && prevOutput === normalizedOutput;
350
- // 方案2: 最近2次输出包含相同的文件/路径名(即使命令不同)
351
- const extractPath = (line) => {
352
- const parts = line.trim().split(/\s+/);
353
- return parts.length > 1 ? parts[parts.length - 1] : '';
354
- };
355
- const currentPath = extractPath(normalizedOutput);
356
- const prevPath = prevOutput ? extractPath(prevOutput) : '';
357
- const isOutputStableByName = currentPath && prevPath && currentPath === prevPath;
358
- if (isOutputStable || isOutputStableByName) {
359
- console.log(chalk_1.default.yellow('[Stabilization] 输出结果已稳定,自动完成'));
360
- console.log(chalk_1.default.cyan(`\n✓ 结果: ${normalizedOutput}\n`));
361
- this.context.addMessage("assistant", result.output);
362
- agentRenderer && (() => { agentRenderer.buffer = ''; agentRenderer.quietMode = true; agentRenderer.finish(); })();
363
- return null; // signal break
364
- }
365
- }
366
- // Duplicate detection: 区分 tool_call 和 shell_cmd
367
- const actualTool = action.payload?.tool_name || action.type;
368
- const params = action.payload?.tool_name === 'shell_cmd' || action.type === 'shell_cmd'
369
- ? { command: (action.payload?.command || action.payload?.parameters?.command || '') }
370
- : (action.payload?.parameters || action.payload);
371
- const currentToolCall = { tool: actualTool, params };
372
- const isDuplicate = lastToolCall &&
373
- lastToolCall.tool === currentToolCall.tool &&
374
- JSON.stringify(lastToolCall.params) === JSON.stringify(currentToolCall.params);
375
- // Build output history for stabilization detection
376
- const outputHistory = [...(lastToolCall?.outputHistory || []), normalizedOutput].slice(-3);
377
- if (isDuplicate && lastToolCall) {
378
- const count = lastToolCall.count + 1;
379
- // 如果是错误输出,提示 AI 换方案
380
- if (isErrorMessage) {
381
- if (count >= 2) {
382
- const failedCmd = actualTool === 'shell_cmd'
383
- ? (lastToolCall.params?.command || '')
384
- : '';
385
- console.log(chalk_1.default.yellow('[Duplicate Error] 相同命令重复失败 2 次,自动终止该路径'));
386
- let errorMsg = `命令执行失败,且已重复尝试 2 次。请换一种完全不同的方法来完成用户任务: "${userInput}"。`;
387
- if (failedCmd) {
388
- errorMsg += `\n⚠️ 命令 "${failedCmd}" 在当前系统不可用,已被列入本次对话黑名单,不要再使用此命令。`;
389
- }
390
- errorMsg += `\n注意:当前系统可能是 macOS(BSD 工具链),不支持 Linux 特定的命令选项。请使用 macOS 兼容的命令。`;
391
- this.context.addMessage('system', errorMsg);
392
- // 重置重复计数让新命令不被当作重复
393
- lastToolCall = { tool: 'reset', params: {}, count: 0, lastOutput: '', outputHistory: [] };
394
- return lastToolCall;
395
- }
396
- console.log(chalk_1.default.gray(`[Repeat Error] 重复失败 (${count + 1}/2),请换方案...`));
397
- }
398
- else if (count >= 2) {
399
- console.log(chalk_1.default.yellow('[Duplicate Detection] 达到重复限制,强制完成'));
400
- console.log(chalk_1.default.cyan(`\n✓ ${action.payload?.tool_name || action.type} 已完成\n`));
401
- agentRenderer && (() => { agentRenderer.buffer = ''; agentRenderer.quietMode = true; agentRenderer.finish(); })();
402
- return null; // signal break
403
- }
404
- else {
405
- console.log(chalk_1.default.gray(`[Repeat Detection] 重复调用 (${count + 1}/2),继续...`));
406
- }
407
- lastToolCall.count = count;
408
- lastToolCall.lastOutput = normalizedOutput;
409
- lastToolCall.outputHistory = outputHistory;
410
- if (!isErrorMessage) {
411
- // 告诉 AI 结果已重复,应该返回答案而不是继续调用工具
412
- this.context.addMessage('system', `同样的操作已经重复执行了 ${count} 次,结果没有变化。请停止继续调用工具,使用 answer 类型返回最终结果给用户。输出: "${result.output.slice(0, 200)}"`);
413
- }
414
- }
415
- else {
416
- lastToolCall = { ...currentToolCall, count: 0, lastOutput: normalizedOutput, outputHistory };
417
- }
418
- // Auto-complete logic for specific tool types
419
- if (action.type === 'tool_call') {
420
- const toolName = action.payload.tool_name;
421
- if (toolName === 'write_file') {
422
- console.log(chalk_1.default.gray('[Auto-Complete] 文件写入成功,自动完成任务'));
423
- console.log(chalk_1.default.green(`✓ 已创建文件: ${action.payload.parameters.path}\n`));
424
- this.context.addMessage("assistant", `已成功创建文件 ${action.payload.parameters.path}`);
425
- agentRenderer && (() => { agentRenderer.buffer = ''; agentRenderer.quietMode = true; agentRenderer.finish(); })();
426
- result.shouldBreak = true;
427
- return lastToolCall;
428
- }
429
- if (this.readOnlyTools.includes(toolName)) {
430
- const requiresAnalysis = /^(.*?)(帮我|请)?(分析|解释|说明|总结)|\b(review|explain)\s+(this|the|it)\b/i.test(userInput);
431
- const requiresWrite = /替换|replace|修改|modify|添加|append|插入|insert|删除|delete|移除|remove|更新|update|改成|改成|改为/i.test(userInput);
432
- // Command 模式下不自动完成,让 AI 自行决定后续操作
433
- if (mode === 'command') {
434
- console.log(chalk_1.default.gray('[Command Mode] 只读工具执行成功,等待 AI 决定下一步'));
435
- return lastToolCall;
436
- }
437
- // REPL/Chat 模式下不自动完成,让 AI 基于工具结果正常回答
438
- if (mode === 'chat') {
439
- // 对于简单的只读查询,直接格式化结果返回,避免额外 LLM 调用
440
- const formatted = this.tryFormatToolResult(result.output, userInput);
441
- if (formatted) {
442
- console.log(chalk_1.default.green(`\n${formatted}\n`));
443
- this.context.addMessage("assistant", formatted);
444
- agentRenderer && (() => { agentRenderer.buffer = ''; agentRenderer.quietMode = true; agentRenderer.finish(); })();
445
- return null; // 直接 break
446
- }
447
- this.context.addMessage('system', `请根据以上工具结果,简洁回答用户的问题。`);
448
- return lastToolCall;
449
- }
450
- if (!requiresAnalysis && !requiresWrite) {
451
- console.log(chalk_1.default.gray('[Auto-Complete] 只读工具执行成功,自动完成任务'));
452
- console.log(chalk_1.default.cyan(`\n📄 ${toolName} 结果:\n`));
453
- console.log(result.output);
454
- this.context.addMessage("assistant", `已成功执行 ${toolName},结果:\n${result.output}`);
455
- agentRenderer && (() => { agentRenderer.buffer = ''; agentRenderer.quietMode = true; agentRenderer.finish(); })();
456
- result.shouldBreak = true;
457
- return lastToolCall;
458
- }
459
- else if (requiresWrite) {
460
- console.log(chalk_1.default.gray('[Write Mode] 文件已读取,继续执行写入操作...'));
461
- if (writeModeState) {
462
- writeModeState.filePath = action.payload.parameters.path;
463
- writeModeState.content = result.output;
464
- }
465
- this.context.addMessage('system', `文件 ${action.payload.parameters.path} 已成功读取。请根据用户要求修改内容后,调用 write_file 工具写入文件。不要重复调用 read_file!`);
466
- }
467
- else {
468
- console.log(chalk_1.default.gray('[Analysis Mode] 文件已读取,继续分析...'));
469
- }
470
- }
471
- }
472
- // Learn from this execution
473
- this.learnFromExecution(userInput, mode, thought).catch(() => { });
474
- return lastToolCall;
475
- }
476
- async handleFailedExecution(result, action, mode, thought, userInput) {
477
- const actualToolName = action.payload?.tool_name || action.type;
478
- this.context.addToolResult(actualToolName, `Error: ${result.error}`);
479
- console.log(chalk_1.default.red(`[ERROR] ${result.error}`));
480
- // Record to error tracker
481
- if (action.type === 'tool_call') {
482
- errorTracker_1.ErrorTracker.recordError(action.payload.tool_name, action.payload.parameters, result.error || 'Unknown error', { mode, model: thought.modelName, userInput });
483
- }
484
- else if (action.type === 'shell_cmd') {
485
- errorTracker_1.ErrorTracker.recordError('shell_cmd', { command: action.payload.command }, result.error || 'Unknown error', { mode, model: thought.modelName, userInput });
486
- }
487
- // Auto-fix for shell_cmd failures
488
- if (action.type === 'shell_cmd' && result.error) {
489
- console.log(chalk_1.default.yellow('[AutoFix] 尝试自动修复命令...'));
490
- try {
491
- const { autoFixCommand } = await Promise.resolve().then(() => __importStar(require('../core/autofix')));
492
- const { getOSProfile } = await Promise.resolve().then(() => __importStar(require('../core/os')));
493
- const os = getOSProfile();
494
- const fixPlan = await autoFixCommand(action.payload.command, result.error || result.output || '', os, thought.modelName);
495
- if (fixPlan && fixPlan.command) {
496
- console.log(chalk_1.default.cyan(`[AutoFix] 建议修复命令: ${fixPlan.command}`));
497
- console.log(chalk_1.default.gray(`[AutoFix] 说明: ${fixPlan.plan || '无'}`));
498
- this.context.addMessage('system', `自动修复建议:${fixPlan.command}\n原因:${fixPlan.plan || '无'}`);
499
- if (fixPlan.risk === 'low') {
500
- console.log(chalk_1.default.yellow('[AutoFix] 修复方案风险低,自动执行...'));
501
- const fixedAction = {
502
- id: (0, crypto_1.randomUUID)(),
503
- type: 'shell_cmd',
504
- payload: { command: fixPlan.command },
505
- riskLevel: 'low',
506
- reasoning: `AutoFix from failed command: ${action.payload.command}`
507
- };
508
- const fixedResult = await executor_1.ToolExecutor.execute(fixedAction);
509
- if (fixedResult.success) {
510
- console.log(chalk_1.default.green('[AutoFix] 修复成功!'));
511
- this.context.addToolResult('shell_cmd', fixedResult.output);
512
- return ''; // clear error
513
- }
514
- else {
515
- console.log(chalk_1.default.red('[AutoFix] 修复失败,继续原始错误处理'));
516
- }
517
- }
518
- }
519
- else {
520
- console.log(chalk_1.default.gray('[AutoFix] 无法生成修复建议'));
521
- }
522
- }
523
- catch (fixError) {
524
- console.warn(chalk_1.default.yellow(`[AutoFix] 修复过程出错: ${fixError.message}`));
525
- }
526
- }
527
- return result.error;
528
- }
529
- /**
530
- * 语义完成检测:输出是否已经是直接答案
531
- * 用于防止 AI 在已经获取答案后继续无意义地查询
532
- */
533
- isSemanticComplete(output, userInput) {
534
- const trimmed = output.trim();
535
- // 输出必须是短的(直接答案通常很短)
536
- if (trimmed.length > 100 || trimmed.length === 0)
537
- return false;
538
- // 输出不能包含多行
539
- if (trimmed.includes('\n'))
540
- return false;
541
- const q = userInput.toLowerCase();
542
- // 1. 问"大小/多少字节" → 输出是纯数字 或 带单位的短文本(如 "4.0K"、"288K"、"1.5MB")
543
- if (/(大小|多少字节|多大|size|bytes?)/.test(q)) {
544
- if (/^\d+(\.\d+)?$/.test(trimmed))
545
- return true;
546
- // 人类可读格式:数字+单位,可能带前后空格和路径
547
- if (/^[\d.]+\s*[KMGT]?B?$/i.test(trimmed))
548
- return true;
549
- // 带路径的格式:如 "4.0K ./package.json"
550
- if (/^[\d.]+\s*[KMGT]?B?\s+\S+/i.test(trimmed))
551
- return true;
552
- }
553
- // 2. 问"几个/多少个/数量/count" → 输出是纯数字
554
- if (/(几个|多少个|数量|count|how many)/.test(q) && /^\d+$/.test(trimmed))
555
- return true;
556
- // 3. 问"最大/最小的文件" → 输出是 "数字 路径" 格式(如 "0 ./foo.txt")
557
- if (/(最大|最小|largest|smallest).*文件/.test(q) && /^\d+\s+\.\//.test(trimmed))
558
- return true;
559
- // 4. 问"最新/最早"的文件 → 输出是单个文件名或带信息的短行
560
- if (/(最新|最旧|最近|最早|newest|latest|oldest)/.test(q) && trimmed.split(/\s+/).length <= 5 && trimmed.length < 80)
561
- return true;
562
- // 5. 问"行数" → 输出是纯数字
563
- if (/(行数|多少行|line.?count|多少 line)/.test(q) && /^\d+$/.test(trimmed))
564
- return true;
565
- // 6. 通用:输出是短小的、看起来像答案的内容(单行,< 50 字符,不是命令输出)
566
- // 排除:以 - 开头(ls -l 风格)、包含多个空格的长行
567
- if (trimmed.length < 50 && !trimmed.startsWith('-') && trimmed.split(/\s+/).length <= 4) {
568
- // 包含数字或路径,看起来像答案
569
- if (/\d+|\.\//.test(trimmed))
570
- return true;
571
- }
572
- return false;
573
- }
574
- /**
575
- * 尝试直接格式化工具结果,避免额外的 LLM 调用
576
- * 适用于简单的只读查询(如 list_files 的结果配合简单问题)
577
- */
578
- tryFormatToolResult(output, userInput) {
579
- // 只对较短的输出直接格式化
580
- if (output.length > 5000)
581
- return null;
582
- // 如果问题需要进一步处理(如找最大/最小文件),不直接格式化
583
- if (/(最大|最小|哪个.*最大|哪个.*最小|largest|smallest|biggest)/.test(userInput)) {
584
- return null;
585
- }
586
- // 尝试解析 JSON 数组(如 list_files 的结果)
587
- try {
588
- const parsed = JSON.parse(output);
589
- if (Array.isArray(parsed) && parsed.length > 0 && parsed[0].path) {
590
- // 如果用户只问"有几个文件"或"列出文件",直接格式化
591
- const files = parsed.filter((f) => f.type === 'file');
592
- const dirs = parsed.filter((f) => f.type === 'directory');
593
- const fileNames = files.map((f) => f.path.split('/').pop()).join('、');
594
- const dirNames = dirs.map((f) => f.path.split('/').pop()).join('、');
595
- let result = `📁 **${files.length}** 个文件`;
596
- if (files.length > 0 && files.length <= 30)
597
- result += `:${fileNames}`;
598
- result += `\n📂 **${dirs.length}** 个目录`;
599
- if (dirs.length > 0 && dirs.length <= 30)
600
- result += `:${dirNames}`;
601
- return result;
602
- }
603
- }
604
- catch { /* not JSON */ }
605
- // 输出较短且为单行,可能是直接答案
606
- if (output.length < 200 && !output.includes('\n')) {
607
- return output;
608
- }
609
- return null;
610
- }
611
- async learnFromExecution(userInput, mode, thought) {
612
- try {
613
- const { createExecutionRecord } = await Promise.resolve().then(() => __importStar(require('../core/executionRecord')));
614
- const { saveExecutionRecord } = await Promise.resolve().then(() => __importStar(require('../core/executionStore')));
615
- const record = createExecutionRecord(`agent-${mode}`, { required: [], preferred: [] }, { aiProxyUrl: { value: '', source: 'built-in' }, defaultModel: { value: '', source: 'built-in' }, accountType: { value: 'free', source: 'built-in' } }, { selected: null, candidates: [], fallbackOccurred: false }, { success: true }, undefined, userInput, mode);
616
- record.llmResult = { plan: thought.parsedPlan };
617
- record.input = { rawInput: userInput };
618
- const savedRecordId = saveExecutionRecord(record);
619
- const { loadExecutionRecord } = await Promise.resolve().then(() => __importStar(require('../core/executionStore')));
620
- const savedRecord = loadExecutionRecord(savedRecordId);
621
- if (savedRecord) {
622
- const { learnSkillFromRecord } = await Promise.resolve().then(() => __importStar(require('./skills')));
623
- learnSkillFromRecord(savedRecord, true);
624
- }
625
- }
626
- catch (error) {
627
- console.warn(chalk_1.default.yellow(`[Skill Learning] Failed: ${error}`));
628
- }
629
- }
630
123
  }
631
124
  exports.AgentRuntime = AgentRuntime;
632
125
  //# sourceMappingURL=AgentRuntime.js.map