@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.
- package/LICENSE +21 -0
- package/README.md +370 -0
- package/README.zh-CN.md +372 -0
- package/dist/audit/index.d.ts +9 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +9 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/logger.d.ts +211 -0
- package/dist/audit/logger.d.ts.map +1 -0
- package/dist/audit/logger.js +268 -0
- package/dist/audit/logger.js.map +1 -0
- package/dist/audit/query.d.ts +164 -0
- package/dist/audit/query.d.ts.map +1 -0
- package/dist/audit/query.js +250 -0
- package/dist/audit/query.js.map +1 -0
- package/dist/conversation/context-builder.d.ts +119 -0
- package/dist/conversation/context-builder.d.ts.map +1 -0
- package/dist/conversation/context-builder.js +252 -0
- package/dist/conversation/context-builder.js.map +1 -0
- package/dist/conversation/index.d.ts +10 -0
- package/dist/conversation/index.d.ts.map +1 -0
- package/dist/conversation/index.js +10 -0
- package/dist/conversation/index.js.map +1 -0
- package/dist/conversation/message-store.d.ts +231 -0
- package/dist/conversation/message-store.d.ts.map +1 -0
- package/dist/conversation/message-store.js +404 -0
- package/dist/conversation/message-store.js.map +1 -0
- package/dist/conversation/session.d.ts +201 -0
- package/dist/conversation/session.d.ts.map +1 -0
- package/dist/conversation/session.js +285 -0
- package/dist/conversation/session.js.map +1 -0
- package/dist/core/agent.d.ts +277 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +674 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/agentic-loop.d.ts +98 -0
- package/dist/core/agentic-loop.d.ts.map +1 -0
- package/dist/core/agentic-loop.js +496 -0
- package/dist/core/agentic-loop.js.map +1 -0
- package/dist/core/index.d.ts +14 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +14 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/intent-parser.d.ts +101 -0
- package/dist/core/intent-parser.d.ts.map +1 -0
- package/dist/core/intent-parser.js +221 -0
- package/dist/core/intent-parser.js.map +1 -0
- package/dist/core/plan-generator.d.ts +133 -0
- package/dist/core/plan-generator.d.ts.map +1 -0
- package/dist/core/plan-generator.js +294 -0
- package/dist/core/plan-generator.js.map +1 -0
- package/dist/core/plugin-manager.d.ts +120 -0
- package/dist/core/plugin-manager.d.ts.map +1 -0
- package/dist/core/plugin-manager.js +369 -0
- package/dist/core/plugin-manager.js.map +1 -0
- package/dist/core/response-handler.d.ts +141 -0
- package/dist/core/response-handler.d.ts.map +1 -0
- package/dist/core/response-handler.js +384 -0
- package/dist/core/response-handler.js.map +1 -0
- package/dist/core/tool-executor.d.ts +143 -0
- package/dist/core/tool-executor.d.ts.map +1 -0
- package/dist/core/tool-executor.js +354 -0
- package/dist/core/tool-executor.js.map +1 -0
- package/dist/core/tool-registry.d.ts +133 -0
- package/dist/core/tool-registry.d.ts.map +1 -0
- package/dist/core/tool-registry.js +252 -0
- package/dist/core/tool-registry.js.map +1 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/knowledge/chunker.d.ts +78 -0
- package/dist/knowledge/chunker.d.ts.map +1 -0
- package/dist/knowledge/chunker.js +233 -0
- package/dist/knowledge/chunker.js.map +1 -0
- package/dist/knowledge/embedder.d.ts +93 -0
- package/dist/knowledge/embedder.d.ts.map +1 -0
- package/dist/knowledge/embedder.js +205 -0
- package/dist/knowledge/embedder.js.map +1 -0
- package/dist/knowledge/index.d.ts +10 -0
- package/dist/knowledge/index.d.ts.map +1 -0
- package/dist/knowledge/index.js +11 -0
- package/dist/knowledge/index.js.map +1 -0
- package/dist/knowledge/loaders/index.d.ts +10 -0
- package/dist/knowledge/loaders/index.d.ts.map +1 -0
- package/dist/knowledge/loaders/index.js +10 -0
- package/dist/knowledge/loaders/index.js.map +1 -0
- package/dist/knowledge/loaders/markdown.d.ts +88 -0
- package/dist/knowledge/loaders/markdown.d.ts.map +1 -0
- package/dist/knowledge/loaders/markdown.js +205 -0
- package/dist/knowledge/loaders/markdown.js.map +1 -0
- package/dist/knowledge/loaders/yaml.d.ts +112 -0
- package/dist/knowledge/loaders/yaml.d.ts.map +1 -0
- package/dist/knowledge/loaders/yaml.js +368 -0
- package/dist/knowledge/loaders/yaml.js.map +1 -0
- package/dist/knowledge/retriever.d.ts +144 -0
- package/dist/knowledge/retriever.d.ts.map +1 -0
- package/dist/knowledge/retriever.js +399 -0
- package/dist/knowledge/retriever.js.map +1 -0
- package/dist/knowledge/store.d.ts +146 -0
- package/dist/knowledge/store.d.ts.map +1 -0
- package/dist/knowledge/store.js +420 -0
- package/dist/knowledge/store.js.map +1 -0
- package/dist/llm/adapter.d.ts +194 -0
- package/dist/llm/adapter.d.ts.map +1 -0
- package/dist/llm/adapter.js +42 -0
- package/dist/llm/adapter.js.map +1 -0
- package/dist/llm/adapters/anyrouter.d.ts +84 -0
- package/dist/llm/adapters/anyrouter.d.ts.map +1 -0
- package/dist/llm/adapters/anyrouter.js +372 -0
- package/dist/llm/adapters/anyrouter.js.map +1 -0
- package/dist/llm/adapters/claude.d.ts +66 -0
- package/dist/llm/adapters/claude.d.ts.map +1 -0
- package/dist/llm/adapters/claude.js +323 -0
- package/dist/llm/adapters/claude.js.map +1 -0
- package/dist/llm/adapters/index.d.ts +12 -0
- package/dist/llm/adapters/index.d.ts.map +1 -0
- package/dist/llm/adapters/index.js +12 -0
- package/dist/llm/adapters/index.js.map +1 -0
- package/dist/llm/adapters/mimo.d.ts +85 -0
- package/dist/llm/adapters/mimo.d.ts.map +1 -0
- package/dist/llm/adapters/mimo.js +316 -0
- package/dist/llm/adapters/mimo.js.map +1 -0
- package/dist/llm/adapters/openai.d.ts +53 -0
- package/dist/llm/adapters/openai.d.ts.map +1 -0
- package/dist/llm/adapters/openai.js +293 -0
- package/dist/llm/adapters/openai.js.map +1 -0
- package/dist/llm/adapters/qwen.d.ts +53 -0
- package/dist/llm/adapters/qwen.d.ts.map +1 -0
- package/dist/llm/adapters/qwen.js +299 -0
- package/dist/llm/adapters/qwen.js.map +1 -0
- package/dist/llm/adapters/siliconflow.d.ts +69 -0
- package/dist/llm/adapters/siliconflow.d.ts.map +1 -0
- package/dist/llm/adapters/siliconflow.js +331 -0
- package/dist/llm/adapters/siliconflow.js.map +1 -0
- package/dist/llm/index.d.ts +9 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +12 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/manager.d.ts +97 -0
- package/dist/llm/manager.d.ts.map +1 -0
- package/dist/llm/manager.js +337 -0
- package/dist/llm/manager.js.map +1 -0
- package/dist/test-utils/arbitraries.d.ts +230 -0
- package/dist/test-utils/arbitraries.d.ts.map +1 -0
- package/dist/test-utils/arbitraries.js +280 -0
- package/dist/test-utils/arbitraries.js.map +1 -0
- package/dist/test-utils/cleanup.d.ts +184 -0
- package/dist/test-utils/cleanup.d.ts.map +1 -0
- package/dist/test-utils/cleanup.js +282 -0
- package/dist/test-utils/cleanup.js.map +1 -0
- package/dist/test-utils/config.d.ts +80 -0
- package/dist/test-utils/config.d.ts.map +1 -0
- package/dist/test-utils/config.js +94 -0
- package/dist/test-utils/config.js.map +1 -0
- package/dist/test-utils/index.d.ts +10 -0
- package/dist/test-utils/index.d.ts.map +1 -0
- package/dist/test-utils/index.js +36 -0
- package/dist/test-utils/index.js.map +1 -0
- package/dist/test-utils/mocks.d.ts +170 -0
- package/dist/test-utils/mocks.d.ts.map +1 -0
- package/dist/test-utils/mocks.js +281 -0
- package/dist/test-utils/mocks.js.map +1 -0
- package/dist/types/config.d.ts +170 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +120 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/knowledge.d.ts +95 -0
- package/dist/types/knowledge.d.ts.map +1 -0
- package/dist/types/knowledge.js +7 -0
- package/dist/types/knowledge.js.map +1 -0
- package/dist/types/loop.d.ts +148 -0
- package/dist/types/loop.d.ts.map +1 -0
- package/dist/types/loop.js +16 -0
- package/dist/types/loop.js.map +1 -0
- package/dist/types/plugin.d.ts +137 -0
- package/dist/types/plugin.d.ts.map +1 -0
- package/dist/types/plugin.js +15 -0
- package/dist/types/plugin.js.map +1 -0
- package/dist/types/response.d.ts +186 -0
- package/dist/types/response.d.ts.map +1 -0
- package/dist/types/response.js +99 -0
- package/dist/types/response.js.map +1 -0
- package/dist/types/streaming.d.ts +478 -0
- package/dist/types/streaming.d.ts.map +1 -0
- package/dist/types/streaming.js +483 -0
- package/dist/types/streaming.js.map +1 -0
- package/dist/types/tool.d.ts +118 -0
- package/dist/types/tool.d.ts.map +1 -0
- package/dist/types/tool.js +42 -0
- package/dist/types/tool.js.map +1 -0
- package/dist/utils/error.d.ts +22 -0
- package/dist/utils/error.d.ts.map +1 -0
- package/dist/utils/error.js +36 -0
- package/dist/utils/error.js.map +1 -0
- 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
|