@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
@@ -14,6 +14,7 @@ import {
14
14
  McpServer,
15
15
  } from "@xalia/xmcp/sdk";
16
16
  import { Client as McpClient } from "@modelcontextprotocol/sdk/client/index.js";
17
+ import { CustomMcpServerDescriptor } from "../chat/data/dataModels";
17
18
 
18
19
  const DEVELOPMENT: boolean = process.env.DEVELOPMENT === "1";
19
20
 
@@ -31,7 +32,7 @@ class SanitizedServerBrief extends McpServerBrief {
31
32
  readonly originalName: string;
32
33
 
33
34
  private constructor() {
34
- super("dummy", "dummy", "dummy", false, {}, "dummy", "dummy");
35
+ super("dummy", "dummy", "dummy", false, {}, "dummy", null, null, undefined);
35
36
  this.originalName = "dummy";
36
37
  }
37
38
 
@@ -79,7 +80,8 @@ export class SkillManager extends McpServerManager implements ISkillManager {
79
80
  displayName: string
80
81
  ) => void,
81
82
  // Redirect to this page after successful authorization
82
- private authorized_url: string | undefined
83
+ private authorized_url: string | undefined,
84
+ private customMcpServers: Record<string, CustomMcpServerDescriptor>
83
85
  ) {
84
86
  super();
85
87
  }
@@ -96,7 +98,8 @@ export class SkillManager extends McpServerManager implements ISkillManager {
96
98
  ) => void,
97
99
  sudoMcpUrl?: string,
98
100
  sudoMcpApiKey?: string,
