@xalia/agent 0.5.8 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -8
- package/dist/agent/src/agent/agent.js +173 -96
- package/dist/agent/src/agent/agentUtils.js +82 -53
- package/dist/agent/src/agent/compressingContextManager.js +102 -0
- package/dist/agent/src/agent/context.js +189 -0
- package/dist/agent/src/agent/dummyLLM.js +46 -5
- package/dist/agent/src/agent/iAgentEventHandler.js +2 -0
- package/dist/agent/src/agent/mcpServerManager.js +22 -23
- package/dist/agent/src/agent/nullAgentEventHandler.js +21 -0
- package/dist/agent/src/agent/nullPlatform.js +14 -0
- package/dist/agent/src/agent/openAILLMStreaming.js +12 -7
- package/dist/agent/src/agent/promptProvider.js +63 -0
- package/dist/agent/src/agent/repeatLLM.js +5 -5
- package/dist/agent/src/agent/sudoMcpServerManager.js +11 -9
- package/dist/agent/src/agent/tokenAuth.js +7 -7
- package/dist/agent/src/agent/tools.js +1 -1
- package/dist/agent/src/chat/client/chatClient.js +733 -0
- package/dist/agent/src/chat/client/connection.js +209 -0
- package/dist/agent/src/chat/client/connection.test.js +188 -0
- package/dist/agent/src/chat/client/constants.js +5 -0
- package/dist/agent/src/chat/client/index.js +15 -0
- package/dist/agent/src/chat/client/interfaces.js +2 -0
- package/dist/agent/src/chat/client/responseHandler.js +105 -0
- package/dist/agent/src/chat/client/sessionClient.js +331 -0
- package/dist/agent/src/chat/client/teamManager.js +2 -0
- package/dist/agent/src/chat/{apiKeyManager.js → data/apiKeyManager.js} +4 -0
- package/dist/agent/src/chat/data/dataModels.js +2 -0
- package/dist/agent/src/chat/data/database.js +749 -0
- package/dist/agent/src/chat/data/dbMcpServerConfigs.js +47 -0
- package/dist/agent/src/chat/protocol/connectionMessages.js +5 -0
- package/dist/agent/src/chat/protocol/constants.js +50 -0
- package/dist/agent/src/chat/protocol/errors.js +22 -0
- package/dist/agent/src/chat/protocol/messages.js +110 -0
- package/dist/agent/src/chat/server/chatContextManager.js +405 -0
- package/dist/agent/src/chat/server/connectionManager.js +352 -0
- package/dist/agent/src/chat/server/connectionManager.test.js +159 -0
- package/dist/agent/src/chat/server/conversation.js +198 -0
- package/dist/agent/src/chat/server/errorUtils.js +23 -0
- package/dist/agent/src/chat/server/openSession.js +869 -0
- package/dist/agent/src/chat/server/server.js +177 -0
- package/dist/agent/src/chat/server/sessionFileManager.js +161 -0
- package/dist/agent/src/chat/server/sessionRegistry.js +700 -0
- package/dist/agent/src/chat/server/sessionRegistry.test.js +97 -0
- package/dist/agent/src/chat/server/test-utils/mockFactories.js +307 -0
- package/dist/agent/src/chat/server/tools.js +243 -0
- package/dist/agent/src/chat/utils/agentSessionMap.js +66 -0
- package/dist/agent/src/chat/utils/approvalManager.js +85 -0
- package/dist/agent/src/{utils → chat/utils}/asyncLock.js +3 -3
- package/dist/agent/src/chat/{asyncQueue.js → utils/asyncQueue.js} +12 -2
- package/dist/agent/src/chat/utils/htmlToText.js +84 -0
- package/dist/agent/src/chat/utils/multiAsyncQueue.js +42 -0
- package/dist/agent/src/chat/utils/search.js +145 -0
- package/dist/agent/src/chat/utils/userResolver.js +46 -0
- package/dist/agent/src/chat/{websocket.js → utils/websocket.js} +2 -0
- package/dist/agent/src/test/agent.test.js +332 -0
- package/dist/agent/src/test/approvalManager.test.js +58 -0
- package/dist/agent/src/test/chatContextManager.test.js +392 -0
- package/dist/agent/src/test/clientServerConnection.test.js +158 -0
- package/dist/agent/src/test/compressingContextManager.test.js +65 -0
- package/dist/agent/src/test/context.test.js +83 -0
- package/dist/agent/src/test/conversation.test.js +89 -0
- package/dist/agent/src/test/db.test.js +262 -90
- package/dist/agent/src/test/dbMcpServerConfigs.test.js +72 -0
- package/dist/agent/src/test/dbTestTools.js +99 -0
- package/dist/agent/src/test/imageLoad.test.js +8 -7
- package/dist/agent/src/test/mcpServerManager.test.js +21 -18
- package/dist/agent/src/test/multiAsyncQueue.test.js +101 -0
- package/dist/agent/src/test/openaiStreaming.test.js +12 -11
- package/dist/agent/src/test/prompt.test.js +5 -4
- package/dist/agent/src/test/promptProvider.test.js +28 -0
- package/dist/agent/src/test/responseHandler.test.js +61 -0
- package/dist/agent/src/test/sudoMcpServerManager.test.js +14 -12
- package/dist/agent/src/test/testTools.js +109 -0
- package/dist/agent/src/test/tools.test.js +31 -0
- package/dist/agent/src/tool/agentChat.js +21 -10
- package/dist/agent/src/tool/agentMain.js +1 -1
- package/dist/agent/src/tool/chatMain.js +235 -58
- package/dist/agent/src/tool/commandPrompt.js +15 -9
- package/dist/agent/src/tool/files.js +20 -16
- package/dist/agent/src/tool/nodePlatform.js +47 -3
- package/dist/agent/src/tool/options.js +4 -4
- package/dist/agent/src/tool/prompt.js +19 -13
- package/eslint.config.mjs +14 -1
- package/package.json +14 -6
- package/scripts/chat_server +8 -0
- package/scripts/setup_chat +7 -2
- package/scripts/shutdown_chat_server +3 -0
- package/scripts/test_chat +135 -17
- package/src/agent/agent.ts +270 -135
- package/src/agent/agentUtils.ts +136 -95
- package/src/agent/compressingContextManager.ts +164 -0
- package/src/agent/context.ts +268 -0
- package/src/agent/dummyLLM.ts +76 -8
- package/src/agent/iAgentEventHandler.ts +54 -0
- package/src/agent/iplatform.ts +1 -0
- package/src/agent/mcpServerManager.ts +32 -30
- package/src/agent/nullAgentEventHandler.ts +20 -0
- package/src/agent/nullPlatform.ts +13 -0
- package/src/agent/openAILLMStreaming.ts +12 -6
- package/src/agent/promptProvider.ts +87 -0
- package/src/agent/repeatLLM.ts +5 -5
- package/src/agent/sudoMcpServerManager.ts +13 -11
- package/src/agent/tokenAuth.ts +7 -7
- package/src/agent/tools.ts +3 -1
- package/src/chat/client/chatClient.ts +900 -0
- package/src/chat/client/connection.test.ts +241 -0
- package/src/chat/client/connection.ts +276 -0
- package/src/chat/client/constants.ts +3 -0
- package/src/chat/client/index.ts +18 -0
- package/src/chat/client/interfaces.ts +34 -0
- package/src/chat/client/responseHandler.ts +131 -0
- package/src/chat/client/sessionClient.ts +443 -0
- package/src/chat/client/teamManager.ts +29 -0
- package/src/chat/{apiKeyManager.ts → data/apiKeyManager.ts} +6 -2
- package/src/chat/data/dataModels.ts +85 -0
- package/src/chat/data/database.ts +982 -0
- package/src/chat/data/dbMcpServerConfigs.ts +59 -0
- package/src/chat/protocol/connectionMessages.ts +49 -0
- package/src/chat/protocol/constants.ts +55 -0
- package/src/chat/protocol/errors.ts +16 -0
- package/src/chat/protocol/messages.ts +682 -0
- package/src/chat/server/README.md +127 -0
- package/src/chat/server/chatContextManager.ts +612 -0
- package/src/chat/server/connectionManager.test.ts +266 -0
- package/src/chat/server/connectionManager.ts +541 -0
- package/src/chat/server/conversation.ts +269 -0
- package/src/chat/server/errorUtils.ts +28 -0
- package/src/chat/server/openSession.ts +1332 -0
- package/src/chat/server/server.ts +177 -0
- package/src/chat/server/sessionFileManager.ts +239 -0
- package/src/chat/server/sessionRegistry.test.ts +138 -0
- package/src/chat/server/sessionRegistry.ts +1064 -0
- package/src/chat/server/test-utils/mockFactories.ts +422 -0
- package/src/chat/server/tools.ts +265 -0
- package/src/chat/utils/agentSessionMap.ts +76 -0
- package/src/chat/utils/approvalManager.ts +111 -0
- package/src/{utils → chat/utils}/asyncLock.ts +3 -3
- package/src/chat/{asyncQueue.ts → utils/asyncQueue.ts} +14 -3
- package/src/chat/utils/htmlToText.ts +61 -0
- package/src/chat/utils/multiAsyncQueue.ts +52 -0
- package/src/chat/utils/search.ts +139 -0
- package/src/chat/utils/userResolver.ts +48 -0
- package/src/chat/{websocket.ts → utils/websocket.ts} +2 -0
- package/src/test/agent.test.ts +487 -0
- package/src/test/approvalManager.test.ts +73 -0
- package/src/test/chatContextManager.test.ts +521 -0
- package/src/test/clientServerConnection.test.ts +207 -0
- package/src/test/compressingContextManager.test.ts +82 -0
- package/src/test/context.test.ts +105 -0
- package/src/test/conversation.test.ts +109 -0
- package/src/test/db.test.ts +351 -103
- package/src/test/dbMcpServerConfigs.test.ts +112 -0
- package/src/test/dbTestTools.ts +153 -0
- package/src/test/imageLoad.test.ts +7 -6
- package/src/test/mcpServerManager.test.ts +19 -14
- package/src/test/multiAsyncQueue.test.ts +125 -0
- package/src/test/openaiStreaming.test.ts +11 -10
- package/src/test/prompt.test.ts +4 -3
- package/src/test/promptProvider.test.ts +33 -0
- package/src/test/responseHandler.test.ts +78 -0
- package/src/test/sudoMcpServerManager.test.ts +22 -15
- package/src/test/testTools.ts +146 -0
- package/src/test/tools.test.ts +39 -0
- package/src/tool/agentChat.ts +26 -12
- package/src/tool/agentMain.ts +1 -1
- package/src/tool/chatMain.ts +283 -100
- package/src/tool/commandPrompt.ts +25 -9
- package/src/tool/files.ts +25 -19
- package/src/tool/nodePlatform.ts +52 -3
- package/src/tool/options.ts +4 -2
- package/src/tool/prompt.ts +22 -15
- package/test_data/dummyllm_script_crash.json +32 -0
- package/test_data/frog.png.b64 +1 -0
- package/vitest.config.ts +39 -0
- package/dist/agent/src/chat/client.js +0 -310
- package/dist/agent/src/chat/conversationManager.js +0 -502
- package/dist/agent/src/chat/db.js +0 -218
- package/dist/agent/src/chat/messages.js +0 -29
- package/dist/agent/src/chat/server.js +0 -158
- package/src/chat/client.ts +0 -445
- package/src/chat/conversationManager.ts +0 -730
- package/src/chat/db.ts +0 -304
- package/src/chat/messages.ts +0 -266
- package/src/chat/server.ts +0 -177
- /package/{frog.png → test_data/frog.png} +0 -0
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ A TypeScript-based AI agent system with MCP (Model Context Protocol) support, mu
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
7
|
This agent provides two primary interfaces:
|
|
8
|
+
|
|
8
9
|
- **Agent Mode**: Single-user conversational AI with tool support
|
|
9
10
|
- **Chat Mode**: Multi-user chat server with shared AI agent sessions
|
|
10
11
|
|
|
@@ -20,28 +21,31 @@ yarn workspaces run build
|
|
|
20
21
|
|
|
21
22
|
### Basic Usage
|
|
22
23
|
|
|
23
|
-
Example of running agent can be found in [test script](../mcppro/scripts/test_script).
|
|
24
|
+
Example of running agent can be found in [test script](../mcppro/scripts/test_script).
|
|
24
25
|
|
|
25
26
|
## Architecture
|
|
26
27
|
|
|
27
28
|
### Core Components
|
|
28
29
|
|
|
29
30
|
#### Agent (`src/agent/`)
|
|
31
|
+
|
|
30
32
|
- **`Agent`**: Main orchestrator class managing conversations, tools, and LLM interactions
|
|
31
|
-
- **`McpServerManager`**: Manages MCP tool servers, enables/disables tools dynamically
|
|
33
|
+
- **`McpServerManager`**: Manages MCP tool servers, enables/disables tools dynamically
|
|
32
34
|
- **`SkillManager`**: Interfaces with SudoMCP backend to discover and connect to hosted MCP servers
|
|
33
|
-
- **LLM Implementations**:
|
|
35
|
+
- **LLM Implementations**:
|
|
34
36
|
- `OpenAILLM`: Standard OpenAI API integration
|
|
35
37
|
- `OpenAILLMStreaming`: Streaming response support
|
|
36
38
|
- `DummyLLM`: Mock implementation for testing
|
|
37
39
|
|
|
38
40
|
#### Chat System (`src/chat/`)
|
|
41
|
+
|
|
39
42
|
- **`ChatClient`/`runServer`**: WebSocket-based real-time communication
|
|
40
43
|
- **`ConversationManager`**: Orchestrates multi-user sessions with shared AI agent
|
|
41
44
|
- **`Database`**: Supabase integration for user management, sessions, and agent profiles
|
|
42
45
|
- **`ApiKeyManager`**: Authentication and authorization
|
|
43
46
|
|
|
44
47
|
#### CLI Tools (`src/tool/`)
|
|
48
|
+
|
|
45
49
|
- **`main.ts`**: Primary entry point with subcommands
|
|
46
50
|
- **`agentMain.ts`**: Single-user agent mode implementation
|
|
47
51
|
- **`chatMain.ts`**: Multi-user chat server/client implementation
|
|
@@ -51,28 +55,33 @@ Example of running agent can be found in [test script](../mcppro/scripts/test_sc
|
|
|
51
55
|
### Agent Mode
|
|
52
56
|
|
|
53
57
|
**Basic conversation:**
|
|
58
|
+
|
|
54
59
|
```bash
|
|
55
60
|
cli/agent-cli
|
|
56
61
|
```
|
|
57
62
|
|
|
58
63
|
**One-shot with specific prompt:**
|
|
64
|
+
|
|
59
65
|
```bash
|
|
60
66
|
echo "Explain quantum computing" > prompt.txt
|
|
61
67
|
cli/agent-cli -1 --prompt prompt.txt
|
|
62
68
|
```
|
|
63
69
|
|
|
64
70
|
**With image analysis:**
|
|
71
|
+
|
|
65
72
|
```bash
|
|
66
73
|
echo "Describe this image" > prompt.txt
|
|
67
74
|
cli/agent-cli --image photo.jpg --prompt prompt.txt
|
|
68
75
|
```
|
|
69
76
|
|
|
70
77
|
**Using agent profile:**
|
|
78
|
+
|
|
71
79
|
```bash
|
|
72
80
|
cli/agent-cli --agent-profile agent/test_data/simplecalc_profile.json
|
|
73
81
|
```
|
|
74
82
|
|
|
75
83
|
**Auto-approve tools:**
|
|
84
|
+
|
|
76
85
|
```bash
|
|
77
86
|
echo "Calculate 15 * 23" > prompt.txt
|
|
78
87
|
cli/agent-cli --approve-tools --prompt prompt.txt
|
|
@@ -81,11 +90,13 @@ cli/agent-cli --approve-tools --prompt prompt.txt
|
|
|
81
90
|
### Chat Mode
|
|
82
91
|
|
|
83
92
|
**Start server:**
|
|
93
|
+
|
|
84
94
|
```bash
|
|
85
95
|
node dist/agent/src/tool/main.js chat server --port 5003
|
|
86
96
|
```
|
|
87
97
|
|
|
88
98
|
**Connect client:**
|
|
99
|
+
|
|
89
100
|
```bash
|
|
90
101
|
node dist/agent/src/tool/main.js chat client \
|
|
91
102
|
--session "project_discussion" \
|
|
@@ -93,6 +104,7 @@ node dist/agent/src/tool/main.js chat client \
|
|
|
93
104
|
```
|
|
94
105
|
|
|
95
106
|
**Run scripted conversation:**
|
|
107
|
+
|
|
96
108
|
```bash
|
|
97
109
|
node dist/agent/src/tool/main.js chat client \
|
|
98
110
|
--session "test" \
|
|
@@ -104,18 +116,21 @@ node dist/agent/src/tool/main.js chat client \
|
|
|
104
116
|
While in agent mode, use these commands:
|
|
105
117
|
|
|
106
118
|
**Tool Management:**
|
|
119
|
+
|
|
107
120
|
- `/ls` - List available MCP servers
|
|
108
|
-
- `/lt` - List current tools (enabled marked with
|
|
121
|
+
- `/lt` - List current tools (enabled marked with \*)
|
|
109
122
|
- `/as <server>` - Add and enable all tools from server
|
|
110
123
|
- `/e <server> <tool>` - Enable specific tool
|
|
111
124
|
- `/d <server> <tool>` - Disable specific tool
|
|
112
125
|
|
|
113
126
|
**Media and Data:**
|
|
127
|
+
|
|
114
128
|
- `:i image.jpg` - Include image with next message
|
|
115
129
|
- `/wc conversation.json` - Save conversation to file
|
|
116
130
|
- `/wa profile.json` - Save agent profile to file
|
|
117
131
|
|
|
118
132
|
**General:**
|
|
133
|
+
|
|
119
134
|
- `/h` - Show help menu
|
|
120
135
|
- `/q` - Quit
|
|
121
136
|
|
|
@@ -129,7 +144,7 @@ LLM_URL=http://localhost:5001/v1 # LLM API endpoint
|
|
|
129
144
|
LLM_API_KEY=your_openai_key # API key for LLM
|
|
130
145
|
LLM_MODEL=gpt-4o # Model name
|
|
131
146
|
|
|
132
|
-
# SudoMCP Integration
|
|
147
|
+
# SudoMCP Integration
|
|
133
148
|
XMCP_URL=http://localhost:5001/ # SudoMCP backend URL
|
|
134
149
|
API_KEY=your_sudomcp_key # SudoMCP API key
|
|
135
150
|
|
|
@@ -150,7 +165,7 @@ Agent profiles define the AI's behavior and available tools:
|
|
|
150
165
|
"model": "gpt-4o",
|
|
151
166
|
"system_prompt": "You are a helpful coding assistant.",
|
|
152
167
|
"mcp_settings": {
|
|
153
|
-
"github": ["create_issue", "list_repos"]
|
|
168
|
+
"github": ["create_issue", "list_repos"]
|
|
154
169
|
}
|
|
155
170
|
}
|
|
156
171
|
```
|
|
@@ -197,7 +212,7 @@ yarn test -- --grep "DB"
|
|
|
197
212
|
# Git commit message generation
|
|
198
213
|
./scripts/git_message
|
|
199
214
|
|
|
200
|
-
# PR description generation
|
|
215
|
+
# PR description generation
|
|
201
216
|
./scripts/pr_message
|
|
202
217
|
|
|
203
218
|
# Code review assistance
|
|
@@ -227,6 +242,6 @@ Save and restore conversation state:
|
|
|
227
242
|
# Save conversation
|
|
228
243
|
cli/agent agent --conversation-output saved_conversation.json
|
|
229
244
|
|
|
230
|
-
# Restore conversation
|
|
245
|
+
# Restore conversation
|
|
231
246
|
cli/agent --conversation saved_conversation.json
|
|
232
247
|
```
|
|
@@ -35,33 +35,30 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.Agent = exports.AgentProfile = void 0;
|
|
37
37
|
exports.createUserMessage = createUserMessage;
|
|
38
|
+
exports.createUserMessageEnsure = createUserMessageEnsure;
|
|
38
39
|
const dotenv = __importStar(require("dotenv"));
|
|
39
40
|
const mcpServerManager_1 = require("./mcpServerManager");
|
|
40
41
|
const assert_1 = require("assert");
|
|
41
42
|
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
42
43
|
var sdk_2 = require("@xalia/xmcp/sdk");
|
|
43
44
|
Object.defineProperty(exports, "AgentProfile", { enumerable: true, get: function () { return sdk_2.AgentProfile; } });
|
|
45
|
+
const MAX_TOOL_CALL_RESPONSE_LENGTH = 4000;
|
|
44
46
|
dotenv.config();
|
|
45
47
|
const logger = (0, sdk_1.getLogger)();
|
|
46
48
|
class Agent {
|
|
47
|
-
constructor(
|
|
48
|
-
|
|
49
|
-
this.
|
|
50
|
-
|
|
49
|
+
constructor(eventHandler, mcpServerManager, llm, contextManager) {
|
|
50
|
+
/// The full list of tools, ready to pass to the LLM
|
|
51
|
+
this.tools = [];
|
|
52
|
+
/// Handlers for "agent" (or "built-in") tools. These do not require
|
|
53
|
+
/// approval from the user.
|
|
54
|
+
this.agentTools = new Map();
|
|
55
|
+
this.eventHandler = eventHandler;
|
|
51
56
|
this.mcpServerManager = mcpServerManager;
|
|
52
|
-
this.tools = tools;
|
|
53
57
|
this.llm = llm;
|
|
54
|
-
this.
|
|
55
|
-
}
|
|
56
|
-
static
|
|
57
|
-
|
|
58
|
-
const messages = [
|
|
59
|
-
{
|
|
60
|
-
role: "system",
|
|
61
|
-
content: systemPrompt ?? "You are a helpful assistant",
|
|
62
|
-
},
|
|
63
|
-
];
|
|
64
|
-
return new Agent(onMessage, onToolCall, messages, mcpServerManager ?? new mcpServerManager_1.McpServerManager(), [], llm);
|
|
58
|
+
this.contextManager = contextManager;
|
|
59
|
+
}
|
|
60
|
+
static initializeWithLLM(eventHandler, llm, contextManager, mcpServerManager) {
|
|
61
|
+
return new Agent(eventHandler, mcpServerManager ?? new mcpServerManager_1.McpServerManager(), llm, contextManager);
|
|
65
62
|
}
|
|
66
63
|
async shutdown() {
|
|
67
64
|
return this.mcpServerManager.shutdown();
|
|
@@ -70,16 +67,9 @@ class Agent {
|
|
|
70
67
|
return new sdk_1.AgentProfile(this.llm.getModel(), this.getSystemPrompt(), this.mcpServerManager.getMcpServerSettings());
|
|
71
68
|
}
|
|
72
69
|
getConversation() {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return structuredClone(this.messages.slice(1));
|
|
77
|
-
}
|
|
78
|
-
setConversation(messages) {
|
|
79
|
-
(0, assert_1.strict)(this.messages[0].role == "system");
|
|
80
|
-
(0, assert_1.strict)(messages.length === 0 || messages[0].role != "system", "conversation contains system msg");
|
|
81
|
-
const newMessages = [this.messages[0]];
|
|
82
|
-
this.messages = newMessages.concat(structuredClone(messages));
|
|
70
|
+
const llmMessages = this.contextManager.getLLMContext();
|
|
71
|
+
(0, assert_1.strict)(llmMessages[0].role === "system", "first message must have system role");
|
|
72
|
+
return [...llmMessages.slice(1)];
|
|
83
73
|
}
|
|
84
74
|
getMcpServerManager() {
|
|
85
75
|
return this.mcpServerManager;
|
|
@@ -95,45 +85,80 @@ class Agent {
|
|
|
95
85
|
return this.userMessageRaw(userMessage);
|
|
96
86
|
}
|
|
97
87
|
async userMessageRaw(userMessage) {
|
|
98
|
-
this.
|
|
99
|
-
|
|
88
|
+
return this.userMessagesRaw([userMessage]);
|
|
89
|
+
}
|
|
90
|
+
async userMessagesRaw(userMessages) {
|
|
91
|
+
// Note: `getLLMContext` returns a copy to we can mutate this array
|
|
92
|
+
const context = this.contextManager.getLLMContext();
|
|
93
|
+
const newMessagesIdx = context.length;
|
|
94
|
+
// Add the new user messages
|
|
95
|
+
context.push(...userMessages);
|
|
96
|
+
let completion = await this.chatCompletion(context);
|
|
100
97
|
let message = completion.choices[0].message;
|
|
101
|
-
|
|
102
|
-
// While there are tool calls to make,
|
|
98
|
+
context.push(message);
|
|
99
|
+
// While there are tool calls to make, invoke them and loop
|
|
103
100
|
while (message.tool_calls && message.tool_calls.length > 0) {
|
|
101
|
+
// TODO: Execute all tool calls in parallel
|
|
102
|
+
// [indexInContext, ToolCallResult][]
|
|
103
|
+
const toolCallResults = [];
|
|
104
104
|
for (const toolCall of message.tool_calls ?? []) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
else {
|
|
122
|
-
this.messages.push({
|
|
123
|
-
role: "tool",
|
|
124
|
-
tool_call_id: toolCall.id,
|
|
125
|
-
content: "User denied tool use request.",
|
|
126
|
-
});
|
|
105
|
+
// Execute the tool call, add the result to the context as an LLM
|
|
106
|
+
// mesage, and record the index of the message alongside the result in
|
|
107
|
+
// `toolCallResults`.
|
|
108
|
+
const result = await this.doToolCall(toolCall);
|
|
109
|
+
toolCallResults.push([context.length, result]);
|
|
110
|
+
context.push({
|
|
111
|
+
role: "tool",
|
|
112
|
+
tool_call_id: toolCall.id,
|
|
113
|
+
content: result.response,
|
|
114
|
+
});
|
|
115
|
+
// If the tool call requested that its args be redacted, this can be
|
|
116
|
+
// done now - before the next LLM invocation.
|
|
117
|
+
if (result.overwriteArgs) {
|
|
118
|
+
logger.debug(`updating args for toolcall ${toolCall.id}: ${result.overwriteArgs}`);
|
|
119
|
+
toolCall.function.arguments = result.overwriteArgs;
|
|
120
|
+
logger.debug(`agent message after update ${JSON.stringify(message)}`);
|
|
127
121
|
}
|
|
128
122
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
this.
|
|
123
|
+
// Now that any args have been overwritten, signal the event handler of
|
|
124
|
+
// the prevoius completion.
|
|
125
|
+
this.eventHandler.onCompletion(message);
|
|
126
|
+
// Get a new completion using the untouched tool call results. Note
|
|
127
|
+
// that, since we are deferring the `onToolCallResult` calls (so they
|
|
128
|
+
// can be redacted), we must take care that the errors in
|
|
129
|
+
// `chatCompletion` do not disrupt this, so the caller has a consistent
|
|
130
|
+
// view of the conversation state.
|
|
131
|
+
try {
|
|
132
|
+
completion = await this.chatCompletion(context); // CAN THROW
|
|
133
|
+
message = completion.choices[0].message;
|
|
134
|
+
context.push(message);
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
// Now that the tool call results have been passed to the LLM, perform
|
|
138
|
+
// any updates on them. Pass the (updated) tool-call-result LLM
|
|
139
|
+
// messages to the event handler - note, we want to do this even if
|
|
140
|
+
// the an error occured, so that the caller has an up-to-date picture
|
|
141
|
+
// of the context state when the error occured.
|
|
142
|
+
toolCallResults.forEach(([indexInContext, tcr]) => {
|
|
143
|
+
const ctxMsg = context[indexInContext];
|
|
144
|
+
if (tcr.overwriteResponse) {
|
|
145
|
+
ctxMsg.content = tcr.overwriteResponse;
|
|
146
|
+
}
|
|
147
|
+
(0, assert_1.strict)(ctxMsg.role === "tool");
|
|
148
|
+
this.eventHandler.onToolCallResult(ctxMsg);
|
|
149
|
+
});
|
|
150
|
+
// Note, if an error DID occur, the ContextManager does not see any of
|
|
151
|
+
// the new context.
|
|
152
|
+
}
|
|
132
153
|
}
|
|
154
|
+
// Signal the event handler of the final completion.
|
|
155
|
+
this.eventHandler.onCompletion(message);
|
|
156
|
+
// Add all new new messages to the context
|
|
157
|
+
this.contextManager.addMessages(context.slice(newMessagesIdx));
|
|
133
158
|
return completion.choices[0].message;
|
|
134
159
|
}
|
|
135
160
|
userMessage(msg, imageB64) {
|
|
136
|
-
this.userMessageEx(msg, imageB64);
|
|
161
|
+
void this.userMessageEx(msg, imageB64);
|
|
137
162
|
}
|
|
138
163
|
getModel() {
|
|
139
164
|
return this.llm.getModel();
|
|
@@ -142,69 +167,116 @@ class Agent {
|
|
|
142
167
|
logger.debug(`Set model ${model}`);
|
|
143
168
|
this.llm.setModel(model);
|
|
144
169
|
}
|
|
145
|
-
/**
|
|
146
|
-
* Clear the conversation.
|
|
147
|
-
*/
|
|
148
|
-
resetConversation() {
|
|
149
|
-
(0, assert_1.strict)(this.messages.length > 0);
|
|
150
|
-
// Keep only the system message
|
|
151
|
-
this.messages.splice(1);
|
|
152
|
-
}
|
|
153
170
|
getSystemPrompt() {
|
|
154
|
-
|
|
155
|
-
return this.messages[0].content;
|
|
171
|
+
return this.contextManager.getAgentPrompt();
|
|
156
172
|
}
|
|
157
173
|
/**
|
|
158
174
|
* Set the system prompt
|
|
159
175
|
*/
|
|
160
176
|
setSystemPrompt(systemMsg) {
|
|
161
|
-
|
|
162
|
-
this.messages[0].content = systemMsg;
|
|
177
|
+
this.contextManager.setAgentPrompt(systemMsg);
|
|
163
178
|
}
|
|
164
|
-
async chatCompletion() {
|
|
179
|
+
async chatCompletion(context) {
|
|
180
|
+
// Compute the full list of available tools
|
|
165
181
|
let tools;
|
|
166
|
-
const
|
|
182
|
+
const mcpTools = this.mcpServerManager.getOpenAITools();
|
|
183
|
+
const enabledTools = this.tools.concat(mcpTools);
|
|
167
184
|
if (enabledTools.length > 0) {
|
|
168
185
|
tools = enabledTools;
|
|
169
186
|
}
|
|
170
|
-
|
|
171
|
-
// `chatCompletion: tools: ${JSON.stringify(tools, undefined, 2)}`
|
|
172
|
-
// );
|
|
173
|
-
const completion = await this.llm.getConversationResponse(this.messages, tools, this.onMessage);
|
|
187
|
+
const completion = await this.llm.getConversationResponse(context, tools, this.eventHandler.onAgentMessage.bind(this.eventHandler));
|
|
174
188
|
logger.debug(`Received chat completion ${JSON.stringify(completion)}`);
|
|
175
189
|
return completion;
|
|
176
190
|
}
|
|
177
|
-
|
|
178
|
-
return this
|
|
179
|
-
.getOpenAITools()
|
|
180
|
-
.map((tool) => tool.function.name);
|
|
191
|
+
addAgentToolProvider(toolProvider) {
|
|
192
|
+
return toolProvider.setup(this);
|
|
181
193
|
}
|
|
182
|
-
|
|
194
|
+
addAgentTool(tool, handler) {
|
|
183
195
|
const name = tool.function.name;
|
|
184
|
-
if (this.
|
|
185
|
-
throw `tool ${name} already added
|
|
196
|
+
if (this.agentTools.has(name)) {
|
|
197
|
+
throw new Error(`tool ${name} already added`);
|
|
186
198
|
}
|
|
187
199
|
logger.debug(`Adding tool ${name}`);
|
|
188
200
|
this.tools.push(tool);
|
|
189
|
-
this.
|
|
201
|
+
this.agentTools.set(name, { handler });
|
|
202
|
+
}
|
|
203
|
+
removeAgentTool(name) {
|
|
204
|
+
if (!this.agentTools.has(name)) {
|
|
205
|
+
logger.warn(`[removeTool] tool ${name} not present`);
|
|
206
|
+
}
|
|
207
|
+
// Find idx of the tool in the list
|
|
208
|
+
const idx = (() => {
|
|
209
|
+
let idx = 0;
|
|
210
|
+
while (idx < this.tools.length) {
|
|
211
|
+
if (this.tools[idx].function.name === name) {
|
|
212
|
+
return idx;
|
|
213
|
+
}
|
|
214
|
+
idx++;
|
|
215
|
+
}
|
|
216
|
+
return -1;
|
|
217
|
+
})();
|
|
218
|
+
(0, assert_1.strict)(idx > -1);
|
|
219
|
+
// Remove entries
|
|
220
|
+
this.tools.splice(idx, 1);
|
|
221
|
+
this.agentTools.delete(name);
|
|
190
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Handle the details of getting approval (if required), invoking the tool
|
|
225
|
+
* handler, informing the IAgentEventHandler of the result, and returns the
|
|
226
|
+
* OpenAI.ChatCompletionToolMessageParam to be used in the conversation.
|
|
227
|
+
*/
|
|
191
228
|
async doToolCall(toolCall) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
229
|
+
// If the tool is and "agent" (internal) tool, we can just execute it.
|
|
230
|
+
// Otherwise, call the event handler to get permission and invoke the
|
|
231
|
+
// external tool handler.
|
|
232
|
+
let result;
|
|
233
|
+
try {
|
|
234
|
+
const toolName = toolCall.function.name;
|
|
235
|
+
const agentTool = this.agentTools.get(toolName);
|
|
236
|
+
const isAgentTool = !!agentTool;
|
|
237
|
+
const approve = await this.eventHandler.onToolCall(toolCall, isAgentTool);
|
|
238
|
+
if (!approve) {
|
|
239
|
+
result = { response: "User denied tool request." };
|
|
240
|
+
}
|
|
241
|
+
else if (isAgentTool) {
|
|
242
|
+
// Internal (agent) tool
|
|
243
|
+
const args = JSON.parse(toolCall.function.arguments);
|
|
244
|
+
result = await agentTool.handler(this, args);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// McpServer tool call (agentTool === undefined)
|
|
248
|
+
const args = JSON.parse(toolCall.function.arguments);
|
|
249
|
+
result = {
|
|
250
|
+
response: await this.mcpServerManager.invoke(toolName, args),
|
|
251
|
+
};
|
|
252
|
+
logger.debug(`tool call result ${JSON.stringify(result)}`);
|
|
253
|
+
}
|
|
199
254
|
}
|
|
200
|
-
|
|
201
|
-
|
|
255
|
+
catch (e) {
|
|
256
|
+
let msg;
|
|
257
|
+
if (e instanceof Error) {
|
|
258
|
+
msg = e.message;
|
|
259
|
+
}
|
|
260
|
+
else if (typeof e === "string") {
|
|
261
|
+
msg = e;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
msg = String(e);
|
|
265
|
+
}
|
|
266
|
+
logger.error(`tool call error: ${msg}`);
|
|
267
|
+
result = {
|
|
268
|
+
response: `tool call error: ${msg}`,
|
|
269
|
+
};
|
|
202
270
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
271
|
+
// Final sanity check on the tool call response length.
|
|
272
|
+
if (result.response.length > MAX_TOOL_CALL_RESPONSE_LENGTH) {
|
|
273
|
+
logger.warn("[Agent.doToolCall]: truncating tool call result.response for call:\n" +
|
|
274
|
+
JSON.stringify(toolCall));
|
|
275
|
+
result.response =
|
|
276
|
+
result.response.slice(0, MAX_TOOL_CALL_RESPONSE_LENGTH) +
|
|
277
|
+
" ..truncated";
|
|
278
|
+
}
|
|
279
|
+
return result;
|
|
208
280
|
}
|
|
209
281
|
}
|
|
210
282
|
exports.Agent = Agent;
|
|
@@ -247,3 +319,8 @@ function createUserMessage(msg, imageB64, name) {
|
|
|
247
319
|
name,
|
|
248
320
|
};
|
|
249
321
|
}
|
|
322
|
+
function createUserMessageEnsure(msg, imageB64, name) {
|
|
323
|
+
const userMsg = createUserMessage(msg, imageB64, name);
|
|
324
|
+
(0, assert_1.strict)(userMsg);
|
|
325
|
+
return userMsg;
|
|
326
|
+
}
|