@xalia/agent 0.5.3 → 0.5.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 (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
@@ -41,10 +41,21 @@ class Database {
41
41
  return undefined;
42
42
  }
43
43
  return {
44
- user_uuid: data.user_uuid,
44
+ uuid: data.user_uuid,
45
45
  nickname: data.users.nickname || `user ${data.user_uuid}`,
46
46
  };
47
47
  }
48
+ async getUserFromUuid(user_uuid) {
49
+ const { data, error } = await this.client
50
+ .from("users")
51
+ .select("*")
52
+ .eq("uuid", user_uuid)
53
+ .maybeSingle();
54
+ if (error) {
55
+ throw error;
56
+ }
57
+ return data;
58
+ }
48
59
  async createUser(user_uuid, email, nickname, timezone) {
49
60
  const payload = {
50
61
  uuid: user_uuid,
@@ -133,6 +144,18 @@ class Database {
133
144
  }
134
145
  return data[0].uuid;
135
146
  }
147
+ async updateAgentProfile(uuid, profile) {
148
+ const payload = {
149
+ profile: profile,
150
+ };
151
+ const { error } = await this.client
152
+ .from("agent_profiles")
153
+ .update(payload)
154
+ .eq("uuid", uuid);
155
+ if (error) {
156
+ throw error;
157
+ }
158
+ }
136
159
  async clearAgentProfiles() {
137
160
  await this.client.from("agent_profiles").delete().neq("uuid", "");
138
161
  }
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FrontendChatClient = void 0;
4
+ class FrontendChatClient {
5
+ constructor(ws, onMessageCB, onConnectionClosedCB) {
6
+ this.ws = ws;
7
+ this.onMessageCB = onMessageCB;
8
+ this.onConnectionClosedCB = onConnectionClosedCB;
9
+ this.closed = false;
10
+ }
11
+ static async initWithParams(host, port, token, params, onMessageCB, onConnectionClosedCB) {
12
+ return new Promise((resolve, reject) => {
13
+ const urlParams = new URLSearchParams(params);
14
+ const url = `ws://${host}:${port}?${urlParams}`;
15
+ const ws = new WebSocket(url, [token]);
16
+ console.log("created ws");
17
+ const client = new FrontendChatClient(ws, onMessageCB, onConnectionClosedCB);
18
+ ws.onopen = async () => {
19
+ console.log("opened");
20
+ ws.onmessage = (ev) => {
21
+ try {
22
+ const msgData = ev.data;
23
+ if (typeof msgData !== "string") {
24
+ throw `expected "string" data, got ${typeof msgData}`;
25
+ }
26
+ console.debug(`[client.onmessage]: ${msgData}`);
27
+ const msg = JSON.parse(msgData);
28
+ client.onMessageCB(msg);
29
+ }
30
+ catch (e) {
31
+ client.close();
32
+ throw e;
33
+ }
34
+ };
35
+ resolve(client);
36
+ };
37
+ ws.onclose = (event) => {
38
+ console.log("closed");
39
+ console.log(`[client] WebSocket connection closed: ${JSON.stringify(event)}`);
40
+ client.closed = true;
41
+ onConnectionClosedCB();
42
+ };
43
+ ws.onerror = (error) => {
44
+ console.error("[client] WebSocket error:", error);
45
+ reject(error);
46
+ client.closed = true;
47
+ onConnectionClosedCB();
48
+ };
49
+ });
50
+ }
51
+ static async init(host, port, token, onMessageCB, onConnectionClosedCB, sessionId = "untitled", agentProfileId = undefined) {
52
+ const params = { session_id: sessionId };
53
+ if (agentProfileId) {
54
+ params["agent_profile_id"] = agentProfileId;
55
+ }
56
+ return FrontendChatClient.initWithParams(host, port, token, params, onMessageCB, onConnectionClosedCB);
57
+ }
58
+ sendMessage(message) {
59
+ if (this.closed) {
60
+ throw new Error("Cannot send message on closed connection");
61
+ }
62
+ const data = JSON.stringify(message);
63
+ this.ws.send(data);
64
+ }
65
+ close() {
66
+ this.closed = true;
67
+ this.onConnectionClosedCB();
68
+ this.ws.close();
69
+ }
70
+ isClosed() {
71
+ return this.closed;
72
+ }
73
+ }
74
+ exports.FrontendChatClient = FrontendChatClient;
@@ -54,7 +54,7 @@ async function resolveAgentProfileId(db, userData, agentProfileIdentifier) {
54
54
  if (ap) {
55
55
  return agentProfileIdentifier;
56
56
  }
57
- ap = await db.getSavedAgentProfileByName(userData.user_uuid, agentProfileIdentifier);
57
+ ap = await db.getSavedAgentProfileByName(userData.uuid, agentProfileIdentifier);
58
58
  logger.debug(`[resolveAgentProfileId]: by name: {JSON.stringify(ap)}`);
59
59
  if (ap) {
60
60
  return ap.uuid;
@@ -69,7 +69,7 @@ async function resolveSessionIdFromIdentifier(db, userData, sessionIdentifier) {
69
69
  // Interpret as an id, or as a number under the current user.
70
70
  session = await db.getSessionById(compound);
71
71
  if (!session) {
72
- session = await db.getSessionByName(userData.user_uuid, compound);
72
+ session = await db.getSessionByName(userData.uuid, compound);
73
73
  }
74
74
  }
75
75
  else {
@@ -110,7 +110,7 @@ async function findOrCreateSession(db, userData, query) {
110
110
  if (!agentProfileId) {
111
111
  throw `no agent profile: ${agentProfileIdParam}`;
112
112
  }
113
- return db.createSession(userData.user_uuid, query.session_id, agentProfileId);
113
+ return db.createSession(userData.uuid, query.session_id, agentProfileId);
114
114
  }
115
115
  async function runServer(port, supabaseUrl, supabaseKey, llmUrl, xmcpUrl) {
116
116
  return new Promise((r, _e) => {
@@ -13,6 +13,11 @@ const AGENT_PROFILE = {
13
13
  system_prompt: "You are an unhelpful agent",
14
14
  mcp_settings: {},
15
15
  };
16
+ const AGENT_PROFILE_2 = {
17
+ model: undefined,
18
+ system_prompt: "You are a very helpful agent.",
19
+ mcp_settings: { simplecalc: [] },
20
+ };
16
21
  async function createDummyUser(db) {
17
22
  const apiClient = new sdk_1.ApiClient(sudoMcpServerManager_1.LOCAL_SERVER_URL, "dummy_key");
18
23
  const user = await apiClient.getUserBrief("dummy_user");
@@ -25,12 +30,12 @@ async function createDummyUser(db) {
25
30
  }
26
31
  }
27
32
  describe("DB", () => {
28
- it("should get existing user", async function () {
33
+ it("should get data for existing user", async function () {
29
34
  const db = getLocalDB();
30
35
  await createDummyUser(db);
31
36
  const dummyUser = await db.getUserDataFromApiKey("dummy_key");
32
37
  (0, chai_1.expect)(dummyUser).eql({
33
- user_uuid: "dummy_user",
38
+ uuid: "dummy_user",
34
39
  nickname: "Dummy User",
35
40
  });
36
41
  });
@@ -39,6 +44,14 @@ describe("DB", () => {
39
44
  const dummyUser = await db.getUserDataFromApiKey("no_such_key");
40
45
  (0, chai_1.expect)(dummyUser).to.equal(undefined);
41
46
  });
47
+ it("should get user by uuid", async function () {
48
+ const db = getLocalDB();
49
+ await createDummyUser(db);
50
+ const dummyUser = await db.getUserFromUuid("dummy_user");
51
+ (0, assert_1.strict)(dummyUser);
52
+ (0, chai_1.expect)(dummyUser.uuid).eql("dummy_user");
53
+ (0, chai_1.expect)(dummyUser.nickname).eql("Dummy User");
54
+ });
42
55
  it("should create and retrieve agent profiles", async function () {
43
56
  const db = getLocalDB();
44
57
  await db.clearAgentProfiles();
@@ -55,6 +68,16 @@ describe("DB", () => {
55
68
  (0, chai_1.expect)(savedAgentProfileByName).eql(savedAgentProfile);
56
69
  (0, chai_1.expect)(agentProfile).eql(AGENT_PROFILE);
57
70
  });
71
+ it("should update agent profiles", async function () {
72
+ const db = getLocalDB();
73
+ await db.clearAgentProfiles();
74
+ const agentProfileId = await db.createAgentProfile("dummy_user", "test_profile", AGENT_PROFILE);
75
+ (0, chai_1.expect)(agentProfileId).to.not.equal(undefined);
76
+ (0, assert_1.strict)(agentProfileId);
77
+ await db.updateAgentProfile(agentProfileId, AGENT_PROFILE_2);
78
+ const agentProfileFromDB = (await db.getAgentProfileById(agentProfileId));
79
+ (0, chai_1.expect)(agentProfileFromDB).eql(AGENT_PROFILE_2);
80
+ });
58
81
  it("should create and retrieve sessions", async function () {
59
82
  const db = getLocalDB();
60
83
  await db.clearAgentProfiles();
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const chai_1 = require("chai");
4
+ const openAILLMStreaming_1 = require("../agent/openAILLMStreaming");
5
+ const TEST_STANDARD = [
6
+ {
7
+ id: "chatcmpl-BzBqzVs5w0kU5we3KIUN1Q4FAZXjg",
8
+ choices: [
9
+ {
10
+ delta: { content: "!" },
11
+ finish_reason: null,
12
+ index: 0,
13
+ logprobs: null,
14
+ },
15
+ ],
16
+ created: 1753923273,
17
+ model: "gpt-4o-2024-08-06",
18
+ object: "chat.completion.chunk",
19
+ service_tier: "default",
20
+ system_fingerprint: "fp_07871e2ad8",
21
+ },
22
+ {
23
+ id: "chatcmpl-BzBqzVs5w0kU5we3KIUN1Q4FAZXjg",
24
+ choices: [{ delta: {}, finish_reason: "stop", index: 0, logprobs: null }],
25
+ created: 1753923273,
26
+ model: "gpt-4o-2024-08-06",
27
+ object: "chat.completion.chunk",
28
+ service_tier: "default",
29
+ system_fingerprint: "fp_07871e2ad8",
30
+ },
31
+ ];
32
+ const TEST_TRAILING_USAGE = [
33
+ {
34
+ id: "gen-1753923406-nsIKHyFRoJqkUntBnQTw",
35
+ choices: [
36
+ {
37
+ delta: { content: "test", role: "assistant" },
38
+ finish_reason: "stop",
39
+ index: 0,
40
+ logprobs: null,
41
+ },
42
+ ],
43
+ created: 1753923406,
44
+ model: "openai/gpt-4o",
45
+ object: "chat.completion.chunk",
46
+ system_fingerprint: "fp_a288987b44",
47
+ },
48
+ {
49
+ id: "gen-1753923406-nsIKHyFRoJqkUntBnQTw",
50
+ choices: [
51
+ {
52
+ delta: { content: "", role: "assistant" },
53
+ finish_reason: null,
54
+ index: 0,
55
+ logprobs: null,
56
+ },
57
+ ],
58
+ created: 1753923406,
59
+ model: "openai/gpt-4o",
60
+ object: "chat.completion.chunk",
61
+ usage: {
62
+ completion_tokens: 50,
63
+ prompt_tokens: 271,
64
+ total_tokens: 321,
65
+ completion_tokens_details: { reasoning_tokens: 0 },
66
+ prompt_tokens_details: { cached_tokens: 0 },
67
+ },
68
+ },
69
+ ];
70
+ describe("OpenAI Streaming", () => {
71
+ it("should support standard termination", async function () {
72
+ const chunks = TEST_STANDARD;
73
+ (0, chai_1.expect)(chunks.length).eql(2);
74
+ const { initMessage } = (0, openAILLMStreaming_1.initializeCompletion)(chunks[0]);
75
+ (0, openAILLMStreaming_1.updateCompletion)(initMessage, chunks[1]);
76
+ (0, chai_1.expect)(initMessage).eql({
77
+ id: "chatcmpl-BzBqzVs5w0kU5we3KIUN1Q4FAZXjg",
78
+ choices: [
79
+ {
80
+ message: {
81
+ content: "!",
82
+ role: "assistant",
83
+ refusal: null,
84
+ tool_calls: undefined,
85
+ },
86
+ finish_reason: "stop",
87
+ index: 0,
88
+ logprobs: null,
89
+ },
90
+ ],
91
+ created: 1753923273,
92
+ model: "gpt-4o-2024-08-06",
93
+ object: "chat.completion",
94
+ service_tier: "default",
95
+ system_fingerprint: "fp_07871e2ad8",
96
+ usage: undefined,
97
+ });
98
+ });
99
+ it("should support trailing usage", async function () {
100
+ const chunks = TEST_TRAILING_USAGE;
101
+ (0, chai_1.expect)(chunks.length).eql(2);
102
+ const { initMessage } = (0, openAILLMStreaming_1.initializeCompletion)(chunks[0]);
103
+ (0, openAILLMStreaming_1.updateCompletion)(initMessage, chunks[1]);
104
+ (0, chai_1.expect)(initMessage).eql({
105
+ id: "gen-1753923406-nsIKHyFRoJqkUntBnQTw",
106
+ choices: [
107
+ {
108
+ message: {
109
+ content: "test",
110
+ role: "assistant",
111
+ refusal: null,
112
+ tool_calls: undefined,
113
+ },
114
+ finish_reason: "stop",
115
+ index: 0,
116
+ logprobs: null,
117
+ },
118
+ ],
119
+ created: 1753923406,
120
+ model: "openai/gpt-4o",
121
+ object: "chat.completion",
122
+ service_tier: undefined,
123
+ system_fingerprint: "fp_a288987b44",
124
+ usage: {
125
+ completion_tokens: 50,
126
+ prompt_tokens: 271,
127
+ total_tokens: 321,
128
+ completion_tokens_details: { reasoning_tokens: 0 },
129
+ prompt_tokens_details: { cached_tokens: 0 },
130
+ },
131
+ });
132
+ });
133
+ });
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const chai_1 = require("chai");
4
- const prompt_1 = require("../tool/prompt");
4
+ const commandPrompt_1 = require("../tool/commandPrompt");
5
5
  describe("Prompt", () => {
6
6
  it("should recognise commands", async function () {
7
7
  const expectedResults = {
@@ -19,7 +19,7 @@ describe("Prompt", () => {
19
19
  },
20
20
  };
21
21
  for (const [prompt, expected] of Object.entries(expectedResults)) {
22
- const res = (0, prompt_1.parsePrompt)(prompt);
22
+ const res = (0, commandPrompt_1.parsePrompt)(prompt);
23
23
  (0, chai_1.expect)(res).eql(expected);
24
24
  }
25
25
  });
@@ -38,7 +38,7 @@ describe("SudoMcpServerManager", async () => {
38
38
  }).timeout(10000);
39
39
  it("should add a new MCP server", async () => {
40
40
  const [, sm] = await getServerManagers();
41
- await sm.addMcpServer("sudomcp_simplecalc");
41
+ await sm.addMcpServer("sudomcp_simplecalc", true);
42
42
  const serverBriefs = sm.getServerBriefs();
43
43
  (0, chai_1.expect)(serverBriefs.some((brief) => brief.name === "sudomcp_simplecalc")).eql(true);
44
44
  }).timeout(20000);
@@ -1,52 +1,18 @@
1
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
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
38
5
  Object.defineProperty(exports, "__esModule", { value: true });
39
6
  exports.runChat = runChat;
40
7
  const yocto_spinner_1 = __importDefault(require("yocto-spinner"));
41
- const fs = __importStar(require("fs"));
42
8
  const chalk_1 = __importDefault(require("chalk"));
43
9
  const tool_1 = require("@xalia/xmcp/tool");
44
10
  const sdk_1 = require("@xalia/xmcp/sdk");
45
- const tools_1 = require("../agent/tools");
46
11
  const agentUtils_1 = require("../agent/agentUtils");
47
12
  const files_1 = require("./files");
48
- const prompt_1 = require("./prompt");
49
13
  const nodePlatform_1 = require("./nodePlatform");
14
+ const commandPrompt_1 = require("./commandPrompt");
15
+ const prompt_1 = require("./prompt");
50
16
  const logger = (0, sdk_1.getLogger)();
51
17
  async function write(msg) {
52
18
  return new Promise((resolve, err) => {
@@ -81,6 +47,8 @@ async function runChat(llmUrl, agentProfile, conversation, prompt, image, llmApi
81
47
  first = true;
82
48
  }
83
49
  };
50
+ const repl = new prompt_1.Prompt();
51
+ const cmdPrompt = new commandPrompt_1.CommandPrompt(repl);
84
52
  let remainingApprovedToolCalls = approveToolsUpTo;
85
53
  const onToolCall = async (toolCall) => {
86
54
  if (remainingApprovedToolCalls !== 0) {
@@ -88,7 +56,7 @@ async function runChat(llmUrl, agentProfile, conversation, prompt, image, llmApi
88
56
  return true;
89
57
  }
90
58
  spinner.stop().clear();
91
- const result = await promptToolCall(repl, toolCall);
59
+ const result = await cmdPrompt.promptToolCall(toolCall);
92
60
  spinner.start();
93
61
  return result;
94
62
  };
@@ -104,11 +72,10 @@ async function runChat(llmUrl, agentProfile, conversation, prompt, image, llmApi
104
72
  if (prompt) {
105
73
  console.log(`USER: ${prompt}`);
106
74
  }
107
- const repl = new prompt_1.Prompt();
108
75
  // Conversation loop
109
76
  while (true) {
110
77
  if (!prompt) {
111
- const [msg, img] = await getNextPrompt(repl, agent, sudoMcpServerManager);
78
+ const [msg, img] = await cmdPrompt.getNextPrompt(agent, sudoMcpServerManager);
112
79
  if (!msg && !img) {
113
80
  break;
114
81
  }
@@ -129,7 +96,7 @@ async function runChat(llmUrl, agentProfile, conversation, prompt, image, llmApi
129
96
  image = undefined;
130
97
  }
131
98
  // Shutdown the agent
132
- repl.shutdown();
99
+ cmdPrompt.shutdown();
133
100
  await agent.shutdown();
134
101
  logger.debug("shutdown done");
135
102
  }
@@ -137,160 +104,3 @@ async function runChat(llmUrl, agentProfile, conversation, prompt, image, llmApi
137
104
  * Read lines from the prompt, parsing any commands, and return once there is
138
105
  * a prompt and/or image for the llm. Both undefined means exit.
139
106
  */
140
- async function getNextPrompt(repl, agent, sudoMcpServerManager) {
141
- while (true) {
142
- // Get a line, detecting the EOF signal.
143
- const line = await repl.run();
144
- if (typeof line === "undefined") {
145
- console.log("closing ...");
146
- return [undefined, undefined];
147
- }
148
- if (line.length === 0) {
149
- continue;
150
- }
151
- // Extract prompt or commands
152
- const { msg, cmds } = (0, prompt_1.parsePrompt)(line);
153
- // If there are no commands, this must be a prompt only
154
- if (!cmds) {
155
- return [msg, undefined];
156
- }
157
- // There are commands. If it's image, return [prompt, image]. If it's
158
- // quit, return [undefined, undefined], otherwise it must be a command.
159
- // Execute it and prompt again.
160
- switch (cmds[0]) {
161
- case "i": // image
162
- return [msg, cmds[1]];
163
- case "q":
164
- case "quit":
165
- case "exit":
166
- return [undefined, undefined];
167
- default:
168
- break;
169
- }
170
- try {
171
- await runCommand(agent, agent.getMcpServerManager(), sudoMcpServerManager, cmds);
172
- }
173
- catch (e) {
174
- console.log(`ERROR: ${e}`);
175
- }
176
- }
177
- }
178
- async function runCommand(agent, mcpServerManager, sudoMcpServerManager, cmds) {
179
- switch (cmds[0]) {
180
- case "lt":
181
- listTools(mcpServerManager);
182
- break;
183
- case "ls":
184
- listServers(sudoMcpServerManager);
185
- break;
186
- case "as":
187
- await addServer(sudoMcpServerManager, cmds[1]);
188
- break;
189
- case "rs":
190
- await mcpServerManager.removeMcpServer(cmds[1]);
191
- break;
192
- case "e":
193
- mcpServerManager.enableTool(cmds[1], cmds[2]);
194
- console.log(`Enabled tool ${cmds[2]} for server ${cmds[1]}`);
195
- break;
196
- case "d":
197
- mcpServerManager.disableTool(cmds[1], cmds[2]);
198
- console.log(`Disabled tool ${cmds[2]} for server ${cmds[1]}`);
199
- break;
200
- case "ea":
201
- mcpServerManager.enableAllTools(cmds[1]);
202
- console.log(`Enabled all tools for server ${cmds[1]}`);
203
- break;
204
- case "da":
205
- mcpServerManager.disableAllTools(cmds[1]);
206
- console.log(`Disabled all tools for server ${cmds[1]}`);
207
- break;
208
- case "wc":
209
- writeConversation(agent, cmds[1]);
210
- break;
211
- case "wa":
212
- writeAgentProfile(agent, cmds[1]);
213
- break;
214
- case "h":
215
- case "help":
216
- case "?":
217
- helpMenu();
218
- break;
219
- default:
220
- console.log(`error: Unknown command ${cmds[0]}`);
221
- }
222
- }
223
- function helpMenu() {
224
- console.log(`Tool management commands:`);
225
- console.log(` ${chalk_1.default.yellow("/lt")} List tools: `);
226
- console.log(` ${chalk_1.default.yellow("/ls")} List servers`);
227
- console.log(` ${chalk_1.default.yellow("/as <server-name>")} Add server`);
228
- console.log(` ${chalk_1.default.yellow("/rs <server-name>")} Remove server`);
229
- console.log(` ${chalk_1.default.yellow("/e <server-name> <tool-name>")} Enable tool`);
230
- console.log(` ${chalk_1.default.yellow("/d <server-name> <tool-name>")} Disable tool`);
231
- console.log(` ${chalk_1.default.yellow("/ea <server-name>")} Enable all tools`);
232
- console.log(` ${chalk_1.default.yellow("/da <server-name>")} Disable all tools`);
233
- console.log(` ${chalk_1.default.yellow("/wc <file-name>")} Write conversation file`);
234
- console.log(` ${chalk_1.default.yellow("/wa <file-name>")} Write agent profile file`);
235
- console.log(` ${chalk_1.default.yellow("/q")} Quit`);
236
- }
237
- function listTools(mcpServerManager) {
238
- console.log("Mcp servers and tools (* - enabled):");
239
- const serverNames = mcpServerManager.getMcpServerNames();
240
- for (const serverName of serverNames) {
241
- const server = mcpServerManager.getMcpServer(serverName);
242
- console.log(` ${chalk_1.default.green(serverName)}`);
243
- const tools = server.getTools();
244
- const enabled = server.getEnabledTools();
245
- for (const tool of tools) {
246
- const isEnabled = enabled[tool.name] ? "*" : " ";
247
- console.log(` [${isEnabled}] ${tool.name}`);
248
- }
249
- }
250
- }
251
- function listServers(sudoMcpServerManager) {
252
- console.log(`Available MCP Servers:`);
253
- const serverBriefs = sudoMcpServerManager.getServerBriefs();
254
- serverBriefs.sort((a, b) => a.name.localeCompare(b.name));
255
- for (const brief of serverBriefs) {
256
- console.log(`- ${chalk_1.default.green(brief.name)}: ${brief.description}`);
257
- }
258
- }
259
- /**
260
- * Adds server and enables all tools.
261
- */
262
- async function addServer(sudoMcpServerManager, serverName) {
263
- try {
264
- await sudoMcpServerManager.addMcpServer(serverName);
265
- }
266
- catch (e) {
267
- if (e instanceof sdk_1.InvalidConfiguration) {
268
- throw `${e}. \n
269
- Have you installed server ${serverName} using the SudoMCP CLI tool?`;
270
- }
271
- else {
272
- throw e;
273
- }
274
- }
275
- sudoMcpServerManager.getMcpServerManager().enableAllTools(serverName);
276
- console.log(`Added server: ${serverName} and enabled all tools.`);
277
- console.log(`Current tool list:`);
278
- listTools(sudoMcpServerManager.getMcpServerManager());
279
- }
280
- function writeConversation(agent, fileName) {
281
- const conversation = agent.getConversation();
282
- fs.writeFileSync(fileName, JSON.stringify(conversation), {
283
- encoding: "utf8",
284
- });
285
- console.log(`Conversation written to ${fileName}`);
286
- }
287
- function writeAgentProfile(agent, fileName) {
288
- const profile = agent.getAgentProfile();
289
- fs.writeFileSync(fileName, JSON.stringify(profile));
290
- console.log(`AgentProfile written to ${fileName}`);
291
- }
292
- async function promptToolCall(repl, toolCall) {
293
- (0, tools_1.displayToolCall)(toolCall);
294
- const response = await repl.run("Approve tool call? (Y/n) ");
295
- return response === "y" || response === "yes" || response == "";
296
- }