@xalia/agent 0.6.8 → 0.6.9
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/.env.development +1 -0
- package/dist/agent/src/agent/agent.js +100 -77
- package/dist/agent/src/agent/agentUtils.js +21 -16
- package/dist/agent/src/agent/compressingContextManager.js +10 -14
- package/dist/agent/src/agent/context.js +101 -127
- package/dist/agent/src/agent/contextWithWorkspace.js +133 -0
- package/dist/agent/src/agent/imageGenLLM.js +0 -6
- package/dist/agent/src/agent/imageGenerator.js +2 -10
- package/dist/agent/src/agent/openAILLMStreaming.js +5 -2
- package/dist/agent/src/agent/sudoMcpServerManager.js +21 -9
- package/dist/agent/src/chat/client/chatClient.js +35 -2
- package/dist/agent/src/chat/client/connection.js +6 -1
- package/dist/agent/src/chat/client/sessionClient.js +0 -7
- package/dist/agent/src/chat/data/dbSessionMessages.js +11 -0
- package/dist/agent/src/chat/protocol/messages.js +4 -0
- package/dist/agent/src/chat/server/chatContextManager.js +149 -139
- package/dist/agent/src/chat/server/imageGeneratorTools.js +19 -8
- package/dist/agent/src/chat/server/openAIRouterLLM.js +114 -0
- package/dist/agent/src/chat/server/openSession.js +57 -58
- package/dist/agent/src/chat/server/server.js +6 -2
- package/dist/agent/src/chat/server/sessionRegistry.js +65 -6
- package/dist/agent/src/chat/server/sessionRegistry.test.js +1 -1
- package/dist/agent/src/chat/server/tools.js +52 -17
- package/dist/agent/src/test/chatContextManager.test.js +31 -29
- package/dist/agent/src/test/clientServerConnection.test.js +1 -2
- package/dist/agent/src/test/compressingContextManager.test.js +22 -36
- package/dist/agent/src/test/context.test.js +55 -17
- package/dist/agent/src/test/contextTestTools.js +87 -0
- package/dist/agent/src/tool/chatMain.js +22 -8
- package/package.json +1 -1
- package/scripts/test_chat +3 -0
- package/src/agent/agent.ts +170 -125
- package/src/agent/agentUtils.ts +31 -20
- package/src/agent/compressingContextManager.ts +13 -44
- package/src/agent/context.ts +165 -159
- package/src/agent/contextWithWorkspace.ts +162 -0
- package/src/agent/imageGenLLM.ts +0 -8
- package/src/agent/imageGenerator.ts +3 -18
- package/src/agent/openAILLMStreaming.ts +20 -3
- package/src/agent/sudoMcpServerManager.ts +41 -20
- package/src/chat/client/chatClient.ts +47 -3
- package/src/chat/client/connection.ts +11 -1
- package/src/chat/client/sessionClient.ts +0 -8
- package/src/chat/data/dataModels.ts +6 -0
- package/src/chat/data/dbSessionMessages.ts +34 -0
- package/src/chat/protocol/messages.ts +35 -8
- package/src/chat/server/chatContextManager.ts +210 -197
- package/src/chat/server/connectionManager.ts +1 -1
- package/src/chat/server/imageGeneratorTools.ts +31 -18
- package/src/chat/server/openAIRouterLLM.ts +171 -0
- package/src/chat/server/openSession.ts +87 -100
- package/src/chat/server/server.ts +6 -2
- package/src/chat/server/sessionFileManager.ts +5 -5
- package/src/chat/server/sessionRegistry.test.ts +0 -1
- package/src/chat/server/sessionRegistry.ts +100 -4
- package/src/chat/server/tools.ts +73 -35
- package/src/test/agent.test.ts +8 -7
- package/src/test/chatContextManager.test.ts +42 -37
- package/src/test/clientServerConnection.test.ts +0 -2
- package/src/test/compressingContextManager.test.ts +29 -34
- package/src/test/context.test.ts +59 -15
- package/src/test/contextTestTools.ts +95 -0
- package/src/tool/chatMain.ts +26 -12
|
@@ -56,7 +56,7 @@ const MESSAGES = [
|
|
|
56
56
|
content: { role: "assistant", content: "msg502" },
|
|
57
57
|
},
|
|
58
58
|
];
|
|
59
|
-
function testSuccessfulAgentLoop(cm, startIdx) {
|
|
59
|
+
async function testSuccessfulAgentLoop(cm, startIdx) {
|
|
60
60
|
// 2 user messages arrive as ClientUserMessage. Assign message indices and
|
|
61
61
|
// convert them to ServerUserMessages.
|
|
62
62
|
const serverUserMessage0 = cm.processUserMessage({ type: "msg", message: "UserMessage0" }, "user0", "User0");
|
|
@@ -83,9 +83,9 @@ function testSuccessfulAgentLoop(cm, startIdx) {
|
|
|
83
83
|
// MESSAGE_INDEX_FULL_INCREMENT
|
|
84
84
|
(0, assert_1.strict)(serverUserMessage0 && serverUserMessage1);
|
|
85
85
|
const userMsgs = [serverUserMessage0, serverUserMessage1];
|
|
86
|
-
const {
|
|
86
|
+
const { contextTx, agentFirstChunk } = await cm.startAgentResponse(userMsgs);
|
|
87
87
|
(0, vitest_1.expect)(agentFirstChunk.message_idx).eql(startIdx + 2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT);
|
|
88
|
-
(0, vitest_1.expect)(
|
|
88
|
+
(0, vitest_1.expect)(contextTx.getLLMContext().slice(1)).eql(userMsgs.map((su) => {
|
|
89
89
|
return {
|
|
90
90
|
role: "user",
|
|
91
91
|
name: su.user_uuid,
|
|
@@ -93,7 +93,7 @@ function testSuccessfulAgentLoop(cm, startIdx) {
|
|
|
93
93
|
};
|
|
94
94
|
}));
|
|
95
95
|
// The agent sends message chunks. Check the message_idx
|
|
96
|
-
const chunkMsg0 =
|
|
96
|
+
const chunkMsg0 = contextTx.processAgentMessageChunk("AgentMe", false);
|
|
97
97
|
(0, vitest_1.expect)(chunkMsg0).eql({
|
|
98
98
|
type: "agent_msg_chunk",
|
|
99
99
|
session_id: "some_session_id",
|
|
@@ -101,7 +101,7 @@ function testSuccessfulAgentLoop(cm, startIdx) {
|
|
|
101
101
|
message: "AgentMe",
|
|
102
102
|
end: false,
|
|
103
103
|
});
|
|
104
|
-
const chunkMsg1 =
|
|
104
|
+
const chunkMsg1 = contextTx.processAgentMessageChunk("ssage2", true);
|
|
105
105
|
(0, vitest_1.expect)(chunkMsg1).eql({
|
|
106
106
|
type: "agent_msg_chunk",
|
|
107
107
|
session_id: "some_session_id",
|
|
@@ -132,7 +132,8 @@ function testSuccessfulAgentLoop(cm, startIdx) {
|
|
|
132
132
|
],
|
|
133
133
|
content: "AgentMessage2",
|
|
134
134
|
};
|
|
135
|
-
|
|
135
|
+
contextTx.addMessage(agentResponseWithToolCall);
|
|
136
|
+
contextTx.processAgentResponse(agentResponseWithToolCall);
|
|
136
137
|
// Meanwhile, a new user message comes in. It is assigned idx
|
|
137
138
|
// startIdx + 3*MESSAGE_INDEX_FULL_INCREMENT.
|
|
138
139
|
const serverUserMessage3 = cm.processUserMessage({ type: "msg", message: "UserMessage3" }, "user3", "User3");
|
|
@@ -159,11 +160,13 @@ function testSuccessfulAgentLoop(cm, startIdx) {
|
|
|
159
160
|
content: "tool_response_1",
|
|
160
161
|
},
|
|
161
162
|
];
|
|
162
|
-
|
|
163
|
+
contextTx.addMessage(toolCallResults[0]);
|
|
164
|
+
contextTx.addMessage(toolCallResults[1]);
|
|
165
|
+
const toolCallMesasge0 = contextTx.processToolCallResult(toolCallResults[0]);
|
|
163
166
|
(0, vitest_1.expect)(toolCallMesasge0.message_idx).eql(startIdx +
|
|
164
167
|
2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT +
|
|
165
168
|
1 * conversation_1.MESSAGE_INDEX_SUB_INCREMENT);
|
|
166
|
-
const toolCallMesasge1 =
|
|
169
|
+
const toolCallMesasge1 = contextTx.processToolCallResult(toolCallResults[1]);
|
|
167
170
|
(0, vitest_1.expect)(toolCallMesasge1.message_idx).eql(startIdx +
|
|
168
171
|
2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT +
|
|
169
172
|
2 * conversation_1.MESSAGE_INDEX_SUB_INCREMENT);
|
|
@@ -172,15 +175,11 @@ function testSuccessfulAgentLoop(cm, startIdx) {
|
|
|
172
175
|
role: "assistant",
|
|
173
176
|
content: "AgentMessage2",
|
|
174
177
|
};
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
cm.
|
|
180
|
-
cm.addMessage(finalAgentResponse);
|
|
181
|
-
// Agent then returns the final response to the caller (OpenSession)
|
|
182
|
-
cm.processAgentResponse(finalAgentResponse);
|
|
183
|
-
const dbMessages = cm.endAgentResponse();
|
|
178
|
+
contextTx.addMessage(finalAgentResponse);
|
|
179
|
+
// Agent then returns the final response to the caller (OpenSession), which
|
|
180
|
+
// sets it on the Tx.
|
|
181
|
+
contextTx.processAgentResponse(finalAgentResponse);
|
|
182
|
+
const dbMessages = await cm.endAgentResponse(contextTx);
|
|
184
183
|
// Expect
|
|
185
184
|
const expectDbMessages = [
|
|
186
185
|
{
|
|
@@ -287,7 +286,7 @@ describe("IndexingCompressingContextManager", () => {
|
|
|
287
286
|
lastEntryIdx: 600, // Should be max(502, 600) = 600
|
|
288
287
|
});
|
|
289
288
|
});
|
|
290
|
-
it("conversation processing behaviour", () => {
|
|
289
|
+
it("conversation processing behaviour", async () => {
|
|
291
290
|
const writer = {
|
|
292
291
|
writeCheckpoint: (_sc) => {
|
|
293
292
|
return new Promise((r) => {
|
|
@@ -295,10 +294,10 @@ describe("IndexingCompressingContextManager", () => {
|
|
|
295
294
|
});
|
|
296
295
|
},
|
|
297
296
|
};
|
|
298
|
-
const cm = new chatContextManager_1.ChatContextManager("sys_prompt", [], "some_session_id", "no_such_user", undefined,
|
|
299
|
-
testSuccessfulAgentLoop(cm, 0);
|
|
297
|
+
const cm = new chatContextManager_1.ChatContextManager("sys_prompt", [], "some_session_id", "no_such_user", undefined, writer, new sessionFileManager_1.MemoryFileManager(), undefined);
|
|
298
|
+
await testSuccessfulAgentLoop(cm, 0);
|
|
300
299
|
});
|
|
301
|
-
it("error handling", () => {
|
|
300
|
+
it("error handling", async () => {
|
|
302
301
|
const writer = {
|
|
303
302
|
writeCheckpoint: (_sc) => {
|
|
304
303
|
return new Promise((r) => {
|
|
@@ -306,7 +305,7 @@ describe("IndexingCompressingContextManager", () => {
|
|
|
306
305
|
});
|
|
307
306
|
},
|
|
308
307
|
};
|
|
309
|
-
const cm = new chatContextManager_1.ChatContextManager("sys_prompt", [], "some_session_id", "no_such_user", undefined,
|
|
308
|
+
const cm = new chatContextManager_1.ChatContextManager("sys_prompt", [], "some_session_id", "no_such_user", undefined, writer, new sessionFileManager_1.MemoryFileManager(), undefined);
|
|
310
309
|
// 1 user messages
|
|
311
310
|
const serverUserMessage0 = cm.processUserMessage({ type: "msg", message: "UserMessage0" }, "user0", "User0");
|
|
312
311
|
(0, vitest_1.expect)(serverUserMessage0).eql({
|
|
@@ -322,11 +321,11 @@ describe("IndexingCompressingContextManager", () => {
|
|
|
322
321
|
// Agent response has a tool call. It is assigned 1 *
|
|
323
322
|
// MESSAGE_INDEX_FULL_INCREMENT
|
|
324
323
|
(0, assert_1.strict)(serverUserMessage0);
|
|
325
|
-
const {
|
|
324
|
+
const { contextTx, agentFirstChunk } = await cm.startAgentResponse([
|
|
326
325
|
serverUserMessage0,
|
|
327
326
|
]);
|
|
328
327
|
(0, vitest_1.expect)(agentFirstChunk.message_idx).eql(1 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT);
|
|
329
|
-
(0, vitest_1.expect)(
|
|
328
|
+
(0, vitest_1.expect)(contextTx.getLLMContext().slice(1)).eql([
|
|
330
329
|
{
|
|
331
330
|
role: "user",
|
|
332
331
|
name: serverUserMessage0.user_uuid,
|
|
@@ -356,7 +355,8 @@ describe("IndexingCompressingContextManager", () => {
|
|
|
356
355
|
],
|
|
357
356
|
content: "AgentMessage2",
|
|
358
357
|
};
|
|
359
|
-
|
|
358
|
+
contextTx.addMessage(agentResponseWithToolCall);
|
|
359
|
+
contextTx.processAgentResponse(agentResponseWithToolCall);
|
|
360
360
|
// The current Agent does not add the tool call results until the next
|
|
361
361
|
// completion has run. Here we are simulating an error during that
|
|
362
362
|
// process, but the Agent still sends the tool call results to the event
|
|
@@ -376,16 +376,18 @@ describe("IndexingCompressingContextManager", () => {
|
|
|
376
376
|
content: "tool_response_1",
|
|
377
377
|
},
|
|
378
378
|
];
|
|
379
|
-
|
|
379
|
+
contextTx.addMessage(toolCallResults[0]);
|
|
380
|
+
contextTx.addMessage(toolCallResults[1]);
|
|
381
|
+
const toolCallMesasge0 = contextTx.processToolCallResult(toolCallResults[0]);
|
|
380
382
|
(0, vitest_1.expect)(toolCallMesasge0.message_idx).eql(1 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT + 1 * conversation_1.MESSAGE_INDEX_SUB_INCREMENT);
|
|
381
|
-
const toolCallMesasge1 =
|
|
383
|
+
const toolCallMesasge1 = contextTx.processToolCallResult(toolCallResults[1]);
|
|
382
384
|
(0, vitest_1.expect)(toolCallMesasge1.message_idx).eql(1 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT + 2 * conversation_1.MESSAGE_INDEX_SUB_INCREMENT);
|
|
383
385
|
// The error is caught by OpenSession, which informs the
|
|
384
386
|
// ChatContextManager.
|
|
385
|
-
|
|
387
|
+
contextTx.revertAgentResponse("an error occured");
|
|
386
388
|
// We should now be able to run the original test (starting from index
|
|
387
389
|
// 2*MESSAGE_INDEX_FULL_INCREMENT). None of our previous messages should
|
|
388
390
|
// hit the DB or the LLM context.
|
|
389
|
-
testSuccessfulAgentLoop(cm, 2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT);
|
|
391
|
+
await testSuccessfulAgentLoop(cm, 2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT);
|
|
390
392
|
});
|
|
391
393
|
});
|
|
@@ -25,7 +25,6 @@ const logger = (0, sdk_1.getLogger)();
|
|
|
25
25
|
const mockEnv = {
|
|
26
26
|
SUPABASE_URL: database_2.SUPABASE_LOCAL_URL,
|
|
27
27
|
SUPABASE_KEY: database_2.SUPABASE_LOCAL_KEY,
|
|
28
|
-
LLM_URL: "http://localhost:8080", // not used in this test
|
|
29
28
|
XMCP_URL: "http://localhost:8081", // not used in this test
|
|
30
29
|
};
|
|
31
30
|
(0, vitest_1.beforeAll)(async () => {
|
|
@@ -43,7 +42,7 @@ const logger = (0, sdk_1.getLogger)();
|
|
|
43
42
|
logger.warn("[Test] Test data setup failed - will test with existing data:", error);
|
|
44
43
|
}
|
|
45
44
|
// Start the real server using runServer
|
|
46
|
-
server = await (0, server_1.runServer)(serverPort, mockEnv.SUPABASE_URL, mockEnv.SUPABASE_KEY, mockEnv.
|
|
45
|
+
server = await (0, server_1.runServer)(serverPort, mockEnv.SUPABASE_URL, mockEnv.SUPABASE_KEY, mockEnv.XMCP_URL);
|
|
47
46
|
// Wait for server to be ready
|
|
48
47
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
49
48
|
});
|
|
@@ -2,41 +2,27 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const vitest_1 = require("vitest");
|
|
4
4
|
const compressingContextManager_1 = require("../agent/compressingContextManager");
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
{ role: "assistant", content: "msg300" },
|
|
8
|
-
{ role: "user", content: "msg400" },
|
|
9
|
-
{
|
|
10
|
-
role: "assistant",
|
|
11
|
-
content: "msg500",
|
|
12
|
-
tool_calls: [
|
|
13
|
-
{
|
|
14
|
-
id: "tool_call_0",
|
|
15
|
-
type: "function",
|
|
16
|
-
function: { name: "tool1", arguments: "" },
|
|
17
|
-
},
|
|
18
|
-
],
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
role: "tool",
|
|
22
|
-
content: "msg501",
|
|
23
|
-
tool_call_id: "tool_call_0",
|
|
24
|
-
},
|
|
25
|
-
{ role: "assistant", content: "msg502" },
|
|
26
|
-
];
|
|
5
|
+
const contextTestTools_1 = require("./contextTestTools");
|
|
6
|
+
const agentUtils_1 = require("../agent/agentUtils");
|
|
27
7
|
describe("Compression context", () => {
|
|
28
|
-
it("context compression", async function () {
|
|
29
|
-
const ccm = new compressingContextManager_1.CompressingContextManager("sys_prompt", [],
|
|
30
|
-
"repeat", // llmModel
|
|
31
|
-
"" // llmApiKey
|
|
8
|
+
it("context compression (basic)", async function () {
|
|
9
|
+
const ccm = new compressingContextManager_1.CompressingContextManager("sys_prompt", [], () => undefined // getLLM
|
|
32
10
|
);
|
|
11
|
+
await (0, contextTestTools_1.basicContextTest)(ccm);
|
|
12
|
+
});
|
|
13
|
+
it("context compression", async function () {
|
|
14
|
+
const llm = await (0, agentUtils_1.createLLM)("", "", "repeat", false, undefined);
|
|
15
|
+
const ccm = new compressingContextManager_1.CompressingContextManager("sys_prompt", [], () => Promise.resolve(llm));
|
|
33
16
|
// Add first 2 messages
|
|
34
|
-
ccm.
|
|
35
|
-
|
|
17
|
+
let tx = await ccm.startTx([contextTestTools_1.MESSAGES[0]]);
|
|
18
|
+
tx.addMessage(contextTestTools_1.MESSAGES[1]);
|
|
19
|
+
await ccm.commit(tx);
|
|
36
20
|
// Start the compression
|
|
37
21
|
const compressionP = ccm.compress();
|
|
38
22
|
// Add more messages and update system prompt
|
|
39
|
-
ccm.
|
|
23
|
+
tx = await ccm.startTx([contextTestTools_1.MESSAGES[2]]);
|
|
24
|
+
tx.addMessages(contextTestTools_1.MESSAGES.slice(3));
|
|
25
|
+
await ccm.commit(tx);
|
|
40
26
|
ccm.setAgentPrompt("New system prompt");
|
|
41
27
|
// Wait for compression to complete, then get the new context
|
|
42
28
|
const summary = await compressionP;
|
|
@@ -51,15 +37,15 @@ describe("Compression context", () => {
|
|
|
51
37
|
content: "New system prompt",
|
|
52
38
|
},
|
|
53
39
|
(0, compressingContextManager_1.createCheckpointMessage)(expectSummary),
|
|
54
|
-
MESSAGES[2], // 400
|
|
55
|
-
MESSAGES[3], // 500
|
|
56
|
-
MESSAGES[4], // 501
|
|
57
|
-
MESSAGES[5], // 502
|
|
40
|
+
contextTestTools_1.MESSAGES[2], // 400
|
|
41
|
+
contextTestTools_1.MESSAGES[3], // 500
|
|
42
|
+
contextTestTools_1.MESSAGES[4], // 501
|
|
43
|
+
contextTestTools_1.MESSAGES[5], // 502
|
|
58
44
|
];
|
|
59
45
|
(0, vitest_1.expect)(summary).eql(expectSummary);
|
|
60
46
|
(0, vitest_1.expect)(context).eql(expectContext);
|
|
61
|
-
// Pending messages
|
|
62
|
-
const pending = ccm.getPending();
|
|
63
|
-
|
|
47
|
+
// // Pending messages should still be intact
|
|
48
|
+
// const pending = ccm.getPending();
|
|
49
|
+
// expect(pending).eql(MESSAGES.slice(2));
|
|
64
50
|
});
|
|
65
51
|
});
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const vitest_1 = require("vitest");
|
|
4
4
|
const assert_1 = require("assert");
|
|
5
|
-
const
|
|
5
|
+
const contextWithWorkspace_1 = require("../agent/contextWithWorkspace");
|
|
6
6
|
const agent_1 = require("../agent/agent");
|
|
7
7
|
const promptProvider_1 = require("../agent/promptProvider");
|
|
8
|
+
const context_1 = require("../agent/context");
|
|
9
|
+
const contextTestTools_1 = require("./contextTestTools");
|
|
8
10
|
describe("ContextManagerWithWorkspace", () => {
|
|
9
11
|
const msgs = [
|
|
10
12
|
{ role: "user", content: "message A" },
|
|
@@ -12,31 +14,53 @@ describe("ContextManagerWithWorkspace", () => {
|
|
|
12
14
|
{ role: "user", content: "message C" },
|
|
13
15
|
{ role: "assistant", content: "message D" },
|
|
14
16
|
];
|
|
15
|
-
it("
|
|
16
|
-
const cm = new context_1.
|
|
17
|
-
|
|
17
|
+
it("context (basic)", async function () {
|
|
18
|
+
const cm = new context_1.ContextManager("sys_prompt", []);
|
|
19
|
+
await (0, contextTestTools_1.basicContextTest)(cm);
|
|
20
|
+
});
|
|
21
|
+
it("context with workspace (basic)", async function () {
|
|
22
|
+
const cm = new contextWithWorkspace_1.ContextManagerWithWorkspace("sys_prompt", []);
|
|
23
|
+
await (0, contextTestTools_1.basicContextTest)(cm);
|
|
24
|
+
});
|
|
25
|
+
it("should correctly manage messages", async function () {
|
|
26
|
+
const cm = new contextWithWorkspace_1.ContextManagerWithWorkspace("sys_msg", msgs.slice(0, 2));
|
|
27
|
+
// set workspace and add new user and assistant messages
|
|
18
28
|
cm.setWorkspace({ role: "user", content: "workspace1" });
|
|
19
|
-
cm.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
(0, vitest_1.expect)(
|
|
29
|
+
const ctxTx1 = await cm.startTx([msgs[2]]);
|
|
30
|
+
ctxTx1.addMessage(msgs[3]);
|
|
31
|
+
// context writer. workspace should be before first assistant message
|
|
32
|
+
(0, vitest_1.expect)(ctxTx1.getLLMContext()).eql([
|
|
23
33
|
{ role: "system", content: "sys_msg" },
|
|
24
|
-
...msgs,
|
|
34
|
+
...msgs.slice(0, 3),
|
|
35
|
+
{ role: "user", content: "workspace1" },
|
|
36
|
+
...msgs.slice(3),
|
|
37
|
+
]);
|
|
38
|
+
// original cm unchanged
|
|
39
|
+
(0, vitest_1.expect)(cm.getLLMContext()).eql([
|
|
40
|
+
{ role: "system", content: "sys_msg" },
|
|
41
|
+
...msgs.slice(0, 2),
|
|
25
42
|
{ role: "user", content: "workspace1" },
|
|
26
43
|
]);
|
|
27
|
-
//
|
|
44
|
+
// Commit
|
|
45
|
+
await cm.commit(ctxTx1);
|
|
46
|
+
// write new messages
|
|
28
47
|
const lastMsg = {
|
|
29
48
|
role: "user",
|
|
30
49
|
content: "hello",
|
|
31
50
|
};
|
|
32
|
-
cm.
|
|
33
|
-
|
|
34
|
-
(0, vitest_1.expect)(ctx2).eql([
|
|
51
|
+
const ctxTx2 = await cm.startTx([lastMsg]);
|
|
52
|
+
(0, vitest_1.expect)(ctxTx2.getLLMContext()).eql([
|
|
35
53
|
{ role: "system", content: "sys_msg" },
|
|
36
54
|
...msgs,
|
|
37
55
|
lastMsg,
|
|
38
56
|
{ role: "user", content: "workspace1" },
|
|
39
57
|
]);
|
|
58
|
+
(0, vitest_1.expect)(cm.getLLMContext()).eql([
|
|
59
|
+
{ role: "system", content: "sys_msg" },
|
|
60
|
+
...msgs,
|
|
61
|
+
{ role: "user", content: "workspace1" },
|
|
62
|
+
]);
|
|
63
|
+
await cm.commit(ctxTx2);
|
|
40
64
|
// context without workspace
|
|
41
65
|
cm.setWorkspace(undefined);
|
|
42
66
|
const ctx3 = cm.getLLMContext();
|
|
@@ -47,7 +71,7 @@ describe("ContextManagerWithWorkspace", () => {
|
|
|
47
71
|
]);
|
|
48
72
|
});
|
|
49
73
|
it("correctly includes all system prompt components", function () {
|
|
50
|
-
const cm = new
|
|
74
|
+
const cm = new contextWithWorkspace_1.ContextManagerWithWorkspace("agent_prompt", msgs.slice(0, 2));
|
|
51
75
|
promptProvider_1.SystemPromptProvider.setGlobalPrompt("global");
|
|
52
76
|
cm.setPromptFragment("frag1", "FRAG1");
|
|
53
77
|
// Get the agentPrompt and llmContext, then undo the global prompt change
|
|
@@ -58,8 +82,8 @@ describe("ContextManagerWithWorkspace", () => {
|
|
|
58
82
|
(0, vitest_1.expect)(agentPrompt).eql("agent_prompt");
|
|
59
83
|
(0, vitest_1.expect)(llmContext[0].content).eql("global\nagent_prompt\nFRAG1");
|
|
60
84
|
});
|
|
61
|
-
it("handles rich workspace messages", function () {
|
|
62
|
-
const cm = new
|
|
85
|
+
it("handles rich workspace messages", async function () {
|
|
86
|
+
const cm = new contextWithWorkspace_1.ContextManagerWithWorkspace("sys_msg", msgs.slice(0, 2));
|
|
63
87
|
(0, vitest_1.expect)(cm.getLLMContext()).eql([
|
|
64
88
|
{ role: "system", content: "sys_msg" },
|
|
65
89
|
...msgs.slice(0, 2),
|
|
@@ -75,7 +99,21 @@ describe("ContextManagerWithWorkspace", () => {
|
|
|
75
99
|
...msgs.slice(0, 2),
|
|
76
100
|
wsMsg,
|
|
77
101
|
]);
|
|
78
|
-
cm.
|
|
102
|
+
const ctxTx = await cm.startTx([msgs[2]]);
|
|
103
|
+
(0, vitest_1.expect)(ctxTx.getLLMContext()).eql([
|
|
104
|
+
{ role: "system", content: "sys_msg" },
|
|
105
|
+
...msgs.slice(0, 3),
|
|
106
|
+
wsMsg,
|
|
107
|
+
]);
|
|
108
|
+
ctxTx.addMessage(msgs[3]);
|
|
109
|
+
(0, vitest_1.expect)(ctxTx.getLLMContext()).eql([
|
|
110
|
+
{ role: "system", content: "sys_msg" },
|
|
111
|
+
...msgs.slice(0, 3),
|
|
112
|
+
wsMsg,
|
|
113
|
+
msgs[3],
|
|
114
|
+
]);
|
|
115
|
+
(0, vitest_1.expect)(ctxTx.newMessages()).eql(msgs.slice(2, 4));
|
|
116
|
+
await cm.commit(ctxTx);
|
|
79
117
|
// context with workspace
|
|
80
118
|
const ctx1 = cm.getLLMContext();
|
|
81
119
|
(0, vitest_1.expect)(ctx1).eql([{ role: "system", content: "sys_msg" }, ...msgs, wsMsg]);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MESSAGES = void 0;
|
|
4
|
+
exports.basicContextTest = basicContextTest;
|
|
5
|
+
const vitest_1 = require("vitest");
|
|
6
|
+
exports.MESSAGES = [
|
|
7
|
+
{ role: "user", content: "msg200" },
|
|
8
|
+
{ role: "assistant", content: "msg300" },
|
|
9
|
+
{ role: "user", content: "msg400" },
|
|
10
|
+
{
|
|
11
|
+
role: "assistant",
|
|
12
|
+
content: "msg500",
|
|
13
|
+
tool_calls: [
|
|
14
|
+
{
|
|
15
|
+
id: "tool_call_0",
|
|
16
|
+
type: "function",
|
|
17
|
+
function: { name: "tool1", arguments: "" },
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
role: "tool",
|
|
23
|
+
content: "msg501",
|
|
24
|
+
tool_call_id: "tool_call_0",
|
|
25
|
+
},
|
|
26
|
+
{ role: "assistant", content: "msg502" },
|
|
27
|
+
];
|
|
28
|
+
async function basicContextTest(cm) {
|
|
29
|
+
(0, vitest_1.expect)(cm.getAgentPrompt()).eql("sys_prompt");
|
|
30
|
+
// Add first 2 messages
|
|
31
|
+
let tx = await cm.startTx([exports.MESSAGES[0]]);
|
|
32
|
+
tx.addMessage(exports.MESSAGES[1]);
|
|
33
|
+
(0, vitest_1.expect)(tx.getLLMContext()).eql([
|
|
34
|
+
{
|
|
35
|
+
role: "system",
|
|
36
|
+
content: "sys_prompt",
|
|
37
|
+
},
|
|
38
|
+
exports.MESSAGES[0],
|
|
39
|
+
exports.MESSAGES[1],
|
|
40
|
+
]);
|
|
41
|
+
await cm.commit(tx);
|
|
42
|
+
// Add more messages
|
|
43
|
+
tx = await cm.startTx([exports.MESSAGES[2]]);
|
|
44
|
+
(0, vitest_1.expect)(tx.getLLMContext()).eql([
|
|
45
|
+
{
|
|
46
|
+
role: "system",
|
|
47
|
+
content: "sys_prompt",
|
|
48
|
+
},
|
|
49
|
+
exports.MESSAGES[0],
|
|
50
|
+
exports.MESSAGES[1],
|
|
51
|
+
exports.MESSAGES[2],
|
|
52
|
+
]);
|
|
53
|
+
tx.addMessages(exports.MESSAGES.slice(3));
|
|
54
|
+
(0, vitest_1.expect)(tx.getLLMContext()).eql([
|
|
55
|
+
{
|
|
56
|
+
role: "system",
|
|
57
|
+
content: "sys_prompt",
|
|
58
|
+
},
|
|
59
|
+
exports.MESSAGES[0],
|
|
60
|
+
exports.MESSAGES[1],
|
|
61
|
+
exports.MESSAGES[2],
|
|
62
|
+
exports.MESSAGES[3],
|
|
63
|
+
exports.MESSAGES[4],
|
|
64
|
+
exports.MESSAGES[5],
|
|
65
|
+
]);
|
|
66
|
+
await cm.commit(tx);
|
|
67
|
+
// Update system prompt
|
|
68
|
+
cm.setAgentPrompt("New system prompt");
|
|
69
|
+
// Wait for compression to complete, then get the new context
|
|
70
|
+
const context = cm.getLLMContext();
|
|
71
|
+
// Expect
|
|
72
|
+
// - callback happened
|
|
73
|
+
// - new context is as expected
|
|
74
|
+
const expectContext = [
|
|
75
|
+
{
|
|
76
|
+
role: "system",
|
|
77
|
+
content: "New system prompt",
|
|
78
|
+
},
|
|
79
|
+
exports.MESSAGES[0], // 400
|
|
80
|
+
exports.MESSAGES[1], // 500
|
|
81
|
+
exports.MESSAGES[2], // 400
|
|
82
|
+
exports.MESSAGES[3], // 500
|
|
83
|
+
exports.MESSAGES[4], // 501
|
|
84
|
+
exports.MESSAGES[5], // 502
|
|
85
|
+
];
|
|
86
|
+
(0, vitest_1.expect)(context).eql(expectContext);
|
|
87
|
+
}
|
|
@@ -139,16 +139,15 @@ const server = (0, cmd_ts_1.command)({
|
|
|
139
139
|
}),
|
|
140
140
|
supabaseUrl: options.supabaseUrl,
|
|
141
141
|
supabaseKey: options.supabaseKey,
|
|
142
|
-
llmUrl: options.llmUrl,
|
|
143
142
|
xmcpUrl: options.xmcpUrl,
|
|
144
143
|
pidFile: options.pidFile,
|
|
145
144
|
},
|
|
146
145
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
147
|
-
handler: async ({ port, supabaseUrl, supabaseKey,
|
|
146
|
+
handler: async ({ port, supabaseUrl, supabaseKey, xmcpUrl, pidFile, }) => {
|
|
148
147
|
if (pidFile) {
|
|
149
148
|
fs.writeFileSync(pidFile, process.pid.toString());
|
|
150
149
|
}
|
|
151
|
-
void (0, server_1.runServer)(port, supabaseUrl, supabaseKey,
|
|
150
|
+
void (0, server_1.runServer)(port, supabaseUrl, supabaseKey, xmcpUrl);
|
|
152
151
|
},
|
|
153
152
|
});
|
|
154
153
|
const client = (0, cmd_ts_1.command)({
|
|
@@ -222,7 +221,9 @@ const client = (0, cmd_ts_1.command)({
|
|
|
222
221
|
${chalk_1.default.yellow("/get-file <name>")} Get file contents
|
|
223
222
|
${chalk_1.default.yellow("/share-session <file>")} Share and write access token
|
|
224
223
|
${chalk_1.default.yellow("/pause-agent <0|1>")} Pause (or unpause) the agent
|
|
225
|
-
${chalk_1.default.yellow("/add-mcp-server
|
|
224
|
+
${chalk_1.default.yellow("/add-custom-mcp-server <server-name> <url>")} Add MCP server
|
|
225
|
+
${chalk_1.default.yellow("/remove-custom-mcp-server <server-name>")} Remove MCP server
|
|
226
|
+
${chalk_1.default.yellow("/list-custom-mcp-servers")} list custom mcp servers
|
|
226
227
|
${chalk_1.default.yellow("/get-resource <server-name> <uri>")} get MCP server resource
|
|
227
228
|
`;
|
|
228
229
|
const cmdPrompt = new commandPrompt_1.CommandPrompt(repl, helpText);
|
|
@@ -291,8 +292,14 @@ const client = (0, cmd_ts_1.command)({
|
|
|
291
292
|
case "pause-agent":
|
|
292
293
|
pauseAgent(sessionClient, cmds[1]);
|
|
293
294
|
break;
|
|
294
|
-
case "add-mcp-server
|
|
295
|
-
|
|
295
|
+
case "add-custom-mcp-server":
|
|
296
|
+
addCustomMcpServer(client, cmds[1], cmds[2]);
|
|
297
|
+
break;
|
|
298
|
+
case "remove-custom-mcp-server":
|
|
299
|
+
removeCustomMcpServer(client, cmds[1]);
|
|
300
|
+
break;
|
|
301
|
+
case "list-custom-mcp-servers":
|
|
302
|
+
listCustomMcpServers(client);
|
|
296
303
|
break;
|
|
297
304
|
case "get-resource":
|
|
298
305
|
await getResource(sessionClient, cmds[1], cmds[2]);
|
|
@@ -364,8 +371,15 @@ async function shareSession(sessionClient, filename) {
|
|
|
364
371
|
function pauseAgent(sessionClient, pause) {
|
|
365
372
|
sessionClient.setAgentPaused(!!parseInt(pause));
|
|
366
373
|
}
|
|
367
|
-
function
|
|
368
|
-
|
|
374
|
+
function addCustomMcpServer(chatClient, name, url) {
|
|
375
|
+
chatClient.addCustomMcpServer(name, "Temporary description", url);
|
|
376
|
+
}
|
|
377
|
+
function removeCustomMcpServer(chatClient, name) {
|
|
378
|
+
chatClient.removeCustomMcpServer(name);
|
|
379
|
+
}
|
|
380
|
+
function listCustomMcpServers(chatClient) {
|
|
381
|
+
const customMcpServers = chatClient.getCustomMcpServers();
|
|
382
|
+
console.log(`Custom mcp serevrs:\n${JSON.stringify(customMcpServers, undefined, 2)}`);
|
|
369
383
|
}
|
|
370
384
|
async function getResource(sessionClient, serverName, resourceUrl) {
|
|
371
385
|
const contents = await sessionClient.getMcpResource(serverName, resourceUrl);
|
package/package.json
CHANGED
package/scripts/test_chat
CHANGED
|
@@ -171,6 +171,8 @@ pushd _test_chat
|
|
|
171
171
|
echo "Choose an animal and generate an image of it" > invoke_gen_image_script
|
|
172
172
|
echo ":list-files" >> invoke_gen_image_script
|
|
173
173
|
echo ":si invoke_gen_image.id" >> invoke_gen_image_script
|
|
174
|
+
echo ':add-custom-mcp-server custom-mcp http://localhost:8002' >> invoke_gen_image_script
|
|
175
|
+
echo ':list-custom-mcp-servers' >> invoke_gen_image_script
|
|
174
176
|
|
|
175
177
|
LOG_LEVEL=debug \
|
|
176
178
|
${agent} chat client \
|
|
@@ -181,6 +183,7 @@ pushd _test_chat
|
|
|
181
183
|
grep '"xalia/fileMimeType":"image/png"' invoke_gen_image_output.txt
|
|
182
184
|
grep 'structuredContent example' invoke_gen_image_output.txt
|
|
183
185
|
grep '_meta example' invoke_gen_image_output.txt
|
|
186
|
+
grep 'custom-mcp": ' invoke_gen_image_output.txt
|
|
184
187
|
|
|
185
188
|
# User 1 tries to join the session. Should be rejected.
|
|
186
189
|
|