@agenticc/core 1.0.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 (196) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +370 -0
  3. package/README.zh-CN.md +372 -0
  4. package/dist/audit/index.d.ts +9 -0
  5. package/dist/audit/index.d.ts.map +1 -0
  6. package/dist/audit/index.js +9 -0
  7. package/dist/audit/index.js.map +1 -0
  8. package/dist/audit/logger.d.ts +211 -0
  9. package/dist/audit/logger.d.ts.map +1 -0
  10. package/dist/audit/logger.js +268 -0
  11. package/dist/audit/logger.js.map +1 -0
  12. package/dist/audit/query.d.ts +164 -0
  13. package/dist/audit/query.d.ts.map +1 -0
  14. package/dist/audit/query.js +250 -0
  15. package/dist/audit/query.js.map +1 -0
  16. package/dist/conversation/context-builder.d.ts +119 -0
  17. package/dist/conversation/context-builder.d.ts.map +1 -0
  18. package/dist/conversation/context-builder.js +252 -0
  19. package/dist/conversation/context-builder.js.map +1 -0
  20. package/dist/conversation/index.d.ts +10 -0
  21. package/dist/conversation/index.d.ts.map +1 -0
  22. package/dist/conversation/index.js +10 -0
  23. package/dist/conversation/index.js.map +1 -0
  24. package/dist/conversation/message-store.d.ts +231 -0
  25. package/dist/conversation/message-store.d.ts.map +1 -0
  26. package/dist/conversation/message-store.js +404 -0
  27. package/dist/conversation/message-store.js.map +1 -0
  28. package/dist/conversation/session.d.ts +201 -0
  29. package/dist/conversation/session.d.ts.map +1 -0
  30. package/dist/conversation/session.js +285 -0
  31. package/dist/conversation/session.js.map +1 -0
  32. package/dist/core/agent.d.ts +277 -0
  33. package/dist/core/agent.d.ts.map +1 -0
  34. package/dist/core/agent.js +674 -0
  35. package/dist/core/agent.js.map +1 -0
  36. package/dist/core/agentic-loop.d.ts +98 -0
  37. package/dist/core/agentic-loop.d.ts.map +1 -0
  38. package/dist/core/agentic-loop.js +496 -0
  39. package/dist/core/agentic-loop.js.map +1 -0
  40. package/dist/core/index.d.ts +14 -0
  41. package/dist/core/index.d.ts.map +1 -0
  42. package/dist/core/index.js +14 -0
  43. package/dist/core/index.js.map +1 -0
  44. package/dist/core/intent-parser.d.ts +101 -0
  45. package/dist/core/intent-parser.d.ts.map +1 -0
  46. package/dist/core/intent-parser.js +221 -0
  47. package/dist/core/intent-parser.js.map +1 -0
  48. package/dist/core/plan-generator.d.ts +133 -0
  49. package/dist/core/plan-generator.d.ts.map +1 -0
  50. package/dist/core/plan-generator.js +294 -0
  51. package/dist/core/plan-generator.js.map +1 -0
  52. package/dist/core/plugin-manager.d.ts +120 -0
  53. package/dist/core/plugin-manager.d.ts.map +1 -0
  54. package/dist/core/plugin-manager.js +369 -0
  55. package/dist/core/plugin-manager.js.map +1 -0
  56. package/dist/core/response-handler.d.ts +141 -0
  57. package/dist/core/response-handler.d.ts.map +1 -0
  58. package/dist/core/response-handler.js +384 -0
  59. package/dist/core/response-handler.js.map +1 -0
  60. package/dist/core/tool-executor.d.ts +143 -0
  61. package/dist/core/tool-executor.d.ts.map +1 -0
  62. package/dist/core/tool-executor.js +354 -0
  63. package/dist/core/tool-executor.js.map +1 -0
  64. package/dist/core/tool-registry.d.ts +133 -0
  65. package/dist/core/tool-registry.d.ts.map +1 -0
  66. package/dist/core/tool-registry.js +252 -0
  67. package/dist/core/tool-registry.js.map +1 -0
  68. package/dist/index.d.ts +44 -0
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.js +47 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/knowledge/chunker.d.ts +78 -0
  73. package/dist/knowledge/chunker.d.ts.map +1 -0
  74. package/dist/knowledge/chunker.js +233 -0
  75. package/dist/knowledge/chunker.js.map +1 -0
  76. package/dist/knowledge/embedder.d.ts +93 -0
  77. package/dist/knowledge/embedder.d.ts.map +1 -0
  78. package/dist/knowledge/embedder.js +205 -0
  79. package/dist/knowledge/embedder.js.map +1 -0
  80. package/dist/knowledge/index.d.ts +10 -0
  81. package/dist/knowledge/index.d.ts.map +1 -0
  82. package/dist/knowledge/index.js +11 -0
  83. package/dist/knowledge/index.js.map +1 -0
  84. package/dist/knowledge/loaders/index.d.ts +10 -0
  85. package/dist/knowledge/loaders/index.d.ts.map +1 -0
  86. package/dist/knowledge/loaders/index.js +10 -0
  87. package/dist/knowledge/loaders/index.js.map +1 -0
  88. package/dist/knowledge/loaders/markdown.d.ts +88 -0
  89. package/dist/knowledge/loaders/markdown.d.ts.map +1 -0
  90. package/dist/knowledge/loaders/markdown.js +205 -0
  91. package/dist/knowledge/loaders/markdown.js.map +1 -0
  92. package/dist/knowledge/loaders/yaml.d.ts +112 -0
  93. package/dist/knowledge/loaders/yaml.d.ts.map +1 -0
  94. package/dist/knowledge/loaders/yaml.js +368 -0
  95. package/dist/knowledge/loaders/yaml.js.map +1 -0
  96. package/dist/knowledge/retriever.d.ts +144 -0
  97. package/dist/knowledge/retriever.d.ts.map +1 -0
  98. package/dist/knowledge/retriever.js +399 -0
  99. package/dist/knowledge/retriever.js.map +1 -0
  100. package/dist/knowledge/store.d.ts +146 -0
  101. package/dist/knowledge/store.d.ts.map +1 -0
  102. package/dist/knowledge/store.js +420 -0
  103. package/dist/knowledge/store.js.map +1 -0
  104. package/dist/llm/adapter.d.ts +194 -0
  105. package/dist/llm/adapter.d.ts.map +1 -0
  106. package/dist/llm/adapter.js +42 -0
  107. package/dist/llm/adapter.js.map +1 -0
  108. package/dist/llm/adapters/anyrouter.d.ts +84 -0
  109. package/dist/llm/adapters/anyrouter.d.ts.map +1 -0
  110. package/dist/llm/adapters/anyrouter.js +372 -0
  111. package/dist/llm/adapters/anyrouter.js.map +1 -0
  112. package/dist/llm/adapters/claude.d.ts +66 -0
  113. package/dist/llm/adapters/claude.d.ts.map +1 -0
  114. package/dist/llm/adapters/claude.js +323 -0
  115. package/dist/llm/adapters/claude.js.map +1 -0
  116. package/dist/llm/adapters/index.d.ts +12 -0
  117. package/dist/llm/adapters/index.d.ts.map +1 -0
  118. package/dist/llm/adapters/index.js +12 -0
  119. package/dist/llm/adapters/index.js.map +1 -0
  120. package/dist/llm/adapters/mimo.d.ts +85 -0
  121. package/dist/llm/adapters/mimo.d.ts.map +1 -0
  122. package/dist/llm/adapters/mimo.js +316 -0
  123. package/dist/llm/adapters/mimo.js.map +1 -0
  124. package/dist/llm/adapters/openai.d.ts +53 -0
  125. package/dist/llm/adapters/openai.d.ts.map +1 -0
  126. package/dist/llm/adapters/openai.js +293 -0
  127. package/dist/llm/adapters/openai.js.map +1 -0
  128. package/dist/llm/adapters/qwen.d.ts +53 -0
  129. package/dist/llm/adapters/qwen.d.ts.map +1 -0
  130. package/dist/llm/adapters/qwen.js +299 -0
  131. package/dist/llm/adapters/qwen.js.map +1 -0
  132. package/dist/llm/adapters/siliconflow.d.ts +69 -0
  133. package/dist/llm/adapters/siliconflow.d.ts.map +1 -0
  134. package/dist/llm/adapters/siliconflow.js +331 -0
  135. package/dist/llm/adapters/siliconflow.js.map +1 -0
  136. package/dist/llm/index.d.ts +9 -0
  137. package/dist/llm/index.d.ts.map +1 -0
  138. package/dist/llm/index.js +12 -0
  139. package/dist/llm/index.js.map +1 -0
  140. package/dist/llm/manager.d.ts +97 -0
  141. package/dist/llm/manager.d.ts.map +1 -0
  142. package/dist/llm/manager.js +337 -0
  143. package/dist/llm/manager.js.map +1 -0
  144. package/dist/test-utils/arbitraries.d.ts +230 -0
  145. package/dist/test-utils/arbitraries.d.ts.map +1 -0
  146. package/dist/test-utils/arbitraries.js +280 -0
  147. package/dist/test-utils/arbitraries.js.map +1 -0
  148. package/dist/test-utils/cleanup.d.ts +184 -0
  149. package/dist/test-utils/cleanup.d.ts.map +1 -0
  150. package/dist/test-utils/cleanup.js +282 -0
  151. package/dist/test-utils/cleanup.js.map +1 -0
  152. package/dist/test-utils/config.d.ts +80 -0
  153. package/dist/test-utils/config.d.ts.map +1 -0
  154. package/dist/test-utils/config.js +94 -0
  155. package/dist/test-utils/config.js.map +1 -0
  156. package/dist/test-utils/index.d.ts +10 -0
  157. package/dist/test-utils/index.d.ts.map +1 -0
  158. package/dist/test-utils/index.js +36 -0
  159. package/dist/test-utils/index.js.map +1 -0
  160. package/dist/test-utils/mocks.d.ts +170 -0
  161. package/dist/test-utils/mocks.d.ts.map +1 -0
  162. package/dist/test-utils/mocks.js +281 -0
  163. package/dist/test-utils/mocks.js.map +1 -0
  164. package/dist/types/config.d.ts +170 -0
  165. package/dist/types/config.d.ts.map +1 -0
  166. package/dist/types/config.js +120 -0
  167. package/dist/types/config.js.map +1 -0
  168. package/dist/types/knowledge.d.ts +95 -0
  169. package/dist/types/knowledge.d.ts.map +1 -0
  170. package/dist/types/knowledge.js +7 -0
  171. package/dist/types/knowledge.js.map +1 -0
  172. package/dist/types/loop.d.ts +148 -0
  173. package/dist/types/loop.d.ts.map +1 -0
  174. package/dist/types/loop.js +16 -0
  175. package/dist/types/loop.js.map +1 -0
  176. package/dist/types/plugin.d.ts +137 -0
  177. package/dist/types/plugin.d.ts.map +1 -0
  178. package/dist/types/plugin.js +15 -0
  179. package/dist/types/plugin.js.map +1 -0
  180. package/dist/types/response.d.ts +186 -0
  181. package/dist/types/response.d.ts.map +1 -0
  182. package/dist/types/response.js +99 -0
  183. package/dist/types/response.js.map +1 -0
  184. package/dist/types/streaming.d.ts +478 -0
  185. package/dist/types/streaming.d.ts.map +1 -0
  186. package/dist/types/streaming.js +483 -0
  187. package/dist/types/streaming.js.map +1 -0
  188. package/dist/types/tool.d.ts +118 -0
  189. package/dist/types/tool.d.ts.map +1 -0
  190. package/dist/types/tool.js +42 -0
  191. package/dist/types/tool.js.map +1 -0
  192. package/dist/utils/error.d.ts +22 -0
  193. package/dist/utils/error.d.ts.map +1 -0
  194. package/dist/utils/error.js +36 -0
  195. package/dist/utils/error.js.map +1 -0
  196. package/package.json +102 -0
