@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.
Files changed (63) hide show
  1. package/.env.development +1 -0
  2. package/dist/agent/src/agent/agent.js +100 -77
  3. package/dist/agent/src/agent/agentUtils.js +21 -16
  4. package/dist/agent/src/agent/compressingContextManager.js +10 -14
  5. package/dist/agent/src/agent/context.js +101 -127
  6. package/dist/agent/src/agent/contextWithWorkspace.js +133 -0
  7. package/dist/agent/src/agent/imageGenLLM.js +0 -6
  8. package/dist/agent/src/agent/imageGenerator.js +2 -10
  9. package/dist/agent/src/agent/openAILLMStreaming.js +5 -2
  10. package/dist/agent/src/agent/sudoMcpServerManager.js +21 -9
  11. package/dist/agent/src/chat/client/chatClient.js +35 -2
  12. package/dist/agent/src/chat/client/connection.js +6 -1
  13. package/dist/agent/src/chat/client/sessionClient.js +0 -7
  14. package/dist/agent/src/chat/data/dbSessionMessages.js +11 -0
  15. package/dist/agent/src/chat/protocol/messages.js +4 -0
  16. package/dist/agent/src/chat/server/chatContextManager.js +149 -139
  17. package/dist/agent/src/chat/server/imageGeneratorTools.js +19 -8
  18. package/dist/agent/src/chat/server/openAIRouterLLM.js +114 -0
  19. package/dist/agent/src/chat/server/openSession.js +57 -58
  20. package/dist/agent/src/chat/server/server.js +6 -2
  21. package/dist/agent/src/chat/server/sessionRegistry.js +65 -6
  22. package/dist/agent/src/chat/server/sessionRegistry.test.js +1 -1
  23. package/dist/agent/src/chat/server/tools.js +52 -17
  24. package/dist/agent/src/test/chatContextManager.test.js +31 -29
  25. package/dist/agent/src/test/clientServerConnection.test.js +1 -2
  26. package/dist/agent/src/test/compressingContextManager.test.js +22 -36
  27. package/dist/agent/src/test/context.test.js +55 -17
  28. package/dist/agent/src/test/contextTestTools.js +87 -0
  29. package/dist/agent/src/tool/chatMain.js +22 -8
  30. package/package.json +1 -1
  31. package/scripts/test_chat +3 -0
  32. package/src/agent/agent.ts +170 -125
  33. package/src/agent/agentUtils.ts +31 -20
  34. package/src/agent/compressingContextManager.ts +13 -44
  35. package/src/agent/context.ts +165 -159
  36. package/src/agent/contextWithWorkspace.ts +162 -0
  37. package/src/agent/imageGenLLM.ts +0 -8
  38. package/src/agent/imageGenerator.ts +3 -18
  39. package/src/agent/openAILLMStreaming.ts +20 -3
  40. package/src/agent/sudoMcpServerManager.ts +41 -20
  41. package/src/chat/client/chatClient.ts +47 -3
  42. package/src/chat/client/connection.ts +11 -1
  43. package/src/chat/client/sessionClient.ts +0 -8
  44. package/src/chat/data/dataModels.ts +6 -0
  45. package/src/chat/data/dbSessionMessages.ts +34 -0
  46. package/src/chat/protocol/messages.ts +35 -8
  47. package/src/chat/server/chatContextManager.ts +210 -197
  48. package/src/chat/server/connectionManager.ts +1 -1
  49. package/src/chat/server/imageGeneratorTools.ts +31 -18
  50. package/src/chat/server/openAIRouterLLM.ts +171 -0
  51. package/src/chat/server/openSession.ts +87 -100
  52. package/src/chat/server/server.ts +6 -2
  53. package/src/chat/server/sessionFileManager.ts +5 -5
  54. package/src/chat/server/sessionRegistry.test.ts +0 -1
  55. package/src/chat/server/sessionRegistry.ts +100 -4
  56. package/src/chat/server/tools.ts +73 -35
  57. package/src/test/agent.test.ts +8 -7
  58. package/src/test/chatContextManager.test.ts +42 -37
  59. package/src/test/clientServerConnection.test.ts +0 -2
  60. package/src/test/compressingContextManager.test.ts +29 -34
  61. package/src/test/context.test.ts +59 -15
  62. package/src/test/contextTestTools.ts +95 -0
  63. 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 { llmUserMessages, agentFirstChunk } = cm.startAgentResponse(userMsgs);
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)(llmUserMessages).eql(userMsgs.map((su) => {
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 = cm.processAgentMessage("AgentMe", false);
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 = cm.processAgentMessage("ssage2", true);
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
- cm.processAgentResponse(agentResponseWithToolCall);
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
- const toolCallMesasge0 = cm.processToolCallResult(toolCallResults[0]);
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 = cm.processToolCallResult(toolCallResults[1]);
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
- // The agent adds the messages to the context at the end of processing
176
- cm.addMessages(llmUserMessages);
177
- cm.addMessage(agentResponseWithToolCall);
178
- cm.addMessage(toolCallResults[0]);
179
- cm.addMessage(toolCallResults[1]);
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, "", "", "", writer, new sessionFileManager_1.MemoryFileManager());
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, "", "", "", writer, new sessionFileManager_1.MemoryFileManager());
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 { llmUserMessages, agentFirstChunk } = cm.startAgentResponse([
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)(llmUserMessages).eql([
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
- cm.processAgentResponse(agentResponseWithToolCall);
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
- const toolCallMesasge0 = cm.processToolCallResult(toolCallResults[0]);
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 = cm.processToolCallResult(toolCallResults[1]);
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
- cm.revertAgentResponse("an error occured");
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.LLM_URL, mockEnv.XMCP_URL);
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 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
- ];
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", [], "", // llmUrl
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.addMessages(MESSAGES.slice(0, 2));
35
- ccm.commit();
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.addMessages(MESSAGES.slice(2));
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 shoudl still be intact
62
- const pending = ccm.getPending();
63
- (0, vitest_1.expect)(pending).eql(MESSAGES.slice(2));
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 context_1 = require("../agent/context");
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("should correctly manage messages", function () {
16
- const cm = new context_1.ContextManagerWithWorkspace("sys_msg", msgs.slice(0, 2));
17
- // set workspace and add new messages
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.addMessages(msgs.slice(2));
20
- // context with workspace
21
- const ctx1 = cm.getLLMContext();
22
- (0, vitest_1.expect)(ctx1).eql([
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
- // context with new message and workspace
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.addMessage(lastMsg);
33
- const ctx2 = cm.getLLMContext();
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 context_1.ContextManagerWithWorkspace("agent_prompt", msgs.slice(0, 2));
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 context_1.ContextManagerWithWorkspace("sys_msg", msgs.slice(0, 2));
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.addMessages(msgs.slice(2));
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, llmUrl, xmcpUrl, pidFile, }) => {
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, llmUrl, xmcpUrl);
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-url <server-name> <url>")} 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-url":
295
- addMcpServerFromUrl(sessionClient, cmds[1], cmds[2]);
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 addMcpServerFromUrl(sessionClient, name, url) {
368
- sessionClient.addMcpServerFromUrl(name, url);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xalia/agent",
3
- "version": "0.6.8",
3
+ "version": "0.6.9",
4
4
  "keywords": [],
5
5
  "author": "",
6
6
  "license": "ISC",
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