@xalia/agent 0.6.4 → 0.6.5
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/dist/agent/src/agent/agent.js +12 -11
- package/dist/agent/src/agent/dummyLLM.js +24 -15
- package/dist/agent/src/agent/llm.js +0 -22
- package/dist/agent/src/agent/mcpServerManager.js +73 -15
- package/dist/agent/src/agent/openAI.js +32 -0
- package/dist/agent/src/agent/openAILLM.js +25 -1
- package/dist/agent/src/agent/openAILLMStreaming.js +8 -3
- package/dist/agent/src/agent/sudoMcpServerManager.js +22 -9
- package/dist/agent/src/chat/client/chatClient.js +2 -1
- package/dist/agent/src/chat/client/sessionClient.js +28 -3
- package/dist/agent/src/chat/data/dbSessionFileModels.js +10 -0
- package/dist/agent/src/chat/protocol/messages.js +1 -0
- package/dist/agent/src/chat/server/chatContextManager.js +4 -4
- package/dist/agent/src/chat/server/conversation.js +1 -1
- package/dist/agent/src/chat/server/imageGeneratorTools.js +7 -4
- package/dist/agent/src/chat/server/openSession.js +85 -12
- package/dist/agent/src/chat/server/sessionFileManager.js +17 -6
- package/dist/agent/src/chat/server/sessionRegistry.js +1 -1
- package/dist/agent/src/chat/server/tools.js +58 -10
- package/dist/agent/src/test/agent.test.js +26 -2
- package/dist/agent/src/test/chatContextManager.test.js +5 -5
- package/dist/agent/src/test/mcpServerManager.test.js +5 -1
- package/dist/agent/src/test/testTools.js +5 -2
- package/dist/agent/src/tool/chatMain.js +23 -3
- package/dist/agent/src/tool/files.js +0 -27
- package/package.json +3 -3
- package/scripts/test_chat +3 -1
- package/src/agent/agent.ts +53 -47
- package/src/agent/agentUtils.ts +7 -7
- package/src/agent/compressingContextManager.ts +4 -9
- package/src/agent/context.ts +28 -37
- package/src/agent/dummyLLM.ts +38 -28
- package/src/agent/iAgentEventHandler.ts +6 -9
- package/src/agent/imageGenLLM.ts +11 -5
- package/src/agent/llm.ts +41 -106
- package/src/agent/mcpServerManager.ts +145 -29
- package/src/agent/openAI.ts +123 -0
- package/src/agent/openAILLM.ts +52 -5
- package/src/agent/openAILLMStreaming.ts +36 -32
- package/src/agent/repeatLLM.ts +5 -6
- package/src/agent/sudoMcpServerManager.ts +48 -16
- package/src/agent/tools.ts +3 -5
- package/src/chat/client/chatClient.ts +3 -1
- package/src/chat/client/sessionClient.ts +47 -7
- package/src/chat/data/dataModels.ts +3 -3
- package/src/chat/data/dbSessionFileModels.ts +22 -0
- package/src/chat/protocol/messages.ts +39 -13
- package/src/chat/server/chatContextManager.ts +20 -24
- package/src/chat/server/conversation.ts +10 -10
- package/src/chat/server/imageGeneratorTools.ts +18 -9
- package/src/chat/server/openSession.ts +111 -22
- package/src/chat/server/sessionFileManager.ts +33 -10
- package/src/chat/server/sessionRegistry.ts +1 -1
- package/src/chat/server/tools.ts +77 -18
- package/src/chat/utils/approvalManager.ts +2 -2
- package/src/test/agent.test.ts +56 -31
- package/src/test/approvalManager.test.ts +2 -2
- package/src/test/chatContextManager.test.ts +11 -14
- package/src/test/compressingContextManager.test.ts +3 -3
- package/src/test/context.test.ts +3 -3
- package/src/test/conversation.test.ts +7 -7
- package/src/test/dbSessionMessages.test.ts +3 -3
- package/src/test/mcpServerManager.test.ts +10 -1
- package/src/test/testTools.ts +44 -33
- package/src/tool/agentChat.ts +10 -8
- package/src/tool/agentMain.ts +2 -2
- package/src/tool/chatMain.ts +38 -6
- package/src/tool/commandPrompt.ts +2 -4
- package/src/tool/files.ts +0 -34
- package/test_data/dummyllm_script_image_gen.json +27 -17
- package/test_data/dummyllm_script_invoke_image_gen_tool.json +9 -2
- package/test_data/dummyllm_script_render_tool.json +29 -0
- package/test_data/dummyllm_script_test_auto_approve.json +81 -0
- package/test_data/dummyllm_script_test_simplecalc_addition.json +29 -0
|
@@ -5,7 +5,7 @@ exports.genImageFileTool = genImageFileTool;
|
|
|
5
5
|
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
6
6
|
const imageGenerator_1 = require("../../agent/imageGenerator");
|
|
7
7
|
const tools_1 = require("./tools");
|
|
8
|
-
const
|
|
8
|
+
const dbSessionFileModels_1 = require("../data/dbSessionFileModels");
|
|
9
9
|
const logger = (0, sdk_1.getLogger)();
|
|
10
10
|
function createImageGenerator(llmUrl, llmApiKey) {
|
|
11
11
|
const imageGenModel = process.env["GEN_IMAGE_MODEL"];
|
|
@@ -97,10 +97,13 @@ async function genImageFileTool(llmUrl, llmApiKey, fileManager) {
|
|
|
97
97
|
? await fileManager.getFileContent(input_name)
|
|
98
98
|
: undefined;
|
|
99
99
|
const image = await imageGenerator.generate(prompt, input_image);
|
|
100
|
-
const mimeType = (0,
|
|
100
|
+
const mimeType = (0, dbSessionFileModels_1.getSessionFileMimeTypeFromDataUrl)(image);
|
|
101
101
|
await fileManager.putFileContent(name, summary, image);
|
|
102
|
-
const
|
|
103
|
-
return {
|
|
102
|
+
const uri = fileManager.getSessionFileRelativeUrl(name);
|
|
103
|
+
return {
|
|
104
|
+
response: uri,
|
|
105
|
+
_meta: { "xalia/fileUri": uri, "xalia/fileMimeType": mimeType },
|
|
106
|
+
};
|
|
104
107
|
};
|
|
105
108
|
return {
|
|
106
109
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
@@ -91,7 +91,7 @@ class ChatSessionPlatform {
|
|
|
91
91
|
}
|
|
92
92
|
// IPlatform.load
|
|
93
93
|
load(filename) {
|
|
94
|
-
if (process.env.
|
|
94
|
+
if (!process.env.NO_DEVELOPMENT) {
|
|
95
95
|
return nodePlatform_1.NODE_PLATFORM.load(filename);
|
|
96
96
|
}
|
|
97
97
|
throw new errors_1.ChatErrorMessage("Platform.load not implemented");
|
|
@@ -211,6 +211,7 @@ class OpenSession {
|
|
|
211
211
|
this.sessionUpdatedAt = sessionData.updated_at;
|
|
212
212
|
this.agentPaused = sessionData.agent_paused;
|
|
213
213
|
fileManager.addEventHandler(this);
|
|
214
|
+
this.updateParticipantsPrompt();
|
|
214
215
|
}
|
|
215
216
|
static async init(db, isPersisted, sessionData, savedAgentProfile, sessionMessages, sessionParticipants, ownerData, ownerApiKey, sessionCheckpoint, llmUrl, xmcpUrl, connectionManager) {
|
|
216
217
|
const sessionId = sessionData.session_uuid;
|
|
@@ -365,8 +366,11 @@ class OpenSession {
|
|
|
365
366
|
* is and `true` is returned. Otherwise false is returned (and the caller
|
|
366
367
|
* is expected to re-throw if they are unable to handle it).
|
|
367
368
|
*/
|
|
368
|
-
handleError(err, from) {
|
|
369
|
+
handleError(err, from, client_message_id) {
|
|
369
370
|
const sendError = (msg) => {
|
|
371
|
+
if (client_message_id) {
|
|
372
|
+
msg.client_message_id = client_message_id;
|
|
373
|
+
}
|
|
370
374
|
if (from) {
|
|
371
375
|
this.sender.sendTo(from, msg);
|
|
372
376
|
}
|
|
@@ -458,11 +462,13 @@ class OpenSession {
|
|
|
458
462
|
servers.forEach((server_name) => {
|
|
459
463
|
const mcpServer = this.skillManager.getMcpServer(server_name);
|
|
460
464
|
const tools = mcpServer.getTools();
|
|
465
|
+
const resources = mcpServer.getResources();
|
|
461
466
|
const enabled_tools = Array.from(mcpServer.getEnabledTools().keys());
|
|
462
467
|
this.sender.connectionManager.sendToConnection(connectionId, {
|
|
463
468
|
type: "mcp_server_added",
|
|
464
469
|
server_name,
|
|
465
470
|
tools,
|
|
471
|
+
resources,
|
|
466
472
|
enabled_tools,
|
|
467
473
|
session_id: this.sessionUUID,
|
|
468
474
|
});
|
|
@@ -535,6 +541,12 @@ class OpenSession {
|
|
|
535
541
|
case "share_session":
|
|
536
542
|
await this.handleShareSession(msg, queuedMessage.from);
|
|
537
543
|
break;
|
|
544
|
+
case "add_mcp_server_from_url":
|
|
545
|
+
void this.handleAddMcpServerFromUrl(msg, queuedMessage.from);
|
|
546
|
+
break;
|
|
547
|
+
case "get_mcp_resource":
|
|
548
|
+
void this.handleGetMcpResource(msg, queuedMessage.from);
|
|
549
|
+
break;
|
|
538
550
|
default: {
|
|
539
551
|
const exhaustive = msg; // Error => non-exhaustive switch-case.
|
|
540
552
|
return exhaustive;
|
|
@@ -570,6 +582,44 @@ class OpenSession {
|
|
|
570
582
|
session_id: this.sessionUUID,
|
|
571
583
|
});
|
|
572
584
|
}
|
|
585
|
+
async handleAddMcpServerFromUrl(msg, from) {
|
|
586
|
+
const skillManager = this.skillManager;
|
|
587
|
+
try {
|
|
588
|
+
const serverName = msg.server_name;
|
|
589
|
+
await skillManager.addMcpServerWithStreamableHTTPUrl(serverName, msg.url);
|
|
590
|
+
this.skillManager.enableAllTools(serverName);
|
|
591
|
+
const server = skillManager.getMcpServer(serverName);
|
|
592
|
+
const tools = server.getTools();
|
|
593
|
+
const resources = server.getResources();
|
|
594
|
+
const enabled_tools = Array.from(server.getEnabledTools().keys());
|
|
595
|
+
this.sender.broadcast({
|
|
596
|
+
type: "mcp_server_added",
|
|
597
|
+
server_name: serverName,
|
|
598
|
+
tools,
|
|
599
|
+
resources,
|
|
600
|
+
enabled_tools,
|
|
601
|
+
session_id: this.sessionUUID,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
catch (err) {
|
|
605
|
+
this.handleError(err, from);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
async handleGetMcpResource(msg, from) {
|
|
609
|
+
try {
|
|
610
|
+
const contents = await this.skillManager.getResource(msg.server_name, msg.uri);
|
|
611
|
+
this.sender.sendTo(from, {
|
|
612
|
+
type: "mcp_resource",
|
|
613
|
+
session_id: this.sessionUUID,
|
|
614
|
+
client_message_id: msg.client_message_id,
|
|
615
|
+
server_name: msg.server_name,
|
|
616
|
+
contents,
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
catch (err) {
|
|
620
|
+
this.handleError(err, from, msg.client_message_id);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
573
623
|
async handleSetWorkspace(msg, sender) {
|
|
574
624
|
this.contextManager.setWorkspace((0, agent_1.createUserMessage)(msg.message, msg.imageB64, sender));
|
|
575
625
|
const m = this.workspaceUserMessageData();
|
|
@@ -730,11 +780,13 @@ class OpenSession {
|
|
|
730
780
|
// Broadcast the message to all participants.
|
|
731
781
|
const server = this.skillManager.getMcpServer(serverName);
|
|
732
782
|
const tools = server.getTools();
|
|
783
|
+
const resources = server.getResources();
|
|
733
784
|
const enabled_tools = Array.from(server.getEnabledTools().keys());
|
|
734
785
|
return {
|
|
735
786
|
type: "mcp_server_added",
|
|
736
787
|
server_name: serverName,
|
|
737
788
|
tools,
|
|
789
|
+
resources,
|
|
738
790
|
enabled_tools,
|
|
739
791
|
session_id: this.sessionUUID,
|
|
740
792
|
};
|
|
@@ -857,15 +909,20 @@ class OpenSession {
|
|
|
857
909
|
// TODO: Do we want to braodcast an "mcp_server_config_updated" message?
|
|
858
910
|
}
|
|
859
911
|
async handleSessionFileGetContent(msg, from) {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
912
|
+
try {
|
|
913
|
+
const data_url = await this.sessionFileManager.getFileContent(msg.name);
|
|
914
|
+
const contentMsg = {
|
|
915
|
+
type: "session_file_content",
|
|
916
|
+
session_id: this.sessionUUID,
|
|
917
|
+
client_message_id: msg.client_message_id,
|
|
918
|
+
name: msg.name,
|
|
919
|
+
data_url,
|
|
920
|
+
};
|
|
921
|
+
this.sender.sendTo(from, contentMsg);
|
|
922
|
+
}
|
|
923
|
+
catch (err) {
|
|
924
|
+
this.handleError(err, from);
|
|
925
|
+
}
|
|
869
926
|
}
|
|
870
927
|
async handleSessionFileDelete(msg) {
|
|
871
928
|
await this.sessionFileManager.deleteFile(msg.name);
|
|
@@ -918,6 +975,7 @@ class OpenSession {
|
|
|
918
975
|
email: participant.email,
|
|
919
976
|
session_id: this.sessionUUID,
|
|
920
977
|
};
|
|
978
|
+
this.updateParticipantsPrompt();
|
|
921
979
|
this.sender.broadcast(broadcastMessage);
|
|
922
980
|
}
|
|
923
981
|
/**
|
|
@@ -973,6 +1031,21 @@ class OpenSession {
|
|
|
973
1031
|
session_id: this.sessionUUID,
|
|
974
1032
|
};
|
|
975
1033
|
}
|
|
1034
|
+
updateParticipantsPrompt() {
|
|
1035
|
+
const participants = this.sessionParticipants;
|
|
1036
|
+
function* promptStrings() {
|
|
1037
|
+
yield "The following map is from user_uuids to real names. In user ";
|
|
1038
|
+
yield 'messages, real names (or derivatives e.g. "Joe" for "Joe ';
|
|
1039
|
+
yield 'Bloggs") will be used to refer to users. Similarly, your ';
|
|
1040
|
+
yield "responses should also use real names.\n";
|
|
1041
|
+
for (const [user_uuid, participant] of participants.entries()) {
|
|
1042
|
+
yield `${user_uuid}: ${participant.nickname}\n`;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
const prompt = Array.from(promptStrings()).join("");
|
|
1046
|
+
logger.info(`[OpenSession.updateParticipantsPrompt] prompt: ${prompt}`);
|
|
1047
|
+
this.contextManager.setPromptFragment("participants", prompt);
|
|
1048
|
+
}
|
|
976
1049
|
}
|
|
977
1050
|
exports.OpenSession = OpenSession;
|
|
978
1051
|
async function loadDataForEmptySession(db, agent_profile_uuid, owner_uuid) {
|
|
@@ -1029,6 +1102,6 @@ async function createContextAndAgent(sessionUUID, systemPrompt, model, sessionMe
|
|
|
1029
1102
|
const eventHandler = new ChatSessionAgentEventHandler(sessionUUID, sender, approvalManager, contextManager);
|
|
1030
1103
|
const xmcpConfig = sdk_1.Configuration.new(ownerApiKey, xmcpUrl, false);
|
|
1031
1104
|
const [agent, skillManager] = await (0, agentUtils_1.createAgentWithoutSkills)(llmUrl, model, eventHandler, platform, contextManager, ownerApiKey, xmcpConfig, undefined, true);
|
|
1032
|
-
await (0, tools_1.addDefaultChatTools)(agent, ownerData.timezone,
|
|
1105
|
+
await (0, tools_1.addDefaultChatTools)(agent, ownerData.timezone, fileManager, llmUrl, ownerApiKey);
|
|
1033
1106
|
return { agent, skillManager, contextManager };
|
|
1034
1107
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ChatSessionFileManager = exports.MemoryFileManager = void 0;
|
|
4
|
-
exports.
|
|
4
|
+
exports.createSessionFilesManagerPrompt = createSessionFilesManagerPrompt;
|
|
5
5
|
exports.fileManagerTool = fileManagerTool;
|
|
6
6
|
const uuid_1 = require("uuid");
|
|
7
7
|
const assert_1 = require("assert");
|
|
@@ -174,16 +174,19 @@ exports.ChatSessionFileManager = ChatSessionFileManager;
|
|
|
174
174
|
/**
|
|
175
175
|
* Return the file list in a form easily parsable by the LLM.
|
|
176
176
|
*/
|
|
177
|
-
function
|
|
177
|
+
function createSessionFilesManagerPrompt(fm) {
|
|
178
178
|
const files = fm.listFiles();
|
|
179
179
|
if (files.length === 0) {
|
|
180
180
|
return "";
|
|
181
181
|
}
|
|
182
|
-
let
|
|
182
|
+
let prompt = "Files can be read/written as required. Create new files conservatively, " +
|
|
183
|
+
"usually when complex content is requested. Use base64 for binary (image," +
|
|
184
|
+
"pdf), ascii for text formats (html,markdown). " +
|
|
185
|
+
"Available files:\nname,type,summary\n";
|
|
183
186
|
for (const f of files) {
|
|
184
|
-
|
|
187
|
+
prompt += `${f.name},${f.mime_type},${f.summary || ""}\n`;
|
|
185
188
|
}
|
|
186
|
-
return
|
|
189
|
+
return prompt;
|
|
187
190
|
}
|
|
188
191
|
const GET_FILE_CONTENT_TOOL = {
|
|
189
192
|
type: "function",
|
|
@@ -266,8 +269,16 @@ function fileManagerTool(fileManager) {
|
|
|
266
269
|
const parsed = parseNameSummaryDataUrl(args);
|
|
267
270
|
const { name, summary, data_url } = parsed;
|
|
268
271
|
const desc = await fileManager.putFileContent(name, summary, data_url);
|
|
272
|
+
const mimeType = (0, dbSessionFileModels_1.getSessionFileMimeTypeFromDataUrl)(data_url);
|
|
269
273
|
parsed.data_url = fileManager.getSessionFileRelativeUrl(name);
|
|
270
|
-
return {
|
|
274
|
+
return {
|
|
275
|
+
response: desc.name,
|
|
276
|
+
overwriteArgs: JSON.stringify(parsed),
|
|
277
|
+
_meta: {
|
|
278
|
+
"xalia/fileUri": parsed.data_url,
|
|
279
|
+
"xalia/fileMimeType": mimeType,
|
|
280
|
+
},
|
|
281
|
+
};
|
|
271
282
|
};
|
|
272
283
|
// `delete_file_content` tool
|
|
273
284
|
//
|
|
@@ -554,7 +554,7 @@ class SessionRegistry {
|
|
|
554
554
|
agentProfileFromTemplate = template.agent_profile;
|
|
555
555
|
}
|
|
556
556
|
const newAgentProfile = agentProfileFromTemplate ||
|
|
557
|
-
new sdk_1.AgentProfile(sdk_1.DEFAULT_AGENT_PROFILE.model, sdk_1.DEFAULT_AGENT_PROFILE.system_prompt, sdk_1.DEFAULT_AGENT_PROFILE.mcp_settings);
|
|
557
|
+
new sdk_1.AgentProfile(message.model || sdk_1.DEFAULT_AGENT_PROFILE.model, sdk_1.DEFAULT_AGENT_PROFILE.system_prompt, sdk_1.DEFAULT_AGENT_PROFILE.mcp_settings);
|
|
558
558
|
const team_uuid = message.team_uuid || undefined;
|
|
559
559
|
if (team_uuid) {
|
|
560
560
|
// TODO: should be able to reconstruct the full SavedAgentProfile in one
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Collection of simple Agent tools.
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.calculatorTool = void 0;
|
|
6
|
+
exports.testTool = exports.calculatorTool = void 0;
|
|
7
7
|
exports.makeParseArgsFn = makeParseArgsFn;
|
|
8
8
|
exports.isoWithTimezone = isoWithTimezone;
|
|
9
9
|
exports.datetimeTool = datetimeTool;
|
|
@@ -14,12 +14,13 @@ exports.openURL = openURL;
|
|
|
14
14
|
exports.openURLTool = openURLTool;
|
|
15
15
|
exports.addDefaultChatTools = addDefaultChatTools;
|
|
16
16
|
const expr_eval_1 = require("expr-eval");
|
|
17
|
-
const htmlToText_1 = require("../utils/htmlToText");
|
|
18
17
|
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
18
|
+
const htmlToText_1 = require("../utils/htmlToText");
|
|
19
19
|
const search_1 = require("../utils/search");
|
|
20
20
|
const sessionFileManager_1 = require("./sessionFileManager");
|
|
21
21
|
const imageGeneratorTools_1 = require("./imageGeneratorTools");
|
|
22
22
|
const logger = (0, sdk_1.getLogger)();
|
|
23
|
+
const DEVELOPMENT = process.env.DEVELOPMENT === "1";
|
|
23
24
|
/**
|
|
24
25
|
* Returns a function which parses an `args` struct and attempts to extract
|
|
25
26
|
* multiple string parameters with the given names. e.g.
|
|
@@ -147,21 +148,39 @@ const RENDER_DESC = {
|
|
|
147
148
|
parameters: {
|
|
148
149
|
type: "object",
|
|
149
150
|
properties: {
|
|
151
|
+
name: {
|
|
152
|
+
type: "string",
|
|
153
|
+
description: "Filename to store the html",
|
|
154
|
+
},
|
|
155
|
+
summary: {
|
|
156
|
+
type: "string",
|
|
157
|
+
description: "One line summary",
|
|
158
|
+
},
|
|
150
159
|
html: {
|
|
151
160
|
type: "string",
|
|
152
161
|
description: "HTML fragment to render",
|
|
153
162
|
},
|
|
154
163
|
},
|
|
155
|
-
required: ["html"],
|
|
164
|
+
required: ["name", "summary", "html"],
|
|
156
165
|
},
|
|
157
166
|
},
|
|
158
167
|
};
|
|
159
|
-
function renderTool(
|
|
160
|
-
const
|
|
168
|
+
function renderTool(fileManager) {
|
|
169
|
+
const getNameSummeryHtml = makeParseArgsFn([
|
|
170
|
+
"name",
|
|
171
|
+
"summary",
|
|
172
|
+
"html",
|
|
173
|
+
]);
|
|
161
174
|
const toolFn = async (_, args) => {
|
|
162
|
-
const { html } =
|
|
163
|
-
|
|
164
|
-
|
|
175
|
+
const { name, summary, html } = getNameSummeryHtml(args);
|
|
176
|
+
const mimeType = "text/html";
|
|
177
|
+
const dataURL = `data:${mimeType},${html}`;
|
|
178
|
+
await fileManager.putFileContent(name, summary, dataURL);
|
|
179
|
+
const uri = fileManager.getSessionFileRelativeUrl(name);
|
|
180
|
+
return {
|
|
181
|
+
response: "",
|
|
182
|
+
_meta: { "xalia/fileUri": uri, "xalia/fileMimeType": mimeType },
|
|
183
|
+
};
|
|
165
184
|
};
|
|
166
185
|
return {
|
|
167
186
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
@@ -250,15 +269,44 @@ function openURLTool() {
|
|
|
250
269
|
},
|
|
251
270
|
};
|
|
252
271
|
}
|
|
272
|
+
const TEST_DESC = {
|
|
273
|
+
type: "function",
|
|
274
|
+
function: {
|
|
275
|
+
name: "test_tool",
|
|
276
|
+
description: "Call me when asked to test tools",
|
|
277
|
+
parameters: {
|
|
278
|
+
type: "object",
|
|
279
|
+
properties: {},
|
|
280
|
+
required: [],
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
exports.testTool = {
|
|
285
|
+
setup: (agent) => {
|
|
286
|
+
const toolFn = (_agent, _args) => {
|
|
287
|
+
// Return an object with structuredContent and _meta
|
|
288
|
+
return Promise.resolve({
|
|
289
|
+
response: "Some text",
|
|
290
|
+
structuredContent: { description: "structuredContent example" },
|
|
291
|
+
_meta: { description: "_meta example" },
|
|
292
|
+
});
|
|
293
|
+
};
|
|
294
|
+
agent.addAgentTool(TEST_DESC, toolFn);
|
|
295
|
+
return Promise.resolve();
|
|
296
|
+
},
|
|
297
|
+
};
|
|
253
298
|
/**
|
|
254
299
|
* Add a set of agent tools for chat sessions.
|
|
255
300
|
*/
|
|
256
|
-
async function addDefaultChatTools(agent, timezone,
|
|
301
|
+
async function addDefaultChatTools(agent, timezone, fileManager, llmUrl, llmApiKey) {
|
|
257
302
|
await agent.addAgentToolProvider(datetimeTool(timezone));
|
|
258
303
|
await agent.addAgentToolProvider(exports.calculatorTool);
|
|
259
|
-
await agent.addAgentToolProvider(renderTool(
|
|
304
|
+
await agent.addAgentToolProvider(renderTool(fileManager));
|
|
260
305
|
await agent.addAgentToolProvider(webSearchTool());
|
|
261
306
|
await agent.addAgentToolProvider(openURLTool());
|
|
262
307
|
await agent.addAgentToolProvider((0, sessionFileManager_1.fileManagerTool)(fileManager));
|
|
263
308
|
await agent.addAgentToolProvider(await (0, imageGeneratorTools_1.genImageFileTool)(llmUrl, llmApiKey, fileManager));
|
|
309
|
+
if (DEVELOPMENT) {
|
|
310
|
+
await agent.addAgentToolProvider(exports.testTool);
|
|
311
|
+
}
|
|
264
312
|
}
|
|
@@ -75,7 +75,7 @@ function createCallTestToolScript(tool_call_ids, param1, param2) {
|
|
|
75
75
|
return new Promise((r) => {
|
|
76
76
|
r({
|
|
77
77
|
response: `tool_result: '${param1}' '${String(param2)}'`,
|
|
78
|
-
|
|
78
|
+
_meta: { type: "text/plain" },
|
|
79
79
|
});
|
|
80
80
|
});
|
|
81
81
|
};
|
|
@@ -96,7 +96,7 @@ function createCallTestToolScript(tool_call_ids, param1, param2) {
|
|
|
96
96
|
content: `tool_result: '${param1}' '${String(param2)}'`,
|
|
97
97
|
role: "tool",
|
|
98
98
|
tool_call_id: id,
|
|
99
|
-
|
|
99
|
+
_meta: { type: "text/plain" },
|
|
100
100
|
});
|
|
101
101
|
}
|
|
102
102
|
// Second tool call (if requested) is tool0
|
|
@@ -370,4 +370,28 @@ describe("Agent", () => {
|
|
|
370
370
|
// Check the Agent has not added the user messages.
|
|
371
371
|
(0, vitest_1.expect)(ctxMgr.getLLMContext().length).eql(1);
|
|
372
372
|
});
|
|
373
|
+
it("supports reasoning token callbacks", async function () {
|
|
374
|
+
const script = [
|
|
375
|
+
{
|
|
376
|
+
finish_reason: "reasoning",
|
|
377
|
+
message: "Some reasoning tokens...",
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
index: 0,
|
|
381
|
+
finish_reason: "stop",
|
|
382
|
+
message: {
|
|
383
|
+
content: "Agent Message",
|
|
384
|
+
refusal: null,
|
|
385
|
+
role: "assistant",
|
|
386
|
+
},
|
|
387
|
+
logprobs: null,
|
|
388
|
+
},
|
|
389
|
+
];
|
|
390
|
+
const llm = new testTools_1.TestDummyLLM(script);
|
|
391
|
+
const eventHandler = new testTools_1.TestAgentEventHandler();
|
|
392
|
+
const ctxMgr = new context_1.ContextManager("SYSTEM_PROMPT", []);
|
|
393
|
+
const agent = agent_1.Agent.initializeWithLLM(eventHandler, llm, ctxMgr);
|
|
394
|
+
await agent.userMessageEx("user message 1");
|
|
395
|
+
(0, vitest_1.expect)(eventHandler.getReasoning()).eql([script[0].message, ""]);
|
|
396
|
+
});
|
|
373
397
|
});
|
|
@@ -47,7 +47,7 @@ const MESSAGES = [
|
|
|
47
47
|
role: "tool",
|
|
48
48
|
content: "msg501",
|
|
49
49
|
tool_call_id: "tool_call_0",
|
|
50
|
-
|
|
50
|
+
_meta: { type: "text/plain" },
|
|
51
51
|
},
|
|
52
52
|
},
|
|
53
53
|
{
|
|
@@ -88,7 +88,7 @@ function testSuccessfulAgentLoop(cm, startIdx) {
|
|
|
88
88
|
(0, vitest_1.expect)(llmUserMessages).eql(userMsgs.map((su) => {
|
|
89
89
|
return {
|
|
90
90
|
role: "user",
|
|
91
|
-
name: su.
|
|
91
|
+
name: su.user_uuid,
|
|
92
92
|
content: su.message,
|
|
93
93
|
};
|
|
94
94
|
}));
|
|
@@ -187,13 +187,13 @@ function testSuccessfulAgentLoop(cm, startIdx) {
|
|
|
187
187
|
message_idx: startIdx + 0,
|
|
188
188
|
sender_uuid: "user0",
|
|
189
189
|
is_for_llm: true,
|
|
190
|
-
content: (0, agent_1.createUserMessageEnsure)(userMsgs[0].message, userMsgs[0].imageB64, "
|
|
190
|
+
content: (0, agent_1.createUserMessageEnsure)(userMsgs[0].message, userMsgs[0].imageB64, "user0"),
|
|
191
191
|
},
|
|
192
192
|
{
|
|
193
193
|
message_idx: startIdx + conversation_1.MESSAGE_INDEX_FULL_INCREMENT,
|
|
194
194
|
sender_uuid: "user1",
|
|
195
195
|
is_for_llm: true,
|
|
196
|
-
content: (0, agent_1.createUserMessageEnsure)(userMsgs[1].message, userMsgs[1].imageB64, "
|
|
196
|
+
content: (0, agent_1.createUserMessageEnsure)(userMsgs[1].message, userMsgs[1].imageB64, "user1"),
|
|
197
197
|
},
|
|
198
198
|
// agent response with tool calls
|
|
199
199
|
{
|
|
@@ -329,7 +329,7 @@ describe("IndexingCompressingContextManager", () => {
|
|
|
329
329
|
(0, vitest_1.expect)(llmUserMessages).eql([
|
|
330
330
|
{
|
|
331
331
|
role: "user",
|
|
332
|
-
name: serverUserMessage0.
|
|
332
|
+
name: serverUserMessage0.user_uuid,
|
|
333
333
|
content: serverUserMessage0.message,
|
|
334
334
|
},
|
|
335
335
|
]);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
/* eslint-disable @typescript-eslint/require-await */
|
|
4
4
|
const vitest_1 = require("vitest");
|
|
5
|
+
const assert_1 = require("assert");
|
|
5
6
|
const mcpServerManager_1 = require("../agent/mcpServerManager");
|
|
6
7
|
// Handle automatically closing at the end of tests.
|
|
7
8
|
let managers = {};
|
|
@@ -41,7 +42,10 @@ async function shutdownAll() {
|
|
|
41
42
|
{
|
|
42
43
|
const qualifiedName = tm.getOpenAITools()[0].function.name;
|
|
43
44
|
const r = await tm.invoke(tm.verifyToolCall(qualifiedName, { a: 1000, b: 10 }));
|
|
44
|
-
|
|
45
|
+
(0, assert_1.strict)(r._meta);
|
|
46
|
+
(0, assert_1.strict)((0, mcpServerManager_1.isMcpServerToolCallMetaData)(r._meta));
|
|
47
|
+
(0, vitest_1.expect)(r._meta["xalia/mcpServerName"]).eql("simplecalc");
|
|
48
|
+
console.log(`response: ${JSON.stringify(r)}`);
|
|
45
49
|
}
|
|
46
50
|
// Disable and check we don't get the spec anymore
|
|
47
51
|
{
|
|
@@ -81,6 +81,9 @@ class TestAgentEventHandler {
|
|
|
81
81
|
getToolCallResults() {
|
|
82
82
|
return this.toolCallResults;
|
|
83
83
|
}
|
|
84
|
+
getReasoning() {
|
|
85
|
+
return this.reasoning;
|
|
86
|
+
}
|
|
84
87
|
}
|
|
85
88
|
exports.TestAgentEventHandler = TestAgentEventHandler;
|
|
86
89
|
/**
|
|
@@ -92,9 +95,9 @@ class TestDummyLLM extends dummyLLM_1.DummyLLM {
|
|
|
92
95
|
super(script);
|
|
93
96
|
this.requests = [];
|
|
94
97
|
}
|
|
95
|
-
getConversationResponse(messages, tools, onMessage) {
|
|
98
|
+
getConversationResponse(messages, tools, onMessage, onReasoning) {
|
|
96
99
|
this.requests.push({ messages, tools });
|
|
97
|
-
return super.getConversationResponse(messages, tools, onMessage);
|
|
100
|
+
return super.getConversationResponse(messages, tools, onMessage, onReasoning);
|
|
98
101
|
}
|
|
99
102
|
getRequests() {
|
|
100
103
|
return this.requests;
|
|
@@ -54,6 +54,7 @@ const options = __importStar(require("./options"));
|
|
|
54
54
|
const commandPrompt_1 = require("./commandPrompt");
|
|
55
55
|
const nodePlatform_1 = require("./nodePlatform");
|
|
56
56
|
const files_1 = require("./files");
|
|
57
|
+
const dbSessionFileModels_1 = require("../chat/data/dbSessionFileModels");
|
|
57
58
|
const logger = (0, sdk_1.getLogger)();
|
|
58
59
|
const listSessions = (0, cmd_ts_1.command)({
|
|
59
60
|
name: "delete-session",
|
|
@@ -221,6 +222,8 @@ const client = (0, cmd_ts_1.command)({
|
|
|
221
222
|
${chalk_1.default.yellow("/get-file <name>")} Get file contents
|
|
222
223
|
${chalk_1.default.yellow("/share-session <file>")} Share and write access token
|
|
223
224
|
${chalk_1.default.yellow("/pause-agent <0|1>")} Pause (or unpause) the agent
|
|
225
|
+
${chalk_1.default.yellow("/add-mcp-server-url <server-name> <url>")} Add MCP server
|
|
226
|
+
${chalk_1.default.yellow("/get-resource <server-name> <uri>")} get MCP server resource
|
|
224
227
|
`;
|
|
225
228
|
const cmdPrompt = new commandPrompt_1.CommandPrompt(repl, helpText);
|
|
226
229
|
const eventHandler = getCLIEventHandler(cmdPrompt);
|
|
@@ -288,6 +291,12 @@ const client = (0, cmd_ts_1.command)({
|
|
|
288
291
|
case "pause-agent":
|
|
289
292
|
pauseAgent(sessionClient, cmds[1]);
|
|
290
293
|
break;
|
|
294
|
+
case "add-mcp-server-url":
|
|
295
|
+
addMcpServerFromUrl(sessionClient, cmds[1], cmds[2]);
|
|
296
|
+
break;
|
|
297
|
+
case "get-resource":
|
|
298
|
+
await getResource(sessionClient, cmds[1], cmds[2]);
|
|
299
|
+
break;
|
|
291
300
|
default:
|
|
292
301
|
console.log(`error: Unknown command ${cmds[0]}`);
|
|
293
302
|
}
|
|
@@ -355,6 +364,13 @@ async function shareSession(sessionClient, filename) {
|
|
|
355
364
|
function pauseAgent(sessionClient, pause) {
|
|
356
365
|
sessionClient.setAgentPaused(!!parseInt(pause));
|
|
357
366
|
}
|
|
367
|
+
function addMcpServerFromUrl(sessionClient, name, url) {
|
|
368
|
+
sessionClient.addMcpServerFromUrl(name, url);
|
|
369
|
+
}
|
|
370
|
+
async function getResource(sessionClient, serverName, resourceUrl) {
|
|
371
|
+
const contents = await sessionClient.getMcpResource(serverName, resourceUrl);
|
|
372
|
+
console.log(`resource: (${serverName}) ${resourceUrl}: ${JSON.stringify(contents)}`);
|
|
373
|
+
}
|
|
358
374
|
function setWorkspaceFromPrompt(sessionClient, cmds) {
|
|
359
375
|
let msg = "";
|
|
360
376
|
let image = undefined;
|
|
@@ -450,9 +466,10 @@ function getCLIEventHandler(cmdPrompt) {
|
|
|
450
466
|
break;
|
|
451
467
|
case "tool_call_result":
|
|
452
468
|
{
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
469
|
+
console.log(`tool call result: ${JSON.stringify(msg.result)}`);
|
|
470
|
+
const metadata = msg.result._meta;
|
|
471
|
+
if ((0, dbSessionFileModels_1.isFileMetaData)(metadata)) {
|
|
472
|
+
console.log(`filemanager file: ${JSON.stringify(metadata)}`);
|
|
456
473
|
// SessionFiles should already hold the file information.
|
|
457
474
|
const sessionFiles = sessionClient.getSessionFiles();
|
|
458
475
|
const { name } = sessionFiles.decodeSessionFileUrl(msg.result.content);
|
|
@@ -470,6 +487,9 @@ function getCLIEventHandler(cmdPrompt) {
|
|
|
470
487
|
r(true);
|
|
471
488
|
}), msg.display_name);
|
|
472
489
|
break;
|
|
490
|
+
case "session_error":
|
|
491
|
+
process_1.stdout.write(chalk_1.default.red(`session_error(${msg.session_id}): ${msg.message}`));
|
|
492
|
+
break;
|
|
473
493
|
default:
|
|
474
494
|
process_1.stdout.write(`(unrecognised) ${JSON.stringify(msg.type)}\n`);
|
|
475
495
|
break;
|
|
@@ -38,7 +38,6 @@ exports.loadFileOrStdin = loadFileOrStdin;
|
|
|
38
38
|
exports.loadImageAsDataUrl = loadImageAsDataUrl;
|
|
39
39
|
exports.loadSessionFileAsDataUrl = loadSessionFileAsDataUrl;
|
|
40
40
|
exports.loadImageAsDataUrlOrUndefined = loadImageAsDataUrlOrUndefined;
|
|
41
|
-
exports.loadServerUrls = loadServerUrls;
|
|
42
41
|
const fs = __importStar(require("fs"));
|
|
43
42
|
const path = __importStar(require("path"));
|
|
44
43
|
const dbSessionFileModels_1 = require("../chat/data/dbSessionFileModels");
|
|
@@ -99,29 +98,3 @@ function loadImageAsDataUrlOrUndefined(file) {
|
|
|
99
98
|
}
|
|
100
99
|
return undefined;
|
|
101
100
|
}
|
|
102
|
-
function loadServerUrls(path) {
|
|
103
|
-
try {
|
|
104
|
-
const file = fs.readFileSync(path, "utf-8");
|
|
105
|
-
const urls = JSON.parse(file);
|
|
106
|
-
// Validate the structure
|
|
107
|
-
if (typeof urls !== "object" || urls === null) {
|
|
108
|
-
throw new Error("Invalid server URLs format: must be an object");
|
|
109
|
-
}
|
|
110
|
-
// Validate each URL is a string
|
|
111
|
-
for (const [key, value] of Object.entries(urls)) {
|
|
112
|
-
if (typeof value !== "string") {
|
|
113
|
-
throw new Error(`Invalid URL format for server ${key}: must be a string`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return urls;
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
if (error instanceof SyntaxError) {
|
|
120
|
-
throw new Error(`Invalid JSON in server URLs file: ${error.message}`);
|
|
121
|
-
}
|
|
122
|
-
if (error instanceof Error) {
|
|
123
|
-
throw new Error(`Failed to load server URLs: ${error.message}`);
|
|
124
|
-
}
|
|
125
|
-
throw error;
|
|
126
|
-
}
|
|
127
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xalia/agent",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "ISC",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"test": "yarn build && vitest run"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@modelcontextprotocol/sdk": "=1.
|
|
21
|
+
"@modelcontextprotocol/sdk": "=1.20.1",
|
|
22
22
|
"@supabase/supabase-js": "2.49.7",
|
|
23
23
|
"@types/readline-sync": "^1.4.8",
|
|
24
24
|
"chalk": "^4.1.2",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"google-search-ts": "^1.0.1",
|
|
31
31
|
"html-to-text": "^9.0.5",
|
|
32
32
|
"node-fetch": "^3.3.2",
|
|
33
|
-
"openai": "=
|
|
33
|
+
"openai": "=6.3.0",
|
|
34
34
|
"readline-sync": "^1.4.10",
|
|
35
35
|
"uuid": "^11.1.0",
|
|
36
36
|
"ws": "8.18.2",
|
package/scripts/test_chat
CHANGED
|
@@ -178,7 +178,9 @@ pushd _test_chat
|
|
|
178
178
|
--agent-profile-id ${invoke_gen_image_profile_id} \
|
|
179
179
|
--script invoke_gen_image_script | tee invoke_gen_image_output.txt
|
|
180
180
|
grep 'frog.png' invoke_gen_image_output.txt
|
|
181
|
-
grep '
|
|
181
|
+
grep '"xalia/fileMimeType":"image/png"' invoke_gen_image_output.txt
|
|
182
|
+
grep 'structuredContent example' invoke_gen_image_output.txt
|
|
183
|
+
grep '_meta example' invoke_gen_image_output.txt
|
|
182
184
|
|
|
183
185
|
# User 1 tries to join the session. Should be rejected.
|
|
184
186
|
|