@xalia/agent 0.5.4 → 0.5.6

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 (43) hide show
  1. package/dist/agent/src/agent/agent.js +16 -9
  2. package/dist/agent/src/agent/agentUtils.js +24 -4
  3. package/dist/agent/src/agent/mcpServerManager.js +19 -9
  4. package/dist/agent/src/agent/openAILLM.js +3 -1
  5. package/dist/agent/src/agent/openAILLMStreaming.js +24 -25
  6. package/dist/agent/src/agent/repeatLLM.js +43 -0
  7. package/dist/agent/src/agent/sudoMcpServerManager.js +12 -6
  8. package/dist/agent/src/chat/client.js +259 -36
  9. package/dist/agent/src/chat/conversationManager.js +243 -24
  10. package/dist/agent/src/chat/db.js +24 -1
  11. package/dist/agent/src/chat/frontendClient.js +74 -0
  12. package/dist/agent/src/chat/server.js +3 -3
  13. package/dist/agent/src/test/db.test.js +25 -2
  14. package/dist/agent/src/test/openaiStreaming.test.js +133 -0
  15. package/dist/agent/src/test/prompt.test.js +2 -2
  16. package/dist/agent/src/test/sudoMcpServerManager.test.js +1 -1
  17. package/dist/agent/src/tool/agentChat.js +7 -197
  18. package/dist/agent/src/tool/chatMain.js +18 -23
  19. package/dist/agent/src/tool/commandPrompt.js +248 -0
  20. package/dist/agent/src/tool/prompt.js +27 -31
  21. package/package.json +1 -1
  22. package/scripts/test_chat +17 -1
  23. package/src/agent/agent.ts +34 -11
  24. package/src/agent/agentUtils.ts +52 -3
  25. package/src/agent/mcpServerManager.ts +43 -13
  26. package/src/agent/openAILLM.ts +3 -1
  27. package/src/agent/openAILLMStreaming.ts +28 -27
  28. package/src/agent/repeatLLM.ts +51 -0
  29. package/src/agent/sudoMcpServerManager.ts +41 -12
  30. package/src/chat/client.ts +353 -40
  31. package/src/chat/conversationManager.ts +345 -33
  32. package/src/chat/db.ts +28 -2
  33. package/src/chat/frontendClient.ts +123 -0
  34. package/src/chat/messages.ts +146 -2
  35. package/src/chat/server.ts +3 -3
  36. package/src/test/db.test.ts +35 -2
  37. package/src/test/openaiStreaming.test.ts +142 -0
  38. package/src/test/prompt.test.ts +1 -1
  39. package/src/test/sudoMcpServerManager.test.ts +1 -1
  40. package/src/tool/agentChat.ts +13 -211
  41. package/src/tool/chatMain.ts +28 -43
  42. package/src/tool/commandPrompt.ts +252 -0
  43. package/src/tool/prompt.ts +33 -32
@@ -37,12 +37,15 @@ exports.chatMain = void 0;
37
37
  const cmd_ts_1 = require("cmd-ts");
38
38
  const process_1 = require("process");
39
39
  const fs = __importStar(require("fs"));
40
+ const assert_1 = require("assert");
40
41
  const tool_1 = require("@xalia/xmcp/tool");
41
42
  const sdk_1 = require("@xalia/xmcp/sdk");
42
43
  const client_1 = require("../chat/client");
43
44
  const server_1 = require("../chat/server");
44
45
  const prompt_1 = require("./prompt");
45
46
  const options = __importStar(require("./options"));
47
+ const commandPrompt_1 = require("./commandPrompt");
48
+ const nodePlatform_1 = require("./nodePlatform");
46
49
  const logger = (0, sdk_1.getLogger)();