@@ -0,0 +1,674 @@
1
+ /**
2
+ * Agent Core
3
+ *
4
+ * Main Agent class that orchestrates the agentic loop, plugin management,
5
+ * knowledge retrieval, and response handling.
6
+ *
7
+ * Refactored to use:
8
+ * - PluginManager for tool management (replacing ToolRegistry)
9
+ * - AgenticLoop for autonomous LLM decision-making
10
+ * - Confirmation mechanism for high-risk operations
11
+ *
12
+ * _Requirements: 1.1-1.7, 4.1, 4.5, 4.7_
13
+ */
14
+ import { v4 as uuidv4 } from 'uuid';
15
+ import { createProcessingStartedEvent, createCompletedEvent, createErrorEvent, createKnowledgeRetrievedEvent, createConfirmationCheckEvent, } from '../types/streaming.js';
16
+ import { LLMManager } from '../llm/manager.js';
17
+ import { KnowledgeStore } from '../knowledge/store.js';
18
+ import { Embedder } from '../knowledge/embedder.js';
19
+ import { Retriever } from '../knowledge/retriever.js';
20
+ import { PluginManager } from './plugin-manager.js';
21
+ import { AgenticLoop } from './agentic-loop.js';
22
+ import { ResponseHandler } from './response-handler.js';
23
+ /**
24
+ * Default behavior configuration
25
+ */
26
+ const DEFAULT_BEHAVIOR = {
27
+ timeoutMs: 30000,
28
+ maxIterations: 10,
29
+ requireConfirmation: true,
30
+ confidenceThreshold: 0.8,
31
+ systemPrompt: `You are a helpful AI assistant for game server management.
32
+ You have access to tools for managing MythicMobs configurations and Boss spawn points.
33
+ When the user asks you to do something, use the appropriate tools to complete the task.
34
+ Always respond in the same language as the user (Chinese or English).
35
+ If you're unsure about something, ask for clarification.`,
36
+ };
37
+ /**
38
+ * Agent
39
+ *
40
+ * Main AI Agent class that coordinates all components to process
41
+ * user messages and generate appropriate responses using the agentic loop.
42
+ */
43
+ export class Agent {
44
+ config;
45
+ behavior;
46
+ // Core components
47
+ llmManager;
48
+ knowledgeStore;
49
+ embedder;
50
+ retriever;
51
+ pluginManager;
52
+ agenticLoop;
53
+ responseHandler;
54
+ // Logger
55
+ logger;
56
+ logLevel;
57
+ metricsEnabled;
58
+ // Plugin context for dependency injection
59
+ pluginContext;
60
+ constructor(config) {
61
+ this.config = config;
62
+ this.behavior = { ...DEFAULT_BEHAVIOR, ...config.behavior };
63
+ // Initialize logging configuration
64
+ this.logLevel = config.logging?.level ?? 'info';
65
+ this.metricsEnabled = config.logging?.enableMetrics ?? false;
66
+ // Initialize logger (custom or default)
67
+ this.logger = config.logging?.logger
68
+ ? this.createCustomLogger(config.logging.logger)
69
+ : this.createLogger();
70
+ // Initialize LLM Manager
71
+ this.llmManager = new LLMManager(config.llm);
72
+ // Initialize Embedder (if LLM supports embeddings)
73
+ if (this.llmManager.supportsEmbeddings()) {
74
+ // BGE models (used by SiliconFlow) output 1024 dimensions
75
+ // OpenAI text-embedding-ada-002 outputs 1536 dimensions
76
+ const embeddingDimension = config.llm.default?.provider === 'siliconflow' ? 1024 : 1536;
77
+ this.embedder = new Embedder(this.llmManager, {
78
+ expectedDimension: embeddingDimension,
79
+ });
80
+ }
81
+ // Initialize Knowledge Store
82
+ this.knowledgeStore = new KnowledgeStore({
83
+ embedder: this.embedder,
84
+ generateEmbeddings: true,
85
+ });
86
+ // Initialize Retriever
87
+ this.retriever = new Retriever(this.knowledgeStore, this.embedder, config.knowledge?.search);
88
+ // Initialize Plugin Manager
89
+ this.pluginManager = new PluginManager({
90
+ conflictStrategy: 'error',
91
+ autoNamespace: true,
92
+ strictValidation: true,
93
+ });
94
+ // Create plugin context for dependency injection
95
+ this.pluginContext = this.createPluginContext();
96
+ this.pluginManager.setContext(this.pluginContext);
97
+ // Initialize Agentic Loop
98
+ const loopConfig = {
99
+ maxIterations: this.behavior.maxIterations,
100
+ iterationTimeout: this.behavior.timeoutMs,
101
+ parallelToolCalls: true,
102
+ continueOnError: true,
103
+ };
104
+ this.agenticLoop = new AgenticLoop(this.llmManager, this.pluginManager, loopConfig);
105
+ // Initialize Response Handler
106
+ this.responseHandler = new ResponseHandler(this.llmManager);
107
+ }
108
+ /**
109
+ * Process a user message and generate a response
110
+ *
111
+ * Pure Stateless Implementation:
112
+ * - Does not maintain session state in memory
113
+ * - History is passed in via options.history (loaded from database)
114
+ * - PendingConfirmation is passed in via options.pendingConfirmation
115
+ * - Returns pendingConfirmation in response for external persistence
116
+ *
117
+ * _Requirements: 1.1, 1.2, 1.3, 2.3, 2.4_
118
+ */
119
+ async chat(message, options) {
120
+ const sessionId = options?.sessionId ?? `session_${Date.now()}`;
121
+ const messageId = options?.messageId ?? `msg_${Date.now()}`;
122
+ const startTime = Date.now();
123
+ // Convert external history to LoopMessage format for AgenticLoop
124
+ const history = this.convertHistoryToLoopMessages(options?.history);
125
+ // Emit processing_started event if streaming is enabled
126
+ // Requirement: 1.1
127
+ if (options?.onEvent) {
128
+ options.onEvent(createProcessingStartedEvent(sessionId, messageId));
129
+ }
130
+ try {
131
+ // Check if this is a confirmation response (pendingConfirmation from database)
132
+ if (options?.pendingConfirmation) {
133
+ return this.handleConfirmationResponse(options.pendingConfirmation, message, options);
134
+ }
135
+ // Build tool context
136
+ const toolContext = this.createToolContext(sessionId);
137
+ // Build system prompt with knowledge context
138
+ let systemPrompt = options?.systemPrompt ?? this.behavior.systemPrompt;
139
+ // Optionally retrieve and inject knowledge
140
+ if (!options?.skipKnowledge) {
141
+ const knowledgeContext = await this.retrieveKnowledgeContextWithEvents(message, sessionId, options?.onEvent);
142
+ if (knowledgeContext) {
143
+ systemPrompt += `\n\nRelevant knowledge:\n${knowledgeContext}`;
144
+ }
145
+ }
146
+ // Check for tools requiring confirmation before running the loop
147
+ if (!options?.skipConfirmation && this.behavior.requireConfirmation) {
148
+ // Emit confirmation_check event if streaming is enabled
149
+ // Requirement: 3.2
150
+ if (options?.onEvent) {
151
+ options.onEvent(createConfirmationCheckEvent(sessionId, 'Checking if operation requires confirmation'));
152
+ }
153
+ const confirmationNeeded = await this.checkConfirmationNeeded(message, systemPrompt);
154
+ if (confirmationNeeded) {
155
+ // Return confirmation response with pendingConfirmation for external persistence
156
+ const confirmResponse = this.createConfirmResponse(confirmationNeeded);
157
+ // Attach pendingConfirmation data for database storage
158
+ confirmResponse.pendingConfirmation = {
159
+ toolName: confirmationNeeded.toolName,
160
+ arguments: confirmationNeeded.arguments,
161
+ userMessage: message,
162
+ timestamp: new Date(),
163
+ };
164
+ return confirmResponse;
165
+ }
166
+ }
167
+ // Run the agentic loop with streaming support
168
+ // Pass history for stateless operation
169
+ const loopResult = await this.agenticLoop.run(message, toolContext, {
170
+ systemPrompt,
171
+ maxIterations: this.behavior.maxIterations,
172
+ timeout: this.behavior.timeoutMs,
173
+ abortSignal: options?.abortSignal,
174
+ onEvent: options?.onEvent,
175
+ sessionId,
176
+ history, // Inject historical messages
177
+ });
178
+ // Convert loop result to agent response
179
+ const response = this.loopResultToResponse(loopResult);
180
+ // Emit completed event if streaming is enabled
181
+ // Requirement: 2.3
182
+ if (options?.onEvent) {
183
+ const totalDuration = Date.now() - startTime;
184
+ options.onEvent(createCompletedEvent(sessionId, messageId, totalDuration, loopResult.iterations, loopResult.toolCalls.length));
185
+ }
186
+ return response;
187
+ }
188
+ catch (error) {
189
+ this.logger.error('Agent processing failed', {
190
+ error: error instanceof Error ? error.message : String(error),
191
+ });
192
+ // Emit error event if streaming is enabled
193
+ // Requirement: 2.4
194
+ if (options?.onEvent) {
195
+ const errorCode = this.mapErrorToCode(error);
196
+ options.onEvent(createErrorEvent(sessionId, errorCode, error instanceof Error ? error.message : String(error), false));
197
+ }
198
+ return this.responseHandler.createErrorResponse(error instanceof Error ? error : new Error(String(error)));
199
+ }
200
+ }
201
+ /**
202
+ * Convert external Message history to LoopMessage format
203
+ */
204
+ convertHistoryToLoopMessages(history) {
205
+ if (!history || history.length === 0) {
206
+ return [];
207
+ }
208
+ return history.map((m) => {
209
+ const loopMessage = {
210
+ role: m.role,
211
+ content: m.content,
212
+ };
213
+ // Include toolCalls if present (for assistant messages)
214
+ if (m.toolCalls && m.toolCalls.length > 0) {
215
+ loopMessage.toolCalls = m.toolCalls.map((tc) => ({
216
+ id: `tc_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
217
+ type: 'function',
218
+ function: {
219
+ name: tc.toolName,
220
+ arguments: JSON.stringify(tc.arguments),
221
+ },
222
+ }));
223
+ }
224
+ return loopMessage;
225
+ });
226
+ }
227
+ /**
228
+ * Map an error to a streaming error code
229
+ */
230
+ mapErrorToCode(error) {
231
+ if (error instanceof Error) {
232
+ const message = error.message.toLowerCase();
233
+ if (message.includes('timeout'))
234
+ return 'TIMEOUT';
235
+ if (message.includes('rate limit'))
236
+ return 'RATE_LIMIT_EXCEEDED';
237
+ if (message.includes('context length'))
238
+ return 'CONTEXT_LENGTH_EXCEEDED';
239
+ if (message.includes('network'))
240
+ return 'NETWORK_ERROR';
241
+ if (message.includes('database'))
242
+ return 'DATABASE_ERROR';
243
+ }
244
+ return 'UNKNOWN_ERROR';
245
+ }
246
+ /**
247
+ * Retrieve knowledge context with streaming events
248
+ * Requirement: 3.1
249
+ */
250
+ async retrieveKnowledgeContextWithEvents(message, sessionId, onEvent) {
251
+ try {
252
+ const results = await this.retriever.search(message, {
253
+ method: 'hybrid',
254
+ topK: 3,
255
+ });
256
+ if (results.length === 0) {
257
+ return null;
258
+ }
259
+ // Emit knowledge_retrieved event if streaming is enabled
260
+ if (onEvent) {
261
+ const categories = [...new Set(results.map((r) => r.document.category))];
262
+ onEvent(createKnowledgeRetrievedEvent(sessionId, results.length, categories));
263
+ }
264
+ return results.map((r) => `[${r.document.category}] ${r.document.content}`).join('\n\n');
265
+ }
266
+ catch (error) {
267
+ this.logger.warn('Failed to retrieve knowledge', {
268
+ error: error instanceof Error ? error.message : String(error),
269
+ });
270
+ return null;
271
+ }
272
+ }
273
+ /**
274
+ * Handle user's response to a confirmation request
275
+ *
276
+ * Stateless version: receives pendingConfirmation from database via options
277
+ */
278
+ async handleConfirmationResponse(pending, message, options) {
279
+ const lowerMessage = message.toLowerCase().trim();
280
+ // Check if user confirmed
281
+ const confirmed = lowerMessage === 'yes' ||
282
+ lowerMessage === 'y' ||
283
+ lowerMessage === '是' ||
284
+ lowerMessage === '确认' ||
285
+ lowerMessage === '确定' ||
286
+ lowerMessage === 'confirm' ||
287
+ lowerMessage === 'ok';
288
+ if (!confirmed) {
289
+ return this.responseHandler.createSimpleExecuteResponse('操作已取消。', { cancelled: true });
290
+ }
291
+ // Re-run with skipConfirmation and without pendingConfirmation
292
+ // This ensures the confirmed operation is executed
293
+ return this.chat(pending.userMessage, {
294
+ ...options,
295
+ skipConfirmation: true,
296
+ pendingConfirmation: undefined, // Clear to avoid infinite loop
297
+ });
298
+ }
299
+ /**
300
+ * Check if any tool in the potential execution requires confirmation
301
+ *
302
+ * _Requirements: 4.7_
303
+ */
304
+ async checkConfirmationNeeded(message, systemPrompt) {
305
+ // Get tools that require confirmation
306
+ const confirmationTools = this.pluginManager
307
+ .listTools()
308
+ .filter((tool) => tool.requiresConfirmation || tool.riskLevel === 'high');
309
+ if (confirmationTools.length === 0) {
310
+ return null;
311
+ }
312
+ // Use LLM to predict which tool might be called
313
+ try {
314
+ const toolDefinitions = this.pluginManager.getToolDefinitions();
315
+ const response = await this.llmManager.generateWithTools('tool_calling', [
316
+ { role: 'system', content: systemPrompt },
317
+ { role: 'user', content: message },
318
+ ], toolDefinitions);
319
+ // Check if any predicted tool requires confirmation
320
+ if (response.toolCalls && response.toolCalls.length > 0) {
321
+ for (const toolCall of response.toolCalls) {
322
+ const tool = this.pluginManager.getTool(toolCall.name);
323
+ if (tool && (tool.requiresConfirmation || tool.riskLevel === 'high')) {
324
+ return {
325
+ toolName: toolCall.name,
326
+ arguments: toolCall.arguments,
327
+ };
328
+ }
329
+ }
330
+ }
331
+ }
332
+ catch (error) {
333
+ this.logger.warn('Failed to check confirmation needed', {
334
+ error: error instanceof Error ? error.message : String(error),
335
+ });
336
+ }
337
+ return null;
338
+ }
339
+ /**
340
+ * Create a confirmation response
341
+ */
342
+ createConfirmResponse(toolInfo) {
343
+ const tool = this.pluginManager.getTool(toolInfo.toolName);
344
+ const riskLevel = tool?.riskLevel ?? 'medium';
345
+ return {
346
+ type: 'confirm',
347
+ message: `即将执行操作: ${toolInfo.toolName},请确认是否继续?`,
348
+ action: {
349
+ type: toolInfo.toolName,
350
+ target: String(toolInfo.arguments.target ?? toolInfo.arguments.name ?? 'unknown'),
351
+ params: toolInfo.arguments,
352
+ },
353
+ risk: riskLevel,
354
+ preview: `工具: ${toolInfo.toolName}\n参数: ${JSON.stringify(toolInfo.arguments, null, 2)}`,
355
+ };
356
+ }
357
+ /**
358
+ * Convert agentic loop result to agent response
359
+ */
360
+ loopResultToResponse(result) {
361
+ // Convert tool call records
362
+ const toolCalls = result.toolCalls.map((tc) => ({
363
+ toolName: tc.toolName,
364
+ arguments: tc.arguments,
365
+ result: {
366
+ success: tc.result.success,
367
+ content: tc.result.content,
368
+ data: tc.result.data,
369
+ },
370
+ }));
371
+ // Handle different loop statuses
372
+ if (result.status === 'error') {
373
+ return this.responseHandler.createErrorResponse(result.error ?? 'Unknown error');
374
+ }
375
+ if (result.status === 'max_iterations') {
376
+ return {
377
+ type: 'execute',
378
+ message: `达到最大迭代次数限制 (${result.iterations})。部分结果: ${result.content}`,
379
+ data: { partial: true, iterations: result.iterations },
380
+ toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
381
+ };
382
+ }
383
+ if (result.status === 'cancelled') {
384
+ return {
385
+ type: 'execute',
386
+ message: '操作已取消。',
387
+ data: { cancelled: true },
388
+ };
389
+ }
390
+ // Successful completion
391
+ return {
392
+ type: 'execute',
393
+ message: result.content,
394
+ data: toolCalls.length > 0 ? { toolCallCount: toolCalls.length } : undefined,
395
+ toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
396
+ };
397
+ }
398
+ /**
399
+ * Retrieve knowledge context for the message
400
+ * @deprecated Use retrieveKnowledgeContextWithEvents instead
401
+ */
402
+ async retrieveKnowledgeContext(message) {
403
+ return this.retrieveKnowledgeContextWithEvents(message, 'default');
404
+ }
405
+ /**
406
+ * Continue processing after user confirmation
407
+ *
408
+ * @deprecated In stateless mode, confirmations should be handled by passing
409
+ * pendingConfirmation via options.pendingConfirmation in chat()
410
+ */
411
+ async confirm(confirmed, sessionId, options) {
412
+ const confirmMessage = confirmed ? 'yes' : 'no';
413
+ return this.chat(confirmMessage, {
414
+ ...options,
415
+ sessionId,
416
+ });
417
+ }
418
+ /**
419
+ * Add knowledge to the knowledge base
420
+ */
421
+ async addKnowledge(content, category, title) {
422
+ const doc = {
423
+ content,
424
+ category,
425
+ title,
426
+ };
427
+ return this.knowledgeStore.addDocument(doc);
428
+ }
429
+ // ============================================
430
+ // Plugin Management Methods
431
+ // _Requirements: 4.1, 4.5_
432
+ // ============================================
433
+ /**
434
+ * Load a plugin
435
+ */
436
+ async loadPlugin(plugin) {
437
+ await this.pluginManager.load(plugin);
438
+ this.logger.info('Plugin loaded', {
439
+ name: plugin.name,
440
+ version: plugin.version,
441
+ toolCount: plugin.tools.length,
442
+ });
443
+ }
444
+ /**
445
+ * Unload a plugin
446
+ */
447
+ async unloadPlugin(pluginName) {
448
+ const result = await this.pluginManager.unload(pluginName);
449
+ if (result) {
450
+ this.logger.info('Plugin unloaded', { name: pluginName });
451
+ }
452
+ return result;
453
+ }
454
+ /**
455
+ * List all loaded plugins
456
+ */
457
+ listPlugins() {
458
+ return this.pluginManager.listPlugins();
459
+ }
460
+ /**
461
+ * Get a specific plugin
462
+ */
463
+ getPlugin(name) {
464
+ return this.pluginManager.getPlugin(name);
465
+ }
466
+ /**
467
+ * Check if a plugin is loaded
468
+ */
469
+ hasPlugin(name) {
470
+ return this.pluginManager.hasPlugin(name);
471
+ }
472
+ // ============================================
473
+ // Legacy Tool Registration (for backward compatibility)
474
+ // ============================================
475
+ /**
476
+ * Register a tool with the agent (legacy method)
477
+ * @deprecated Use loadPlugin instead
478
+ */
479
+ registerTool(tool) {
480
+ // Create a simple plugin wrapper for the tool
481
+ const plugin = {
482
+ name: `legacy_${tool.name}`,
483
+ version: '1.0.0',
484
+ description: `Legacy tool: ${tool.description}`,
485
+ tools: [tool],
486
+ };
487
+ this.pluginManager.load(plugin).catch((err) => {
488
+ this.logger.error('Failed to register legacy tool', {
489
+ tool: tool.name,
490
+ error: err instanceof Error ? err.message : String(err),
491
+ });
492
+ });
493
+ }
494
+ /**
495
+ * Register multiple tools (legacy method)
496
+ * @deprecated Use loadPlugin instead
497
+ */
498
+ registerTools(tools) {
499
+ if (tools.length === 0)
500
+ return;
501
+ const plugin = {
502
+ name: 'legacy_tools',
503
+ version: '1.0.0',
504
+ description: 'Legacy tools bundle',
505
+ tools,
506
+ };
507
+ this.pluginManager.load(plugin).catch((err) => {
508
+ this.logger.error('Failed to register legacy tools', {
509
+ error: err instanceof Error ? err.message : String(err),
510
+ });
511
+ });
512
+ }
513
+ // ============================================
514
+ // Session Management (Stateless - Deprecated Methods)
515
+ // ============================================
516
+ /**
517
+ * @deprecated In stateless mode, history should be passed via options.history in chat()
518
+ * This method is kept for backward compatibility but does nothing.
519
+ */
520
+ importSessionHistory(_sessionId, _messages) {
521
+ this.logger.warn('importSessionHistory is deprecated in stateless mode. Pass history via options.history in chat()');
522
+ }
523
+ /**
524
+ * @deprecated In stateless mode, Agent does not maintain session state.
525
+ * Always returns false.
526
+ */
527
+ hasSessionHistory(_sessionId) {
528
+ this.logger.warn('hasSessionHistory is deprecated in stateless mode. Agent does not maintain session state.');
529
+ return false;
530
+ }
531
+ /**
532
+ * @deprecated In stateless mode, history is not stored in Agent.
533
+ * Always returns empty array.
534
+ */
535
+ getHistory(_sessionId) {
536
+ this.logger.warn('getHistory is deprecated in stateless mode. History should be loaded from database.');
537
+ return [];
538
+ }
539
+ /**
540
+ * @deprecated In stateless mode, sessions are managed externally (database).
541
+ * Returns a generated UUID for compatibility.
542
+ */
543
+ createSession(_metadata) {
544
+ this.logger.warn('createSession is deprecated in stateless mode. Sessions should be created in database.');
545
+ return uuidv4();
546
+ }
547
+ // ============================================
548
+ // Context and Logger Creation
549
+ // ============================================
550
+ /**
551
+ * Create tool context for execution
552
+ */
553
+ createToolContext(sessionId) {
554
+ return {
555
+ knowledgeBase: this.knowledgeStore,
556
+ sessionId,
557
+ logger: this.logger,
558
+ };
559
+ }
560
+ /**
561
+ * Create plugin context for dependency injection
562
+ */
563
+ createPluginContext() {
564
+ const appConfig = {
565
+ env: process.env.NODE_ENV ?? 'development',
566
+ debug: process.env.DEBUG === 'true',
567
+ };
568
+ return {
569
+ logger: this.logger,
570
+ knowledgeBase: this.knowledgeStore,
571
+ config: appConfig,
572
+ services: {},
573
+ };
574
+ }
575
+ /**
576
+ * Create a logger instance with log level filtering
577
+ */
578
+ createLogger() {
579
+ const levels = { debug: 0, info: 1, warn: 2, error: 3 };
580
+ const currentLevel = levels[this.logLevel];
581
+ return {
582
+ info: (message, data) => {
583
+ if (currentLevel <= levels.info) {
584
+ console.log(`[Agent INFO] ${message}`, data ?? '');
585
+ }
586
+ },
587
+ warn: (message, data) => {
588
+ if (currentLevel <= levels.warn) {
589
+ console.warn(`[Agent WARN] ${message}`, data ?? '');
590
+ }
591
+ },
592
+ error: (message, data) => {
593
+ if (currentLevel <= levels.error) {
594
+ console.error(`[Agent ERROR] ${message}`, data ?? '');
595
+ }
596
+ },
597
+ debug: (message, data) => {
598
+ if (currentLevel <= levels.debug) {
599
+ console.debug(`[Agent DEBUG] ${message}`, data ?? '');
600
+ }
601
+ },
602
+ };
603
+ }
604
+ /**
605
+ * Create a logger wrapper for custom logger
606
+ */
607
+ createCustomLogger(customLogger) {
608
+ return {
609
+ info: customLogger.info.bind(customLogger),
610
+ warn: customLogger.warn.bind(customLogger),
611
+ error: customLogger.error.bind(customLogger),
612
+ debug: customLogger.debug.bind(customLogger),
613
+ };
614
+ }
615
+ // ============================================
616
+ // Accessors
617
+ // ============================================
618
+ /**
619
+ * Get the LLM Manager
620
+ */
621
+ getLLMManager() {
622
+ return this.llmManager;
623
+ }
624
+ /**
625
+ * Get the Plugin Manager
626
+ */
627
+ getPluginManager() {
628
+ return this.pluginManager;
629
+ }
630
+ /**
631
+ * Get the Knowledge Store
632
+ */
633
+ getKnowledgeStore() {
634
+ return this.knowledgeStore;
635
+ }
636
+ /**
637
+ * Get the Retriever
638
+ */
639
+ getRetriever() {
640
+ return this.retriever;
641
+ }
642
+ /**
643
+ * Get the Agentic Loop
644
+ */
645
+ getAgenticLoop() {
646
+ return this.agenticLoop;
647
+ }
648
+ /**
649
+ * Get the current session ID
650
+ * @deprecated In stateless mode, session ID should be managed externally.
651
+ * Returns a placeholder value for compatibility.
652
+ */
653
+ getCurrentSessionId() {
654
+ return 'stateless';
655
+ }
656
+ }
657
+ /**
658
+ * Create an Agent with simplified configuration
659
+ */
660
+ export function createAgent(llmProvider, apiKey, model, options) {
661
+ const config = {
662
+ llm: {
663
+ mode: 'single',
664
+ default: {
665
+ provider: llmProvider,
666
+ apiKey,
667
+ model,
668
+ },
669
+ },
670
+ ...options,
671
+ };
672
+ return new Agent(config);
673
+ }
674
+ //# sourceMappingURL=agent.js.map