@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.
Files changed (185) hide show
  1. package/README.md +23 -8
  2. package/dist/agent/src/agent/agent.js +173 -96
  3. package/dist/agent/src/agent/agentUtils.js +82 -53
  4. package/dist/agent/src/agent/compressingContextManager.js +102 -0
  5. package/dist/agent/src/agent/context.js +189 -0
  6. package/dist/agent/src/agent/dummyLLM.js +46 -5
  7. package/dist/agent/src/agent/iAgentEventHandler.js +2 -0
  8. package/dist/agent/src/agent/mcpServerManager.js +22 -23
  9. package/dist/agent/src/agent/nullAgentEventHandler.js +21 -0
  10. package/dist/agent/src/agent/nullPlatform.js +14 -0
  11. package/dist/agent/src/agent/openAILLMStreaming.js +12 -7
  12. package/dist/agent/src/agent/promptProvider.js +63 -0
  13. package/dist/agent/src/agent/repeatLLM.js +5 -5
  14. package/dist/agent/src/agent/sudoMcpServerManager.js +11 -9
  15. package/dist/agent/src/agent/tokenAuth.js +7 -7
  16. package/dist/agent/src/agent/tools.js +1 -1
  17. package/dist/agent/src/chat/client/chatClient.js +733 -0
  18. package/dist/agent/src/chat/client/connection.js +209 -0
  19. package/dist/agent/src/chat/client/connection.test.js +188 -0
  20. package/dist/agent/src/chat/client/constants.js +5 -0
  21. package/dist/agent/src/chat/client/index.js +15 -0
  22. package/dist/agent/src/chat/client/interfaces.js +2 -0
  23. package/dist/agent/src/chat/client/responseHandler.js +105 -0
  24. package/dist/agent/src/chat/client/sessionClient.js +331 -0
  25. package/dist/agent/src/chat/client/teamManager.js +2 -0
  26. package/dist/agent/src/chat/{apiKeyManager.js → data/apiKeyManager.js} +4 -0
  27. package/dist/agent/src/chat/data/dataModels.js +2 -0
  28. package/dist/agent/src/chat/data/database.js +749 -0
  29. package/dist/agent/src/chat/data/dbMcpServerConfigs.js +47 -0
  30. package/dist/agent/src/chat/protocol/connectionMessages.js +5 -0
  31. package/dist/agent/src/chat/protocol/constants.js +50 -0
  32. package/dist/agent/src/chat/protocol/errors.js +22 -0
  33. package/dist/agent/src/chat/protocol/messages.js +110 -0
  34. package/dist/agent/src/chat/server/chatContextManager.js +405 -0
  35. package/dist/agent/src/chat/server/connectionManager.js +352 -0
  36. package/dist/agent/src/chat/server/connectionManager.test.js +159 -0
  37. package/dist/agent/src/chat/server/conversation.js +198 -0
  38. package/dist/agent/src/chat/server/errorUtils.js +23 -0
  39. package/dist/agent/src/chat/server/openSession.js +869 -0
  40. package/dist/agent/src/chat/server/server.js +177 -0
  41. package/dist/agent/src/chat/server/sessionFileManager.js +161 -0
  42. package/dist/agent/src/chat/server/sessionRegistry.js +700 -0
  43. package/dist/agent/src/chat/server/sessionRegistry.test.js +97 -0
  44. package/dist/agent/src/chat/server/test-utils/mockFactories.js +307 -0
  45. package/dist/agent/src/chat/server/tools.js +243 -0
  46. package/dist/agent/src/chat/utils/agentSessionMap.js +66 -0
  47. package/dist/agent/src/chat/utils/approvalManager.js +85 -0
  48. package/dist/agent/src/{utils → chat/utils}/asyncLock.js +3 -3
  49. package/dist/agent/src/chat/{asyncQueue.js → utils/asyncQueue.js} +12 -2
  50. package/dist/agent/src/chat/utils/htmlToText.js +84 -0
  51. package/dist/agent/src/chat/utils/multiAsyncQueue.js +42 -0
  52. package/dist/agent/src/chat/utils/search.js +145 -0
  53. package/dist/agent/src/chat/utils/userResolver.js +46 -0
  54. package/dist/agent/src/chat/{websocket.js → utils/websocket.js} +2 -0
  55. package/dist/agent/src/test/agent.test.js +332 -0
  56. package/dist/agent/src/test/approvalManager.test.js +58 -0
  57. package/dist/agent/src/test/chatContextManager.test.js +392 -0
  58. package/dist/agent/src/test/clientServerConnection.test.js +158 -0
  59. package/dist/agent/src/test/compressingContextManager.test.js +65 -0
  60. package/dist/agent/src/test/context.test.js +83 -0
  61. package/dist/agent/src/test/conversation.test.js +89 -0
  62. package/dist/agent/src/test/db.test.js +262 -90
  63. package/dist/agent/src/test/dbMcpServerConfigs.test.js +72 -0
  64. package/dist/agent/src/test/dbTestTools.js +99 -0
  65. package/dist/agent/src/test/imageLoad.test.js +8 -7
  66. package/dist/agent/src/test/mcpServerManager.test.js +21 -18
  67. package/dist/agent/src/test/multiAsyncQueue.test.js +101 -0
  68. package/dist/agent/src/test/openaiStreaming.test.js +12 -11
  69. package/dist/agent/src/test/prompt.test.js +5 -4
  70. package/dist/agent/src/test/promptProvider.test.js +28 -0
  71. package/dist/agent/src/test/responseHandler.test.js +61 -0
  72. package/dist/agent/src/test/sudoMcpServerManager.test.js +14 -12
  73. package/dist/agent/src/test/testTools.js +109 -0
  74. package/dist/agent/src/test/tools.test.js +31 -0
  75. package/dist/agent/src/tool/agentChat.js +21 -10
  76. package/dist/agent/src/tool/agentMain.js +1 -1
  77. package/dist/agent/src/tool/chatMain.js +235 -58
  78. package/dist/agent/src/tool/commandPrompt.js +15 -9
  79. package/dist/agent/src/tool/files.js +20 -16
  80. package/dist/agent/src/tool/nodePlatform.js +47 -3
  81. package/dist/agent/src/tool/options.js +4 -4
  82. package/dist/agent/src/tool/prompt.js +19 -13
  83. package/eslint.config.mjs +14 -1
  84. package/package.json +14 -6
  85. package/scripts/chat_server +8 -0
  86. package/scripts/setup_chat +7 -2
  87. package/scripts/shutdown_chat_server +3 -0
  88. package/scripts/test_chat +135 -17
  89. package/src/agent/agent.ts +270 -135
  90. package/src/agent/agentUtils.ts +136 -95
  91. package/src/agent/compressingContextManager.ts +164 -0
  92. package/src/agent/context.ts +268 -0
  93. package/src/agent/dummyLLM.ts +76 -8
  94. package/src/agent/iAgentEventHandler.ts +54 -0
  95. package/src/agent/iplatform.ts +1 -0
  96. package/src/agent/mcpServerManager.ts +32 -30
  97. package/src/agent/nullAgentEventHandler.ts +20 -0
  98. package/src/agent/nullPlatform.ts +13 -0
  99. package/src/agent/openAILLMStreaming.ts +12 -6
  100. package/src/agent/promptProvider.ts +87 -0
  101. package/src/agent/repeatLLM.ts +5 -5
  102. package/src/agent/sudoMcpServerManager.ts +13 -11
  103. package/src/agent/tokenAuth.ts +7 -7
  104. package/src/agent/tools.ts +3 -1
  105. package/src/chat/client/chatClient.ts +900 -0
  106. package/src/chat/client/connection.test.ts +241 -0
  107. package/src/chat/client/connection.ts +276 -0
  108. package/src/chat/client/constants.ts +3 -0
  109. package/src/chat/client/index.ts +18 -0
  110. package/src/chat/client/interfaces.ts +34 -0
  111. package/src/chat/client/responseHandler.ts +131 -0
  112. package/src/chat/client/sessionClient.ts +443 -0
  113. package/src/chat/client/teamManager.ts +29 -0
  114. package/src/chat/{apiKeyManager.ts → data/apiKeyManager.ts} +6 -2
  115. package/src/chat/data/dataModels.ts +85 -0
  116. package/src/chat/data/database.ts +982 -0
  117. package/src/chat/data/dbMcpServerConfigs.ts +59 -0
  118. package/src/chat/protocol/connectionMessages.ts +49 -0
  119. package/src/chat/protocol/constants.ts +55 -0
  120. package/src/chat/protocol/errors.ts +16 -0
  121. package/src/chat/protocol/messages.ts +682 -0
  122. package/src/chat/server/README.md +127 -0
  123. package/src/chat/server/chatContextManager.ts +612 -0
  124. package/src/chat/server/connectionManager.test.ts +266 -0
  125. package/src/chat/server/connectionManager.ts +541 -0
  126. package/src/chat/server/conversation.ts +269 -0
  127. package/src/chat/server/errorUtils.ts +28 -0
  128. package/src/chat/server/openSession.ts +1332 -0
  129. package/src/chat/server/server.ts +177 -0
  130. package/src/chat/server/sessionFileManager.ts +239 -0
  131. package/src/chat/server/sessionRegistry.test.ts +138 -0
  132. package/src/chat/server/sessionRegistry.ts +1064 -0
  133. package/src/chat/server/test-utils/mockFactories.ts +422 -0
  134. package/src/chat/server/tools.ts +265 -0
  135. package/src/chat/utils/agentSessionMap.ts +76 -0
  136. package/src/chat/utils/approvalManager.ts +111 -0
  137. package/src/{utils → chat/utils}/asyncLock.ts +3 -3
  138. package/src/chat/{asyncQueue.ts → utils/asyncQueue.ts} +14 -3
  139. package/src/chat/utils/htmlToText.ts +61 -0
  140. package/src/chat/utils/multiAsyncQueue.ts +52 -0
  141. package/src/chat/utils/search.ts +139 -0
  142. package/src/chat/utils/userResolver.ts +48 -0
  143. package/src/chat/{websocket.ts → utils/websocket.ts} +2 -0
  144. package/src/test/agent.test.ts +487 -0
  145. package/src/test/approvalManager.test.ts +73 -0
  146. package/src/test/chatContextManager.test.ts +521 -0
  147. package/src/test/clientServerConnection.test.ts +207 -0
  148. package/src/test/compressingContextManager.test.ts +82 -0
  149. package/src/test/context.test.ts +105 -0
  150. package/src/test/conversation.test.ts +109 -0
  151. package/src/test/db.test.ts +351 -103
  152. package/src/test/dbMcpServerConfigs.test.ts +112 -0
  153. package/src/test/dbTestTools.ts +153 -0
  154. package/src/test/imageLoad.test.ts +7 -6
  155. package/src/test/mcpServerManager.test.ts +19 -14
  156. package/src/test/multiAsyncQueue.test.ts +125 -0
  157. package/src/test/openaiStreaming.test.ts +11 -10
  158. package/src/test/prompt.test.ts +4 -3
  159. package/src/test/promptProvider.test.ts +33 -0
  160. package/src/test/responseHandler.test.ts +78 -0
  161. package/src/test/sudoMcpServerManager.test.ts +22 -15
  162. package/src/test/testTools.ts +146 -0
  163. package/src/test/tools.test.ts +39 -0
  164. package/src/tool/agentChat.ts +26 -12
  165. package/src/tool/agentMain.ts +1 -1
  166. package/src/tool/chatMain.ts +283 -100
  167. package/src/tool/commandPrompt.ts +25 -9
  168. package/src/tool/files.ts +25 -19
  169. package/src/tool/nodePlatform.ts +52 -3
  170. package/src/tool/options.ts +4 -2
  171. package/src/tool/prompt.ts +22 -15
  172. package/test_data/dummyllm_script_crash.json +32 -0
  173. package/test_data/frog.png.b64 +1 -0
  174. package/vitest.config.ts +39 -0
  175. package/dist/agent/src/chat/client.js +0 -310
  176. package/dist/agent/src/chat/conversationManager.js +0 -502
  177. package/dist/agent/src/chat/db.js +0 -218
  178. package/dist/agent/src/chat/messages.js +0 -29
  179. package/dist/agent/src/chat/server.js +0 -158
  180. package/src/chat/client.ts +0 -445
  181. package/src/chat/conversationManager.ts +0 -730
  182. package/src/chat/db.ts +0 -304
  183. package/src/chat/messages.ts +0 -266
  184. package/src/chat/server.ts +0 -177
  185. /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
+ });