@xalia/agent 0.5.8 → 0.6.0
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/README.md +23 -8
- package/dist/agent/src/agent/agent.js +173 -96
- package/dist/agent/src/agent/agentUtils.js +82 -53
- package/dist/agent/src/agent/compressingContextManager.js +102 -0
- package/dist/agent/src/agent/context.js +189 -0
- package/dist/agent/src/agent/dummyLLM.js +46 -5
- package/dist/agent/src/agent/iAgentEventHandler.js +2 -0
- package/dist/agent/src/agent/mcpServerManager.js +22 -23
- package/dist/agent/src/agent/nullAgentEventHandler.js +21 -0
- package/dist/agent/src/agent/nullPlatform.js +14 -0
- package/dist/agent/src/agent/openAILLMStreaming.js +12 -7
- package/dist/agent/src/agent/promptProvider.js +63 -0
- package/dist/agent/src/agent/repeatLLM.js +5 -5
- package/dist/agent/src/agent/sudoMcpServerManager.js +11 -9
- package/dist/agent/src/agent/tokenAuth.js +7 -7
- package/dist/agent/src/agent/tools.js +1 -1
- package/dist/agent/src/chat/client/chatClient.js +733 -0
- package/dist/agent/src/chat/client/connection.js +209 -0
- package/dist/agent/src/chat/client/connection.test.js +188 -0
- package/dist/agent/src/chat/client/constants.js +5 -0
- package/dist/agent/src/chat/client/index.js +15 -0
- package/dist/agent/src/chat/client/interfaces.js +2 -0
- package/dist/agent/src/chat/client/responseHandler.js +105 -0
- package/dist/agent/src/chat/client/sessionClient.js +331 -0
- package/dist/agent/src/chat/client/teamManager.js +2 -0
- package/dist/agent/src/chat/{apiKeyManager.js → data/apiKeyManager.js} +4 -0
- package/dist/agent/src/chat/data/dataModels.js +2 -0
- package/dist/agent/src/chat/data/database.js +749 -0
- package/dist/agent/src/chat/data/dbMcpServerConfigs.js +47 -0
- package/dist/agent/src/chat/protocol/connectionMessages.js +5 -0
- package/dist/agent/src/chat/protocol/constants.js +50 -0
- package/dist/agent/src/chat/protocol/errors.js +22 -0
- package/dist/agent/src/chat/protocol/messages.js +110 -0
- package/dist/agent/src/chat/server/chatContextManager.js +405 -0
- package/dist/agent/src/chat/server/connectionManager.js +352 -0
- package/dist/agent/src/chat/server/connectionManager.test.js +159 -0
- package/dist/agent/src/chat/server/conversation.js +198 -0
- package/dist/agent/src/chat/server/errorUtils.js +23 -0
- package/dist/agent/src/chat/server/openSession.js +869 -0
- package/dist/agent/src/chat/server/server.js +177 -0
- package/dist/agent/src/chat/server/sessionFileManager.js +161 -0
- package/dist/agent/src/chat/server/sessionRegistry.js +700 -0
- package/dist/agent/src/chat/server/sessionRegistry.test.js +97 -0
- package/dist/agent/src/chat/server/test-utils/mockFactories.js +307 -0
- package/dist/agent/src/chat/server/tools.js +243 -0
- package/dist/agent/src/chat/utils/agentSessionMap.js +66 -0
- package/dist/agent/src/chat/utils/approvalManager.js +85 -0
- package/dist/agent/src/{utils → chat/utils}/asyncLock.js +3 -3
- package/dist/agent/src/chat/{asyncQueue.js → utils/asyncQueue.js} +12 -2
- package/dist/agent/src/chat/utils/htmlToText.js +84 -0
- package/dist/agent/src/chat/utils/multiAsyncQueue.js +42 -0
- package/dist/agent/src/chat/utils/search.js +145 -0
- package/dist/agent/src/chat/utils/userResolver.js +46 -0
- package/dist/agent/src/chat/{websocket.js → utils/websocket.js} +2 -0
- package/dist/agent/src/test/agent.test.js +332 -0
- package/dist/agent/src/test/approvalManager.test.js +58 -0
- package/dist/agent/src/test/chatContextManager.test.js +392 -0
- package/dist/agent/src/test/clientServerConnection.test.js +158 -0
- package/dist/agent/src/test/compressingContextManager.test.js +65 -0
- package/dist/agent/src/test/context.test.js +83 -0
- package/dist/agent/src/test/conversation.test.js +89 -0
- package/dist/agent/src/test/db.test.js +262 -90
- package/dist/agent/src/test/dbMcpServerConfigs.test.js +72 -0
- package/dist/agent/src/test/dbTestTools.js +99 -0
- package/dist/agent/src/test/imageLoad.test.js +8 -7
- package/dist/agent/src/test/mcpServerManager.test.js +21 -18
- package/dist/agent/src/test/multiAsyncQueue.test.js +101 -0
- package/dist/agent/src/test/openaiStreaming.test.js +12 -11
- package/dist/agent/src/test/prompt.test.js +5 -4
- package/dist/agent/src/test/promptProvider.test.js +28 -0
- package/dist/agent/src/test/responseHandler.test.js +61 -0
- package/dist/agent/src/test/sudoMcpServerManager.test.js +14 -12
- package/dist/agent/src/test/testTools.js +109 -0
- package/dist/agent/src/test/tools.test.js +31 -0
- package/dist/agent/src/tool/agentChat.js +21 -10
- package/dist/agent/src/tool/agentMain.js +1 -1
- package/dist/agent/src/tool/chatMain.js +235 -58
- package/dist/agent/src/tool/commandPrompt.js +15 -9
- package/dist/agent/src/tool/files.js +20 -16
- package/dist/agent/src/tool/nodePlatform.js +47 -3
- package/dist/agent/src/tool/options.js +4 -4
- package/dist/agent/src/tool/prompt.js +19 -13
- package/eslint.config.mjs +14 -1
- package/package.json +14 -6
- package/scripts/chat_server +8 -0
- package/scripts/setup_chat +7 -2
- package/scripts/shutdown_chat_server +3 -0
- package/scripts/test_chat +135 -17
- package/src/agent/agent.ts +270 -135
- package/src/agent/agentUtils.ts +136 -95
- package/src/agent/compressingContextManager.ts +164 -0
- package/src/agent/context.ts +268 -0
- package/src/agent/dummyLLM.ts +76 -8
- package/src/agent/iAgentEventHandler.ts +54 -0
- package/src/agent/iplatform.ts +1 -0
- package/src/agent/mcpServerManager.ts +32 -30
- package/src/agent/nullAgentEventHandler.ts +20 -0
- package/src/agent/nullPlatform.ts +13 -0
- package/src/agent/openAILLMStreaming.ts +12 -6
- package/src/agent/promptProvider.ts +87 -0
- package/src/agent/repeatLLM.ts +5 -5
- package/src/agent/sudoMcpServerManager.ts +13 -11
- package/src/agent/tokenAuth.ts +7 -7
- package/src/agent/tools.ts +3 -1
- package/src/chat/client/chatClient.ts +900 -0
- package/src/chat/client/connection.test.ts +241 -0
- package/src/chat/client/connection.ts +276 -0
- package/src/chat/client/constants.ts +3 -0
- package/src/chat/client/index.ts +18 -0
- package/src/chat/client/interfaces.ts +34 -0
- package/src/chat/client/responseHandler.ts +131 -0
- package/src/chat/client/sessionClient.ts +443 -0
- package/src/chat/client/teamManager.ts +29 -0
- package/src/chat/{apiKeyManager.ts → data/apiKeyManager.ts} +6 -2
- package/src/chat/data/dataModels.ts +85 -0
- package/src/chat/data/database.ts +982 -0
- package/src/chat/data/dbMcpServerConfigs.ts +59 -0
- package/src/chat/protocol/connectionMessages.ts +49 -0
- package/src/chat/protocol/constants.ts +55 -0
- package/src/chat/protocol/errors.ts +16 -0
- package/src/chat/protocol/messages.ts +682 -0
- package/src/chat/server/README.md +127 -0
- package/src/chat/server/chatContextManager.ts +612 -0
- package/src/chat/server/connectionManager.test.ts +266 -0
- package/src/chat/server/connectionManager.ts +541 -0
- package/src/chat/server/conversation.ts +269 -0
- package/src/chat/server/errorUtils.ts +28 -0
- package/src/chat/server/openSession.ts +1332 -0
- package/src/chat/server/server.ts +177 -0
- package/src/chat/server/sessionFileManager.ts +239 -0
- package/src/chat/server/sessionRegistry.test.ts +138 -0
- package/src/chat/server/sessionRegistry.ts +1064 -0
- package/src/chat/server/test-utils/mockFactories.ts +422 -0
- package/src/chat/server/tools.ts +265 -0
- package/src/chat/utils/agentSessionMap.ts +76 -0
- package/src/chat/utils/approvalManager.ts +111 -0
- package/src/{utils → chat/utils}/asyncLock.ts +3 -3
- package/src/chat/{asyncQueue.ts → utils/asyncQueue.ts} +14 -3
- package/src/chat/utils/htmlToText.ts +61 -0
- package/src/chat/utils/multiAsyncQueue.ts +52 -0
- package/src/chat/utils/search.ts +139 -0
- package/src/chat/utils/userResolver.ts +48 -0
- package/src/chat/{websocket.ts → utils/websocket.ts} +2 -0
- package/src/test/agent.test.ts +487 -0
- package/src/test/approvalManager.test.ts +73 -0
- package/src/test/chatContextManager.test.ts +521 -0
- package/src/test/clientServerConnection.test.ts +207 -0
- package/src/test/compressingContextManager.test.ts +82 -0
- package/src/test/context.test.ts +105 -0
- package/src/test/conversation.test.ts +109 -0
- package/src/test/db.test.ts +351 -103
- package/src/test/dbMcpServerConfigs.test.ts +112 -0
- package/src/test/dbTestTools.ts +153 -0
- package/src/test/imageLoad.test.ts +7 -6
- package/src/test/mcpServerManager.test.ts +19 -14
- package/src/test/multiAsyncQueue.test.ts +125 -0
- package/src/test/openaiStreaming.test.ts +11 -10
- package/src/test/prompt.test.ts +4 -3
- package/src/test/promptProvider.test.ts +33 -0
- package/src/test/responseHandler.test.ts +78 -0
- package/src/test/sudoMcpServerManager.test.ts +22 -15
- package/src/test/testTools.ts +146 -0
- package/src/test/tools.test.ts +39 -0
- package/src/tool/agentChat.ts +26 -12
- package/src/tool/agentMain.ts +1 -1
- package/src/tool/chatMain.ts +283 -100
- package/src/tool/commandPrompt.ts +25 -9
- package/src/tool/files.ts +25 -19
- package/src/tool/nodePlatform.ts +52 -3
- package/src/tool/options.ts +4 -2
- package/src/tool/prompt.ts +22 -15
- package/test_data/dummyllm_script_crash.json +32 -0
- package/test_data/frog.png.b64 +1 -0
- package/vitest.config.ts +39 -0
- package/dist/agent/src/chat/client.js +0 -310
- package/dist/agent/src/chat/conversationManager.js +0 -502
- package/dist/agent/src/chat/db.js +0 -218
- package/dist/agent/src/chat/messages.js +0 -29
- package/dist/agent/src/chat/server.js +0 -158
- package/src/chat/client.ts +0 -445
- package/src/chat/conversationManager.ts +0 -730
- package/src/chat/db.ts +0 -304
- package/src/chat/messages.ts +0 -266
- package/src/chat/server.ts +0 -177
- /package/{frog.png → test_data/frog.png} +0 -0
package/src/tool/chatMain.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
option,
|
|
5
|
-
flag,
|
|
6
|
-
string,
|
|
7
|
-
number,
|
|
8
|
-
subcommands,
|
|
9
|
-
} from "cmd-ts";
|
|
1
|
+
/* eslint-disable @typescript-eslint/only-throw-error */
|
|
2
|
+
|
|
3
|
+
import { command, optional, option, string, number, subcommands } from "cmd-ts";
|
|
10
4
|
import { stdout } from "process";
|
|
11
5
|
import * as fs from "fs";
|
|
12
6
|
import { strict as assert } from "assert";
|
|
@@ -14,22 +8,105 @@ import { strict as assert } from "assert";
|
|
|
14
8
|
import { configuration } from "@xalia/xmcp/tool";
|
|
15
9
|
import { getLogger } from "@xalia/xmcp/sdk";
|
|
16
10
|
|
|
17
|
-
import { IConversation } from "../agent/agent";
|
|
18
|
-
import { ChatClient } from "../chat/client";
|
|
19
|
-
import { runServer } from "../chat/server";
|
|
20
11
|
import {
|
|
21
|
-
|
|
12
|
+
ChatClient,
|
|
13
|
+
SessionClient,
|
|
14
|
+
IChatClientEventHandler,
|
|
15
|
+
} from "../chat/client";
|
|
16
|
+
import {
|
|
17
|
+
runServer,
|
|
18
|
+
resolveSessionIdFromIdentifier,
|
|
19
|
+
} from "../chat/server/server";
|
|
20
|
+
import {
|
|
22
21
|
ServerToClient,
|
|
23
22
|
decodeAssistantMessageParam,
|
|
24
|
-
} from "../chat/messages";
|
|
23
|
+
} from "../chat/protocol/messages";
|
|
25
24
|
|
|
26
25
|
import { IPrompt, Prompt, ScriptPrompt } from "./prompt";
|
|
27
26
|
import * as options from "./options";
|
|
28
27
|
import { CommandPrompt } from "./commandPrompt";
|
|
29
28
|
import { NODE_PLATFORM } from "./nodePlatform";
|
|
29
|
+
import { Database, createSessionParticipantMap } from "../chat/data/database";
|
|
30
|
+
import { loadImageB64 } from "./files";
|
|
30
31
|
|
|
31
32
|
const logger = getLogger();
|
|
32
33
|
|
|
34
|
+
const listSessions = command({
|
|
35
|
+
name: "delete-session",
|
|
36
|
+
args: {
|
|
37
|
+
supabaseUrl: options.supabaseUrl,
|
|
38
|
+
supabaseKey: options.supabaseKey,
|
|
39
|
+
},
|
|
40
|
+
handler: async ({ supabaseUrl, supabaseKey }): Promise<void> => {
|
|
41
|
+
const db = new Database(supabaseUrl, supabaseKey);
|
|
42
|
+
console.log(JSON.stringify(await db.sessionsGet()));
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const clearSessions = command({
|
|
47
|
+
name: "clear-sessions",
|
|
48
|
+
args: {
|
|
49
|
+
supabaseUrl: options.supabaseUrl,
|
|
50
|
+
supabaseKey: options.supabaseKey,
|
|
51
|
+
},
|
|
52
|
+
handler: async ({ supabaseUrl, supabaseKey }): Promise<void> => {
|
|
53
|
+
const db = new Database(supabaseUrl, supabaseKey);
|
|
54
|
+
await db.clearSessions();
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const deleteSession = command({
|
|
59
|
+
name: "delete-session",
|
|
60
|
+
args: {
|
|
61
|
+
supabaseUrl: options.supabaseUrl,
|
|
62
|
+
supabaseKey: options.supabaseKey,
|
|
63
|
+
session: option({
|
|
64
|
+
type: string,
|
|
65
|
+
long: "session",
|
|
66
|
+
description:
|
|
67
|
+
"Session identifier (id, name, user/name or name of new session)",
|
|
68
|
+
env: "SESSION",
|
|
69
|
+
}),
|
|
70
|
+
},
|
|
71
|
+
handler: async ({ supabaseUrl, supabaseKey, session }): Promise<void> => {
|
|
72
|
+
const db = new Database(supabaseUrl, supabaseKey);
|
|
73
|
+
const sessionId = await resolveSessionIdFromIdentifier(db, session);
|
|
74
|
+
if (sessionId) {
|
|
75
|
+
await db.sessionDeleteById(sessionId);
|
|
76
|
+
} else {
|
|
77
|
+
throw `No such session ${session}`;
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const listParticipants = command({
|
|
83
|
+
name: "list-participants",
|
|
84
|
+
args: {
|
|
85
|
+
supabaseUrl: options.supabaseUrl,
|
|
86
|
+
supabaseKey: options.supabaseKey,
|
|
87
|
+
session: option({
|
|
88
|
+
type: string,
|
|
89
|
+
long: "session",
|
|
90
|
+
description:
|
|
91
|
+
"Session identifier (id, name, user/name or name of new session)",
|
|
92
|
+
env: "SESSION",
|
|
93
|
+
}),
|
|
94
|
+
},
|
|
95
|
+
handler: async ({ supabaseUrl, supabaseKey, session }): Promise<void> => {
|
|
96
|
+
const db = new Database(supabaseUrl, supabaseKey);
|
|
97
|
+
const sessionId = await resolveSessionIdFromIdentifier(db, session);
|
|
98
|
+
if (sessionId) {
|
|
99
|
+
const participants = await db.sessionGetParticipants(sessionId);
|
|
100
|
+
|
|
101
|
+
console.log(`participants: ${JSON.stringify(participants)}`);
|
|
102
|
+
const users = createSessionParticipantMap(participants);
|
|
103
|
+
console.log(JSON.stringify(users));
|
|
104
|
+
} else {
|
|
105
|
+
throw `No such session ${session}`;
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
33
110
|
const server = command({
|
|
34
111
|
name: "server",
|
|
35
112
|
args: {
|
|
@@ -46,6 +123,7 @@ const server = command({
|
|
|
46
123
|
xmcpUrl: options.xmcpUrl,
|
|
47
124
|
pidFile: options.pidFile,
|
|
48
125
|
},
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
49
127
|
handler: async ({
|
|
50
128
|
port,
|
|
51
129
|
supabaseUrl,
|
|
@@ -58,58 +136,61 @@ const server = command({
|
|
|
58
136
|
fs.writeFileSync(pidFile, process.pid.toString());
|
|
59
137
|
}
|
|
60
138
|
|
|
61
|
-
runServer(port, supabaseUrl, supabaseKey, llmUrl, xmcpUrl);
|
|
139
|
+
void runServer(port, supabaseUrl, supabaseKey, llmUrl, xmcpUrl);
|
|
62
140
|
},
|
|
63
141
|
});
|
|
64
142
|
|
|
65
143
|
const client = command({
|
|
66
144
|
name: "client",
|
|
67
145
|
args: {
|
|
68
|
-
|
|
146
|
+
url: option({
|
|
69
147
|
type: string,
|
|
70
|
-
long: "
|
|
148
|
+
long: "url",
|
|
71
149
|
short: "h",
|
|
72
|
-
env: "
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
port: option({
|
|
76
|
-
type: number,
|
|
77
|
-
long: "port",
|
|
78
|
-
short: "p",
|
|
79
|
-
env: "CHAT_SERVER_PORT",
|
|
80
|
-
defaultValue: () => 5003,
|
|
150
|
+
env: "CHAT_URL",
|
|
151
|
+
description: "Chat server url",
|
|
152
|
+
defaultValue: () => "ws://localhost:5003",
|
|
81
153
|
}),
|
|
82
154
|
apiKey: options.apiKey,
|
|
83
|
-
|
|
84
|
-
type: string,
|
|
85
|
-
long: "session",
|
|
86
|
-
description: "
|
|
87
|
-
env: "
|
|
155
|
+
sessionId: option({
|
|
156
|
+
type: optional(string),
|
|
157
|
+
long: "session-id",
|
|
158
|
+
description: "Join existing session with the given UUID",
|
|
159
|
+
env: "SESSION_UUID",
|
|
160
|
+
}),
|
|
161
|
+
teamId: option({
|
|
162
|
+
type: optional(string),
|
|
163
|
+
long: "team-id",
|
|
164
|
+
description:
|
|
165
|
+
"Team uuid, if not provided, a user session will be created.",
|
|
166
|
+
env: "TEAM_ID",
|
|
88
167
|
}),
|
|
89
|
-
|
|
168
|
+
agentProfileId: option({
|
|
90
169
|
type: optional(string),
|
|
91
|
-
long: "agent-profile",
|
|
92
|
-
description: "Create new session
|
|
93
|
-
env: "
|
|
170
|
+
long: "agent-profile-id",
|
|
171
|
+
description: "Create new session with agent profile (uuid)",
|
|
172
|
+
env: "AGENT_PROFILE_ID",
|
|
173
|
+
}),
|
|
174
|
+
sessionTitle: option({
|
|
175
|
+
type: optional(string),
|
|
176
|
+
long: "session-title",
|
|
177
|
+
description: "Title for new session",
|
|
178
|
+
env: "SESSION_TITLE",
|
|
94
179
|
}),
|
|
95
180
|
script: option({
|
|
96
181
|
type: optional(string),
|
|
97
182
|
long: "script",
|
|
98
183
|
description: "Script (file) to execute and then exit.",
|
|
99
184
|
}),
|
|
100
|
-
test: flag({
|
|
101
|
-
long: "test",
|
|
102
|
-
description: "Run a test client and disconnect",
|
|
103
|
-
}),
|
|
104
185
|
},
|
|
105
186
|
handler: async ({
|
|
106
|
-
|
|
107
|
-
port,
|
|
187
|
+
url,
|
|
108
188
|
apiKey,
|
|
109
|
-
|
|
110
|
-
|
|
189
|
+
sessionId,
|
|
190
|
+
agentProfileId,
|
|
191
|
+
sessionTitle,
|
|
192
|
+
teamId,
|
|
111
193
|
script,
|
|
112
|
-
test,
|
|
113
194
|
}): Promise<void> => {
|
|
114
195
|
if (!apiKey) {
|
|
115
196
|
// TODO: configFIle param list in ../main.ts
|
|
@@ -117,12 +198,6 @@ const client = command({
|
|
|
117
198
|
apiKey = sudomcpConfig.api_key;
|
|
118
199
|
}
|
|
119
200
|
|
|
120
|
-
if (test) {
|
|
121
|
-
await runClientTest(host, port, apiKey, session);
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const onMessage = getCLIOnMessage();
|
|
126
201
|
const repl: IPrompt = (() => {
|
|
127
202
|
if (script) {
|
|
128
203
|
const scriptLines = fs.readFileSync(script, "utf8").split("\n");
|
|
@@ -132,40 +207,87 @@ const client = command({
|
|
|
132
207
|
})();
|
|
133
208
|
const cmdPrompt = new CommandPrompt(repl);
|
|
134
209
|
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
apiKey,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
agentProfile
|
|
144
|
-
);
|
|
210
|
+
const eventHandler = getCLIEventHandler(cmdPrompt);
|
|
211
|
+
|
|
212
|
+
const [client, sessionClient] = await (async () => {
|
|
213
|
+
const newClient = await ChatClient.init(url, apiKey, eventHandler);
|
|
214
|
+
if (sessionId) {
|
|
215
|
+
if (agentProfileId || sessionTitle) {
|
|
216
|
+
throw "t agent-profile-id/session-title set joining existing session";
|
|
217
|
+
}
|
|
145
218
|
|
|
219
|
+
console.log(`joining existing session: ${sessionId} ...`);
|
|
220
|
+
const newSessionClient = await newClient.connectToSession(sessionId);
|
|
221
|
+
return [newClient, newSessionClient];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const newSessionClient = await newClient.createNewSession(
|
|
225
|
+
sessionTitle || "New Chat",
|
|
226
|
+
agentProfileId,
|
|
227
|
+
teamId
|
|
228
|
+
);
|
|
229
|
+
return [newClient, newSessionClient];
|
|
230
|
+
})();
|
|
146
231
|
logger.debug("client created");
|
|
147
232
|
|
|
233
|
+
const onUnknownCommand = (cmds: string[]) => {
|
|
234
|
+
switch (cmds[0]) {
|
|
235
|
+
case "aa":
|
|
236
|
+
sessionClient.setAutoApproval(cmds[1], cmds[2], !!cmds[3]);
|
|
237
|
+
break;
|
|
238
|
+
case "error":
|
|
239
|
+
sessionClient.sendMessage({ type: "fatal_error", message: cmds[1] });
|
|
240
|
+
break;
|
|
241
|
+
case "ap": // Add Participant
|
|
242
|
+
case "au":
|
|
243
|
+
client.addTeamMember(cmds[1], cmds[2]);
|
|
244
|
+
break;
|
|
245
|
+
case "rp": // Remove Participant
|
|
246
|
+
case "ru":
|
|
247
|
+
client.removeTeamMember(cmds[1], cmds[2]);
|
|
248
|
+
break;
|
|
249
|
+
case "lp": // List Participants
|
|
250
|
+
case "lu":
|
|
251
|
+
console.log(JSON.stringify(sessionClient.getParticipants()));
|
|
252
|
+
break;
|
|
253
|
+
case "si": // Write Session id to file
|
|
254
|
+
fs.writeFileSync(cmds[1], sessionClient.getSessionUUID());
|
|
255
|
+
break;
|
|
256
|
+
case "ct": // Create Team
|
|
257
|
+
client.createNewTeam(cmds[1], []);
|
|
258
|
+
break;
|
|
259
|
+
case "ci": // Write team id to file
|
|
260
|
+
fs.writeFileSync(cmds[1], client.getCurrentTeamId() ?? "");
|
|
261
|
+
break;
|
|
262
|
+
case "sw": // Set workspace
|
|
263
|
+
setWorkspaceFromPrompt(sessionClient, cmds);
|
|
264
|
+
break;
|
|
265
|
+
default:
|
|
266
|
+
console.log(`error: Unknown command ${cmds[0]}`);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
148
271
|
while (true) {
|
|
149
272
|
const [msgText, imageFile] = await cmdPrompt.getNextPrompt(
|
|
150
|
-
|
|
151
|
-
|
|
273
|
+
sessionClient,
|
|
274
|
+
sessionClient.getSudoMcpServerManager(),
|
|
275
|
+
onUnknownCommand
|
|
152
276
|
);
|
|
153
277
|
assert(imageFile === undefined);
|
|
154
278
|
|
|
155
279
|
if (msgText === undefined) {
|
|
156
280
|
logger.debug("exiting...");
|
|
157
|
-
|
|
281
|
+
logger.info("exiting...");
|
|
282
|
+
client.shutdown();
|
|
158
283
|
return;
|
|
159
284
|
}
|
|
160
285
|
|
|
161
286
|
// TODO: other prompts
|
|
162
287
|
|
|
163
288
|
if (msgText.length > 0) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
message: msgText,
|
|
167
|
-
};
|
|
168
|
-
client.sendMessage(msg);
|
|
289
|
+
logger.debug(`sending message: ${msgText}`);
|
|
290
|
+
sessionClient.userMessage(msgText);
|
|
169
291
|
} else {
|
|
170
292
|
logger.debug("(ignoring empty message)");
|
|
171
293
|
}
|
|
@@ -173,12 +295,66 @@ const client = command({
|
|
|
173
295
|
},
|
|
174
296
|
});
|
|
175
297
|
|
|
176
|
-
function
|
|
298
|
+
function setWorkspaceFromPrompt(
|
|
299
|
+
sessionClient: SessionClient,
|
|
300
|
+
cmds: string[]
|
|
301
|
+
): void {
|
|
302
|
+
let msg = "";
|
|
303
|
+
let image = undefined;
|
|
304
|
+
if (!cmds[1]) {
|
|
305
|
+
sessionClient.setWorkspace(undefined, undefined);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (cmds[1].startsWith("file:")) {
|
|
309
|
+
image = loadImageB64(cmds[1].slice(5));
|
|
310
|
+
msg = cmds.slice(2).join(" ");
|
|
311
|
+
console.log(`setting workspace: ${msg} (image: ${image})`);
|
|
312
|
+
} else {
|
|
313
|
+
msg = cmds.slice(1).join(" ");
|
|
314
|
+
console.log(`setting workspace: ${msg}`);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
sessionClient.setWorkspace(msg, image);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function getCLIEventHandler(
|
|
321
|
+
cmdPrompt?: CommandPrompt
|
|
322
|
+
): IChatClientEventHandler {
|
|
323
|
+
let numErrors = 0;
|
|
324
|
+
|
|
325
|
+
const onClosed = async () => {
|
|
326
|
+
logger.debug("[onClosed]");
|
|
327
|
+
if (cmdPrompt) {
|
|
328
|
+
// TODO: check if this fully shuts down
|
|
329
|
+
await cmdPrompt.shutdown();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// If there have been errors, exit with an error code.
|
|
333
|
+
process.exit(numErrors);
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const onError = (message: string) => {
|
|
337
|
+
numErrors++;
|
|
338
|
+
logger.debug("[onError]");
|
|
339
|
+
console.error(message);
|
|
340
|
+
};
|
|
341
|
+
|
|
177
342
|
let startMsg = true;
|
|
178
|
-
|
|
343
|
+
const onMessage = async (msg: ServerToClient, client: ChatClient) => {
|
|
344
|
+
let sessionClient: SessionClient | undefined = undefined;
|
|
345
|
+
try {
|
|
346
|
+
sessionClient = client.getCurrentSession("[chatMain.onMessage]");
|
|
347
|
+
} catch (error) {
|
|
348
|
+
logger.error(String(error));
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
179
352
|
switch (msg.type) {
|
|
180
353
|
case "user_msg":
|
|
181
|
-
stdout.write(`[${msg.
|
|
354
|
+
stdout.write(`[${msg.user_uuid}]: ${msg.message || ""}\n`);
|
|
355
|
+
if (msg.imageB64) {
|
|
356
|
+
stdout.write(`[${msg.user_uuid}]: IMAGE: ${msg.imageB64}\n`);
|
|
357
|
+
}
|
|
182
358
|
break;
|
|
183
359
|
case "agent_msg":
|
|
184
360
|
{
|
|
@@ -199,40 +375,43 @@ function getCLIOnMessage(): (msg: ServerToClient) => void {
|
|
|
199
375
|
startMsg = true;
|
|
200
376
|
}
|
|
201
377
|
break;
|
|
378
|
+
case "approve_tool_call":
|
|
379
|
+
{
|
|
380
|
+
const result = cmdPrompt
|
|
381
|
+
? await cmdPrompt.promptToolCall(msg.tool_call)
|
|
382
|
+
: true;
|
|
383
|
+
logger.debug(`cmdPrompt.promptToolCall returned: ${String(result)}`);
|
|
384
|
+
sessionClient.toolCallApprovalResult(
|
|
385
|
+
msg.id,
|
|
386
|
+
result,
|
|
387
|
+
false /* auto-approve */
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
break;
|
|
391
|
+
case "render_html":
|
|
392
|
+
await NODE_PLATFORM.renderHTML(msg.html);
|
|
393
|
+
break;
|
|
394
|
+
case "authenticate":
|
|
395
|
+
NODE_PLATFORM.openUrl(
|
|
396
|
+
msg.url,
|
|
397
|
+
new Promise<boolean>((r) => {
|
|
398
|
+
r(true);
|
|
399
|
+
}),
|
|
400
|
+
msg.display_name
|
|
401
|
+
);
|
|
402
|
+
break;
|
|
403
|
+
|
|
202
404
|
default:
|
|
203
|
-
stdout.write(`(unrecognised) ${JSON.stringify(msg)}\n`);
|
|
405
|
+
stdout.write(`(unrecognised) ${JSON.stringify(msg.type)}\n`);
|
|
204
406
|
break;
|
|
205
407
|
}
|
|
206
408
|
};
|
|
207
|
-
}
|
|
208
409
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
): Promise<void> {
|
|
215
|
-
const client = await ChatClient.init(
|
|
216
|
-
host,
|
|
217
|
-
port,
|
|
218
|
-
apiKey,
|
|
219
|
-
getCLIOnMessage(),
|
|
220
|
-
async () => {},
|
|
221
|
-
NODE_PLATFORM,
|
|
222
|
-
session
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
for (let i = 1; i <= 60; i++) {
|
|
226
|
-
await new Promise((r) => setTimeout(r, 1000));
|
|
227
|
-
|
|
228
|
-
const message: ClientUserMessage = {
|
|
229
|
-
type: "msg",
|
|
230
|
-
message: `add ${i} and ${i + 1}`,
|
|
231
|
-
};
|
|
232
|
-
client.sendMessage(message);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
client.close();
|
|
410
|
+
return {
|
|
411
|
+
onMessage,
|
|
412
|
+
onError,
|
|
413
|
+
onClosed: onClosed,
|
|
414
|
+
};
|
|
236
415
|
}
|
|
237
416
|
|
|
238
417
|
export const chatMain = subcommands({
|
|
@@ -240,5 +419,9 @@ export const chatMain = subcommands({
|
|
|
240
419
|
cmds: {
|
|
241
420
|
server,
|
|
242
421
|
client,
|
|
422
|
+
"clear-sessions": clearSessions,
|
|
423
|
+
"delete-session": deleteSession,
|
|
424
|
+
"list-sessions": listSessions,
|
|
425
|
+
"list-participants": listParticipants,
|
|
243
426
|
},
|
|
244
427
|
});
|
|
@@ -11,6 +11,8 @@ import { IMcpServerManager } from "../agent/mcpServerManager";
|
|
|
11
11
|
|
|
12
12
|
import { IPrompt } from "./prompt";
|
|
13
13
|
|
|
14
|
+
type OnUnknownCommand = (cmds: string[]) => void;
|
|
15
|
+
|
|
14
16
|
/**
|
|
15
17
|
* A prompt parser which can accept commands or messages from an IPrompt.
|
|
16
18
|
* Commands are sent to an IMcpServerManager, ISkillManager and IConversation.
|
|
@@ -28,8 +30,10 @@ export class CommandPrompt {
|
|
|
28
30
|
|
|
29
31
|
async getNextPrompt(
|
|
30
32
|
agent: IConversation,
|
|
31
|
-
sudoMcpServerManager: ISkillManager
|
|
33
|
+
sudoMcpServerManager: ISkillManager,
|
|
34
|
+
onUnknownCommand?: OnUnknownCommand
|
|
32
35
|
): Promise<[string | undefined, string | undefined]> {
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
33
37
|
while (true) {
|
|
34
38
|
// Get a line, detecting the EOF signal.
|
|
35
39
|
const line = await this.prompt.run();
|
|
@@ -64,9 +68,14 @@ export class CommandPrompt {
|
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
try {
|
|
67
|
-
await this.runCommand(
|
|
71
|
+
await this.runCommand(
|
|
72
|
+
agent,
|
|
73
|
+
sudoMcpServerManager,
|
|
74
|
+
cmds,
|
|
75
|
+
onUnknownCommand
|
|
76
|
+
);
|
|
68
77
|
} catch (e) {
|
|
69
|
-
console.log(`ERROR: ${e}`);
|
|
78
|
+
console.log(`ERROR: ${String(e)}`);
|
|
70
79
|
}
|
|
71
80
|
}
|
|
72
81
|
}
|
|
@@ -74,7 +83,8 @@ export class CommandPrompt {
|
|
|
74
83
|
async runCommand(
|
|
75
84
|
agent: IConversation,
|
|
76
85
|
sudoMcpServerManager: ISkillManager,
|
|
77
|
-
cmds: string[]
|
|
86
|
+
cmds: string[],
|
|
87
|
+
onUnknownCommand?: OnUnknownCommand
|
|
78
88
|
) {
|
|
79
89
|
switch (cmds[0]) {
|
|
80
90
|
case "lt":
|
|
@@ -117,7 +127,11 @@ export class CommandPrompt {
|
|
|
117
127
|
this.helpMenu();
|
|
118
128
|
break;
|
|
119
129
|
default:
|
|
120
|
-
|
|
130
|
+
if (onUnknownCommand) {
|
|
131
|
+
onUnknownCommand(cmds);
|
|
132
|
+
} else {
|
|
133
|
+
console.log(`error: Unknown command ${cmds[0]}`);
|
|
134
|
+
}
|
|
121
135
|
}
|
|
122
136
|
}
|
|
123
137
|
|
|
@@ -147,7 +161,7 @@ export class CommandPrompt {
|
|
|
147
161
|
const tools = server.getTools();
|
|
148
162
|
const enabled = server.getEnabledTools();
|
|
149
163
|
for (const tool of tools) {
|
|
150
|
-
const isEnabled = enabled
|
|
164
|
+
const isEnabled = enabled.get(tool.name) ? "*" : " ";
|
|
151
165
|
console.log(` [${isEnabled}] ${tool.name}`);
|
|
152
166
|
}
|
|
153
167
|
}
|
|
@@ -171,8 +185,8 @@ export class CommandPrompt {
|
|
|
171
185
|
await sudoMcpServerManager.addMcpServer(serverName, true);
|
|
172
186
|
} catch (e) {
|
|
173
187
|
if (e instanceof InvalidConfiguration) {
|
|
174
|
-
throw `${e}. \n
|
|
175
|
-
Have you installed server ${serverName} using the SudoMCP CLI tool
|
|
188
|
+
throw new Error(`${e.msg}. \n
|
|
189
|
+
Have you installed server ${serverName} using the SudoMCP CLI tool?`);
|
|
176
190
|
} else {
|
|
177
191
|
throw e;
|
|
178
192
|
}
|
|
@@ -201,7 +215,9 @@ Have you installed server ${serverName} using the SudoMCP CLI tool?`;
|
|
|
201
215
|
): Promise<boolean> {
|
|
202
216
|
displayToolCall(toolCall);
|
|
203
217
|
const response = await this.prompt.run("Approve tool call? (Y/n) ");
|
|
204
|
-
return
|
|
218
|
+
return (
|
|
219
|
+
response?.toLowerCase() === "y" || response === "yes" || response == ""
|
|
220
|
+
);
|
|
205
221
|
}
|
|
206
222
|
}
|
|
207
223
|
|
package/src/tool/files.ts
CHANGED
|
@@ -23,7 +23,7 @@ export async function loadFileOrStdin(file: string): Promise<string> {
|
|
|
23
23
|
process.stdin.setEncoding("utf8");
|
|
24
24
|
|
|
25
25
|
process.stdin.on("data", (chunk) => {
|
|
26
|
-
data += chunk;
|
|
26
|
+
data += chunk.toString();
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
process.stdin.on("end", () => {
|
|
@@ -39,26 +39,30 @@ export async function loadFileOrStdin(file: string): Promise<string> {
|
|
|
39
39
|
return fs.readFileSync(file, { encoding: "utf8" });
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
export function loadImageB64(file: string): string {
|
|
43
|
+
const ext = path.extname(file).slice(1).toLowerCase();
|
|
44
|
+
const mimeType = {
|
|
45
|
+
jpg: "image/jpeg",
|
|
46
|
+
jpeg: "image/jpeg",
|
|
47
|
+
png: "image/png",
|
|
48
|
+
gif: "image/gif",
|
|
49
|
+
svg: "image/svg+xml",
|
|
50
|
+
webp: "image/webp",
|
|
51
|
+
}[ext];
|
|
52
|
+
if (!mimeType) {
|
|
53
|
+
throw Error(`invalid file extension: ${ext}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const fileBuffer = fs.readFileSync(file);
|
|
57
|
+
const imgB64 = fileBuffer.toString("base64");
|
|
58
|
+
return `data:${mimeType};base64,${imgB64}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
42
61
|
export function loadImageB64OrUndefined(
|
|
43
62
|
file: string | undefined
|
|
44
63
|
): string | undefined {
|
|
45
64
|
if (file) {
|
|
46
|
-
|
|
47
|
-
const mimeType = {
|
|
48
|
-
jpg: "image/jpeg",
|
|
49
|
-
jpeg: "image/jpeg",
|
|
50
|
-
png: "image/png",
|
|
51
|
-
gif: "image/gif",
|
|
52
|
-
svg: "image/svg+xml",
|
|
53
|
-
webp: "image/webp",
|
|
54
|
-
}[ext];
|
|
55
|
-
if (!mimeType) {
|
|
56
|
-
throw Error(`invalid file extension: ${ext}`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const fileBuffer = fs.readFileSync(file);
|
|
60
|
-
const imgB64 = fileBuffer.toString("base64");
|
|
61
|
-
return `data:${mimeType};base64,${imgB64}`;
|
|
65
|
+
return loadImageB64(file);
|
|
62
66
|
}
|
|
63
67
|
return undefined;
|
|
64
68
|
}
|
|
@@ -66,7 +70,7 @@ export function loadImageB64OrUndefined(
|
|
|
66
70
|
export function loadServerUrls(path: string): McpServerUrls {
|
|
67
71
|
try {
|
|
68
72
|
const file = fs.readFileSync(path, "utf-8");
|
|
69
|
-
const urls = JSON.parse(file);
|
|
73
|
+
const urls: unknown = JSON.parse(file);
|
|
70
74
|
|
|
71
75
|
// Validate the structure
|
|
72
76
|
if (typeof urls !== "object" || urls === null) {
|
|
@@ -74,7 +78,9 @@ export function loadServerUrls(path: string): McpServerUrls {
|
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
// Validate each URL is a string
|
|
77
|
-
for (const [key, value] of Object.entries(
|
|
81
|
+
for (const [key, value] of Object.entries(
|
|
82
|
+
urls as Record<string, unknown>
|
|
83
|
+
)) {
|
|
78
84
|
if (typeof value !== "string") {
|
|
79
85
|
throw new Error(
|
|
80
86
|
`Invalid URL format for server ${key}: must be a string`
|