@xalia/agent 0.5.7 → 0.6.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/README.md +23 -8
- package/dist/agent/src/agent/agent.js +176 -96
- package/dist/agent/src/agent/agentUtils.js +82 -59
- 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/mcpServerManager.js +23 -24
- 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 +26 -14
- 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 +23 -21
- 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/utils/websocket.js +16 -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 +271 -83
- 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 +23 -20
- package/dist/agent/src/test/multiAsyncQueue.test.js +101 -0
- package/dist/agent/src/test/openaiStreaming.test.js +64 -35
- 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 +24 -25
- 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 +241 -58
- package/dist/agent/src/tool/commandPrompt.js +22 -17
- 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 +283 -138
- package/src/agent/agentUtils.ts +143 -108
- 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 +35 -31
- package/src/agent/nullAgentEventHandler.ts +20 -0
- package/src/agent/nullPlatform.ts +13 -0
- package/src/agent/openAILLMStreaming.ts +26 -13
- package/src/agent/promptProvider.ts +87 -0
- package/src/agent/repeatLLM.ts +5 -5
- package/src/agent/sudoMcpServerManager.ts +30 -29
- 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/utils/websocket.ts +16 -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 +358 -89
- 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 +21 -16
- package/src/test/multiAsyncQueue.test.ts +125 -0
- package/src/test/openaiStreaming.test.ts +71 -36
- 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 +32 -30
- 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 +292 -100
- package/src/tool/commandPrompt.ts +28 -19
- 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 -349
- package/dist/agent/src/chat/conversationManager.js +0 -392
- package/dist/agent/src/chat/db.js +0 -209
- package/dist/agent/src/chat/frontendClient.js +0 -74
- package/dist/agent/src/chat/server.js +0 -158
- package/src/chat/client.ts +0 -455
- package/src/chat/conversationManager.ts +0 -595
- package/src/chat/db.ts +0 -290
- package/src/chat/frontendClient.ts +0 -123
- package/src/chat/messages.ts +0 -235
- package/src/chat/server.ts +0 -177
- /package/dist/agent/src/{chat/messages.js → agent/iAgentEventHandler.js} +0 -0
- /package/{frog.png → test_data/frog.png} +0 -0
|
@@ -25,7 +25,7 @@ class McpServerInfo {
|
|
|
25
25
|
}
|
|
26
26
|
this.tools = tools;
|
|
27
27
|
this.toolsMap = toolsMap;
|
|
28
|
-
this.enabledToolsMap =
|
|
28
|
+
this.enabledToolsMap = new Map();
|
|
29
29
|
}
|
|
30
30
|
getEnabledTools() {
|
|
31
31
|
return this.enabledToolsMap;
|
|
@@ -44,10 +44,10 @@ exports.McpServerInfo = McpServerInfo;
|
|
|
44
44
|
*/
|
|
45
45
|
class McpServerInfoRW extends McpServerInfo {
|
|
46
46
|
enableTool(toolName) {
|
|
47
|
-
this.enabledToolsMap
|
|
47
|
+
this.enabledToolsMap.set(toolName, true);
|
|
48
48
|
}
|
|
49
49
|
disableTool(toolName) {
|
|
50
|
-
|
|
50
|
+
this.enabledToolsMap.delete(toolName);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
exports.McpServerInfoRW = McpServerInfoRW;
|
|
@@ -59,13 +59,13 @@ exports.McpServerInfoRW = McpServerInfoRW;
|
|
|
59
59
|
class McpServerInfoInternal extends McpServerInfoRW {
|
|
60
60
|
constructor(client, tools) {
|
|
61
61
|
super(tools);
|
|
62
|
-
const callbacks =
|
|
62
|
+
const callbacks = new Map();
|
|
63
63
|
for (const mcpTool of tools) {
|
|
64
64
|
const toolName = mcpTool.name;
|
|
65
65
|
// Create callback
|
|
66
66
|
const callback = async (argStr) => {
|
|
67
67
|
logger.debug(`cb for ${toolName} invoked with args (${typeof argStr}): ` +
|
|
68
|
-
|
|
68
|
+
JSON.stringify(argStr));
|
|
69
69
|
const argsObj = JSON.parse(argStr);
|
|
70
70
|
const toolResult = await client.callTool({
|
|
71
71
|
name: toolName,
|
|
@@ -82,7 +82,7 @@ class McpServerInfoInternal extends McpServerInfoRW {
|
|
|
82
82
|
(0, assert_1.strict)(typeof content0Text === "string");
|
|
83
83
|
return content0Text;
|
|
84
84
|
};
|
|
85
|
-
callbacks
|
|
85
|
+
callbacks.set(toolName, callback);
|
|
86
86
|
}
|
|
87
87
|
this.client = client;
|
|
88
88
|
this.callbacks = callbacks;
|
|
@@ -91,7 +91,7 @@ class McpServerInfoInternal extends McpServerInfoRW {
|
|
|
91
91
|
await this.client.close();
|
|
92
92
|
}
|
|
93
93
|
getCallback(toolName) {
|
|
94
|
-
return this.callbacks
|
|
94
|
+
return this.callbacks.get(toolName);
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
/**
|
|
@@ -101,27 +101,27 @@ class McpServerInfoInternal extends McpServerInfoRW {
|
|
|
101
101
|
*/
|
|
102
102
|
class McpServerManager {
|
|
103
103
|
constructor() {
|
|
104
|
-
this.mcpServers =
|
|
104
|
+
this.mcpServers = new Map();
|
|
105
105
|
this.enabledToolsDirty = true;
|
|
106
106
|
this.enabledOpenAITools = [];
|
|
107
107
|
}
|
|
108
108
|
async shutdown() {
|
|
109
|
-
await Promise.all(
|
|
109
|
+
await Promise.all(Array.from(this.mcpServers.entries()).map(([name, server]) => {
|
|
110
110
|
logger.debug(`shutting down: ${name}...`);
|
|
111
|
-
|
|
111
|
+
return server.shutdown();
|
|
112
112
|
}));
|
|
113
|
-
this.mcpServers
|
|
113
|
+
this.mcpServers.clear();
|
|
114
114
|
}
|
|
115
115
|
hasMcpServer(mcpServerName) {
|
|
116
|
-
return
|
|
116
|
+
return this.mcpServers.has(mcpServerName);
|
|
117
117
|
}
|
|
118
118
|
getMcpServerNames() {
|
|
119
|
-
return
|
|
119
|
+
return Array.from(this.mcpServers.keys());
|
|
120
120
|
}
|
|
121
121
|
getMcpServer(mcpServerName) {
|
|
122
122
|
return this.getMcpServerInternal(mcpServerName);
|
|
123
123
|
}
|
|
124
|
-
async
|
|
124
|
+
async addMcpServerWithSSEUrl(mcpServerName, url, apiKey, tools) {
|
|
125
125
|
logger.debug(`Adding mcp server ${mcpServerName}: ${url}`);
|
|
126
126
|
const sseTransportOptions = {};
|
|
127
127
|
if (apiKey) {
|
|
@@ -154,7 +154,7 @@ class McpServerManager {
|
|
|
154
154
|
const mcpTools = await client.listTools();
|
|
155
155
|
tools = mcpTools.tools;
|
|
156
156
|
}
|
|
157
|
-
this.mcpServers
|
|
157
|
+
this.mcpServers.set(mcpServerName, new McpServerInfoInternal(client, tools));
|
|
158
158
|
}
|
|
159
159
|
catch (e) {
|
|
160
160
|
await client.close();
|
|
@@ -163,7 +163,7 @@ class McpServerManager {
|
|
|
163
163
|
}
|
|
164
164
|
async removeMcpServer(mcpServerName) {
|
|
165
165
|
const server = this.getMcpServerInternal(mcpServerName);
|
|
166
|
-
|
|
166
|
+
this.mcpServers.delete(mcpServerName);
|
|
167
167
|
await server.shutdown();
|
|
168
168
|
this.enabledToolsDirty = true;
|
|
169
169
|
}
|
|
@@ -215,7 +215,7 @@ class McpServerManager {
|
|
|
215
215
|
const server = this.getMcpServerInternal(mcpServerName);
|
|
216
216
|
const cb = server.getCallback(toolName);
|
|
217
217
|
if (!cb) {
|
|
218
|
-
throw `Unknown tool ${qualifiedToolName}
|
|
218
|
+
throw new Error(`Unknown tool ${qualifiedToolName}`);
|
|
219
219
|
}
|
|
220
220
|
return cb(JSON.stringify(args));
|
|
221
221
|
}
|
|
@@ -230,8 +230,8 @@ class McpServerManager {
|
|
|
230
230
|
//
|
|
231
231
|
// may be interpreted as "all tools for <server>". If the client has left
|
|
232
232
|
// a server with no tools enabled, we mark it as disabled.
|
|
233
|
-
for (const [serverName, server] of
|
|
234
|
-
const tools =
|
|
233
|
+
for (const [serverName, server] of this.mcpServers) {
|
|
234
|
+
const tools = Array.from(server.getEnabledTools().keys());
|
|
235
235
|
if (tools.length > 0) {
|
|
236
236
|
config[serverName] = tools;
|
|
237
237
|
}
|
|
@@ -239,11 +239,11 @@ class McpServerManager {
|
|
|
239
239
|
return config;
|
|
240
240
|
}
|
|
241
241
|
getMcpServerInternal(mcpServerName) {
|
|
242
|
-
const server = this.mcpServers
|
|
242
|
+
const server = this.mcpServers.get(mcpServerName);
|
|
243
243
|
if (server) {
|
|
244
244
|
return server;
|
|
245
245
|
}
|
|
246
|
-
throw Error(`unknown server ${mcpServerName}`);
|
|
246
|
+
throw Error(`[getMcpServerInternal] unknown server ${mcpServerName}`);
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
exports.McpServerManager = McpServerManager;
|
|
@@ -262,13 +262,12 @@ function splitQualifiedName(qualifiedToolName) {
|
|
|
262
262
|
}
|
|
263
263
|
function computeOpenAIToolList(mcpServers) {
|
|
264
264
|
const openaiTools = [];
|
|
265
|
-
for (const mcpServerName
|
|
266
|
-
const mcpServer = mcpServers[mcpServerName];
|
|
265
|
+
for (const [mcpServerName, mcpServer] of mcpServers) {
|
|
267
266
|
const tools = mcpServer.getTools();
|
|
268
267
|
const enabled = mcpServer.getEnabledTools();
|
|
269
268
|
for (const mcpTool of tools) {
|
|
270
269
|
const toolName = mcpTool.name;
|
|
271
|
-
if (enabled
|
|
270
|
+
if (enabled.get(toolName)) {
|
|
272
271
|
const qualifiedName = computeQualifiedName(mcpServerName, toolName);
|
|
273
272
|
const openaiTool = mcpToolToOpenAITool(mcpTool, qualifiedName);
|
|
274
273
|
openaiTools.push(openaiTool);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NULL_AGENT_EVENT_HANDLER = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Trivial IAgentEventHandler implementation which does not track any messages
|
|
6
|
+
* and does not allow tool calls.
|
|
7
|
+
*/
|
|
8
|
+
exports.NULL_AGENT_EVENT_HANDLER = {
|
|
9
|
+
onCompletion: () => { },
|
|
10
|
+
onToolCallResult: () => { },
|
|
11
|
+
onAgentMessage: () => {
|
|
12
|
+
return new Promise((r) => {
|
|
13
|
+
r();
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
onToolCall: () => {
|
|
17
|
+
return new Promise((r) => {
|
|
18
|
+
r(false);
|
|
19
|
+
});
|
|
20
|
+
},
|
|
21
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NULL_PLATFORM = void 0;
|
|
4
|
+
exports.NULL_PLATFORM = {
|
|
5
|
+
openUrl: () => {
|
|
6
|
+
throw new Error("null_platform openUrl called");
|
|
7
|
+
},
|
|
8
|
+
load: () => {
|
|
9
|
+
throw new Error("null_platform load called");
|
|
10
|
+
},
|
|
11
|
+
renderHTML: () => {
|
|
12
|
+
throw new Error("null_platform renderHTML called");
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -39,9 +39,6 @@ function updateToolCallFunction(existingFn, deltaFn // eslint-disable-line
|
|
|
39
39
|
// The function can have either (or possibly both) field(s) empty.
|
|
40
40
|
// `arguments` has been observered to arrive in chunks. The same is
|
|
41
41
|
// probably true of `name`.
|
|
42
|
-
if (!deltaFn) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
42
|
if (deltaFn.name) {
|
|
46
43
|
existingFn.name += deltaFn.name;
|
|
47
44
|
}
|
|
@@ -107,8 +104,8 @@ function updateToolCalls(toolCalls, deltaToolCall // eslint-disable-line
|
|
|
107
104
|
if (typeof toolCalls === "undefined") {
|
|
108
105
|
toolCalls = [];
|
|
109
106
|
}
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
if (deltaToolCall.index >= toolCalls.length ||
|
|
108
|
+
!toolCalls[deltaToolCall.index]) {
|
|
112
109
|
toolCalls[deltaToolCall.index] = initialToolCall(deltaToolCall);
|
|
113
110
|
}
|
|
114
111
|
else {
|
|
@@ -118,6 +115,7 @@ function updateToolCalls(toolCalls, deltaToolCall // eslint-disable-line
|
|
|
118
115
|
}
|
|
119
116
|
function initializeCompletionMessage(delta) {
|
|
120
117
|
(0, assert_1.strict)(delta.role === undefined || delta.role == "assistant");
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
121
119
|
(0, assert_1.strict)(!delta.function_call);
|
|
122
120
|
// export interface ChatCompletionChunk.Choice.Delta {
|
|
123
121
|
// content?: string | null;
|
|
@@ -155,7 +153,9 @@ function initializeCompletionMessage(delta) {
|
|
|
155
153
|
};
|
|
156
154
|
}
|
|
157
155
|
function updateCompletionMessage(message, delta) {
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
158
157
|
(0, assert_1.strict)(message.role === "assistant");
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
159
159
|
(0, assert_1.strict)(!message.function_call);
|
|
160
160
|
(0, assert_1.strict)(!message.audio);
|
|
161
161
|
(0, assert_1.strict)(message.tool_calls instanceof Array ||
|
|
@@ -187,6 +187,7 @@ function updateCompletionMessage(message, delta) {
|
|
|
187
187
|
message.content = delta.content;
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
190
191
|
(0, assert_1.strict)(!delta.function_call);
|
|
191
192
|
if (delta.refusal) {
|
|
192
193
|
if (message.refusal) {
|
|
@@ -255,7 +256,9 @@ function updateCompletionChoice(completionChoice, chunkChoice) {
|
|
|
255
256
|
(0, assert_1.strict)(completionChoice.index === chunkChoice.index);
|
|
256
257
|
updateCompletionMessage(completionChoice.message, chunkChoice.delta);
|
|
257
258
|
if (chunkChoice.finish_reason) {
|
|
258
|
-
(0, assert_1.strict)(
|
|
259
|
+
(0, assert_1.strict)(
|
|
260
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
261
|
+
completionChoice.finish_reason === null, `finish_reason already set: (${completionChoice.finish_reason})`);
|
|
259
262
|
completionChoice.finish_reason = chunkChoice.finish_reason;
|
|
260
263
|
return true;
|
|
261
264
|
}
|
|
@@ -263,9 +266,10 @@ function updateCompletionChoice(completionChoice, chunkChoice) {
|
|
|
263
266
|
}
|
|
264
267
|
function initializeCompletionChoices(chunkChoices) {
|
|
265
268
|
// Technically, one choice could be done and the other still have some
|
|
266
|
-
// content to stream. We keep it simple for now and
|
|
267
|
-
//
|
|
268
|
-
// done.
|
|
269
|
+
// content to stream. We keep it simple for now and allow zero or one
|
|
270
|
+
// choice per chunk, which allows us to mark everything as done if any
|
|
271
|
+
// choice we hit is done. Zero choices can occur in usage-only chunks at
|
|
272
|
+
// the end of the stream.
|
|
269
273
|
(0, assert_1.strict)(chunkChoices.length < 2);
|
|
270
274
|
let msgDone = false;
|
|
271
275
|
const choices = [];
|
|
@@ -280,10 +284,11 @@ function initializeCompletionChoices(chunkChoices) {
|
|
|
280
284
|
}
|
|
281
285
|
function updateCompletionChoices(completionChoices, chunkChoices) {
|
|
282
286
|
// Technically, one choice could be done and the other still have some
|
|
283
|
-
// content to stream. We keep it simple for now and
|
|
284
|
-
//
|
|
285
|
-
// done.
|
|
286
|
-
|
|
287
|
+
// content to stream. We keep it simple for now and allow zero or one
|
|
288
|
+
// choice per chunk, which allows us to mark everything as done if any
|
|
289
|
+
// choice we hit is done. Zero choices can occur in usage-only chunks at
|
|
290
|
+
// the end of the stream.
|
|
291
|
+
(0, assert_1.strict)(chunkChoices.length < 2);
|
|
287
292
|
(0, assert_1.strict)(completionChoices.length === 1);
|
|
288
293
|
let msgDone = false;
|
|
289
294
|
for (const chunkChoice of chunkChoices) {
|
|
@@ -391,14 +396,18 @@ class OpenAILLMStreaming {
|
|
|
391
396
|
messages,
|
|
392
397
|
tools,
|
|
393
398
|
stream: true,
|
|
399
|
+
stream_options: {
|
|
400
|
+
include_usage: true,
|
|
401
|
+
},
|
|
394
402
|
});
|
|
395
403
|
// Check the type casting above
|
|
396
404
|
if (!chunks.iterator) {
|
|
397
|
-
throw "not a stream";
|
|
405
|
+
throw new Error("not a stream");
|
|
398
406
|
}
|
|
399
407
|
let aggregatedMessage;
|
|
400
408
|
for await (const chunk of chunks) {
|
|
401
409
|
logger.debug(`[stream] chunk: ${JSON.stringify(chunk)}`);
|
|
410
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
402
411
|
if (chunk.object !== "chat.completion.chunk") {
|
|
403
412
|
// logger.warn("[stream]: unexpected message");
|
|
404
413
|
continue;
|
|
@@ -413,7 +422,10 @@ class OpenAILLMStreaming {
|
|
|
413
422
|
}
|
|
414
423
|
if (onMessage) {
|
|
415
424
|
// Inform the call of a message fragment if it contains any text.
|
|
425
|
+
// Note: chunks may have zero choices (e.g., usage-only chunks), so
|
|
426
|
+
// we safely access the first choice.
|
|
416
427
|
const delta = chunk.choices[0]?.delta;
|
|
428
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
417
429
|
if (delta?.content) {
|
|
418
430
|
await onMessage(delta.content, false);
|
|
419
431
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SystemPromptProvider = void 0;
|
|
4
|
+
const assert_1 = require("assert");
|
|
5
|
+
/**
|
|
6
|
+
* Manage a structured system-prompt, built up of several components which can
|
|
7
|
+
* be updated at different frequencies.
|
|
8
|
+
*/
|
|
9
|
+
class SystemPromptProvider {
|
|
10
|
+
constructor(agentPrompt) {
|
|
11
|
+
this.agentPrompt = agentPrompt;
|
|
12
|
+
this.fragments = new Map();
|
|
13
|
+
this.fullPrompt = "";
|
|
14
|
+
this.updatePrompt = true;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* This is not expected to change dynamically, but to be set exactly once at
|
|
18
|
+
* startup. Thus existing prompts are not recomputed if this is updated.
|
|
19
|
+
*/
|
|
20
|
+
static setGlobalPrompt(globalPrompt) {
|
|
21
|
+
SystemPromptProvider.globalPrompt = globalPrompt || "";
|
|
22
|
+
}
|
|
23
|
+
getAgentPrompt() {
|
|
24
|
+
return this.agentPrompt;
|
|
25
|
+
}
|
|
26
|
+
setAgentPrompt(agentPrompt) {
|
|
27
|
+
this.agentPrompt = agentPrompt;
|
|
28
|
+
this.updatePrompt = true;
|
|
29
|
+
}
|
|
30
|
+
setFragment(fragmentID, prompt) {
|
|
31
|
+
this.fragments.set(fragmentID, prompt);
|
|
32
|
+
this.updatePrompt = true;
|
|
33
|
+
}
|
|
34
|
+
removeFragment(fragmentID) {
|
|
35
|
+
this.fragments.delete(fragmentID);
|
|
36
|
+
this.updatePrompt = true;
|
|
37
|
+
}
|
|
38
|
+
getSystemPrompt() {
|
|
39
|
+
if (this.updatePrompt) {
|
|
40
|
+
this.computePrompt();
|
|
41
|
+
}
|
|
42
|
+
(0, assert_1.strict)(!this.updatePrompt);
|
|
43
|
+
return this.fullPrompt;
|
|
44
|
+
}
|
|
45
|
+
computePrompt() {
|
|
46
|
+
const global = SystemPromptProvider.globalPrompt.trimEnd();
|
|
47
|
+
const globalPost = global.length > 0 ? "\n" : "";
|
|
48
|
+
let prompt = SystemPromptProvider.globalPrompt.trimEnd() +
|
|
49
|
+
globalPost +
|
|
50
|
+
this.agentPrompt.trimEnd();
|
|
51
|
+
// TODO: allow some control over the ordering?
|
|
52
|
+
for (const v of this.fragments.values()) {
|
|
53
|
+
prompt += "\n" + v.trimEnd();
|
|
54
|
+
}
|
|
55
|
+
this.fullPrompt = prompt;
|
|
56
|
+
this.updatePrompt = false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.SystemPromptProvider = SystemPromptProvider;
|
|
60
|
+
/**
|
|
61
|
+
* A global prompt, common to all agents.
|
|
62
|
+
*/
|
|
63
|
+
SystemPromptProvider.globalPrompt = "";
|
|
@@ -10,11 +10,11 @@ class RepeatLLM {
|
|
|
10
10
|
return "repeat";
|
|
11
11
|
}
|
|
12
12
|
getUrl() {
|
|
13
|
-
throw "cannot get url for RepeatLLM";
|
|
13
|
+
throw new Error("cannot get url for RepeatLLM");
|
|
14
14
|
}
|
|
15
15
|
async getConversationResponse(_messages, _tools, onMessage) {
|
|
16
|
-
await new Promise((r) => setTimeout(r,
|
|
17
|
-
const content = `Message number ${this.idx++}`;
|
|
16
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
17
|
+
const content = `Message number ${String(this.idx++)}`;
|
|
18
18
|
const response = {
|
|
19
19
|
finish_reason: "stop",
|
|
20
20
|
index: 0,
|
|
@@ -26,10 +26,10 @@ class RepeatLLM {
|
|
|
26
26
|
},
|
|
27
27
|
};
|
|
28
28
|
if (onMessage) {
|
|
29
|
-
onMessage(content, true);
|
|
29
|
+
void onMessage(content, true);
|
|
30
30
|
}
|
|
31
31
|
return {
|
|
32
|
-
id:
|
|
32
|
+
id: String(this.idx),
|
|
33
33
|
choices: [response],
|
|
34
34
|
created: Date.now(),
|
|
35
35
|
model: "dummyLlmModel",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SkillManager = exports.LOCAL_SERVER_URL = void 0;
|
|
4
|
+
const mcpServerManager_1 = require("./mcpServerManager");
|
|
4
5
|
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
5
6
|
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
6
7
|
const logger = (0, sdk_1.getLogger)();
|
|
@@ -25,11 +26,11 @@ class SanitizedServerBrief extends sdk_1.McpServerBrief {
|
|
|
25
26
|
* Manages access to the catalogue of servers hosted by sudomcp. Supports
|
|
26
27
|
* adding these servers to McpServerManager.
|
|
27
28
|
*/
|
|
28
|
-
class SkillManager {
|
|
29
|
-
constructor(
|
|
29
|
+
class SkillManager extends mcpServerManager_1.McpServerManager {
|
|
30
|
+
constructor(apiClient, serverBriefs, serverBriefsMap, toolCache, openUrl,
|
|
30
31
|
// Redirect to this page after successful authorization
|
|
31
32
|
authorized_url) {
|
|
32
|
-
|
|
33
|
+
super();
|
|
33
34
|
this.apiClient = apiClient;
|
|
34
35
|
this.serverBriefs = serverBriefs;
|
|
35
36
|
this.serverBriefsMap = serverBriefsMap;
|
|
@@ -41,18 +42,21 @@ class SkillManager {
|
|
|
41
42
|
* Initialize an ApiClient to interface with SudoMCP backend and
|
|
42
43
|
* fetch the current list of ServerBriefs.
|
|
43
44
|
*/
|
|
44
|
-
static async initialize(
|
|
45
|
+
static async initialize(openUrl, sudoMcpUrl, sudoMcpApiKey, authorized_url) {
|
|
45
46
|
// TODO: Keep it on here and pass to `McpServerManager.addMcpServer`
|
|
46
47
|
const apiClient = new sdk_1.ApiClient(sudoMcpUrl ?? sdk_1.DEFAULT_SERVER_URL, sudoMcpApiKey ?? "dummy_key");
|
|
47
48
|
// Fetch server list
|
|
48
49
|
const servers = await apiClient.listServers();
|
|
49
50
|
const [mcpServers, mcpServersMap] = buildServersList(servers);
|
|
50
|
-
return new SkillManager(
|
|
51
|
+
return new SkillManager(apiClient, mcpServers, mcpServersMap, new Map(), openUrl, authorized_url);
|
|
52
|
+
}
|
|
53
|
+
async shutdown() {
|
|
54
|
+
return super.shutdown();
|
|
51
55
|
}
|
|
52
56
|
/// TODO: Bit awkward that we have to restore via this class, but it's the
|
|
53
57
|
/// only class which knows how to restore (restart) the mcp servers.
|
|
54
58
|
async restoreMcpSettings(mcpSettings) {
|
|
55
|
-
|
|
59
|
+
return this.restoreConfiguration(mcpSettings);
|
|
56
60
|
}
|
|
57
61
|
/**
|
|
58
62
|
* Load the configuration from sudomcp, and enable the specified tools.
|
|
@@ -73,15 +77,17 @@ class SkillManager {
|
|
|
73
77
|
}
|
|
74
78
|
if (enabled.length === 0) {
|
|
75
79
|
logger.debug(` restoring "${serverName}": (all tools)`);
|
|
76
|
-
this.
|
|
80
|
+
this.enableAllTools(serverName);
|
|
77
81
|
return;
|
|
78
82
|
}
|
|
79
83
|
logger.debug(` restoring "${serverName}": ${JSON.stringify(enabled)}`);
|
|
80
84
|
for (const toolName of enabled) {
|
|
81
|
-
this.
|
|
85
|
+
this.enableTool(serverName, toolName);
|
|
82
86
|
}
|
|
83
87
|
};
|
|
84
|
-
Object.entries(mcpConfig).map((e) =>
|
|
88
|
+
Object.entries(mcpConfig).map((e) => {
|
|
89
|
+
enableTools(e);
|
|
90
|
+
});
|
|
85
91
|
}
|
|
86
92
|
/**
|
|
87
93
|
* Query backend for server list, clear tool cache.
|
|
@@ -91,31 +97,28 @@ class SkillManager {
|
|
|
91
97
|
const [mcpServers, mcpServersMap] = buildServersList(servers);
|
|
92
98
|
this.serverBriefs = mcpServers;
|
|
93
99
|
this.serverBriefsMap = mcpServersMap;
|
|
94
|
-
this.toolCache =
|
|
100
|
+
this.toolCache = new Map();
|
|
95
101
|
}
|
|
96
|
-
|
|
102
|
+
hasServerBrief(serverName) {
|
|
97
103
|
return !!this.serverBriefsMap[serverName];
|
|
98
104
|
}
|
|
99
105
|
getServerBriefs() {
|
|
100
106
|
return this.serverBriefs;
|
|
101
107
|
}
|
|
102
|
-
getMcpServerManager() {
|
|
103
|
-
return this.mcpServerManager;
|
|
104
|
-
}
|
|
105
108
|
/**
|
|
106
109
|
* Return tool list for a given MCP server. Queries the backend
|
|
107
110
|
* if necessary and caches the result.
|
|
108
111
|
*/
|
|
109
112
|
async getServerTools(serverName) {
|
|
110
113
|
// Check cache
|
|
111
|
-
let tools = this.toolCache
|
|
114
|
+
let tools = this.toolCache.get(serverName);
|
|
112
115
|
if (tools) {
|
|
113
116
|
return tools;
|
|
114
117
|
}
|
|
115
118
|
// Query backend (using the original name)
|
|
116
119
|
const originalName = this.serverBriefsMap[serverName].originalName;
|
|
117
120
|
tools = await this.apiClient.listTools(originalName);
|
|
118
|
-
this.toolCache
|
|
121
|
+
this.toolCache.set(serverName, tools);
|
|
119
122
|
return tools;
|
|
120
123
|
}
|
|
121
124
|
/**
|
|
@@ -131,10 +134,9 @@ class SkillManager {
|
|
|
131
134
|
version: "1.0.0",
|
|
132
135
|
});
|
|
133
136
|
await connectServer(client, this.apiClient, mcpserver, this.openUrl, this.authorized_url);
|
|
134
|
-
|
|
135
|
-
await msm.addMcpServerWithClient(client, serverName, tools);
|
|
137
|
+
await this.addMcpServerWithClient(client, serverName, tools);
|
|
136
138
|
if (enableAll) {
|
|
137
|
-
|
|
139
|
+
this.enableAllTools(serverName);
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
getOriginalName(serverName) {
|
|
@@ -158,9 +160,9 @@ async function connectServer(client, apiClient, mcpServer, openUrl, authorized_u
|
|
|
158
160
|
logger.info(`authenticate at url: ${url}`);
|
|
159
161
|
openUrl(url, authenticatedP, mcpServer.title);
|
|
160
162
|
const authResult = await authenticatedP;
|
|
161
|
-
logger.info(`authResult: ${authResult}`);
|
|
163
|
+
logger.info(`authResult: ${String(authResult)}`);
|
|
162
164
|
if (!authResult) {
|
|
163
|
-
throw "authentication failed";
|
|
165
|
+
throw new Error("authentication failed");
|
|
164
166
|
}
|
|
165
167
|
return connectServer(client, apiClient, mcpServer, openUrl, authorized_url, true);
|
|
166
168
|
}
|
|
@@ -6,16 +6,16 @@ class TokenAuth {
|
|
|
6
6
|
this.token = token;
|
|
7
7
|
}
|
|
8
8
|
get redirectUrl() {
|
|
9
|
-
throw "unimpl";
|
|
9
|
+
throw new Error("unimpl: redirectUrl");
|
|
10
10
|
}
|
|
11
11
|
get clientMetadata() {
|
|
12
|
-
throw "unimpl";
|
|
12
|
+
throw new Error("unimpl: clientMetadata");
|
|
13
13
|
}
|
|
14
14
|
clientInformation() {
|
|
15
15
|
return undefined;
|
|
16
16
|
}
|
|
17
17
|
saveClientInformation(_clientInformation) {
|
|
18
|
-
throw "unimpl";
|
|
18
|
+
throw new Error("unimpl: saveClientInformation");
|
|
19
19
|
}
|
|
20
20
|
tokens() {
|
|
21
21
|
return {
|
|
@@ -24,16 +24,16 @@ class TokenAuth {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
saveTokens(_tokens) {
|
|
27
|
-
throw "unimpl";
|
|
27
|
+
throw new Error("unimpl: saveTokens");
|
|
28
28
|
}
|
|
29
29
|
redirectToAuthorization(_authorizationUrl) {
|
|
30
|
-
throw "unimpl";
|
|
30
|
+
throw new Error("unimpl: redirectToAuthorization");
|
|
31
31
|
}
|
|
32
32
|
saveCodeVerifier(_codeVerifier) {
|
|
33
|
-
throw "unimpl";
|
|
33
|
+
throw new Error("unimpl: saveCodeVerifier");
|
|
34
34
|
}
|
|
35
35
|
codeVerifier() {
|
|
36
|
-
throw "unimpl";
|
|
36
|
+
throw new Error("unimpl: codeVerifier");
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
exports.TokenAuth = TokenAuth;
|
|
@@ -35,7 +35,7 @@ exports.toolCallbacks = {
|
|
|
35
35
|
if (!isTemperatureArgs(args) || !args.location) {
|
|
36
36
|
return `Location required`;
|
|
37
37
|
}
|
|
38
|
-
return `The temperature in ${args.location} is 22 degrees ${args.unit}`;
|
|
38
|
+
return `The temperature in ${args.location} is 22 degrees ${args.unit ?? "celsius"}`;
|
|
39
39
|
},
|
|
40
40
|
};
|
|
41
41
|
function displayToolCall(toolCall) {
|