@xalia/agent 0.5.0 → 0.5.1
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 +46 -7
- package/dist/{agent.js → agent/src/agent/agent.js} +5 -4
- package/dist/{agentUtils.js → agent/src/agent/agentUtils.js} +10 -9
- package/dist/{mcpServerManager.js → agent/src/agent/mcpServerManager.js} +2 -1
- package/dist/{sudoMcpServerManager.js → agent/src/agent/sudoMcpServerManager.js} +4 -4
- package/dist/agent/src/chat/apiKeyManager.js +23 -0
- package/dist/agent/src/chat/asyncQueue.js +41 -0
- package/dist/agent/src/chat/client.js +126 -0
- package/dist/agent/src/chat/conversationManager.js +173 -0
- package/dist/agent/src/chat/db.js +186 -0
- package/dist/agent/src/chat/messages.js +2 -0
- package/dist/agent/src/chat/server.js +158 -0
- package/dist/agent/src/index.js +2 -0
- package/dist/agent/src/test/db.test.js +73 -0
- package/dist/{test → agent/src/test}/imageLoad.test.js +1 -1
- package/dist/{test → agent/src/test}/mcpServerManager.test.js +1 -1
- package/dist/{test → agent/src/test}/prompt.test.js +1 -1
- package/dist/{test → agent/src/test}/sudoMcpServerManager.test.js +3 -3
- package/dist/{chat.js → agent/src/tool/agentChat.js} +5 -5
- package/dist/{main.js → agent/src/tool/agentMain.js} +9 -15
- package/dist/agent/src/tool/chatMain.js +207 -0
- package/dist/agent/src/tool/main.js +54 -0
- package/dist/{options.js → agent/src/tool/options.js} +36 -2
- package/dist/agent/src/utils/asyncLock.js +45 -0
- package/dist/supabase/database.types.js +8 -0
- package/eslint.config.mjs +14 -14
- package/package.json +9 -15
- package/scripts/test_chat +84 -0
- package/src/{agent.ts → agent/agent.ts} +22 -11
- package/src/{agentUtils.ts → agent/agentUtils.ts} +13 -14
- package/src/{mcpServerManager.ts → agent/mcpServerManager.ts} +2 -1
- package/src/{sudoMcpServerManager.ts → agent/sudoMcpServerManager.ts} +3 -3
- package/src/chat/apiKeyManager.ts +24 -0
- package/src/chat/asyncQueue.ts +51 -0
- package/src/chat/client.ts +142 -0
- package/src/chat/conversationManager.ts +283 -0
- package/src/chat/db.ts +264 -0
- package/src/chat/messages.ts +91 -0
- package/src/chat/server.ts +177 -0
- package/src/test/db.test.ts +103 -0
- package/src/test/imageLoad.test.ts +1 -1
- package/src/test/mcpServerManager.test.ts +1 -1
- package/src/test/prompt.test.ts +1 -1
- package/src/test/sudoMcpServerManager.test.ts +6 -10
- package/src/{chat.ts → tool/agentChat.ts} +26 -24
- package/src/{main.ts → tool/agentMain.ts} +12 -19
- package/src/tool/chatMain.ts +250 -0
- package/src/{files.ts → tool/files.ts} +1 -1
- package/src/tool/main.ts +25 -0
- package/src/{nodePlatform.ts → tool/nodePlatform.ts} +1 -1
- package/src/{options.ts → tool/options.ts} +40 -1
- package/src/utils/asyncLock.ts +43 -0
- package/test_data/simplecalc_profile.json +1 -1
- package/test_data/sudomcp_import_profile.json +1 -1
- package/test_data/test_script_profile.json +1 -1
- package/tsconfig.json +1 -1
- package/scripts/test_script +0 -60
- /package/dist/{dummyLLM.js → agent/src/agent/dummyLLM.js} +0 -0
- /package/dist/{iplatform.js → agent/src/agent/iplatform.js} +0 -0
- /package/dist/{llm.js → agent/src/agent/llm.js} +0 -0
- /package/dist/{openAILLM.js → agent/src/agent/openAILLM.js} +0 -0
- /package/dist/{openAILLMStreaming.js → agent/src/agent/openAILLMStreaming.js} +0 -0
- /package/dist/{tokenAuth.js → agent/src/agent/tokenAuth.js} +0 -0
- /package/dist/{tools.js → agent/src/agent/tools.js} +0 -0
- /package/dist/{files.js → agent/src/tool/files.js} +0 -0
- /package/dist/{nodePlatform.js → agent/src/tool/nodePlatform.js} +0 -0
- /package/dist/{prompt.js → agent/src/tool/prompt.js} +0 -0
- /package/src/{dummyLLM.ts → agent/dummyLLM.ts} +0 -0
- /package/src/{iplatform.ts → agent/iplatform.ts} +0 -0
- /package/src/{llm.ts → agent/llm.ts} +0 -0
- /package/src/{openAILLM.ts → agent/openAILLM.ts} +0 -0
- /package/src/{openAILLMStreaming.ts → agent/openAILLMStreaming.ts} +0 -0
- /package/src/{tokenAuth.ts → agent/tokenAuth.ts} +0 -0
- /package/src/{tools.ts → agent/tools.ts} +0 -0
- /package/src/{test/prompt.test.src → index.ts} +0 -0
- /package/src/{prompt.ts → tool/prompt.ts} +0 -0
@@ -0,0 +1,207 @@
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
36
|
+
exports.chatMain = void 0;
|
37
|
+
const cmd_ts_1 = require("cmd-ts");
|
38
|
+
const process_1 = require("process");
|
39
|
+
const fs = __importStar(require("fs"));
|
40
|
+
const tool_1 = require("@xalia/xmcp/tool");
|
41
|
+
const sdk_1 = require("@xalia/xmcp/sdk");
|
42
|
+
const client_1 = require("../chat/client");
|
43
|
+
const server_1 = require("../chat/server");
|
44
|
+
const prompt_1 = require("./prompt");
|
45
|
+
const options = __importStar(require("./options"));
|
46
|
+
const logger = (0, sdk_1.getLogger)();
|
47
|
+
const server = (0, cmd_ts_1.command)({
|
48
|
+
name: "server",
|
49
|
+
args: {
|
50
|
+
port: (0, cmd_ts_1.option)({
|
51
|
+
type: cmd_ts_1.number,
|
52
|
+
long: "port",
|
53
|
+
short: "p",
|
54
|
+
env: "CHAT_SERVER_PORT",
|
55
|
+
defaultValue: () => 5003,
|
56
|
+
}),
|
57
|
+
supabaseUrl: options.supabaseUrl,
|
58
|
+
supabaseKey: options.supabaseKey,
|
59
|
+
llmUrl: options.llmUrl,
|
60
|
+
xmcpUrl: options.xmcpUrl,
|
61
|
+
pidFile: options.pidFile,
|
62
|
+
},
|
63
|
+
handler: async ({ port, supabaseUrl, supabaseKey, llmUrl, xmcpUrl, pidFile, }) => {
|
64
|
+
if (pidFile) {
|
65
|
+
fs.writeFileSync(pidFile, process.pid.toString());
|
66
|
+
}
|
67
|
+
(0, server_1.runServer)(port, supabaseUrl, supabaseKey, llmUrl, xmcpUrl);
|
68
|
+
},
|
69
|
+
});
|
70
|
+
const client = (0, cmd_ts_1.command)({
|
71
|
+
name: "client",
|
72
|
+
args: {
|
73
|
+
host: (0, cmd_ts_1.option)({
|
74
|
+
type: cmd_ts_1.string,
|
75
|
+
long: "host",
|
76
|
+
short: "h",
|
77
|
+
env: "CHAT_SERVER_HOST",
|
78
|
+
defaultValue: () => "localhost",
|
79
|
+
}),
|
80
|
+
port: (0, cmd_ts_1.option)({
|
81
|
+
type: cmd_ts_1.number,
|
82
|
+
long: "port",
|
83
|
+
short: "p",
|
84
|
+
env: "CHAT_SERVER_PORT",
|
85
|
+
defaultValue: () => 5003,
|
86
|
+
}),
|
87
|
+
apiKey: options.apiKey,
|
88
|
+
session: (0, cmd_ts_1.option)({
|
89
|
+
type: cmd_ts_1.string,
|
90
|
+
long: "session",
|
91
|
+
description: "Session identifier (id, name, user/name)",
|
92
|
+
env: "SESSION",
|
93
|
+
}),
|
94
|
+
agentProfile: (0, cmd_ts_1.option)({
|
95
|
+
type: (0, cmd_ts_1.optional)(cmd_ts_1.string),
|
96
|
+
long: "agent-profile",
|
97
|
+
description: "Create new session using agent profile (id, name)",
|
98
|
+
env: "AGENT_PROFILE",
|
99
|
+
}),
|
100
|
+
script: (0, cmd_ts_1.option)({
|
101
|
+
type: (0, cmd_ts_1.optional)(cmd_ts_1.string),
|
102
|
+
long: "script",
|
103
|
+
description: "Script (file) to execute and then exit.",
|
104
|
+
}),
|
105
|
+
test: (0, cmd_ts_1.flag)({
|
106
|
+
long: "test",
|
107
|
+
description: "Run a test client and disconnect",
|
108
|
+
}),
|
109
|
+
},
|
110
|
+
handler: async ({ host, port, apiKey, session, agentProfile, script, test, }) => {
|
111
|
+
if (!apiKey) {
|
112
|
+
// TODO: configFIle param list in ../main.ts
|
113
|
+
const sudomcpConfig = tool_1.configuration.loadEnsureConfig();
|
114
|
+
apiKey = sudomcpConfig.api_key;
|
115
|
+
}
|
116
|
+
if (test) {
|
117
|
+
await runClientTest(host, port, apiKey, session);
|
118
|
+
return;
|
119
|
+
}
|
120
|
+
if (script) {
|
121
|
+
await runScript(host, port, apiKey, session, agentProfile, script);
|
122
|
+
return;
|
123
|
+
}
|
124
|
+
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);
|
127
|
+
logger.debug("client created");
|
128
|
+
while (true) {
|
129
|
+
const msgText = await repl.run("ME: ");
|
130
|
+
logger.debug(`prompt got '${msgText}'`);
|
131
|
+
if (msgText === undefined) {
|
132
|
+
logger.debug("exiting...");
|
133
|
+
client.close();
|
134
|
+
return;
|
135
|
+
}
|
136
|
+
if (msgText.length > 0) {
|
137
|
+
const msg = {
|
138
|
+
type: "msg",
|
139
|
+
message: msgText,
|
140
|
+
};
|
141
|
+
client.sendMessage(msg);
|
142
|
+
}
|
143
|
+
else {
|
144
|
+
logger.debug("(ignoring empty message)");
|
145
|
+
}
|
146
|
+
}
|
147
|
+
},
|
148
|
+
});
|
149
|
+
function getCLIOnMessage() {
|
150
|
+
let startMsg = true;
|
151
|
+
return (msg) => {
|
152
|
+
switch (msg.type) {
|
153
|
+
case "user_msg":
|
154
|
+
process_1.stdout.write(`[${msg.from}]: ${msg.message}\n`);
|
155
|
+
break;
|
156
|
+
case "agent_msg":
|
157
|
+
process_1.stdout.write(`[AGENT]: ${msg.message}\n`);
|
158
|
+
break;
|
159
|
+
case "agent_msg_chunk":
|
160
|
+
if (startMsg) {
|
161
|
+
process_1.stdout.write("[AGENT]: ");
|
162
|
+
startMsg = false;
|
163
|
+
}
|
164
|
+
process_1.stdout.write(msg.message);
|
165
|
+
if (msg.end) {
|
166
|
+
process_1.stdout.write("\n");
|
167
|
+
startMsg = true;
|
168
|
+
}
|
169
|
+
break;
|
170
|
+
default:
|
171
|
+
process_1.stdout.write(`(${JSON.stringify(msg)})\n`);
|
172
|
+
break;
|
173
|
+
}
|
174
|
+
};
|
175
|
+
}
|
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
|
+
async function runClientTest(host, port, apiKey, session) {
|
190
|
+
const client = await client_1.ChatClient.init(host, port, apiKey, getCLIOnMessage(), () => { }, session);
|
191
|
+
for (let i = 1; i <= 60; i++) {
|
192
|
+
await new Promise((r) => setTimeout(r, 1000));
|
193
|
+
const message = {
|
194
|
+
type: "msg",
|
195
|
+
message: `add ${i} and ${i + 1}`,
|
196
|
+
};
|
197
|
+
client.sendMessage(message);
|
198
|
+
}
|
199
|
+
client.close();
|
200
|
+
}
|
201
|
+
exports.chatMain = (0, cmd_ts_1.subcommands)({
|
202
|
+
name: "chat",
|
203
|
+
cmds: {
|
204
|
+
server,
|
205
|
+
client,
|
206
|
+
},
|
207
|
+
});
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
"use strict";
|
3
|
+
// -*- typescript -*-
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
5
|
+
if (k2 === undefined) k2 = k;
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
9
|
+
}
|
10
|
+
Object.defineProperty(o, k2, desc);
|
11
|
+
}) : (function(o, m, k, k2) {
|
12
|
+
if (k2 === undefined) k2 = k;
|
13
|
+
o[k2] = m[k];
|
14
|
+
}));
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
17
|
+
}) : function(o, v) {
|
18
|
+
o["default"] = v;
|
19
|
+
});
|
20
|
+
var __importStar = (this && this.__importStar) || (function () {
|
21
|
+
var ownKeys = function(o) {
|
22
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
23
|
+
var ar = [];
|
24
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
25
|
+
return ar;
|
26
|
+
};
|
27
|
+
return ownKeys(o);
|
28
|
+
};
|
29
|
+
return function (mod) {
|
30
|
+
if (mod && mod.__esModule) return mod;
|
31
|
+
var result = {};
|
32
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
33
|
+
__setModuleDefault(result, mod);
|
34
|
+
return result;
|
35
|
+
};
|
36
|
+
})();
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
38
|
+
const dotenv = __importStar(require("dotenv"));
|
39
|
+
const cmd_ts_1 = require("cmd-ts");
|
40
|
+
const chatMain_1 = require("./chatMain");
|
41
|
+
const agentMain_1 = require("./agentMain");
|
42
|
+
dotenv.config();
|
43
|
+
const main = (0, cmd_ts_1.subcommands)({
|
44
|
+
name: "chat",
|
45
|
+
cmds: {
|
46
|
+
agent: agentMain_1.agentMain,
|
47
|
+
chat: chatMain_1.chatMain,
|
48
|
+
},
|
49
|
+
});
|
50
|
+
function handleError(msg) {
|
51
|
+
console.error(msg);
|
52
|
+
process.exit(1);
|
53
|
+
}
|
54
|
+
(0, cmd_ts_1.run)(main, process.argv.slice(2)).catch(handleError);
|
@@ -1,9 +1,10 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.llmUrl = exports.llmApiKey = exports.approveToolsUpTo = exports.approveTools = exports.oneShot = exports.llmModel = exports.systemPromptFile = exports.imageFile = exports.promptFile = void 0;
|
3
|
+
exports.pidFile = exports.supabaseKey = exports.supabaseUrl = exports.apiKey = exports.xmcpUrl = exports.llmUrl = exports.llmApiKey = exports.approveToolsUpTo = exports.approveTools = exports.oneShot = exports.llmModel = exports.systemPromptFile = exports.imageFile = exports.promptFile = void 0;
|
4
4
|
exports.secretOption = secretOption;
|
5
5
|
const cmd_ts_1 = require("cmd-ts");
|
6
|
-
const agentUtils_1 = require("
|
6
|
+
const agentUtils_1 = require("../agent/agentUtils");
|
7
|
+
const db_1 = require("../chat/db");
|
7
8
|
/// Prevents env content from being displayed in the help text.
|
8
9
|
function secretOption({ long, short, env, description, }) {
|
9
10
|
if (env) {
|
@@ -77,3 +78,36 @@ exports.llmUrl = (0, cmd_ts_1.option)({
|
|
77
78
|
env: "LLM_URL",
|
78
79
|
defaultValue: () => agentUtils_1.DEFAULT_LLM_URL,
|
79
80
|
});
|
81
|
+
exports.xmcpUrl = (0, cmd_ts_1.option)({
|
82
|
+
type: cmd_ts_1.string,
|
83
|
+
long: "xmcp-url",
|
84
|
+
description: "XMCP URL (OpenAI compatible)",
|
85
|
+
env: "XMCP_URL",
|
86
|
+
defaultValue: () => "http://localhost:5001/",
|
87
|
+
});
|
88
|
+
exports.apiKey = (0, cmd_ts_1.option)({
|
89
|
+
type: (0, cmd_ts_1.optional)(cmd_ts_1.string),
|
90
|
+
long: "api-key",
|
91
|
+
short: "k",
|
92
|
+
description: "API key",
|
93
|
+
env: "API_KEY",
|
94
|
+
});
|
95
|
+
exports.supabaseUrl = (0, cmd_ts_1.option)({
|
96
|
+
type: cmd_ts_1.string,
|
97
|
+
long: "supabase-url",
|
98
|
+
description: "Supabase URL",
|
99
|
+
env: "SUPABASE_URL",
|
100
|
+
defaultValue: () => db_1.SUPABASE_LOCAL_URL,
|
101
|
+
});
|
102
|
+
exports.supabaseKey = (0, cmd_ts_1.option)({
|
103
|
+
type: cmd_ts_1.string,
|
104
|
+
long: "supabase-key",
|
105
|
+
description: "Supabase Key",
|
106
|
+
env: "SUPABASE_KEY",
|
107
|
+
defaultValue: () => db_1.SUPABASE_LOCAL_KEY,
|
108
|
+
});
|
109
|
+
exports.pidFile = (0, cmd_ts_1.option)({
|
110
|
+
type: (0, cmd_ts_1.optional)(cmd_ts_1.string),
|
111
|
+
long: "pid-file",
|
112
|
+
description: "write pid file",
|
113
|
+
});
|
@@ -0,0 +1,45 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.AsyncLock = void 0;
|
4
|
+
const assert_1 = require("assert");
|
5
|
+
/**
|
6
|
+
* Support awaiting exclusive access to a resource.
|
7
|
+
*/
|
8
|
+
class AsyncLock {
|
9
|
+
constructor() {
|
10
|
+
this.running = false;
|
11
|
+
this.callbacks = [];
|
12
|
+
}
|
13
|
+
lockAndProcess(cb) {
|
14
|
+
return new Promise((r, e) => {
|
15
|
+
this.callbacks.push(async () => {
|
16
|
+
try {
|
17
|
+
const result = await cb();
|
18
|
+
r(result);
|
19
|
+
}
|
20
|
+
catch (err) {
|
21
|
+
e(err);
|
22
|
+
}
|
23
|
+
});
|
24
|
+
if (!this.running) {
|
25
|
+
this.run();
|
26
|
+
}
|
27
|
+
});
|
28
|
+
}
|
29
|
+
async run() {
|
30
|
+
(0, assert_1.strict)(!this.running);
|
31
|
+
this.running = true;
|
32
|
+
while (this.callbacks.length > 0) {
|
33
|
+
const cb = this.callbacks.shift();
|
34
|
+
(0, assert_1.strict)(cb);
|
35
|
+
try {
|
36
|
+
await cb();
|
37
|
+
}
|
38
|
+
catch (e) {
|
39
|
+
(0, assert_1.strict)(false, `errors should not propagate: ${e}`);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
this.running = false;
|
43
|
+
}
|
44
|
+
}
|
45
|
+
exports.AsyncLock = AsyncLock;
|
package/eslint.config.mjs
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
import eslint from
|
2
|
-
import tseslint from
|
1
|
+
import eslint from "@eslint/js";
|
2
|
+
import tseslint from "typescript-eslint";
|
3
3
|
|
4
4
|
export default tseslint.config(
|
5
5
|
eslint.configs.recommended,
|
6
6
|
tseslint.configs.recommended,
|
7
7
|
{
|
8
|
-
|
8
|
+
rules: {
|
9
9
|
"@typescript-eslint/no-unused-vars": [
|
10
10
|
"error",
|
11
11
|
{
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
}
|
12
|
+
args: "all",
|
13
|
+
argsIgnorePattern: "^_",
|
14
|
+
caughtErrors: "all",
|
15
|
+
caughtErrorsIgnorePattern: "^_",
|
16
|
+
destructuredArrayIgnorePattern: "^_",
|
17
|
+
varsIgnorePattern: "^_",
|
18
|
+
ignoreRestSiblings: true,
|
19
|
+
},
|
20
20
|
],
|
21
21
|
"max-len": ["error", { code: 80 }],
|
22
|
-
"linebreak-style": ["error", "unix"]
|
23
|
-
}
|
24
|
-
}
|
22
|
+
"linebreak-style": ["error", "unix"],
|
23
|
+
},
|
24
|
+
},
|
25
25
|
);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@xalia/agent",
|
3
|
-
"version": "0.5.
|
3
|
+
"version": "0.5.1",
|
4
4
|
"keywords": [],
|
5
5
|
"author": "",
|
6
6
|
"license": "ISC",
|
@@ -9,34 +9,28 @@
|
|
9
9
|
"node": ">=20.0.0"
|
10
10
|
},
|
11
11
|
"bin": {
|
12
|
-
"agent": "dist/main.js"
|
12
|
+
"agent": "dist/agent/src/tool/main.js"
|
13
13
|
},
|
14
14
|
"scripts": {
|
15
|
-
"build": "tsc && node -e \"require('fs').chmodSync('dist/main.js', '755')\"",
|
15
|
+
"build": "tsc && node -e \"require('fs').chmodSync('dist/agent/src/tool/main.js', '755')\"",
|
16
16
|
"lint": "eslint src && yarn prettier --check src/**/*.ts",
|
17
17
|
"format": "yarn prettier --write src/**/*.ts",
|
18
|
-
"test": "yarn --emoji false build && mocha dist/test/**.js --reporter-option maxDiffSize=0"
|
18
|
+
"test": "yarn --emoji false build && mocha dist/agent/src/test/**.js --reporter-option maxDiffSize=0"
|
19
19
|
},
|
20
20
|
"dependencies": {
|
21
21
|
"@modelcontextprotocol/sdk": "=1.11.1",
|
22
|
+
"@supabase/supabase-js": "2.49.7",
|
22
23
|
"@types/readline-sync": "^1.4.8",
|
23
24
|
"chalk": "^4.1.2",
|
24
25
|
"cmd-ts": "^0.13.0",
|
25
26
|
"dotenv": "^16.5.0",
|
26
27
|
"openai": "=4.98.0",
|
27
28
|
"readline-sync": "^1.4.10",
|
28
|
-
"
|
29
|
+
"uuid": "^11.1.0",
|
30
|
+
"yocto-spinner": "^0.2.2",
|
31
|
+
"ws": "8.18.2"
|
29
32
|
},
|
30
33
|
"devDependencies": {
|
31
|
-
"@
|
32
|
-
"@types/chai": "^4.0.0",
|
33
|
-
"@types/mocha": "^10.0.10",
|
34
|
-
"@types/node": "^22.13.9",
|
35
|
-
"chai": "^4.0.0",
|
36
|
-
"eslint": "^9.24.0",
|
37
|
-
"mocha": "^11.1.0",
|
38
|
-
"prettier": "3.5.3",
|
39
|
-
"typescript": "^5.8.3",
|
40
|
-
"typescript-eslint": "^8.29.1"
|
34
|
+
"@types/ws": "^8.18.0"
|
41
35
|
}
|
42
36
|
}
|
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# Assumes backend and DB are already running
|
4
|
+
|
5
|
+
. ../mcppro/env/bin/activate
|
6
|
+
|
7
|
+
set -x
|
8
|
+
set -e
|
9
|
+
|
10
|
+
|
11
|
+
yarn
|
12
|
+
yarn build
|
13
|
+
agent=`realpath dist/agent/src/tool/main.js`
|
14
|
+
client=`realpath ../client/dist/src/tool/main.js`
|
15
|
+
|
16
|
+
function stop_server() {
|
17
|
+
if [ -e chat.pid ] ; then
|
18
|
+
kill `cat chat.pid` || echo -n
|
19
|
+
fi
|
20
|
+
}
|
21
|
+
|
22
|
+
stop_server
|
23
|
+
|
24
|
+
LOG_LEVEL=debug ${agent} chat server --pid-file chat.pid > chat_server.log 2>&1 &
|
25
|
+
sleep 1
|
26
|
+
|
27
|
+
mkdir -p _test_chat
|
28
|
+
pushd _test_chat
|
29
|
+
|
30
|
+
echo "XMCP_ADMIN_SECRET=admin_secret" > .env
|
31
|
+
|
32
|
+
# Create 2 users and give them api keys
|
33
|
+
|
34
|
+
${client} user get chatuser0 || \
|
35
|
+
mcppro dev add-user chatuser0 chatuser0@users.com "Europe/London"
|
36
|
+
${client} user get chatuser1 || \
|
37
|
+
mcppro dev add-user chatuser1 chatuser1@users.com "UTC"
|
38
|
+
|
39
|
+
apikey0=`${client} admin get-api-keys chatuser0 | jq -r .default.api_key`
|
40
|
+
if [ "null" == "${apikey0}" ] ; then
|
41
|
+
apikey0=`${client} admin create-api-key chatuser0 default [] | jq -r .api_key`
|
42
|
+
fi
|
43
|
+
apikey1=`${client} admin get-api-keys chatuser1 | jq -r .default.api_key`
|
44
|
+
if [ "null" == "${apikey1}" ] ; then
|
45
|
+
apikey1=`${client} admin create-api-key chatuser1 default [] | jq -r .api_key`
|
46
|
+
fi
|
47
|
+
|
48
|
+
echo "${apikey0}" > chatuser0.apikey
|
49
|
+
echo "${apikey1}" > chatuser1.apikey
|
50
|
+
|
51
|
+
# User 0 creates an agent profile
|
52
|
+
|
53
|
+
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
|
54
|
+
${client} config --force --dev --api-key ${apikey0}
|
55
|
+
${client} agent-profile set profile0 --profile profile0.json
|
56
|
+
|
57
|
+
# User 0 creates a session and asks for a joke
|
58
|
+
|
59
|
+
echo 'tell me a joke' > script0
|
60
|
+
${agent} chat client --session test_session --agent-profile profile0 --script script0
|
61
|
+
|
62
|
+
# Run both clients with a script that continues the conversation
|
63
|
+
|
64
|
+
echo -e "why is that funny?\ntell me another.\nwhat is my name?\nhow old are you?\nwhere are your parents?" > script1
|
65
|
+
${agent} chat client --session chatuser0/test_session --api-key ${apikey1} --script script1 >chatuser1.output &
|
66
|
+
${agent} chat client --session chatuser0/test_session --script script1
|
67
|
+
|
68
|
+
# The name chatuser0 should appear in chatuser1's session output
|
69
|
+
|
70
|
+
if ! (grep chatuser0 chatuser1.output) ; then
|
71
|
+
echo "error: expected chatuser0 in chatuser1's output"
|
72
|
+
exit 1
|
73
|
+
fi
|
74
|
+
|
75
|
+
popd
|
76
|
+
|
77
|
+
stop_server
|
78
|
+
|
79
|
+
set +e
|
80
|
+
set +x
|
81
|
+
|
82
|
+
echo "============================"
|
83
|
+
echo "== CHAT TEST PASSED =="
|
84
|
+
echo "============================"
|
@@ -14,6 +14,11 @@ export type ToolHandler = (args: unknown) => string;
|
|
14
14
|
|
15
15
|
export type McpServerUrls = (name: string) => string;
|
16
16
|
|
17
|
+
export type ChatCompletionMessageParam = OpenAI.ChatCompletionMessageParam;
|
18
|
+
|
19
|
+
export type ChatCompletionMessageToolCall =
|
20
|
+
OpenAI.ChatCompletionMessageToolCall;
|
21
|
+
|
17
22
|
// Role: If content, give it to UI
|
18
23
|
export type OnMessageCB = {
|
19
24
|
(msg: string, msgEnd: boolean): Promise<void>;
|
@@ -21,7 +26,7 @@ export type OnMessageCB = {
|
|
21
26
|
|
22
27
|
// Role: If tool calls, prompt for permission to handle them
|
23
28
|
export type OnToolCallCB = {
|
24
|
-
(msg:
|
29
|
+
(msg: ChatCompletionMessageToolCall): Promise<boolean>;
|
25
30
|
};
|
26
31
|
|
27
32
|
dotenv.config();
|
@@ -33,7 +38,7 @@ export class Agent {
|
|
33
38
|
private constructor(
|
34
39
|
public onMessage: OnMessageCB,
|
35
40
|
public onToolCall: OnToolCallCB,
|
36
|
-
private messages:
|
41
|
+
private messages: ChatCompletionMessageParam[],
|
37
42
|
private mcpServerManager: McpServerManager,
|
38
43
|
private tools: OpenAI.ChatCompletionTool[],
|
39
44
|
private llm: ILLM
|
@@ -78,7 +83,7 @@ export class Agent {
|
|
78
83
|
);
|
79
84
|
}
|
80
85
|
|
81
|
-
public getConversation():
|
86
|
+
public getConversation(): ChatCompletionMessageParam[] {
|
82
87
|
assert(
|
83
88
|
this.messages[0].role == "system",
|
84
89
|
"first message must have system role"
|
@@ -88,11 +93,14 @@ export class Agent {
|
|
88
93
|
return structuredClone(this.messages.slice(1));
|
89
94
|
}
|
90
95
|
|
91
|
-
public setConversation(messages:
|
96
|
+
public setConversation(messages: ChatCompletionMessageParam[]) {
|
92
97
|
assert(this.messages[0].role == "system");
|
93
|
-
assert(
|
98
|
+
assert(
|
99
|
+
messages.length === 0 || messages[0].role != "system",
|
100
|
+
"conversation contains system msg"
|
101
|
+
);
|
94
102
|
|
95
|
-
const newMessages:
|
103
|
+
const newMessages: ChatCompletionMessageParam[] = [this.messages[0]];
|
96
104
|
this.messages = newMessages.concat(structuredClone(messages));
|
97
105
|
}
|
98
106
|
|
@@ -102,9 +110,10 @@ export class Agent {
|
|
102
110
|
|
103
111
|
public async userMessage(
|
104
112
|
msg?: string,
|
105
|
-
imageB64?: string
|
106
|
-
|
107
|
-
|
113
|
+
imageB64?: string,
|
114
|
+
name?: string
|
115
|
+
): Promise<ChatCompletionMessageParam | undefined> {
|
116
|
+
const userMessage = createUserMessage(msg, imageB64, name);
|
108
117
|
if (!userMessage) {
|
109
118
|
return undefined;
|
110
119
|
}
|
@@ -216,7 +225,7 @@ export class Agent {
|
|
216
225
|
}
|
217
226
|
|
218
227
|
async doToolCall(
|
219
|
-
toolCall:
|
228
|
+
toolCall: ChatCompletionMessageToolCall
|
220
229
|
): Promise<OpenAI.ChatCompletionToolMessageParam> {
|
221
230
|
const name = toolCall.function.name;
|
222
231
|
const args = JSON.parse(toolCall.function.arguments);
|
@@ -244,7 +253,8 @@ export class Agent {
|
|
244
253
|
**/
|
245
254
|
export function createUserMessage(
|
246
255
|
msg?: string,
|
247
|
-
imageB64?: string
|
256
|
+
imageB64?: string,
|
257
|
+
name?: string
|
248
258
|
): ChatCompletionUserMessageParam | undefined {
|
249
259
|
const content = (() => {
|
250
260
|
if (!imageB64) {
|
@@ -279,5 +289,6 @@ export function createUserMessage(
|
|
279
289
|
return {
|
280
290
|
role: "user",
|
281
291
|
content,
|
292
|
+
name,
|
282
293
|
};
|
283
294
|
}
|