@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.
Files changed (74) hide show
  1. package/dist/agent/src/agent/agent.js +12 -11
  2. package/dist/agent/src/agent/dummyLLM.js +24 -15
  3. package/dist/agent/src/agent/llm.js +0 -22
  4. package/dist/agent/src/agent/mcpServerManager.js +73 -15
  5. package/dist/agent/src/agent/openAI.js +32 -0
  6. package/dist/agent/src/agent/openAILLM.js +25 -1
  7. package/dist/agent/src/agent/openAILLMStreaming.js +8 -3
  8. package/dist/agent/src/agent/sudoMcpServerManager.js +22 -9
  9. package/dist/agent/src/chat/client/chatClient.js +2 -1
  10. package/dist/agent/src/chat/client/sessionClient.js +28 -3
  11. package/dist/agent/src/chat/data/dbSessionFileModels.js +10 -0
  12. package/dist/agent/src/chat/protocol/messages.js +1 -0
  13. package/dist/agent/src/chat/server/chatContextManager.js +4 -4
  14. package/dist/agent/src/chat/server/conversation.js +1 -1
  15. package/dist/agent/src/chat/server/imageGeneratorTools.js +7 -4
  16. package/dist/agent/src/chat/server/openSession.js +85 -12
  17. package/dist/agent/src/chat/server/sessionFileManager.js +17 -6
  18. package/dist/agent/src/chat/server/sessionRegistry.js +1 -1
  19. package/dist/agent/src/chat/server/tools.js +58 -10
  20. package/dist/agent/src/test/agent.test.js +26 -2
  21. package/dist/agent/src/test/chatContextManager.test.js +5 -5
  22. package/dist/agent/src/test/mcpServerManager.test.js +5 -1
  23. package/dist/agent/src/test/testTools.js +5 -2
  24. package/dist/agent/src/tool/chatMain.js +23 -3
  25. package/dist/agent/src/tool/files.js +0 -27
  26. package/package.json +3 -3
  27. package/scripts/test_chat +3 -1
  28. package/src/agent/agent.ts +53 -47
  29. package/src/agent/agentUtils.ts +7 -7
  30. package/src/agent/compressingContextManager.ts +4 -9
  31. package/src/agent/context.ts +28 -37
  32. package/src/agent/dummyLLM.ts +38 -28
  33. package/src/agent/iAgentEventHandler.ts +6 -9
  34. package/src/agent/imageGenLLM.ts +11 -5
  35. package/src/agent/llm.ts +41 -106
  36. package/src/agent/mcpServerManager.ts +145 -29
  37. package/src/agent/openAI.ts +123 -0
  38. package/src/agent/openAILLM.ts +52 -5
  39. package/src/agent/openAILLMStreaming.ts +36 -32
  40. package/src/agent/repeatLLM.ts +5 -6
  41. package/src/agent/sudoMcpServerManager.ts +48 -16
  42. package/src/agent/tools.ts +3 -5
  43. package/src/chat/client/chatClient.ts +3 -1
  44. package/src/chat/client/sessionClient.ts +47 -7
  45. package/src/chat/data/dataModels.ts +3 -3
  46. package/src/chat/data/dbSessionFileModels.ts +22 -0
  47. package/src/chat/protocol/messages.ts +39 -13
  48. package/src/chat/server/chatContextManager.ts +20 -24
  49. package/src/chat/server/conversation.ts +10 -10
  50. package/src/chat/server/imageGeneratorTools.ts +18 -9
  51. package/src/chat/server/openSession.ts +111 -22
  52. package/src/chat/server/sessionFileManager.ts +33 -10
  53. package/src/chat/server/sessionRegistry.ts +1 -1
  54. package/src/chat/server/tools.ts +77 -18
  55. package/src/chat/utils/approvalManager.ts +2 -2
  56. package/src/test/agent.test.ts +56 -31
  57. package/src/test/approvalManager.test.ts +2 -2
  58. package/src/test/chatContextManager.test.ts +11 -14
  59. package/src/test/compressingContextManager.test.ts +3 -3
  60. package/src/test/context.test.ts +3 -3
  61. package/src/test/conversation.test.ts +7 -7
  62. package/src/test/dbSessionMessages.test.ts +3 -3
  63. package/src/test/mcpServerManager.test.ts +10 -1
  64. package/src/test/testTools.ts +44 -33
  65. package/src/tool/agentChat.ts +10 -8
  66. package/src/tool/agentMain.ts +2 -2
  67. package/src/tool/chatMain.ts +38 -6
  68. package/src/tool/commandPrompt.ts +2 -4
  69. package/src/tool/files.ts +0 -34
  70. package/test_data/dummyllm_script_image_gen.json +27 -17
  71. package/test_data/dummyllm_script_invoke_image_gen_tool.json +9 -2
  72. package/test_data/dummyllm_script_render_tool.json +29 -0
  73. package/test_data/dummyllm_script_test_auto_approve.json +81 -0
  74. 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 mimeTypes_1 = require("../data/mimeTypes");
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, mimeTypes_1.getMimeTypeFromDataUrl)(image);
100
+ const mimeType = (0, dbSessionFileModels_1.getSessionFileMimeTypeFromDataUrl)(image);
101
101
  await fileManager.putFileContent(name, summary, image);
102
- const url = fileManager.getSessionFileRelativeUrl(name);
103
- return { response: url, metadata: { type: mimeType } };
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.DEVELOPMENT === "1") {
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
- const data_url = await this.sessionFileManager.getFileContent(msg.name);
861
- const contentMsg = {
862
- type: "session_file_content",
863
- session_id: this.sessionUUID,
864
- client_message_id: msg.client_message_id,
865
- name: msg.name,
866
- data_url,
867
- };
868
- this.sender.sendTo(from, contentMsg);
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, platform, fileManager, llmUrl, ownerApiKey);
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.listFilesForLLM = listFilesForLLM;
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 listFilesForLLM(fm) {
177
+ function createSessionFilesManagerPrompt(fm) {
178
178
  const files = fm.listFiles();
179
179
  if (files.length === 0) {
180
180
  return "";
181
181
  }
182
- let summary = "Available files:\nname,type,summary\n";
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
- summary += `${f.name},${f.mime_type},${f.summary || ""}\n`;
187
+ prompt += `${f.name},${f.mime_type},${f.summary || ""}\n`;
185
188
  }
186
- return summary;
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 { response: desc.name, overwriteArgs: JSON.stringify(parsed) };
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(platform) {
160
- const getHtml = makeParseArgsFn(["html"]);
168
+ function renderTool(fileManager) {
169
+ const getNameSummeryHtml = makeParseArgsFn([
170
+ "name",
171
+ "summary",
172
+ "html",
173
+ ]);
161
174
  const toolFn = async (_, args) => {
162
- const { html } = getHtml(args);
163
- await platform.renderHTML(html);
164
- return { response: "" };
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, platform, fileManager, llmUrl, llmApiKey) {
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(platform));
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
- metadata: { type: "text/plain" },
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
- metadata: { type: "text/plain" },
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
- metadata: { type: "text/plain" },
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.user_nickname,
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, "User0"),
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, "User1"),
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.user_nickname,
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
- console.log(`response: ${r}`);
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
- const metadata = msg.result.metadata;
454
- if (metadata) {
455
- console.log(`tool call result metadata: ${JSON.stringify(metadata)}`);
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.4",
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.11.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": "=4.98.0",
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 'metadata: {"type":"image/png"}' invoke_gen_image_output.txt
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