99
- authorized_url?: string
101
+ authorized_url?: string,
102
+ customMcpServers?: Record<string, CustomMcpServerDescriptor>
100
103
  ): Promise<SkillManager> {
101
104
  // TODO: Keep it on here and pass to `McpServerManager.addMcpServer`
102
105
  const apiClient = new ApiClient(
@@ -106,31 +109,29 @@ export class SkillManager extends McpServerManager implements ISkillManager {
106
109
  // Fetch server list
107
110
  const servers = await apiClient.listServers();
108
111
 
112
+ customMcpServers = customMcpServers || {};
109
113
  if (DEVELOPMENT) {
110
- servers.push(
111
- new McpServerBrief(
112
- "local_dev",
113
- "Local Dev Server",
114
+ customMcpServers["local_dev"] = {
115
+ name: "local_dev",
116
+ description:
114
117
  "A local mcp server using streamable-HTTP for development use. " +
115
- `URL: ${DEV_MCP_SERVER_URL}`,
116
- false,
117
- {},
118
- "",
119
- null,
120
- null,
121
- DEV_MCP_SERVER_URL
122
- )
123
- );
118
+ `URL: ${DEV_MCP_SERVER_URL}`,
119
+ url: DEV_MCP_SERVER_URL,
120
+ };
124
121
  }
125
122
 
126
- const [mcpServers, mcpServersMap] = buildServersList(servers);
123
+ const [mcpServers, mcpServersMap] = buildServersList(
124
+ servers,
125
+ customMcpServers
126
+ );
127
127
  return new SkillManager(
128
128
  apiClient,
129
129
  mcpServers,
130
130
  mcpServersMap,
131
131
  new Map(),
132
132
  openUrl,
133
- authorized_url
133
+ authorized_url,
134
+ customMcpServers
134
135
  );
135
136
  }
136
137
 
@@ -188,7 +189,10 @@ export class SkillManager extends McpServerManager implements ISkillManager {
188
189
  */
189
190
  public async refresh(): Promise<void> {
190
191
  const servers = await this.apiClient.listServers();
191
- const [mcpServers, mcpServersMap] = buildServersList(servers);
192
+ const [mcpServers, mcpServersMap] = buildServersList(
193
+ servers,
194
+ this.customMcpServers
195
+ );
192
196
  this.serverBriefs = mcpServers;
193
197
  this.serverBriefsMap = mcpServersMap;
194
198
  this.toolCache = new Map();
@@ -321,7 +325,8 @@ async function connectServer(
321
325
  * holding the original name in a new field).
322
326
  */
323
327
  function buildServersList(
324
- serverList: McpServerBrief[]
328
+ serverList: McpServerBrief[],
329
+ customServers: Record<string, CustomMcpServerDescriptor>
325
330
  ): [SanitizedServerBrief[], { [serverName: string]: SanitizedServerBrief }] {
326
331
  const servers: SanitizedServerBrief[] = [];
327
332
  const serversMap: { [serverName: string]: SanitizedServerBrief } = {};
@@ -332,6 +337,22 @@ function buildServersList(
332
337
  serversMap[ss.name] = ss;
333
338
  }
334
339
 
340
+ for (const [name, desc] of Object.entries(customServers)) {
341
+ const brief = new McpServerBrief(
342
+ name,
343
+ desc.name,
344
+ desc.description,
345
+ false,
346
+ {},
347
+ "",
348
+ null,
349
+ null,
350
+ desc.url
351
+ );
352
+ const ss = SanitizedServerBrief.fromServerBrief(brief);
353
+ servers.push(ss);
354
+ serversMap[ss.name] = ss;
355
+ }
335
356
  return [servers, serversMap];
336
357
  }
337
358
 
@@ -28,6 +28,8 @@ import {
28
28
  ServerControlAgentProfileCreated,
29
29
  ServerControlAgentProfileDeleted,
30
30
  ClientControlSessionCreate,
31
+ ServerControlCustomMcpServerAdded,
32
+ ServerControlCustomMcpServerRemoved,
31
33
  } from "../protocol/messages";
32
34
  import { SESSION_JOIN_TIMEOUT } from "./constants";
33
35
  import { ITeamManager } from "./teamManager";
@@ -81,6 +83,8 @@ export class ChatClient implements ITeamManager {
81
83
  JoinCreateResponse,
82
84
  CreatedClient
83
85
  >;
86
+ // server_name => url
87
+ private customMcpServers: Map<string, string>;
84
88
 
85
89
  private constructor(
86
90
  connection: Connection<ClientToServer, ServerToClient>,
@@ -106,6 +110,7 @@ export class ChatClient implements ITeamManager {
106
110
  this.onJoinCreateResponse.bind(this),
107
111
  Math.max(SESSION_JOIN_TIMEOUT)
108
112
  );
113
+ this.customMcpServers = new Map();
109
114
  }
110
115
 
111
116
  /**
@@ -537,6 +542,26 @@ export class ChatClient implements ITeamManager {
537
542
  }
538
543
  }
539
544
 
545
+ getCustomMcpServers(): Record<string, string> {
546
+ return Object.fromEntries(this.customMcpServers.entries());
547
+ }
548
+
549
+ addCustomMcpServer(server_name: string, description: string, url: string) {
550
+ this.connection.send({
551
+ type: "control_add_custom_mcp_server",
552
+ server_name,
553
+ description,
554
+ url,
555
+ });
556
+ }
557
+
558
+ removeCustomMcpServer(server_name: string) {
559
+ this.connection.send({
560
+ type: "control_remove_custom_mcp_server",
561
+ server_name,
562
+ });
563
+ }
564
+
540
565
  /**
541
566
  * Delete a session from the agent session map
542
567
  * @param agentSessionMap - the agent session map
@@ -618,6 +643,12 @@ export class ChatClient implements ITeamManager {
618
643
  case "control_team_members_updated":
619
644
  this.handleTeamMembersUpdated(msg);
620
645
  break;
646
+ case "control_custom_mcp_server_added":
647
+ this.handleCustomMcpServerAdded(msg);
648
+ break;
649
+ case "control_custom_mcp_server_removed":
650
+ this.handleCustomMcpServerRemoved(msg);
651
+ break;
621
652
  default: {
622
653
  const _exhaustive: never = msg;
623
654
  throw new Error(`unexpected control msg: ${JSON.stringify(msg)}`);
@@ -701,8 +732,11 @@ export class ChatClient implements ITeamManager {
701
732
  const sessionClient = this.activeSessions.get(sessionId);
702
733
  if (sessionClient) {
703
734
  sessionClient.handleMessage(msg);
704
- // we are only calling the onMessage for session scoped message for now.
705
- void this.eventHandler.onMessage(msg, this);
735
+ // Only forward messages to UI if they're for the current session
736
+ // This prevents streaming messages from leaking into other chats
737
+ if (sessionId === this.currentSessionId) {
738
+ void this.eventHandler.onMessage(msg, this);
739
+ }
706
740
  } else {
707
741
  throw new Error(`Session client not found for session ${sessionId}`);
708
742
  }
@@ -773,7 +807,7 @@ export class ChatClient implements ITeamManager {
773
807
  this.upsertSessionToAgentSessionMap(msg);
774
808
  }
775
809
 
776
- handleControlError(msg: ServerControlError): void {
810
+ private handleControlError(msg: ServerControlError): void {
777
811
  if (this.sessionJoinAwaiter.onMessage(msg)) {
778
812
  return;
779
813
  }
@@ -833,6 +867,16 @@ export class ChatClient implements ITeamManager {
833
867
  agentSession.updated_at = new Date(msg.updated_at).getTime();
834
868
  }
835
869
 
870
+ private handleCustomMcpServerAdded(msg: ServerControlCustomMcpServerAdded) {
871
+ this.customMcpServers.set(msg.server_name, msg.url);
872
+ }
873
+
874
+ private handleCustomMcpServerRemoved(
875
+ msg: ServerControlCustomMcpServerRemoved
876
+ ) {
877
+ this.customMcpServers.delete(msg.server_name);
878
+ }
879
+
836
880
  // This is the immediate callback of the ResponseAwaiter for session
837
881
  // joining. It must create and register the client.
838
882
  private onJoinCreateResponse(msg: JoinCreateResponse): CreatedClient {
@@ -108,7 +108,17 @@ export class Connection<ClientMsgT extends MsgBase, ServerMsgT extends MsgBase>
108
108
  ): void {
109
109
  if (this.state === "ready" && this.ws) {
110
110
  const data = JSON.stringify(message);
111
- logger.debug(`[Connection.send] ${data}`);
111
+
112
+ // Truncate base64 image data for logging to avoid console spam
113
+ let logData = data;
114
+ if (data.includes('"imageB64":"data:image/')) {
115
+ logData = data.replace(
116
+ /"imageB64":"data:image\/[^"]+"/,
117
+ '"imageB64":"<base64 image data truncated>"'
118
+ );
119
+ }
120
+ logger.debug(`[Connection.send] ${logData}`);
121
+
112
122
  this.ws.send(data);
113
123
  } else {
114
124
  throw new Error(`Cannot send message: connection state is ${this.state}`);
@@ -309,14 +309,6 @@ export class SessionClient implements ISessionMessageSender, IConversation {
309
309
  });
310
310
  }
311
311
 
312
- addMcpServerFromUrl(server_name: string, url: string) {
313
- this.sendSessionMessage({
314
- type: "add_mcp_server_from_url",
315
- server_name,
316
- url,
317
- });
318
- }
319
-
320
312
  async getMcpResource(
321
313
  server_name: string,
322
314
  uri: string
@@ -93,3 +93,9 @@ export type TeamInfo = {
93
93
  * Map from user_uuid to role
94
94
  */
95
95
  export type SessionParticipantMap = Map<string, TeamParticipant>;
96
+
97
+ export type CustomMcpServerDescriptor = {
98
+ name: string;
99
+ description: string;
100
+ url: string;
101
+ };
@@ -65,4 +65,38 @@ export class DbSessionMessages extends DbClientBase {
65
65
  throw error;
66
66
  }
67
67
  }
68
+
69
+ async searchMessages(
70
+ userUuid: string,
71
+ searchQuery: string,
72
+ limit = 50
73
+ ): Promise<
74
+ Array<{
75
+ session_uuid: string;
76
+ message_idx: number;
77
+ sender_uuid?: string;
78
+ content: unknown;
79
+ match_snippet: string;
80
+ }>
81
+ > {
82
+ type SearchResult = {
83
+ session_uuid: string;
84
+ message_idx: number;
85
+ sender_uuid?: string;
86
+ content: unknown;
87
+ match_snippet: string;
88
+ };
89
+
90
+ const result = await this.client.rpc("search_session_messages", {
91
+ p_user_uuid: userUuid,
92
+ p_search_query: searchQuery,
93
+ p_limit: limit,
94
+ });
95
+
96
+ if (result.error) {
97
+ throw result.error;
98
+ }
99
+
100
+ return (result.data as SearchResult[] | null) || [];
101
+ }
68
102
  }
@@ -13,6 +13,7 @@ import {
13
13
  TeamParticipant,
14
14
  UserMessageData,
15
15
  SessionDescriptor,
16
+ CustomMcpServerDescriptor,
16
17
  } from "../data/dataModels";
17
18
  import { SessionFileDescriptor } from "../data/dbSessionFileModels";
18
19
  import {
@@ -114,6 +115,18 @@ export type ClientControlRemoveTeamUser = {
114
115
  target_team_id: string;
115
116
  } & ClientToServerBase;
116
117
 
118
+ export type ClientControlAddCustomMcpServer = {
119
+ type: "control_add_custom_mcp_server";
120
+ server_name: string;
121
+ description: string;
122
+ url: string;
123
+ };
124
+
125
+ export type ClientControlRemoveCustomMcpServer = {
126
+ type: "control_remove_custom_mcp_server";
127
+ server_name: string;
128
+ };
129
+
117
130
  export type ClientControlMessage =
118
131
  | ClientControlAgentProfileCreate
119
132
  | ClientControlAgentProfileDelete
@@ -123,7 +136,9 @@ export type ClientControlMessage =
123
136
  | ClientControlTeamCreate
124
137
  | ClientControlSessionDelete
125
138
  | ClientControlAddTeamUser
126
- | ClientControlRemoveTeamUser;
139
+ | ClientControlRemoveTeamUser
140
+ | ClientControlAddCustomMcpServer
141
+ | ClientControlRemoveCustomMcpServer;
127
142
 
128
143
  //
129
144
  // Messages for specific sessions
@@ -217,12 +232,6 @@ export type ClientSetAgentPaused = {
217
232
  paused: boolean;
218
233
  };
219
234
 
220
- export type ClientAddMcpServerFromUrl = {
221
- type: "add_mcp_server_from_url";
222
- server_name: string;
223
- url: string;
224
- };
225
-
226
235
  export type ClientGetMcpResource = {
227
236
  type: "get_mcp_resource";
228
237
  client_message_id: string;
@@ -263,7 +272,6 @@ export type ClientSessionMessageData =
263
272
  | ClientSetSystemPrompt
264
273
  | ClientSetModel
265
274
  | ClientSetAgentPaused
266
- | ClientAddMcpServerFromUrl
267
275
  | ClientGetMcpResource
268
276
  | ClientSetMcpServerConfig
269
277
  | ClientShareSession;
@@ -291,6 +299,8 @@ export function isClientControlMessage(
291
299
  case "control_team_create":
292
300
  case "control_add_team_user":
293
301
  case "control_remove_team_user":
302
+ case "control_add_custom_mcp_server":
303
+ case "control_remove_custom_mcp_server":
294
304
  return true;
295
305
  default: {
296
306
  const _: never = msg;
@@ -382,6 +392,7 @@ export type ServerAgentMessageChunk = {
382
392
  message_idx: number;
383
393
  alternative?: string;
384
394
  end: boolean;
395
+ alt?: string;
385
396
  } & ServerSessionMessage;
386
397
 
387
398
  export type ServerReasoningChunk = {
@@ -641,6 +652,7 @@ export type ServerControlSessionList = {
641
652
  user_sessions: Array<SessionDescriptor>;
642
653
  user_agents: Array<SavedAgentProfile>;
643
654
  team_sessions: Array<TeamInfo>;
655
+ custom_mcp_servers: Record<string, CustomMcpServerDescriptor>;
644
656
  } & ControlResponseMessage;
645
657
 
646
658
  /**
@@ -662,6 +674,17 @@ export type ServerControlSessionDeleted = {
662
674
  team_uuid?: string;
663
675
  } & ControlResponseMessage;
664
676
 
677
+ export type ServerControlCustomMcpServerAdded = {
678
+ type: "control_custom_mcp_server_added";
679
+ server_name: string;
680
+ url: string;
681
+ };
682
+
683
+ export type ServerControlCustomMcpServerRemoved = {
684
+ type: "control_custom_mcp_server_removed";
685
+ server_name: string;
686
+ };
687
+
665
688
  /**
666
689
  * Connection-level errors
667
690
  * Error in response to an invalid request from the client
@@ -681,6 +704,8 @@ export type ServerControlMessage =
681
704
  | ServerControlAgentProfileDeleted
682
705
  | ServerControlTeamCreated
683
706
  | ServerControlTeamMembersUpdated
707
+ | ServerControlCustomMcpServerAdded
708
+ | ServerControlCustomMcpServerRemoved
684
709
  | ServerControlError;
685
710
 
686
711
  export type ServerSessionScopedMessage =
@@ -713,6 +738,8 @@ export function isServerControlMessage(
713
738
  case "control_session_deleted":
714
739
  case "control_team_created":
715
740
  case "control_team_members_updated":
741
+ case "control_custom_mcp_server_added":
742
+ case "control_custom_mcp_server_removed":
716
743
  case "control_error":
717
744
  return true;
718
745
  default: {