@xalia/agent 0.5.4 → 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.
- package/dist/agent/src/agent/agent.js +16 -9
- package/dist/agent/src/agent/agentUtils.js +24 -4
- package/dist/agent/src/agent/mcpServerManager.js +19 -9
- package/dist/agent/src/agent/openAILLM.js +3 -1
- package/dist/agent/src/agent/openAILLMStreaming.js +24 -25
- package/dist/agent/src/agent/repeatLLM.js +43 -0
- package/dist/agent/src/agent/sudoMcpServerManager.js +12 -6
- package/dist/agent/src/chat/client.js +259 -36
- package/dist/agent/src/chat/conversationManager.js +243 -24
- package/dist/agent/src/chat/db.js +24 -1
- package/dist/agent/src/chat/frontendClient.js +74 -0
- package/dist/agent/src/chat/server.js +3 -3
- package/dist/agent/src/test/db.test.js +25 -2
- package/dist/agent/src/test/openaiStreaming.test.js +133 -0
- package/dist/agent/src/test/prompt.test.js +2 -2
- package/dist/agent/src/test/sudoMcpServerManager.test.js +1 -1
- package/dist/agent/src/tool/agentChat.js +7 -197
- package/dist/agent/src/tool/chatMain.js +18 -23
- package/dist/agent/src/tool/commandPrompt.js +248 -0
- package/dist/agent/src/tool/prompt.js +27 -31
- package/package.json +1 -1
- package/scripts/test_chat +17 -1
- package/src/agent/agent.ts +34 -11
- package/src/agent/agentUtils.ts +52 -3
- package/src/agent/mcpServerManager.ts +43 -13
- package/src/agent/openAILLM.ts +3 -1
- package/src/agent/openAILLMStreaming.ts +28 -27
- package/src/agent/repeatLLM.ts +51 -0
- package/src/agent/sudoMcpServerManager.ts +41 -12
- package/src/chat/client.ts +353 -40
- package/src/chat/conversationManager.ts +345 -33
- package/src/chat/db.ts +28 -2
- package/src/chat/frontendClient.ts +123 -0
- package/src/chat/messages.ts +146 -2
- package/src/chat/server.ts +3 -3
- package/src/test/db.test.ts +35 -2
- package/src/test/openaiStreaming.test.ts +142 -0
- package/src/test/prompt.test.ts +1 -1
- package/src/test/sudoMcpServerManager.test.ts +1 -1
- package/src/tool/agentChat.ts +13 -211
- package/src/tool/chatMain.ts +28 -43
- package/src/tool/commandPrompt.ts +252 -0
- package/src/tool/prompt.ts +33 -32
package/src/chat/messages.ts
CHANGED
@@ -4,6 +4,8 @@ import {
|
|
4
4
|
ChatCompletionToolMessageParam,
|
5
5
|
ChatCompletionMessageToolCall,
|
6
6
|
} from "openai/resources.mjs";
|
7
|
+
import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
8
|
+
import { McpServerBrief } from "@xalia/xmcp/sdk";
|
7
9
|
|
8
10
|
/**
|
9
11
|
* Message from a user to the server
|
@@ -13,6 +15,84 @@ export type ClientUserMessage = {
|
|
13
15
|
message: string;
|
14
16
|
};
|
15
17
|
|
18
|
+
/**
|
19
|
+
* Message to add an MCP server to the agent
|
20
|
+
*/
|
21
|
+
export type ClientAddMcpServer = {
|
22
|
+
type: "add_mcp_server";
|
23
|
+
server_name: string;
|
24
|
+
enable_all: boolean;
|
25
|
+
};
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Message to remove an MCP server from the agent
|
29
|
+
*/
|
30
|
+
export type ClientRemoveMcpServer = {
|
31
|
+
type: "remove_mcp_server";
|
32
|
+
server_name: string;
|
33
|
+
};
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Enable a specific tool
|
37
|
+
*/
|
38
|
+
export type ClientEnableMcpServerTool = {
|
39
|
+
type: "enable_mcp_server_tool";
|
40
|
+
server_name: string;
|
41
|
+
tool: string;
|
42
|
+
};
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Disable a specific tool
|
46
|
+
*/
|
47
|
+
export type ClientDisableMcpServerTool = {
|
48
|
+
type: "disable_mcp_server_tool";
|
49
|
+
server_name: string;
|
50
|
+
tool: string;
|
51
|
+
};
|
52
|
+
|
53
|
+
export type ClientEnableAllMcpServerTools = {
|
54
|
+
type: "enable_all_mcp_server_tools";
|
55
|
+
server_name: string;
|
56
|
+
};
|
57
|
+
|
58
|
+
export type ClientDisableAllMcpServerTools = {
|
59
|
+
type: "disable_all_mcp_server_tools";
|
60
|
+
server_name: string;
|
61
|
+
};
|
62
|
+
|
63
|
+
export type ClientSetSystemPrompt = {
|
64
|
+
type: "set_system_prompt";
|
65
|
+
system_prompt: string;
|
66
|
+
};
|
67
|
+
|
68
|
+
export type ClientSetModel = {
|
69
|
+
type: "set_model";
|
70
|
+
model: string;
|
71
|
+
};
|
72
|
+
|
73
|
+
export type ClientToServer =
|
74
|
+
| ClientUserMessage
|
75
|
+
| ClientAddMcpServer
|
76
|
+
| ClientRemoveMcpServer
|
77
|
+
| ClientEnableMcpServerTool
|
78
|
+
| ClientDisableMcpServerTool
|
79
|
+
| ClientEnableAllMcpServerTools
|
80
|
+
| ClientDisableAllMcpServerTools
|
81
|
+
| ClientSetSystemPrompt
|
82
|
+
| ClientSetModel;
|
83
|
+
|
84
|
+
///
|
85
|
+
/// Server -> Client Messages
|
86
|
+
///
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Error in response to an invalid request from the client
|
90
|
+
*/
|
91
|
+
export type ServerError = {
|
92
|
+
type: "error";
|
93
|
+
message: string;
|
94
|
+
};
|
95
|
+
|
16
96
|
/**
|
17
97
|
* (from server) Chat history
|
18
98
|
*/
|
@@ -57,6 +137,11 @@ export type ServerAgentMessageChunk = {
|
|
57
137
|
end: boolean;
|
58
138
|
};
|
59
139
|
|
140
|
+
export type ServerMcpServerBriefs = {
|
141
|
+
type: "mcp_server_briefs";
|
142
|
+
server_briefs: McpServerBrief[];
|
143
|
+
};
|
144
|
+
|
60
145
|
/**
|
61
146
|
* For information only (to keep the chat window consistent).
|
62
147
|
*/
|
@@ -80,12 +165,71 @@ export type ServerTyping = {
|
|
80
165
|
from: string;
|
81
166
|
};
|
82
167
|
|
83
|
-
export type
|
168
|
+
export type ServerMcpServerAdded = {
|
169
|
+
type: "mcp_server_added";
|
170
|
+
server_name: string;
|
171
|
+
tools: Tool[];
|
172
|
+
enabled_tools: string[];
|
173
|
+
};
|
174
|
+
|
175
|
+
export type ServerMcpServerRemoved = {
|
176
|
+
type: "mcp_server_removed";
|
177
|
+
server_name: string;
|
178
|
+
};
|
179
|
+
|
180
|
+
export type ServerMcpServerToolEnabled = {
|
181
|
+
type: "mcp_server_tool_enabled";
|
182
|
+
server_name: string;
|
183
|
+
tool: string;
|
184
|
+
};
|
185
|
+
|
186
|
+
export type ServerMcpServerToolDisabled = {
|
187
|
+
type: "mcp_server_tool_disabled";
|
188
|
+
server_name: string;
|
189
|
+
tool: string;
|
190
|
+
};
|
191
|
+
|
192
|
+
export type ServerSystemPromptUpdated = {
|
193
|
+
type: "system_prompt_updated";
|
194
|
+
system_prompt: string;
|
195
|
+
};
|
196
|
+
|
197
|
+
export type ServerModelUpdated = {
|
198
|
+
type: "model_updated";
|
199
|
+
model: string;
|
200
|
+
};
|
201
|
+
|
202
|
+
export type ServerToClientStateUpdate =
|
203
|
+
| ServerMcpServerAdded
|
204
|
+
| ServerMcpServerRemoved
|
205
|
+
| ServerMcpServerToolEnabled
|
206
|
+
| ServerMcpServerToolDisabled
|
207
|
+
| ServerSystemPromptUpdated
|
208
|
+
| ServerModelUpdated;
|
209
|
+
|
210
|
+
export type ServerOpenUrl = {
|
211
|
+
type: "open_url";
|
212
|
+
url: string;
|
213
|
+
display_name: string;
|
214
|
+
};
|
215
|
+
|
216
|
+
export type ServerApproveToolCall = {
|
217
|
+
type: "approve_tool_call";
|
218
|
+
tbd: string;
|
219
|
+
};
|
220
|
+
|
221
|
+
export type ServerToClientActions = ServerOpenUrl | ServerApproveToolCall;
|
84
222
|
|
85
223
|
export type ServerToClient =
|
224
|
+
| ServerError
|
86
225
|
| ServerUserJoined
|
87
226
|
| ServerUserLeft
|
88
227
|
| ServerUserMessage
|
89
228
|
| ServerAgentMessage
|
90
229
|
| ServerAgentMessageChunk
|
91
|
-
|
|
230
|
+
| ServerMcpServerBriefs
|
231
|
+
| ServerToolCall
|
232
|
+
| ServerToolCallResult
|
233
|
+
| ServerTyping
|
234
|
+
| ServerToClientStateUpdate
|
235
|
+
| ServerToClientActions;
|
package/src/chat/server.ts
CHANGED
@@ -31,7 +31,7 @@ async function resolveAgentProfileId(
|
|
31
31
|
}
|
32
32
|
|
33
33
|
ap = await db.getSavedAgentProfileByName(
|
34
|
-
userData.
|
34
|
+
userData.uuid,
|
35
35
|
agentProfileIdentifier
|
36
36
|
);
|
37
37
|
logger.debug(`[resolveAgentProfileId]: by name: {JSON.stringify(ap)}`);
|
@@ -54,7 +54,7 @@ async function resolveSessionIdFromIdentifier(
|
|
54
54
|
// Interpret as an id, or as a number under the current user.
|
55
55
|
session = await db.getSessionById(compound);
|
56
56
|
if (!session) {
|
57
|
-
session = await db.getSessionByName(userData.
|
57
|
+
session = await db.getSessionByName(userData.uuid, compound);
|
58
58
|
}
|
59
59
|
} else {
|
60
60
|
session = await db.getSessionByName(compound[0], compound[1]);
|
@@ -114,7 +114,7 @@ async function findOrCreateSession(
|
|
114
114
|
throw `no agent profile: ${agentProfileIdParam}`;
|
115
115
|
}
|
116
116
|
|
117
|
-
return db.createSession(userData.
|
117
|
+
return db.createSession(userData.uuid, query.session_id, agentProfileId);
|
118
118
|
}
|
119
119
|
|
120
120
|
export async function runServer(
|
package/src/test/db.test.ts
CHANGED
@@ -15,6 +15,12 @@ const AGENT_PROFILE: AgentProfile = {
|
|
15
15
|
mcp_settings: {},
|
16
16
|
};
|
17
17
|
|
18
|
+
const AGENT_PROFILE_2: AgentProfile = {
|
19
|
+
model: undefined,
|
20
|
+
system_prompt: "You are a very helpful agent.",
|
21
|
+
mcp_settings: { simplecalc: [] },
|
22
|
+
};
|
23
|
+
|
18
24
|
async function createDummyUser(db: Database): Promise<void> {
|
19
25
|
const apiClient = new ApiClient(LOCAL_SERVER_URL, "dummy_key");
|
20
26
|
const user = await apiClient.getUserBrief("dummy_user");
|
@@ -28,12 +34,12 @@ async function createDummyUser(db: Database): Promise<void> {
|
|
28
34
|
}
|
29
35
|
|
30
36
|
describe("DB", () => {
|
31
|
-
it("should get existing user", async function () {
|
37
|
+
it("should get data for existing user", async function () {
|
32
38
|
const db = getLocalDB();
|
33
39
|
await createDummyUser(db);
|
34
40
|
const dummyUser = await db.getUserDataFromApiKey("dummy_key");
|
35
41
|
expect(dummyUser).eql({
|
36
|
-
|
42
|
+
uuid: "dummy_user",
|
37
43
|
nickname: "Dummy User",
|
38
44
|
});
|
39
45
|
});
|
@@ -44,6 +50,15 @@ describe("DB", () => {
|
|
44
50
|
expect(dummyUser).to.equal(undefined);
|
45
51
|
});
|
46
52
|
|
53
|
+
it("should get user by uuid", async function () {
|
54
|
+
const db = getLocalDB();
|
55
|
+
await createDummyUser(db);
|
56
|
+
const dummyUser = await db.getUserFromUuid("dummy_user");
|
57
|
+
assert(dummyUser);
|
58
|
+
expect(dummyUser.uuid).eql("dummy_user");
|
59
|
+
expect(dummyUser.nickname).eql("Dummy User");
|
60
|
+
});
|
61
|
+
|
47
62
|
it("should create and retrieve agent profiles", async function () {
|
48
63
|
const db = getLocalDB();
|
49
64
|
|
@@ -74,6 +89,24 @@ describe("DB", () => {
|
|
74
89
|
expect(agentProfile).eql(AGENT_PROFILE);
|
75
90
|
});
|
76
91
|
|
92
|
+
it("should update agent profiles", async function () {
|
93
|
+
const db = getLocalDB();
|
94
|
+
|
95
|
+
await db.clearAgentProfiles();
|
96
|
+
const agentProfileId = await db.createAgentProfile(
|
97
|
+
"dummy_user",
|
98
|
+
"test_profile",
|
99
|
+
AGENT_PROFILE
|
100
|
+
);
|
101
|
+
expect(agentProfileId).to.not.equal(undefined);
|
102
|
+
assert(agentProfileId);
|
103
|
+
|
104
|
+
await db.updateAgentProfile(agentProfileId, AGENT_PROFILE_2);
|
105
|
+
|
106
|
+
const agentProfileFromDB = (await db.getAgentProfileById(agentProfileId))!;
|
107
|
+
expect(agentProfileFromDB).eql(AGENT_PROFILE_2);
|
108
|
+
});
|
109
|
+
|
77
110
|
it("should create and retrieve sessions", async function () {
|
78
111
|
const db = getLocalDB();
|
79
112
|
|
@@ -0,0 +1,142 @@
|
|
1
|
+
import { expect } from "chai";
|
2
|
+
|
3
|
+
import { OpenAI } from "openai";
|
4
|
+
import {
|
5
|
+
initializeCompletion,
|
6
|
+
updateCompletion,
|
7
|
+
} from "../agent/openAILLMStreaming";
|
8
|
+
|
9
|
+
const TEST_STANDARD: OpenAI.Chat.Completions.ChatCompletionChunk[] = [
|
10
|
+
{
|
11
|
+
id: "chatcmpl-BzBqzVs5w0kU5we3KIUN1Q4FAZXjg",
|
12
|
+
choices: [
|
13
|
+
{
|
14
|
+
delta: { content: "!" },
|
15
|
+
finish_reason: null,
|
16
|
+
index: 0,
|
17
|
+
logprobs: null,
|
18
|
+
},
|
19
|
+
],
|
20
|
+
created: 1753923273,
|
21
|
+
model: "gpt-4o-2024-08-06",
|
22
|
+
object: "chat.completion.chunk",
|
23
|
+
service_tier: "default",
|
24
|
+
system_fingerprint: "fp_07871e2ad8",
|
25
|
+
},
|
26
|
+
{
|
27
|
+
id: "chatcmpl-BzBqzVs5w0kU5we3KIUN1Q4FAZXjg",
|
28
|
+
choices: [{ delta: {}, finish_reason: "stop", index: 0, logprobs: null }],
|
29
|
+
created: 1753923273,
|
30
|
+
model: "gpt-4o-2024-08-06",
|
31
|
+
object: "chat.completion.chunk",
|
32
|
+
service_tier: "default",
|
33
|
+
system_fingerprint: "fp_07871e2ad8",
|
34
|
+
},
|
35
|
+
];
|
36
|
+
|
37
|
+
const TEST_TRAILING_USAGE: OpenAI.Chat.Completions.ChatCompletionChunk[] = [
|
38
|
+
{
|
39
|
+
id: "gen-1753923406-nsIKHyFRoJqkUntBnQTw",
|
40
|
+
choices: [
|
41
|
+
{
|
42
|
+
delta: { content: "test", role: "assistant" },
|
43
|
+
finish_reason: "stop",
|
44
|
+
index: 0,
|
45
|
+
logprobs: null,
|
46
|
+
},
|
47
|
+
],
|
48
|
+
created: 1753923406,
|
49
|
+
model: "openai/gpt-4o",
|
50
|
+
object: "chat.completion.chunk",
|
51
|
+
system_fingerprint: "fp_a288987b44",
|
52
|
+
},
|
53
|
+
{
|
54
|
+
id: "gen-1753923406-nsIKHyFRoJqkUntBnQTw",
|
55
|
+
choices: [
|
56
|
+
{
|
57
|
+
delta: { content: "", role: "assistant" },
|
58
|
+
finish_reason: null,
|
59
|
+
index: 0,
|
60
|
+
logprobs: null,
|
61
|
+
},
|
62
|
+
],
|
63
|
+
created: 1753923406,
|
64
|
+
model: "openai/gpt-4o",
|
65
|
+
object: "chat.completion.chunk",
|
66
|
+
usage: {
|
67
|
+
completion_tokens: 50,
|
68
|
+
prompt_tokens: 271,
|
69
|
+
total_tokens: 321,
|
70
|
+
completion_tokens_details: { reasoning_tokens: 0 },
|
71
|
+
prompt_tokens_details: { cached_tokens: 0 },
|
72
|
+
},
|
73
|
+
},
|
74
|
+
];
|
75
|
+
|
76
|
+
describe("OpenAI Streaming", () => {
|
77
|
+
it("should support standard termination", async function () {
|
78
|
+
const chunks = TEST_STANDARD;
|
79
|
+
expect(chunks.length).eql(2);
|
80
|
+
const { initMessage } = initializeCompletion(chunks[0]);
|
81
|
+
updateCompletion(initMessage, chunks[1]);
|
82
|
+
|
83
|
+
expect(initMessage).eql({
|
84
|
+
id: "chatcmpl-BzBqzVs5w0kU5we3KIUN1Q4FAZXjg",
|
85
|
+
choices: [
|
86
|
+
{
|
87
|
+
message: {
|
88
|
+
content: "!",
|
89
|
+
role: "assistant",
|
90
|
+
refusal: null,
|
91
|
+
tool_calls: undefined,
|
92
|
+
},
|
93
|
+
finish_reason: "stop",
|
94
|
+
index: 0,
|
95
|
+
logprobs: null,
|
96
|
+
},
|
97
|
+
],
|
98
|
+
created: 1753923273,
|
99
|
+
model: "gpt-4o-2024-08-06",
|
100
|
+
object: "chat.completion",
|
101
|
+
service_tier: "default",
|
102
|
+
system_fingerprint: "fp_07871e2ad8",
|
103
|
+
usage: undefined,
|
104
|
+
});
|
105
|
+
});
|
106
|
+
|
107
|
+
it("should support trailing usage", async function () {
|
108
|
+
const chunks = TEST_TRAILING_USAGE;
|
109
|
+
expect(chunks.length).eql(2);
|
110
|
+
const { initMessage } = initializeCompletion(chunks[0]);
|
111
|
+
updateCompletion(initMessage, chunks[1]);
|
112
|
+
|
113
|
+
expect(initMessage).eql({
|
114
|
+
id: "gen-1753923406-nsIKHyFRoJqkUntBnQTw",
|
115
|
+
choices: [
|
116
|
+
{
|
117
|
+
message: {
|
118
|
+
content: "test",
|
119
|
+
role: "assistant",
|
120
|
+
refusal: null,
|
121
|
+
tool_calls: undefined,
|
122
|
+
},
|
123
|
+
finish_reason: "stop",
|
124
|
+
index: 0,
|
125
|
+
logprobs: null,
|
126
|
+
},
|
127
|
+
],
|
128
|
+
created: 1753923406,
|
129
|
+
model: "openai/gpt-4o",
|
130
|
+
object: "chat.completion",
|
131
|
+
service_tier: undefined,
|
132
|
+
system_fingerprint: "fp_a288987b44",
|
133
|
+
usage: {
|
134
|
+
completion_tokens: 50,
|
135
|
+
prompt_tokens: 271,
|
136
|
+
total_tokens: 321,
|
137
|
+
completion_tokens_details: { reasoning_tokens: 0 },
|
138
|
+
prompt_tokens_details: { cached_tokens: 0 },
|
139
|
+
},
|
140
|
+
});
|
141
|
+
});
|
142
|
+
});
|
package/src/test/prompt.test.ts
CHANGED
@@ -47,7 +47,7 @@ describe("SudoMcpServerManager", async () => {
|
|
47
47
|
|
48
48
|
it("should add a new MCP server", async () => {
|
49
49
|
const [, sm] = await getServerManagers();
|
50
|
-
await sm.addMcpServer("sudomcp_simplecalc");
|
50
|
+
await sm.addMcpServer("sudomcp_simplecalc", true);
|
51
51
|
const serverBriefs = sm.getServerBriefs();
|
52
52
|
expect(
|
53
53
|
serverBriefs.some((brief) => brief.name === "sudomcp_simplecalc")
|
package/src/tool/agentChat.ts
CHANGED
@@ -1,21 +1,18 @@
|
|
1
1
|
import yocto from "yocto-spinner";
|
2
2
|
import { Spinner } from "yocto-spinner";
|
3
|
-
import * as fs from "fs";
|
4
3
|
import OpenAI from "openai";
|
5
4
|
import chalk from "chalk";
|
6
5
|
|
7
6
|
import { configuration, utils } from "@xalia/xmcp/tool";
|
8
|
-
import { getLogger
|
7
|
+
import { getLogger } from "@xalia/xmcp/sdk";
|
9
8
|
|
10
|
-
import {
|
11
|
-
import { McpServerManager } from "../agent/mcpServerManager";
|
12
|
-
import { displayToolCall } from "../agent/tools";
|
13
|
-
import { SkillManager } from "../agent/sudoMcpServerManager";
|
9
|
+
import { AgentProfile } from "../agent/agent";
|
14
10
|
import { createAgentWithSkills } from "../agent/agentUtils";
|
15
11
|
|
16
12
|
import { loadImageB64OrUndefined } from "./files";
|
17
|
-
import { Prompt, parsePrompt } from "./prompt";
|
18
13
|
import { NODE_PLATFORM } from "./nodePlatform";
|
14
|
+
import { CommandPrompt } from "./commandPrompt";
|
15
|
+
import { IPrompt, Prompt } from "./prompt";
|
19
16
|
|
20
17
|
const logger = getLogger();
|
21
18
|
|
@@ -66,6 +63,9 @@ export async function runChat(
|
|
66
63
|
}
|
67
64
|
};
|
68
65
|
|
66
|
+
const repl: IPrompt = new Prompt();
|
67
|
+
const cmdPrompt = new CommandPrompt(repl);
|
68
|
+
|
69
69
|
let remainingApprovedToolCalls = approveToolsUpTo;
|
70
70
|
const onToolCall = async (toolCall: OpenAI.ChatCompletionMessageToolCall) => {
|
71
71
|
if (remainingApprovedToolCalls !== 0) {
|
@@ -74,7 +74,7 @@ export async function runChat(
|
|
74
74
|
}
|
75
75
|
|
76
76
|
spinner.stop().clear();
|
77
|
-
const result = await promptToolCall(
|
77
|
+
const result = await cmdPrompt.promptToolCall(toolCall);
|
78
78
|
spinner.start();
|
79
79
|
return result;
|
80
80
|
};
|
@@ -108,12 +108,13 @@ export async function runChat(
|
|
108
108
|
console.log(`USER: ${prompt}`);
|
109
109
|
}
|
110
110
|
|
111
|
-
const repl = new Prompt();
|
112
|
-
|
113
111
|
// Conversation loop
|
114
112
|
while (true) {
|
115
113
|
if (!prompt) {
|
116
|
-
const [msg, img] = await getNextPrompt(
|
114
|
+
const [msg, img] = await cmdPrompt.getNextPrompt(
|
115
|
+
agent,
|
116
|
+
sudoMcpServerManager
|
117
|
+
);
|
117
118
|
if (!msg && !img) {
|
118
119
|
break;
|
119
120
|
}
|
@@ -137,7 +138,7 @@ export async function runChat(
|
|
137
138
|
|
138
139
|
// Shutdown the agent
|
139
140
|
|
140
|
-
|
141
|
+
cmdPrompt.shutdown();
|
141
142
|
await agent.shutdown();
|
142
143
|
|
143
144
|
logger.debug("shutdown done");
|
@@ -147,202 +148,3 @@ export async function runChat(
|
|
147
148
|
* Read lines from the prompt, parsing any commands, and return once there is
|
148
149
|
* a prompt and/or image for the llm. Both undefined means exit.
|
149
150
|
*/
|
150
|
-
async function getNextPrompt(
|
151
|
-
repl: Prompt,
|
152
|
-
agent: Agent,
|
153
|
-
sudoMcpServerManager: SkillManager
|
154
|
-
): Promise<[string | undefined, string | undefined]> {
|
155
|
-
while (true) {
|
156
|
-
// Get a line, detecting the EOF signal.
|
157
|
-
const line = await repl.run();
|
158
|
-
if (typeof line === "undefined") {
|
159
|
-
console.log("closing ...");
|
160
|
-
return [undefined, undefined];
|
161
|
-
}
|
162
|
-
if (line.length === 0) {
|
163
|
-
continue;
|
164
|
-
}
|
165
|
-
|
166
|
-
// Extract prompt or commands
|
167
|
-
const { msg, cmds } = parsePrompt(line);
|
168
|
-
|
169
|
-
// If there are no commands, this must be a prompt only
|
170
|
-
if (!cmds) {
|
171
|
-
return [msg, undefined];
|
172
|
-
}
|
173
|
-
|
174
|
-
// There are commands. If it's image, return [prompt, image]. If it's
|
175
|
-
// quit, return [undefined, undefined], otherwise it must be a command.
|
176
|
-
// Execute it and prompt again.
|
177
|
-
switch (cmds[0]) {
|
178
|
-
case "i": // image
|
179
|
-
return [msg, cmds[1]];
|
180
|
-
case "q":
|
181
|
-
case "quit":
|
182
|
-
case "exit":
|
183
|
-
return [undefined, undefined];
|
184
|
-
default:
|
185
|
-
break;
|
186
|
-
}
|
187
|
-
|
188
|
-
try {
|
189
|
-
await runCommand(
|
190
|
-
agent,
|
191
|
-
agent.getMcpServerManager(),
|
192
|
-
sudoMcpServerManager,
|
193
|
-
cmds
|
194
|
-
);
|
195
|
-
} catch (e) {
|
196
|
-
console.log(`ERROR: ${e}`);
|
197
|
-
}
|
198
|
-
}
|
199
|
-
}
|
200
|
-
|
201
|
-
async function runCommand(
|
202
|
-
agent: Agent,
|
203
|
-
mcpServerManager: McpServerManager,
|
204
|
-
sudoMcpServerManager: SkillManager,
|
205
|
-
cmds: string[]
|
206
|
-
) {
|
207
|
-
switch (cmds[0]) {
|
208
|
-
case "lt":
|
209
|
-
listTools(mcpServerManager);
|
210
|
-
break;
|
211
|
-
case "ls":
|
212
|
-
listServers(sudoMcpServerManager);
|
213
|
-
break;
|
214
|
-
case "as":
|
215
|
-
await addServer(sudoMcpServerManager, cmds[1]);
|
216
|
-
break;
|
217
|
-
case "rs":
|
218
|
-
await mcpServerManager.removeMcpServer(cmds[1]);
|
219
|
-
break;
|
220
|
-
case "e":
|
221
|
-
mcpServerManager.enableTool(cmds[1], cmds[2]);
|
222
|
-
console.log(`Enabled tool ${cmds[2]} for server ${cmds[1]}`);
|
223
|
-
break;
|
224
|
-
case "d":
|
225
|
-
mcpServerManager.disableTool(cmds[1], cmds[2]);
|
226
|
-
console.log(`Disabled tool ${cmds[2]} for server ${cmds[1]}`);
|
227
|
-
break;
|
228
|
-
case "ea":
|
229
|
-
mcpServerManager.enableAllTools(cmds[1]);
|
230
|
-
console.log(`Enabled all tools for server ${cmds[1]}`);
|
231
|
-
break;
|
232
|
-
case "da":
|
233
|
-
mcpServerManager.disableAllTools(cmds[1]);
|
234
|
-
console.log(`Disabled all tools for server ${cmds[1]}`);
|
235
|
-
break;
|
236
|
-
case "wc":
|
237
|
-
writeConversation(agent, cmds[1]);
|
238
|
-
break;
|
239
|
-
case "wa":
|
240
|
-
writeAgentProfile(agent, cmds[1]);
|
241
|
-
break;
|
242
|
-
case "h":
|
243
|
-
case "help":
|
244
|
-
case "?":
|
245
|
-
helpMenu();
|
246
|
-
break;
|
247
|
-
default:
|
248
|
-
console.log(`error: Unknown command ${cmds[0]}`);
|
249
|
-
}
|
250
|
-
}
|
251
|
-
|
252
|
-
function helpMenu() {
|
253
|
-
console.log(`Tool management commands:`);
|
254
|
-
console.log(` ${chalk.yellow("/lt")} List tools: `);
|
255
|
-
console.log(` ${chalk.yellow("/ls")} List servers`);
|
256
|
-
console.log(` ${chalk.yellow("/as <server-name>")} Add server`);
|
257
|
-
console.log(
|
258
|
-
` ${chalk.yellow("/rs <server-name>")} Remove server`
|
259
|
-
);
|
260
|
-
console.log(` ${chalk.yellow("/e <server-name> <tool-name>")} Enable tool`);
|
261
|
-
console.log(` ${chalk.yellow("/d <server-name> <tool-name>")} Disable tool`);
|
262
|
-
console.log(
|
263
|
-
` ${chalk.yellow("/ea <server-name>")} Enable all tools`
|
264
|
-
);
|
265
|
-
console.log(
|
266
|
-
` ${chalk.yellow("/da <server-name>")} Disable all tools`
|
267
|
-
);
|
268
|
-
console.log(
|
269
|
-
` ${chalk.yellow("/wc <file-name>")} Write conversation file`
|
270
|
-
);
|
271
|
-
console.log(
|
272
|
-
` ${chalk.yellow("/wa <file-name>")} Write agent profile file`
|
273
|
-
);
|
274
|
-
|
275
|
-
console.log(` ${chalk.yellow("/q")} Quit`);
|
276
|
-
}
|
277
|
-
|
278
|
-
function listTools(mcpServerManager: McpServerManager) {
|
279
|
-
console.log("Mcp servers and tools (* - enabled):");
|
280
|
-
|
281
|
-
const serverNames = mcpServerManager.getMcpServerNames();
|
282
|
-
for (const serverName of serverNames) {
|
283
|
-
const server = mcpServerManager.getMcpServer(serverName);
|
284
|
-
console.log(` ${chalk.green(serverName)}`);
|
285
|
-
const tools = server.getTools();
|
286
|
-
const enabled = server.getEnabledTools();
|
287
|
-
for (const tool of tools) {
|
288
|
-
const isEnabled = enabled[tool.name] ? "*" : " ";
|
289
|
-
console.log(` [${isEnabled}] ${tool.name}`);
|
290
|
-
}
|
291
|
-
}
|
292
|
-
}
|
293
|
-
|
294
|
-
function listServers(sudoMcpServerManager: SkillManager) {
|
295
|
-
console.log(`Available MCP Servers:`);
|
296
|
-
|
297
|
-
const serverBriefs = sudoMcpServerManager.getServerBriefs();
|
298
|
-
serverBriefs.sort((a, b) => a.name.localeCompare(b.name));
|
299
|
-
for (const brief of serverBriefs) {
|
300
|
-
console.log(`- ${chalk.green(brief.name)}: ${brief.description}`);
|
301
|
-
}
|
302
|
-
}
|
303
|
-
|
304
|
-
/**
|
305
|
-
* Adds server and enables all tools.
|
306
|
-
*/
|
307
|
-
async function addServer(
|
308
|
-
sudoMcpServerManager: SkillManager,
|
309
|
-
serverName: string
|
310
|
-
) {
|
311
|
-
try {
|
312
|
-
await sudoMcpServerManager.addMcpServer(serverName);
|
313
|
-
} catch (e) {
|
314
|
-
if (e instanceof InvalidConfiguration) {
|
315
|
-
throw `${e}. \n
|
316
|
-
Have you installed server ${serverName} using the SudoMCP CLI tool?`;
|
317
|
-
} else {
|
318
|
-
throw e;
|
319
|
-
}
|
320
|
-
}
|
321
|
-
sudoMcpServerManager.getMcpServerManager().enableAllTools(serverName);
|
322
|
-
console.log(`Added server: ${serverName} and enabled all tools.`);
|
323
|
-
console.log(`Current tool list:`);
|
324
|
-
listTools(sudoMcpServerManager.getMcpServerManager());
|
325
|
-
}
|
326
|
-
|
327
|
-
function writeConversation(agent: Agent, fileName: string) {
|
328
|
-
const conversation = agent.getConversation();
|
329
|
-
fs.writeFileSync(fileName, JSON.stringify(conversation), {
|
330
|
-
encoding: "utf8",
|
331
|
-
});
|
332
|
-
console.log(`Conversation written to ${fileName}`);
|
333
|
-
}
|
334
|
-
|
335
|
-
function writeAgentProfile(agent: Agent, fileName: string) {
|
336
|
-
const profile = agent.getAgentProfile();
|
337
|
-
fs.writeFileSync(fileName, JSON.stringify(profile));
|
338
|
-
console.log(`AgentProfile written to ${fileName}`);
|
339
|
-
}
|
340
|
-
|
341
|
-
async function promptToolCall(
|
342
|
-
repl: Prompt,
|
343
|
-
toolCall: OpenAI.ChatCompletionMessageToolCall
|
344
|
-
): Promise<boolean> {
|
345
|
-
displayToolCall(toolCall);
|
346
|
-
const response = await repl.run("Approve tool call? (Y/n) ");
|
347
|
-
return response === "y" || response === "yes" || response == "";
|
348
|
-
}
|