@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
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const chatContextManager_1 = require("../chat/server/chatContextManager");
|
|
5
|
+
const agent_1 = require("../agent/agent");
|
|
6
|
+
const compressingContextManager_1 = require("../agent/compressingContextManager");
|
|
7
|
+
const conversation_1 = require("../chat/server/conversation");
|
|
8
|
+
const sessionFileManager_1 = require("../chat/server/sessionFileManager");
|
|
9
|
+
const MESSAGES = [
|
|
10
|
+
{
|
|
11
|
+
message_idx: 200,
|
|
12
|
+
is_for_llm: true,
|
|
13
|
+
sender_uuid: "user",
|
|
14
|
+
content: { role: "user", content: "msg200" },
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
message_idx: 300,
|
|
18
|
+
is_for_llm: true,
|
|
19
|
+
content: { role: "assistant", content: "msg300" },
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
message_idx: 400,
|
|
23
|
+
is_for_llm: true,
|
|
24
|
+
sender_uuid: "user",
|
|
25
|
+
content: { role: "user", content: "msg400" },
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
message_idx: 500,
|
|
29
|
+
is_for_llm: true,
|
|
30
|
+
content: {
|
|
31
|
+
role: "assistant",
|
|
32
|
+
content: "msg500",
|
|
33
|
+
tool_calls: [
|
|
34
|
+
{
|
|
35
|
+
id: "tool_call_0",
|
|
36
|
+
type: "function",
|
|
37
|
+
function: { name: "tool1", arguments: "" },
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
message_idx: 501,
|
|
44
|
+
is_for_llm: true,
|
|
45
|
+
content: {
|
|
46
|
+
role: "tool",
|
|
47
|
+
content: "msg501",
|
|
48
|
+
tool_call_id: "tool_call_0",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
message_idx: 502,
|
|
53
|
+
is_for_llm: true,
|
|
54
|
+
content: { role: "assistant", content: "msg502" },
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
function testSuccessfulAgentLoop(cm, startIdx) {
|
|
58
|
+
// 2 user messages arrive as ClientUserMessage. Assign message indices and
|
|
59
|
+
// convert them to ServerUserMessages.
|
|
60
|
+
const serverUserMessage0 = cm.processUserMessage({
|
|
61
|
+
type: "msg",
|
|
62
|
+
message: "UserMessage0",
|
|
63
|
+
}, "user0");
|
|
64
|
+
(0, vitest_1.expect)(serverUserMessage0).eql({
|
|
65
|
+
type: "user_msg",
|
|
66
|
+
session_id: "some_session_id",
|
|
67
|
+
user_uuid: "user0",
|
|
68
|
+
message_idx: startIdx + 0 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT,
|
|
69
|
+
message: "UserMessage0",
|
|
70
|
+
});
|
|
71
|
+
const serverUserMessage1 = cm.processUserMessage({
|
|
72
|
+
type: "msg",
|
|
73
|
+
message: "UserMessage1",
|
|
74
|
+
}, "user1");
|
|
75
|
+
(0, vitest_1.expect)(serverUserMessage1).eql({
|
|
76
|
+
type: "user_msg",
|
|
77
|
+
session_id: "some_session_id",
|
|
78
|
+
user_uuid: "user1",
|
|
79
|
+
message_idx: startIdx + 1 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT,
|
|
80
|
+
message: "UserMessage1",
|
|
81
|
+
});
|
|
82
|
+
// Begin processing an agent response.
|
|
83
|
+
//
|
|
84
|
+
// Agent response has a tool call. It is assigned 2 *
|
|
85
|
+
// MESSAGE_INDEX_FULL_INCREMENT
|
|
86
|
+
const userMsgs = [serverUserMessage0, serverUserMessage1];
|
|
87
|
+
const { llmUserMessages, agentFirstChunk } = cm.startAgentResponse(userMsgs);
|
|
88
|
+
(0, vitest_1.expect)(agentFirstChunk.message_idx).eql(startIdx + 2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT);
|
|
89
|
+
(0, vitest_1.expect)(llmUserMessages).eql(userMsgs.map((su) => {
|
|
90
|
+
return {
|
|
91
|
+
role: "user",
|
|
92
|
+
name: su.user_uuid,
|
|
93
|
+
content: su.message,
|
|
94
|
+
};
|
|
95
|
+
}));
|
|
96
|
+
// The agent sends message chunks. Check the message_idx
|
|
97
|
+
const chunkMsg0 = cm.processAgentMessage("AgentMe", false);
|
|
98
|
+
(0, vitest_1.expect)(chunkMsg0).eql({
|
|
99
|
+
type: "agent_msg_chunk",
|
|
100
|
+
session_id: "some_session_id",
|
|
101
|
+
message_idx: startIdx + 2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT,
|
|
102
|
+
message: "AgentMe",
|
|
103
|
+
end: false,
|
|
104
|
+
});
|
|
105
|
+
const chunkMsg1 = cm.processAgentMessage("ssage2", true);
|
|
106
|
+
(0, vitest_1.expect)(chunkMsg1).eql({
|
|
107
|
+
type: "agent_msg_chunk",
|
|
108
|
+
session_id: "some_session_id",
|
|
109
|
+
message_idx: startIdx + 2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT,
|
|
110
|
+
message: "ssage2",
|
|
111
|
+
end: true,
|
|
112
|
+
});
|
|
113
|
+
// A full response (with toolcall) is returned
|
|
114
|
+
const agentResponseWithToolCall = {
|
|
115
|
+
role: "assistant",
|
|
116
|
+
tool_calls: [
|
|
117
|
+
{
|
|
118
|
+
id: "tool_call_0",
|
|
119
|
+
type: "function",
|
|
120
|
+
function: {
|
|
121
|
+
name: "tool0",
|
|
122
|
+
arguments: "",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: "tool_call_1",
|
|
127
|
+
type: "function",
|
|
128
|
+
function: {
|
|
129
|
+
name: "tool1",
|
|
130
|
+
arguments: "arg1",
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
content: "AgentMessage2",
|
|
135
|
+
};
|
|
136
|
+
cm.processAgentResponse(agentResponseWithToolCall);
|
|
137
|
+
// Meanwhile, a new user message comes in. It is assigned idx
|
|
138
|
+
// startIdx + 3*MESSAGE_INDEX_FULL_INCREMENT.
|
|
139
|
+
const serverUserMessage3 = cm.processUserMessage({
|
|
140
|
+
type: "msg",
|
|
141
|
+
message: "UserMessage3",
|
|
142
|
+
}, "user3");
|
|
143
|
+
(0, vitest_1.expect)(serverUserMessage3).eql({
|
|
144
|
+
type: "user_msg",
|
|
145
|
+
session_id: "some_session_id",
|
|
146
|
+
user_uuid: "user3",
|
|
147
|
+
message_idx: startIdx + 3 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT,
|
|
148
|
+
message: "UserMessage3",
|
|
149
|
+
});
|
|
150
|
+
// Tool call results arrive. They should have indices:
|
|
151
|
+
// - 2*MESSAGE_INDEX_FULL_INCREMENT + 1*MESSAGE_INDEX_SUB_INCREMENT
|
|
152
|
+
// - 2*MESSAGE_INDEX_FULL_INCREMENT + 2*MESSAGE_INDEX_SUB_INCREMENT
|
|
153
|
+
const toolCallResults = [
|
|
154
|
+
{
|
|
155
|
+
role: "tool",
|
|
156
|
+
tool_call_id: "tool_call_0",
|
|
157
|
+
content: "tool_response_0",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
role: "tool",
|
|
161
|
+
tool_call_id: "tool_call_1",
|
|
162
|
+
content: "tool_response_1",
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
const toolCallMesasge0 = cm.processToolCallResult(toolCallResults[0]);
|
|
166
|
+
(0, vitest_1.expect)(toolCallMesasge0.message_idx).eql(startIdx +
|
|
167
|
+
2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT +
|
|
168
|
+
1 * conversation_1.MESSAGE_INDEX_SUB_INCREMENT);
|
|
169
|
+
const toolCallMesasge1 = cm.processToolCallResult(toolCallResults[1]);
|
|
170
|
+
(0, vitest_1.expect)(toolCallMesasge1.message_idx).eql(startIdx +
|
|
171
|
+
2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT +
|
|
172
|
+
2 * conversation_1.MESSAGE_INDEX_SUB_INCREMENT);
|
|
173
|
+
// Final agent response
|
|
174
|
+
const finalAgentResponse = {
|
|
175
|
+
role: "assistant",
|
|
176
|
+
content: "AgentMessage2",
|
|
177
|
+
};
|
|
178
|
+
// The agent adds the messages to the context at the end of processing
|
|
179
|
+
cm.addMessages(llmUserMessages);
|
|
180
|
+
cm.addMessage(agentResponseWithToolCall);
|
|
181
|
+
cm.addMessage(toolCallResults[0]);
|
|
182
|
+
cm.addMessage(toolCallResults[1]);
|
|
183
|
+
cm.addMessage(finalAgentResponse);
|
|
184
|
+
// Agent then returns the final response to the caller (OpenSession)
|
|
185
|
+
cm.processAgentResponse(finalAgentResponse);
|
|
186
|
+
const dbMessages = cm.endAgentResponse();
|
|
187
|
+
// Expect
|
|
188
|
+
const expectDbMessages = [
|
|
189
|
+
{
|
|
190
|
+
message_idx: startIdx + 0,
|
|
191
|
+
sender_uuid: "user0",
|
|
192
|
+
is_for_llm: true,
|
|
193
|
+
content: (0, agent_1.createUserMessageEnsure)(userMsgs[0].message, userMsgs[0].imageB64, "user0"),
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
message_idx: startIdx + conversation_1.MESSAGE_INDEX_FULL_INCREMENT,
|
|
197
|
+
sender_uuid: "user1",
|
|
198
|
+
is_for_llm: true,
|
|
199
|
+
content: (0, agent_1.createUserMessageEnsure)(userMsgs[1].message, userMsgs[1].imageB64, "user1"),
|
|
200
|
+
},
|
|
201
|
+
// agent response with tool calls
|
|
202
|
+
{
|
|
203
|
+
message_idx: startIdx + conversation_1.MESSAGE_INDEX_FULL_INCREMENT * 2,
|
|
204
|
+
is_for_llm: true,
|
|
205
|
+
content: agentResponseWithToolCall,
|
|
206
|
+
},
|
|
207
|
+
// 2 tool results
|
|
208
|
+
{
|
|
209
|
+
message_idx: startIdx +
|
|
210
|
+
2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT +
|
|
211
|
+
conversation_1.MESSAGE_INDEX_SUB_INCREMENT,
|
|
212
|
+
is_for_llm: true,
|
|
213
|
+
content: toolCallResults[0],
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
message_idx: startIdx +
|
|
217
|
+
2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT +
|
|
218
|
+
2 * conversation_1.MESSAGE_INDEX_SUB_INCREMENT,
|
|
219
|
+
is_for_llm: true,
|
|
220
|
+
content: toolCallResults[1],
|
|
221
|
+
},
|
|
222
|
+
// Final agent message
|
|
223
|
+
{
|
|
224
|
+
message_idx: startIdx +
|
|
225
|
+
2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT +
|
|
226
|
+
3 * conversation_1.MESSAGE_INDEX_SUB_INCREMENT,
|
|
227
|
+
is_for_llm: true,
|
|
228
|
+
content: finalAgentResponse,
|
|
229
|
+
},
|
|
230
|
+
];
|
|
231
|
+
(0, vitest_1.expect)(dbMessages).eql(expectDbMessages);
|
|
232
|
+
}
|
|
233
|
+
describe("IndexingCompressingContextManager", () => {
|
|
234
|
+
// TODO: move to contextCompression.test.ts
|
|
235
|
+
it("restoring with checkpoint", function () {
|
|
236
|
+
// Checkpoint with index 50 (smaller than all)
|
|
237
|
+
const checkpoint50 = {
|
|
238
|
+
message_idx: 50,
|
|
239
|
+
summary: "SUMMARY_50",
|
|
240
|
+
};
|
|
241
|
+
const resolved50 = (0, chatContextManager_1.resolveConversationWithCheckpoint)(MESSAGES, checkpoint50);
|
|
242
|
+
(0, vitest_1.expect)(resolved50).eql({
|
|
243
|
+
messages: [
|
|
244
|
+
(0, compressingContextManager_1.createCheckpointMessage)(checkpoint50.summary),
|
|
245
|
+
...MESSAGES.map((msg) => msg.content),
|
|
246
|
+
],
|
|
247
|
+
lastEntryIdx: 502,
|
|
248
|
+
});
|
|
249
|
+
// Checkpoint with index 300 (in the middle)
|
|
250
|
+
const checkpoint300 = {
|
|
251
|
+
message_idx: 300,
|
|
252
|
+
summary: "SUMMARY_300",
|
|
253
|
+
};
|
|
254
|
+
const resolved300 = (0, chatContextManager_1.resolveConversationWithCheckpoint)(MESSAGES, checkpoint300);
|
|
255
|
+
(0, vitest_1.expect)(resolved300).eql({
|
|
256
|
+
messages: [
|
|
257
|
+
(0, compressingContextManager_1.createCheckpointMessage)(checkpoint300.summary),
|
|
258
|
+
// Messages after 300: 400, 500, 501, 502
|
|
259
|
+
MESSAGES[2].content, // msg400
|
|
260
|
+
MESSAGES[3].content, // msg500 (with tool_calls)
|
|
261
|
+
MESSAGES[4].content, // msg501 (tool result)
|
|
262
|
+
MESSAGES[5].content, // msg502
|
|
263
|
+
],
|
|
264
|
+
lastEntryIdx: 502,
|
|
265
|
+
});
|
|
266
|
+
// Checkpoint with index 502 (at the end)
|
|
267
|
+
const checkpoint502 = {
|
|
268
|
+
message_idx: 502,
|
|
269
|
+
summary: "SUMMARY_502",
|
|
270
|
+
};
|
|
271
|
+
const resolved502 = (0, chatContextManager_1.resolveConversationWithCheckpoint)(MESSAGES, checkpoint502);
|
|
272
|
+
(0, vitest_1.expect)(resolved502).eql({
|
|
273
|
+
messages: [
|
|
274
|
+
(0, compressingContextManager_1.createCheckpointMessage)(checkpoint502.summary),
|
|
275
|
+
// No messages after 502
|
|
276
|
+
],
|
|
277
|
+
lastEntryIdx: 502,
|
|
278
|
+
});
|
|
279
|
+
// Checkpoint with index 600 (greater than all messages)
|
|
280
|
+
const checkpoint600 = {
|
|
281
|
+
message_idx: 600,
|
|
282
|
+
summary: "SUMMARY_600",
|
|
283
|
+
};
|
|
284
|
+
const resolved600 = (0, chatContextManager_1.resolveConversationWithCheckpoint)(MESSAGES, checkpoint600);
|
|
285
|
+
(0, vitest_1.expect)(resolved600).eql({
|
|
286
|
+
messages: [
|
|
287
|
+
(0, compressingContextManager_1.createCheckpointMessage)(checkpoint600.summary),
|
|
288
|
+
// No messages after 600 (checkpoint covers all messages)
|
|
289
|
+
],
|
|
290
|
+
lastEntryIdx: 600, // Should be max(502, 600) = 600
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
it("conversation processing behaviour", () => {
|
|
294
|
+
const writer = {
|
|
295
|
+
writeCheckpoint: (_sc) => {
|
|
296
|
+
return new Promise((r) => {
|
|
297
|
+
r();
|
|
298
|
+
});
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
const cm = new chatContextManager_1.ChatContextManager("sys_prompt", [], "some_session_id", "no_such_user", undefined, "", "", "", writer, new sessionFileManager_1.MemoryFileManager());
|
|
302
|
+
testSuccessfulAgentLoop(cm, 0);
|
|
303
|
+
});
|
|
304
|
+
it("error handling", () => {
|
|
305
|
+
const writer = {
|
|
306
|
+
writeCheckpoint: (_sc) => {
|
|
307
|
+
return new Promise((r) => {
|
|
308
|
+
r();
|
|
309
|
+
});
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
const cm = new chatContextManager_1.ChatContextManager("sys_prompt", [], "some_session_id", "no_such_user", undefined, "", "", "", writer, new sessionFileManager_1.MemoryFileManager());
|
|
313
|
+
// 1 user messages
|
|
314
|
+
const serverUserMessage0 = cm.processUserMessage({ type: "msg", message: "UserMessage0" }, "user0");
|
|
315
|
+
(0, vitest_1.expect)(serverUserMessage0).eql({
|
|
316
|
+
type: "user_msg",
|
|
317
|
+
session_id: "some_session_id",
|
|
318
|
+
user_uuid: "user0",
|
|
319
|
+
message_idx: 0 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT,
|
|
320
|
+
message: "UserMessage0",
|
|
321
|
+
});
|
|
322
|
+
// Begin processing an agent response.
|
|
323
|
+
//
|
|
324
|
+
// Agent response has a tool call. It is assigned 1 *
|
|
325
|
+
// MESSAGE_INDEX_FULL_INCREMENT
|
|
326
|
+
const { llmUserMessages, agentFirstChunk } = cm.startAgentResponse([
|
|
327
|
+
serverUserMessage0,
|
|
328
|
+
]);
|
|
329
|
+
(0, vitest_1.expect)(agentFirstChunk.message_idx).eql(1 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT);
|
|
330
|
+
(0, vitest_1.expect)(llmUserMessages).eql([
|
|
331
|
+
{
|
|
332
|
+
role: "user",
|
|
333
|
+
name: serverUserMessage0.user_uuid,
|
|
334
|
+
content: serverUserMessage0.message,
|
|
335
|
+
},
|
|
336
|
+
]);
|
|
337
|
+
// A full response (with toolcall) is returned
|
|
338
|
+
const agentResponseWithToolCall = {
|
|
339
|
+
role: "assistant",
|
|
340
|
+
tool_calls: [
|
|
341
|
+
{
|
|
342
|
+
id: "tool_call_0",
|
|
343
|
+
type: "function",
|
|
344
|
+
function: {
|
|
345
|
+
name: "tool0",
|
|
346
|
+
arguments: "",
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
id: "tool_call_1",
|
|
351
|
+
type: "function",
|
|
352
|
+
function: {
|
|
353
|
+
name: "tool1",
|
|
354
|
+
arguments: "arg1",
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
content: "AgentMessage2",
|
|
359
|
+
};
|
|
360
|
+
cm.processAgentResponse(agentResponseWithToolCall);
|
|
361
|
+
// The current Agent does not add the tool call results until the next
|
|
362
|
+
// completion has run. Here we are simulating an error during that
|
|
363
|
+
// process, but the Agent still sends the tool call results to the event
|
|
364
|
+
// handler, which passes them in here.
|
|
365
|
+
// Tool call results arrive. They should have indices:
|
|
366
|
+
// - 1*MESSAGE_INDEX_FULL_INCREMENT + 1*MESSAGE_INDEX_SUB_INCREMENT
|
|
367
|
+
// - 1*MESSAGE_INDEX_FULL_INCREMENT + 2*MESSAGE_INDEX_SUB_INCREMENT
|
|
368
|
+
const toolCallResults = [
|
|
369
|
+
{
|
|
370
|
+
role: "tool",
|
|
371
|
+
tool_call_id: "tool_call_0",
|
|
372
|
+
content: "tool_response_0",
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
role: "tool",
|
|
376
|
+
tool_call_id: "tool_call_1",
|
|
377
|
+
content: "tool_response_1",
|
|
378
|
+
},
|
|
379
|
+
];
|
|
380
|
+
const toolCallMesasge0 = cm.processToolCallResult(toolCallResults[0]);
|
|
381
|
+
(0, vitest_1.expect)(toolCallMesasge0.message_idx).eql(1 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT + 1 * conversation_1.MESSAGE_INDEX_SUB_INCREMENT);
|
|
382
|
+
const toolCallMesasge1 = cm.processToolCallResult(toolCallResults[1]);
|
|
383
|
+
(0, vitest_1.expect)(toolCallMesasge1.message_idx).eql(1 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT + 2 * conversation_1.MESSAGE_INDEX_SUB_INCREMENT);
|
|
384
|
+
// The error is caught by OpenSession, which informs the
|
|
385
|
+
// ChatContextManager.
|
|
386
|
+
cm.revertAgentResponse("an error occured");
|
|
387
|
+
// We should now be able to run the original test (starting from index
|
|
388
|
+
// 2*MESSAGE_INDEX_FULL_INCREMENT). None of our previous messages should
|
|
389
|
+
// hit the DB or the LLM context.
|
|
390
|
+
testSuccessfulAgentLoop(cm, 2 * conversation_1.MESSAGE_INDEX_FULL_INCREMENT);
|
|
391
|
+
});
|
|
392
|
+
});
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const connection_1 = require("../chat/client/connection");
|
|
5
|
+
const server_1 = require("../chat/server/server");
|
|
6
|
+
const database_1 = require("../chat/data/database");
|
|
7
|
+
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
8
|
+
const database_2 = require("../chat/data/database");
|
|
9
|
+
const sessionRegistry_1 = require("../chat/server/sessionRegistry");
|
|
10
|
+
// Mock uuid to have predictable client message IDs
|
|
11
|
+
vitest_1.vi.mock("uuid", () => ({
|
|
12
|
+
v4: () => "mock-uuid-123",
|
|
13
|
+
}));
|
|
14
|
+
const logger = (0, sdk_1.getLogger)();
|
|
15
|
+
(0, vitest_1.describe)("Client-Server WebSocket Integration", () => {
|
|
16
|
+
let server;
|
|
17
|
+
let db;
|
|
18
|
+
const serverPort = 5105; // Different port to avoid conflicts
|
|
19
|
+
// Test API key data
|
|
20
|
+
const testApiKey = "integration_test";
|
|
21
|
+
const testUserUuid = "test_user_0";
|
|
22
|
+
const testUserName = "default";
|
|
23
|
+
const testScopes = [];
|
|
24
|
+
// Mock environment variables for server
|
|
25
|
+
const mockEnv = {
|
|
26
|
+
SUPABASE_URL: database_2.SUPABASE_LOCAL_URL,
|
|
27
|
+
SUPABASE_KEY: database_2.SUPABASE_LOCAL_KEY,
|
|
28
|
+
LLM_URL: "http://localhost:8080", // not used in this test
|
|
29
|
+
XMCP_URL: "http://localhost:8081", // not used in this test
|
|
30
|
+
};
|
|
31
|
+
(0, vitest_1.beforeAll)(async () => {
|
|
32
|
+
// Set up environment variables
|
|
33
|
+
Object.assign(process.env, mockEnv);
|
|
34
|
+
// Initialize database connection
|
|
35
|
+
db = new database_1.Database(mockEnv.SUPABASE_URL, mockEnv.SUPABASE_KEY);
|
|
36
|
+
// Try to insert test data, but don't fail if there are database issues
|
|
37
|
+
try {
|
|
38
|
+
await cleanupTestData();
|
|
39
|
+
await insertTestApiKey();
|
|
40
|
+
logger.info("[Test] Test data setup completed successfully");
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
logger.warn("[Test] Test data setup failed - will test with existing data:", error);
|
|
44
|
+
}
|
|
45
|
+
// Start the real server using runServer
|
|
46
|
+
server = await (0, server_1.runServer)(serverPort, mockEnv.SUPABASE_URL, mockEnv.SUPABASE_KEY, mockEnv.LLM_URL, mockEnv.XMCP_URL);
|
|
47
|
+
// Wait for server to be ready
|
|
48
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
49
|
+
});
|
|
50
|
+
async function cleanupTestData() {
|
|
51
|
+
try {
|
|
52
|
+
// Delete test API key first (foreign key constraint)
|
|
53
|
+
await db
|
|
54
|
+
.getClientForTesting()
|
|
55
|
+
.from("api_keys")
|
|
56
|
+
.delete()
|
|
57
|
+
.eq("api_key", testApiKey);
|
|
58
|
+
// Delete test user
|
|
59
|
+
await db
|
|
60
|
+
.getClientForTesting()
|
|
61
|
+
.from("users")
|
|
62
|
+
.delete()
|
|
63
|
+
.eq("uuid", testUserUuid);
|
|
64
|
+
logger.info("[Test] Cleaned up existing test data");
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
logger.warn("[Test] Error during cleanup (expected if no data exists):", error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function insertTestApiKey() {
|
|
71
|
+
try {
|
|
72
|
+
// Use Database class methods which include timezone handling
|
|
73
|
+
await db.createUser(testUserUuid, "test@example.com", testUserName, "UTC");
|
|
74
|
+
logger.info("[Test] User created successfully");
|
|
75
|
+
await db.addApiKey(testUserUuid, testApiKey, testUserName, testScopes, true // is_default
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
logger.error("[Test] Database method insert failed:", error);
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
(0, vitest_1.afterAll)(async () => {
|
|
84
|
+
// Clean up test data
|
|
85
|
+
await cleanupTestData();
|
|
86
|
+
// Close server
|
|
87
|
+
server.close();
|
|
88
|
+
});
|
|
89
|
+
(0, vitest_1.describe)("Connection Setup and List Sessions", () => {
|
|
90
|
+
(0, vitest_1.it)("should establish connection with valid API key", async () => {
|
|
91
|
+
// Inject test agent profiles and sessions into the database
|
|
92
|
+
const agentProfile1 = await db.createAgentProfile(testUserUuid, undefined, "Test agent profile 1", {
|
|
93
|
+
model: undefined,
|
|
94
|
+
system_prompt: "You are a test agent",
|
|
95
|
+
mcp_settings: {},
|
|
96
|
+
});
|
|
97
|
+
const agentProfile2 = await db.createAgentProfile(testUserUuid, undefined, "Test agent profile 2", {
|
|
98
|
+
model: undefined,
|
|
99
|
+
system_prompt: "You are a test agent",
|
|
100
|
+
mcp_settings: {},
|
|
101
|
+
});
|
|
102
|
+
if (!agentProfile1 || !agentProfile2) {
|
|
103
|
+
throw new Error("Failed to create agent profiles");
|
|
104
|
+
}
|
|
105
|
+
const agentProfile1Id = agentProfile1.uuid;
|
|
106
|
+
const agentProfile2Id = agentProfile2.uuid;
|
|
107
|
+
const session1CreateData = (0, sessionRegistry_1.userSessionDataCreate)(testUserUuid, "Test Session 1", agentProfile1Id);
|
|
108
|
+
const session1Id = session1CreateData.session_uuid;
|
|
109
|
+
await db.sessionCreate(session1CreateData);
|
|
110
|
+
const session2CreateData = (0, sessionRegistry_1.userSessionDataCreate)(testUserUuid, "Test Session 2", agentProfile2Id);
|
|
111
|
+
const session2Id = session2CreateData.session_uuid;
|
|
112
|
+
await db.sessionCreate(session2CreateData);
|
|
113
|
+
// Log session creation for debugging
|
|
114
|
+
logger.info(`[Test] Created sessions: ${session1Id}, ${session2Id}`);
|
|
115
|
+
const client = new connection_1.Connection({
|
|
116
|
+
url: `ws://localhost:${String(serverPort)}`,
|
|
117
|
+
token: testApiKey,
|
|
118
|
+
});
|
|
119
|
+
// Wait for session_list response
|
|
120
|
+
const responsePromise = new Promise((resolve) => {
|
|
121
|
+
client.on("control_session_list", (msg) => {
|
|
122
|
+
resolve(msg);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
await client.connect();
|
|
126
|
+
(0, vitest_1.expect)(client.getState()).toBe("ready");
|
|
127
|
+
client.send({
|
|
128
|
+
type: "control_get_session_list",
|
|
129
|
+
client_message_id: "test-msg-id",
|
|
130
|
+
});
|
|
131
|
+
const response = await responsePromise;
|
|
132
|
+
(0, vitest_1.expect)(response.type).toBe("control_session_list");
|
|
133
|
+
(0, vitest_1.expect)(response.user_sessions).toBeDefined();
|
|
134
|
+
(0, vitest_1.expect)(response.user_sessions.length).toBe(2);
|
|
135
|
+
// Verify session details
|
|
136
|
+
const sessionIds = response.user_sessions.map((s) => s.session_uuid);
|
|
137
|
+
(0, vitest_1.expect)(sessionIds).toContain(session1Id);
|
|
138
|
+
(0, vitest_1.expect)(sessionIds).toContain(session2Id);
|
|
139
|
+
const session1Data = response.user_sessions.find((s) => s.session_uuid === session1Id);
|
|
140
|
+
(0, vitest_1.expect)(session1Data?.title).toBe("Test Session 1");
|
|
141
|
+
const session2Data = response.user_sessions.find((s) => s.session_uuid === session2Id);
|
|
142
|
+
(0, vitest_1.expect)(session2Data?.title).toBe("Test Session 2");
|
|
143
|
+
client.close();
|
|
144
|
+
}, 15000);
|
|
145
|
+
});
|
|
146
|
+
(0, vitest_1.describe)("Authentication Error Handling", () => {
|
|
147
|
+
(0, vitest_1.it)("should handle invalid API key gracefully", async () => {
|
|
148
|
+
const invalidApiKey = "invalid-test-key";
|
|
149
|
+
const client = new connection_1.Connection({
|
|
150
|
+
url: `ws://localhost:${String(serverPort)}`,
|
|
151
|
+
token: invalidApiKey,
|
|
152
|
+
});
|
|
153
|
+
await (0, vitest_1.expect)(client.connect()).rejects.toThrow();
|
|
154
|
+
(0, vitest_1.expect)(client.getState()).toBe("closed");
|
|
155
|
+
client.close();
|
|
156
|
+
}, 4000);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const compressingContextManager_1 = require("../agent/compressingContextManager");
|
|
5
|
+
const MESSAGES = [
|
|
6
|
+
{ role: "user", content: "msg200" },
|
|
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
|
+
];
|
|
27
|
+
describe("Compression context", () => {
|
|
28
|
+
it("context compression", async function () {
|
|
29
|
+
const ccm = new compressingContextManager_1.CompressingContextManager("sys_prompt", [], "", // llmUrl
|
|
30
|
+
"repeat", // llmModel
|
|
31
|
+
"" // llmApiKey
|
|
32
|
+
);
|
|
33
|
+
// Add first 2 messages
|
|
34
|
+
ccm.addMessages(MESSAGES.slice(0, 2));
|
|
35
|
+
ccm.commit();
|
|
36
|
+
// Start the compression
|
|
37
|
+
const compressionP = ccm.compress();
|
|
38
|
+
// Add more messages and update system prompt
|
|
39
|
+
ccm.addMessages(MESSAGES.slice(2));
|
|
40
|
+
ccm.setAgentPrompt("New system prompt");
|
|
41
|
+
// Wait for compression to complete, then get the new context
|
|
42
|
+
const summary = await compressionP;
|
|
43
|
+
const context = ccm.getLLMContext();
|
|
44
|
+
// Expect
|
|
45
|
+
// - callback happened
|
|
46
|
+
// - new context is as expected
|
|
47
|
+
const expectSummary = "Message number 0";
|
|
48
|
+
const expectContext = [
|
|
49
|
+
{
|
|
50
|
+
role: "system",
|
|
51
|
+
content: "New system prompt",
|
|
52
|
+
},
|
|
53
|
+
(0, compressingContextManager_1.createCheckpointMessage)(expectSummary),
|
|
54
|
+
MESSAGES[2], // 400
|
|
55
|
+
MESSAGES[3], // 500
|
|
56
|
+
MESSAGES[4], // 501
|
|
57
|
+
MESSAGES[5], // 502
|
|
58
|
+
];
|
|
59
|
+
(0, vitest_1.expect)(summary).eql(expectSummary);
|
|
60
|
+
(0, vitest_1.expect)(context).eql(expectContext);
|
|
61
|
+
// Pending messages shoudl still be intact
|
|
62
|
+
const pending = ccm.getPending();
|
|
63
|
+
(0, vitest_1.expect)(pending).eql(MESSAGES.slice(2));
|
|
64
|
+
});
|
|
65
|
+
});
|