47
50
  const server = (0, cmd_ts_1.command)({
48
51
  name: "server",
@@ -117,22 +120,27 @@ const client = (0, cmd_ts_1.command)({
117
120
  await runClientTest(host, port, apiKey, session);
118
121
  return;
119
122
  }
120
- if (script) {
121
- await runScript(host, port, apiKey, session, agentProfile, script);
122
- return;
123
- }
124
123
  const onMessage = getCLIOnMessage();
125
- const repl = new prompt_1.Prompt();
126
- const client = await client_1.ChatClient.init(host, port, apiKey, onMessage, () => repl.shutdown(), session, agentProfile);
124
+ const repl = (() => {
125
+ if (script) {
126
+ const scriptLines = fs.readFileSync(script, "utf8").split("\n");
127
+ return new prompt_1.ScriptPrompt(scriptLines);
128
+ }
129
+ return new prompt_1.Prompt();
130
+ })();
131
+ const cmdPrompt = new commandPrompt_1.CommandPrompt(repl);
132
+ const client = await client_1.ChatClient.init(host, port, apiKey, onMessage, async () => await cmdPrompt.shutdown(), nodePlatform_1.NODE_PLATFORM, session, agentProfile);
127
133
  logger.debug("client created");
128
134
  while (true) {
129
- const msgText = await repl.run("ME: ");
130
- logger.debug(`prompt got '${msgText}'`);
135
+ const [msgText, imageFile] = await cmdPrompt.getNextPrompt(undefined, // TODO: client
136
+ client.getSudoMcpServerManager());
137
+ (0, assert_1.strict)(imageFile === undefined);
131
138
  if (msgText === undefined) {
132
139
  logger.debug("exiting...");
133
140
  client.close();
134
141
  return;
135
142
  }
143
+ // TODO: other prompts
136
144
  if (msgText.length > 0) {
137
145
  const msg = {
138
146
  type: "msg",
@@ -168,26 +176,13 @@ function getCLIOnMessage() {
168
176
  }
169
177
  break;
170
178
  default:
171
- process_1.stdout.write(`(${JSON.stringify(msg)})\n`);
179
+ process_1.stdout.write(`(unrecognised) ${JSON.stringify(msg)}\n`);
172
180
  break;
173
181
  }
174
182
  };
175
183
  }
176
- async function runScript(host, port, apiKey, session, agentProfile, script) {
177
- const client = await client_1.ChatClient.init(host, port, apiKey, getCLIOnMessage(), () => { }, session, agentProfile);
178
- const scriptData = fs.readFileSync(script, "utf8");
179
- const lines = scriptData.split("\n");
180
- for (const line of lines) {
181
- await new Promise((r) => setTimeout(r, 1000));
182
- client.sendMessage({ type: "msg", message: line });
183
- }
184
- // 2 second wait period
185
- await new Promise((r) => setTimeout(r, 2000));
186
- client.close();
187
- return;
188
- }
189
184
  async function runClientTest(host, port, apiKey, session) {
190
- const client = await client_1.ChatClient.init(host, port, apiKey, getCLIOnMessage(), () => { }, session);
185
+ const client = await client_1.ChatClient.init(host, port, apiKey, getCLIOnMessage(), async () => { }, nodePlatform_1.NODE_PLATFORM, session);
191
186
  for (let i = 1; i <= 60; i++) {
192
187
  await new Promise((r) => setTimeout(r, 1000));
193
188
  const message = {
@@ -0,0 +1,248 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.CommandPrompt = void 0;
40
+ exports.parsePrompt = parsePrompt;
41
+ const fs = __importStar(require("fs"));
42
+ const chalk_1 = __importDefault(require("chalk"));
43
+ const sdk_1 = require("@xalia/xmcp/sdk");
44
+ const tools_1 = require("../agent/tools");
45
+ /**
46
+ * A prompt parser which can accept commands or messages from an IPrompt.
47
+ * Commands are sent to an IMcpServerManager, ISkillManager and IConversation.
48
+ */
49
+ class CommandPrompt {
50
+ constructor(prompt) {
51
+ this.prompt = prompt;
52
+ }
53
+ shutdown() {
54
+ return this.prompt.shutdown();
55
+ }
56
+ async getNextPrompt(agent, sudoMcpServerManager) {
57
+ while (true) {
58
+ // Get a line, detecting the EOF signal.
59
+ const line = await this.prompt.run();
60
+ if (typeof line === "undefined") {
61
+ console.log("closing ...");
62
+ return [undefined, undefined];
63
+ }
64
+ if (line.length === 0) {
65
+ continue;
66
+ }
67
+ // Extract prompt or commands
68
+ const { msg, cmds } = parsePrompt(line);
69
+ // If there are no commands, this must be a prompt only
70
+ if (!cmds) {
71
+ return [msg, undefined];
72
+ }
73
+ // There are commands. If it's image, return [prompt, image]. If it's
74
+ // quit, return [undefined, undefined], otherwise it must be a command.
75
+ // Execute it and prompt again.
76
+ switch (cmds[0]) {
77
+ case "i": // image
78
+ return [msg, cmds[1]];
79
+ case "q":
80
+ case "quit":
81
+ case "exit":
82
+ return [undefined, undefined];
83
+ default:
84
+ break;
85
+ }
86
+ try {
87
+ await this.runCommand(agent, sudoMcpServerManager.getMcpServerManager(), sudoMcpServerManager, cmds);
88
+ }
89
+ catch (e) {
90
+ console.log(`ERROR: ${e}`);
91
+ }
92
+ }
93
+ }
94
+ async runCommand(agent, mcpServerManager, sudoMcpServerManager, cmds) {
95
+ switch (cmds[0]) {
96
+ case "lt":
97
+ this.listTools(mcpServerManager);
98
+ break;
99
+ case "ls":
100
+ this.listServers(sudoMcpServerManager);
101
+ break;
102
+ case "as":
103
+ await this.addServer(sudoMcpServerManager, cmds[1]);
104
+ break;
105
+ case "rs":
106
+ await mcpServerManager.removeMcpServer(cmds[1]);
107
+ break;
108
+ case "e":
109
+ mcpServerManager.enableTool(cmds[1], cmds[2]);
110
+ console.log(`Enabled tool ${cmds[2]} for server ${cmds[1]}`);
111
+ break;
112
+ case "d":
113
+ mcpServerManager.disableTool(cmds[1], cmds[2]);
114
+ console.log(`Disabled tool ${cmds[2]} for server ${cmds[1]}`);
115
+ break;
116
+ case "ea":
117
+ mcpServerManager.enableAllTools(cmds[1]);
118
+ console.log(`Enabled all tools for server ${cmds[1]}`);
119
+ break;
120
+ case "da":
121
+ mcpServerManager.disableAllTools(cmds[1]);
122
+ console.log(`Disabled all tools for server ${cmds[1]}`);
123
+ break;
124
+ case "wc":
125
+ this.writeConversation(agent, cmds[1]);
126
+ break;
127
+ case "wa":
128
+ this.writeAgentProfile(agent, cmds[1]);
129
+ break;
130
+ case "h":
131
+ case "help":
132
+ case "?":
133
+ this.helpMenu();
134
+ break;
135
+ default:
136
+ console.log(`error: Unknown command ${cmds[0]}`);
137
+ }
138
+ }
139
+ helpMenu() {
140
+ const write = console.log;
141
+ write(`Tool management commands:`);
142
+ write(` ${chalk_1.default.yellow("/lt")} List tools: `);
143
+ write(` ${chalk_1.default.yellow("/ls")} List servers`);
144
+ write(` ${chalk_1.default.yellow("/as <server>")} Add server`);
145
+ write(` ${chalk_1.default.yellow("/rs <server>")} Remove server`);
146
+ write(` ${chalk_1.default.yellow("/e <server> <tool>")} Enable tool`);
147
+ write(` ${chalk_1.default.yellow("/d <server> <tool>")} Disable tool`);
148
+ write(` ${chalk_1.default.yellow("/ea <server>")} Enable all tools`);
149
+ write(` ${chalk_1.default.yellow("/da <server>")} Disable all tools`);
150
+ write(` ${chalk_1.default.yellow("/wc <file-name>")} Write conversation file`);
151
+ write(` ${chalk_1.default.yellow("/wa <file-name>")} Write agent profile file`);
152
+ write(` ${chalk_1.default.yellow("/q")} Quit`);
153
+ }
154
+ listTools(mcpServerManager) {
155
+ console.log("Mcp servers and tools (* - enabled):");
156
+ const serverNames = mcpServerManager.getMcpServerNames();
157
+ for (const serverName of serverNames) {
158
+ const server = mcpServerManager.getMcpServer(serverName);
159
+ console.log(` ${chalk_1.default.green(serverName)}`);
160
+ const tools = server.getTools();
161
+ const enabled = server.getEnabledTools();
162
+ for (const tool of tools) {
163
+ const isEnabled = enabled[tool.name] ? "*" : " ";
164
+ console.log(` [${isEnabled}] ${tool.name}`);
165
+ }
166
+ }
167
+ }
168
+ listServers(sudoMcpServerManager) {
169
+ console.log(`Available MCP Servers:`);
170
+ const serverBriefs = sudoMcpServerManager.getServerBriefs();
171
+ serverBriefs.sort((a, b) => a.name.localeCompare(b.name));
172
+ for (const brief of serverBriefs) {
173
+ console.log(`- ${chalk_1.default.green(brief.name)}: ${brief.description}`);
174
+ }
175
+ }
176
+ /**
177
+ * Adds server and enables all tools.
178
+ */
179
+ async addServer(sudoMcpServerManager, serverName) {
180
+ try {
181
+ await sudoMcpServerManager.addMcpServer(serverName, true);
182
+ }
183
+ catch (e) {
184
+ if (e instanceof sdk_1.InvalidConfiguration) {
185
+ throw `${e}. \n
186
+ Have you installed server ${serverName} using the SudoMCP CLI tool?`;
187
+ }
188
+ else {
189
+ throw e;
190
+ }
191
+ }
192
+ const msm = sudoMcpServerManager.getMcpServerManager();
193
+ console.log(`Added server: ${serverName} (enabled all tools).`);
194
+ console.log(`Current tool list:`);
195
+ this.listTools(msm);
196
+ }
197
+ writeConversation(agent, fileName) {
198
+ const conversation = agent.getConversation();
199
+ fs.writeFileSync(fileName, JSON.stringify(conversation), {
200
+ encoding: "utf8",
201
+ });
202
+ console.log(`Conversation written to ${fileName}`);
203
+ }
204
+ writeAgentProfile(agent, fileName) {
205
+ const profile = agent.getAgentProfile();
206
+ fs.writeFileSync(fileName, JSON.stringify(profile));
207
+ console.log(`AgentProfile written to ${fileName}`);
208
+ }
209
+ async promptToolCall(toolCall) {
210
+ (0, tools_1.displayToolCall)(toolCall);
211
+ const response = await this.prompt.run("Approve tool call? (Y/n) ");
212
+ return response === "y" || response === "yes" || response == "";
213
+ }
214
+ }
215
+ exports.CommandPrompt = CommandPrompt;
216
+ /**
217
+ * Support prompts of the form:
218
+ * - some text (msg: some text, cmds: undefined)
219
+ * - :i image.png some text (msg: some text, cmds: ["i", "image.png"])
220
+ * - :i image.png (msg: undefined, cmds: ["i", "image.png"])
221
+ * - :l (msg: undefined, cmds: ["l"])
222
+ * - :e toolName .. (msg: undefined, cmds: ["e", "toolName", ...])
223
+ * - :ea toolName .. (msg: undefined, cmds: ["ea", "toolName", ...])
224
+ */
225
+ function parsePrompt(prompt) {
226
+ prompt = prompt.trim();
227
+ let msg = undefined;
228
+ let cmds = undefined;
229
+ if (prompt.startsWith(":") || prompt.startsWith("/")) {
230
+ cmds = prompt.split(" ");
231
+ cmds[0] = cmds[0].slice(1);
232
+ if (cmds[0] == "i") {
233
+ // :i is special as it may have a trailing message
234
+ const fileDelim = prompt.indexOf(" ", 3);
235
+ if (fileDelim < 0) {
236
+ cmds = [cmds[0], prompt.slice(3)];
237
+ }
238
+ else {
239
+ msg = prompt.slice(fileDelim + 1);
240
+ cmds = [cmds[0], prompt.slice(3, fileDelim)];
241
+ }
242
+ }
243
+ }
244
+ else {
245
+ msg = prompt;
246
+ }
247
+ return { msg, cmds };
248
+ }
@@ -3,9 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Prompt = void 0;
7
- exports.parsePrompt = parsePrompt;
6
+ exports.ScriptPrompt = exports.Prompt = void 0;
7
+ const sdk_1 = require("@xalia/xmcp/sdk");
8
8
  const readline_1 = __importDefault(require("readline"));
9
+ const logger = (0, sdk_1.getLogger)();
9
10
  const DEFAULT_PROMPT = "USER: ";
10
11
  class Prompt {
11
12
  constructor() {
@@ -37,7 +38,7 @@ class Prompt {
37
38
  }
38
39
  });
39
40
  }
40
- shutdown() {
41
+ async shutdown() {
41
42
  this.prompt.close();
42
43
  }
43
44
  resolve() {
@@ -49,35 +50,30 @@ class Prompt {
49
50
  }
50
51
  exports.Prompt = Prompt;
51
52
  /**
52
- * Support prompts:
53
- * - some text (msg: some text, cmds: undefined)
54
- * - :i image.png some text (msg: some text, cmds: ["i", "image.png"])
55
- * - :i image.png (msg: undefined, cmds: ["i", "image.png"])
56
- * - :l (msg: undefined, cmds: ["l"])
57
- * - :e toolName .. (msg: undefined, cmds: ["e", "toolName", ...])
58
- * - :ea toolName .. (msg: undefined, cmds: ["ea", "toolName", ...])
53
+ * A prompt which just reads from a script
59
54
  */
60
- function parsePrompt(prompt) {
61
- prompt = prompt.trim();
62
- let msg = undefined;
63
- let cmds = undefined;
64
- if (prompt.startsWith(":") || prompt.startsWith("/")) {
65
- cmds = prompt.split(" ");
66
- cmds[0] = cmds[0].slice(1);
67
- if (cmds[0] == "i") {
68
- // :i is special as it may have a trailing message
69
- const fileDelim = prompt.indexOf(" ", 3);
70
- if (fileDelim < 0) {
71
- cmds = [cmds[0], prompt.slice(3)];
72
- }
73
- else {
74
- msg = prompt.slice(fileDelim + 1);
75
- cmds = [cmds[0], prompt.slice(3, fileDelim)];
76
- }
77
- }
55
+ class ScriptPrompt {
56
+ constructor(lines) {
57
+ this.lines = lines;
78
58
  }
79
- else {
80
- msg = prompt;
59
+ run(_prompt) {
60
+ return new Promise((r) => {
61
+ setTimeout(() => {
62
+ logger.debug(`[ScriptPrompt.run]: ${this.lines.length} remaining`);
63
+ if (this.lines.length === 0) {
64
+ logger.debug("[ScriptPrompt.run]: returning undefined");
65
+ r(undefined);
66
+ }
67
+ else {
68
+ const line = this.lines.shift();
69
+ logger.debug(`[ScriptPrompt.run]: returning: ${line}`);
70
+ r(line);
71
+ }
72
+ }, 1000);
73
+ });
74
+ }
75
+ shutdown() {
76
+ return new Promise((r) => setTimeout(r, 100));
81
77
  }
82
- return { msg, cmds };
83
78
  }
79
+ exports.ScriptPrompt = ScriptPrompt;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xalia/agent",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "keywords": [],
5
5
  "author": "",
6
6
  "license": "ISC",
package/scripts/test_chat CHANGED
@@ -73,7 +73,7 @@ pushd _test_chat
73
73
 
74
74
  # User 0 creates an agent profile
75
75
 
76
- echo '{"system_prompt":"You are a helpful agent talking to multiple users. Users put their name at the beginning of their messages, e.g. `chatuser1: <message>`.","mcp_settings":{"duckduckgo-search":[]}}' > profile0.json
76
+ echo '{"system_prompt":"You are a helpful agent in a multi-user chat session. If you have nothing to contribute and are not being addressed, just say `No comment`.","model":"repeat","mcp_settings":{"duckduckgo-search":[]}}' > profile0.json
77
77
  ${client} config --force --dev --api-key ${apikey0}
78
78
  ${client} agent-profile set profile0 --profile profile0.json
79
79
 
@@ -82,6 +82,22 @@ pushd _test_chat
82
82
  echo 'tell me a joke' > script0
83
83
  ${agent} chat client --session test_session --agent-profile profile0 --script script0
84
84
 
85
+ # Check that the duckduckgo-search server is available
86
+ # Have a participant remove it and check that got reflected in the db
87
+ # Have a participant re-add it and check that got reflected in the db
88
+
89
+ ${client} agent-profile get profile0 | grep duckduckgo
90
+ echo ":rs duckduckgo-search" > script_rm_duckduckgo
91
+ ${agent} chat client --session chatuser0/test_session --script script_rm_duckduckgo
92
+ sleep 1
93
+ (${client} agent-profile get profile0 | grep duckduckgo) && ( \
94
+ echo "ERROR: expected duckduckgo to be removed from agent profile" ; \
95
+ exit 1 \
96
+ )
97
+ echo ":as duckduckgo-search" > script_add_duckduckgo
98
+ ${agent} chat client --session chatuser0/test_session --script script_add_duckduckgo
99
+ ${client} agent-profile get profile0 | grep duckduckgo
100
+
85
101
  # Run both clients with a script that continues the conversation
86
102
 
87
103
  echo -e "why is that funny?\ntell me another.\nwhat is my name?\nhow old are you?\nwhere are your parents?" > script1
@@ -29,10 +29,24 @@ export type OnToolCallCB = {
29
29
  (msg: ChatCompletionMessageToolCall): Promise<boolean>;
30
30
  };
31
31
 
32
+ export interface IConversation {
33
+ userMessage(msg?: string, imageB64?: string): void;
34
+ getConversation(): ChatCompletionMessageParam[];
35
+
36
+ // TODO: remove?
37
+ resetConversation(): void;
38
+
39
+ getAgentProfile(): AgentProfile;
40
+ getSystemPrompt(): string;
41
+ setSystemPrompt(systemPrompt: string): void;
42
+ getModel(): string;
43
+ setModel(model: string): void;
44
+ }
45
+
32
46
  dotenv.config();
33
47
  const logger = getLogger();
34
48
 
35
- export class Agent {
49
+ export class Agent implements IConversation {
36
50
  private toolHandlers: { [toolName: string]: ToolHandler } = {};
37
51
 
38
52
  private constructor(
@@ -48,7 +62,8 @@ export class Agent {
48
62
  onMessage: OnMessageCB,
49
63
  onToolCall: OnToolCallCB,
50
64
  systemPrompt: string | undefined,
51
- llm: ILLM
65
+ llm: ILLM,
66
+ mcpServerManager?: McpServerManager
52
67
  ): Promise<Agent> {
53
68
  // Initialize messages with system prompt
54
69
  const messages = [
@@ -58,14 +73,11 @@ export class Agent {
58
73
  } as OpenAI.ChatCompletionMessageParam,
59
74
  ];
60
75
 
61
- // Create the server manager
62
- const mcpServerManager = new McpServerManager();
63
-
64
76
  return new Agent(
65
77
  onMessage,
66
78
  onToolCall,
67
79
  messages,
68
- mcpServerManager,
80
+ mcpServerManager ?? new McpServerManager(),
69
81
  [],
70
82
  llm
71
83
  );
@@ -78,7 +90,7 @@ export class Agent {
78
90
  public getAgentProfile(): AgentProfile {
79
91
  return new AgentProfile(
80
92
  this.llm.getModel(),
81
- this.getSystemMessage(),
93
+ this.getSystemPrompt(),
82
94
  this.mcpServerManager.getMcpServerSettings()
83
95
  );
84
96
  }
@@ -108,7 +120,10 @@ export class Agent {
108
120
  return this.mcpServerManager;
109
121
  }
110
122
 
111
- public async userMessage(
123
+ /**
124
+ * Like `userMessage`, but can be awaited, and accepts the user name.
125
+ */
126
+ public async userMessageEx(
112
127
  msg?: string,
113
128
  imageB64?: string,
114
129
  name?: string
@@ -159,7 +174,15 @@ export class Agent {
159
174
  return completion.choices[0].message;
160
175
  }
161
176
 
162
- public chooseModel(model: string) {
177
+ public userMessage(msg?: string, imageB64?: string): void {
178
+ this.userMessageEx(msg, imageB64);
179
+ }
180
+
181
+ public getModel(): string {
182
+ return this.llm.getModel();
183
+ }
184
+
185
+ public setModel(model: string) {
163
186
  logger.debug(`Set model ${model}`);
164
187
  this.llm.setModel(model);
165
188
  }
@@ -173,7 +196,7 @@ export class Agent {
173
196
  this.messages.splice(1);
174
197
  }
175
198
 
176
- public getSystemMessage(): string {
199
+ public getSystemPrompt(): string {
177
200
  assert(this.messages[0].role === "system");
178
201
  return this.messages[0].content as string;
179
202
  }
@@ -181,7 +204,7 @@ export class Agent {
181
204
  /**
182
205
  * Set the system prompt
183
206
  */
184
- public setSystemMessage(systemMsg: string) {
207
+ public setSystemPrompt(systemMsg: string) {
185
208
  assert(this.messages[0].role === "system");
186
209
  this.messages[0].content = systemMsg;
187
210
  }
@@ -9,12 +9,19 @@ import { OpenAILLMStreaming } from "./openAILLMStreaming";
9
9
  import { DummyLLM } from "./dummyLLM";
10
10
  import { ILLM } from "./llm";
11
11
  import { strict as assert } from "assert";
12
+ import { RepeatLLM } from "./repeatLLM";
13
+ import { McpServerManager } from "./mcpServerManager";
12
14
 
13
15
  const logger = getLogger();
14
16
 
15
17
  export const DEFAULT_LLM_URL = "http://localhost:5001/v1";
16
18
  export const DEFAULT_LLM_MODEL = "gpt-4o";
17
19
 
20
+ export const XALIA_APP_HEADER = {
21
+ "HTTP-Referer": "xalia.ai",
22
+ "X-Title": "Xalia",
23
+ };
24
+
18
25
  /**
19
26
  * Util function to create an Agent from some config information.
20
27
  */
@@ -26,7 +33,8 @@ async function createAgent(
26
33
  onToolCall: OnToolCallCB,
27
34
  platform: IPlatform,
28
35
  openaiApiKey: string | undefined,
29
- stream: boolean = false
36
+ stream: boolean = false,
37
+ mcpServerManager?: McpServerManager
30
38
  ): Promise<Agent> {
31
39
  let llm: ILLM | undefined;
32
40
 
@@ -41,6 +49,8 @@ async function createAgent(
41
49
  const responses: OpenAI.ChatCompletion.Choice[] = JSON.parse(script);
42
50
  logger.debug(`Initializing Dummy Agent: ${llmUrl}`);
43
51
  llm = new DummyLLM(responses);
52
+ } else if (model === "repeat") {
53
+ llm = new RepeatLLM();
44
54
  } else {
45
55
  // Regular Agent
46
56
  if (!openaiApiKey) {
@@ -56,7 +66,13 @@ async function createAgent(
56
66
  }
57
67
 
58
68
  assert(llm);
59
- return Agent.initializeWithLLM(onMessage, onToolCall, systemPrompt, llm);
69
+ return Agent.initializeWithLLM(
70
+ onMessage,
71
+ onToolCall,
72
+ systemPrompt,
73
+ llm,
74
+ mcpServerManager
75
+ );
60
76
  }
61
77
 
62
78
  /**
@@ -109,6 +125,39 @@ export async function createAgentWithSkills(
109
125
  return [agent, sudoMcpServerManager];
110
126
  }
111
127
 
128
+ export async function createAgentFromSkillManager(
129
+ llmUrl: string,
130
+ agentProfile: AgentProfile,
131
+ onMessage: OnMessageCB,
132
+ onToolCall: OnToolCallCB,
133
+ platform: IPlatform,
134
+ llmApiKey: string | undefined,
135
+ skillManager: SkillManager,
136
+ conversation: OpenAI.ChatCompletionMessageParam[] | undefined,
137
+ stream: boolean = false
138
+ ): Promise<Agent> {
139
+ // Create agent
140
+ logger.debug("[createAgentAndSudoMcpServerManager] creating agent ...");
141
+ const mcpServerManager = skillManager.getMcpServerManager();
142
+ const agent = await createAgent(
143
+ llmUrl,
144
+ agentProfile.model,
145
+ agentProfile.system_prompt,
146
+ onMessage,
147
+ onToolCall,
148
+ platform,
149
+ llmApiKey,
150
+ stream,
151
+ mcpServerManager
152
+ );
153
+ if (conversation) {
154
+ agent.setConversation(conversation);
155
+ }
156
+
157
+ logger.debug("[createAgentWithSkills] done");
158
+ return agent;
159
+ }
160
+
112
161
  /**
113
162
  * An "non-interactive" agent is one which is not intended to be used
114
163
  * interactively (settings cannot be dyanmically adjusted, intermediate
@@ -182,7 +231,7 @@ export async function runOneShot(
182
231
  approveToolsUpTo
183
232
  );
184
233
 
185
- const response = await agent.userMessage(prompt, image);
234
+ const response = await agent.userMessageEx(prompt, image);
186
235
  await agent.shutdown();
187
236
  logger.debug("[runOneShot]: shutdown done");
188
237