@xalia/agent 0.6.10 → 0.6.11

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 (161) hide show
  1. package/package.json +5 -2
  2. package/.env.development +0 -6
  3. package/.env.test +0 -7
  4. package/.prettierrc.json +0 -11
  5. package/context_system.md +0 -498
  6. package/eslint.config.mjs +0 -38
  7. package/scripts/chat_server +0 -8
  8. package/scripts/git_message +0 -31
  9. package/scripts/git_wip +0 -21
  10. package/scripts/pr_message +0 -18
  11. package/scripts/pr_review +0 -16
  12. package/scripts/setup_chat +0 -90
  13. package/scripts/shutdown_chat_server +0 -42
  14. package/scripts/start_chat_server +0 -24
  15. package/scripts/sudomcp_import +0 -23
  16. package/scripts/test_chat +0 -327
  17. package/src/agent/agent.ts +0 -699
  18. package/src/agent/agentUtils.ts +0 -286
  19. package/src/agent/compressingContextManager.ts +0 -129
  20. package/src/agent/context.ts +0 -265
  21. package/src/agent/contextWithWorkspace.ts +0 -162
  22. package/src/agent/documentSummarizer.ts +0 -157
  23. package/src/agent/dummyLLM.ts +0 -130
  24. package/src/agent/iAgentEventHandler.ts +0 -64
  25. package/src/agent/imageGenLLM.ts +0 -101
  26. package/src/agent/imageGenerator.ts +0 -45
  27. package/src/agent/iplatform.ts +0 -18
  28. package/src/agent/llm.ts +0 -74
  29. package/src/agent/mcpServerManager.ts +0 -541
  30. package/src/agent/nullAgentEventHandler.ts +0 -26
  31. package/src/agent/nullPlatform.ts +0 -13
  32. package/src/agent/openAI.ts +0 -123
  33. package/src/agent/openAILLM.ts +0 -99
  34. package/src/agent/openAILLMStreaming.ts +0 -648
  35. package/src/agent/promptProvider.ts +0 -87
  36. package/src/agent/repeatLLM.ts +0 -62
  37. package/src/agent/sudoMcpServerManager.ts +0 -361
  38. package/src/agent/test_data/harrypotter.txt +0 -6065
  39. package/src/agent/tokenAuth.ts +0 -50
  40. package/src/agent/tokenCounter.test.ts +0 -243
  41. package/src/agent/tokenCounter.ts +0 -483
  42. package/src/agent/toolSettings.ts +0 -24
  43. package/src/agent/tools/calculatorTool.ts +0 -50
  44. package/src/agent/tools/contentExtractors/htmlToText.ts +0 -61
  45. package/src/agent/tools/contentExtractors/pdfToText.ts +0 -60
  46. package/src/agent/tools/datetimeTool.ts +0 -41
  47. package/src/agent/tools/fileManager/fileManagerTool.ts +0 -199
  48. package/src/agent/tools/fileManager/index.ts +0 -50
  49. package/src/agent/tools/fileManager/memoryFileManager.ts +0 -120
  50. package/src/agent/tools/fileManager/mimeTypes.ts +0 -60
  51. package/src/agent/tools/fileManager/prompt.ts +0 -38
  52. package/src/agent/tools/fileManager/types.ts +0 -189
  53. package/src/agent/tools/index.ts +0 -49
  54. package/src/agent/tools/openUrlTool.ts +0 -62
  55. package/src/agent/tools/renderTool.ts +0 -92
  56. package/src/agent/tools/utils.ts +0 -74
  57. package/src/agent/tools/webSearch.ts +0 -138
  58. package/src/agent/tools/webSearchTool.ts +0 -44
  59. package/src/chat/client/chatClient.ts +0 -967
  60. package/src/chat/client/connection.test.ts +0 -241
  61. package/src/chat/client/connection.ts +0 -286
  62. package/src/chat/client/constants.ts +0 -1
  63. package/src/chat/client/index.ts +0 -21
  64. package/src/chat/client/interfaces.ts +0 -34
  65. package/src/chat/client/sessionClient.ts +0 -574
  66. package/src/chat/client/sessionFiles.ts +0 -142
  67. package/src/chat/client/teamManager.ts +0 -29
  68. package/src/chat/constants.ts +0 -6
  69. package/src/chat/data/apiKeyManager.ts +0 -76
  70. package/src/chat/data/dataModels.ts +0 -107
  71. package/src/chat/data/database.ts +0 -997
  72. package/src/chat/data/dbMcpServerConfigs.ts +0 -59
  73. package/src/chat/data/dbSessionFiles.ts +0 -107
  74. package/src/chat/data/dbSessionMessages.ts +0 -102
  75. package/src/chat/protocol/connectionMessages.ts +0 -49
  76. package/src/chat/protocol/constants.ts +0 -55
  77. package/src/chat/protocol/errors.ts +0 -16
  78. package/src/chat/protocol/messages.ts +0 -899
  79. package/src/chat/server/README.md +0 -127
  80. package/src/chat/server/chatContextManager.ts +0 -660
  81. package/src/chat/server/connectionManager.test.ts +0 -246
  82. package/src/chat/server/connectionManager.ts +0 -506
  83. package/src/chat/server/conversation.ts +0 -319
  84. package/src/chat/server/errorUtils.ts +0 -28
  85. package/src/chat/server/imageGeneratorTools.ts +0 -179
  86. package/src/chat/server/openAIRouterLLM.ts +0 -168
  87. package/src/chat/server/openSession.ts +0 -1945
  88. package/src/chat/server/openSessionMessageSender.ts +0 -4
  89. package/src/chat/server/promptRefiner.ts +0 -106
  90. package/src/chat/server/server.ts +0 -178
  91. package/src/chat/server/sessionFileManager.ts +0 -151
  92. package/src/chat/server/sessionRegistry.test.ts +0 -137
  93. package/src/chat/server/sessionRegistry.ts +0 -1553
  94. package/src/chat/server/test-utils/mockFactories.ts +0 -422
  95. package/src/chat/server/titleGenerator.test.ts +0 -103
  96. package/src/chat/server/titleGenerator.ts +0 -143
  97. package/src/chat/server/tools.ts +0 -170
  98. package/src/chat/utils/agentSessionMap.ts +0 -76
  99. package/src/chat/utils/approvalManager.ts +0 -189
  100. package/src/chat/utils/asyncLock.ts +0 -43
  101. package/src/chat/utils/asyncQueue.ts +0 -62
  102. package/src/chat/utils/multiAsyncQueue.ts +0 -66
  103. package/src/chat/utils/responseAwaiter.ts +0 -181
  104. package/src/chat/utils/userResolver.ts +0 -48
  105. package/src/chat/utils/websocket.ts +0 -16
  106. package/src/index.ts +0 -0
  107. package/src/test/agent.test.ts +0 -584
  108. package/src/test/approvalManager.test.ts +0 -141
  109. package/src/test/chatContextManager.test.ts +0 -552
  110. package/src/test/clientServerConnection.test.ts +0 -205
  111. package/src/test/compressingContextManager.test.ts +0 -77
  112. package/src/test/context.test.ts +0 -150
  113. package/src/test/contextTestTools.ts +0 -95
  114. package/src/test/conversation.test.ts +0 -109
  115. package/src/test/db.test.ts +0 -363
  116. package/src/test/dbMcpServerConfigs.test.ts +0 -112
  117. package/src/test/dbSessionFiles.test.ts +0 -258
  118. package/src/test/dbSessionMessages.test.ts +0 -85
  119. package/src/test/dbTestTools.ts +0 -157
  120. package/src/test/imageLoad.test.ts +0 -15
  121. package/src/test/mcpServerManager.test.ts +0 -114
  122. package/src/test/multiAsyncQueue.test.ts +0 -183
  123. package/src/test/openaiStreaming.test.ts +0 -177
  124. package/src/test/prompt.test.ts +0 -27
  125. package/src/test/promptProvider.test.ts +0 -33
  126. package/src/test/responseAwaiter.test.ts +0 -103
  127. package/src/test/sudoMcpServerManager.test.ts +0 -63
  128. package/src/test/testTools.ts +0 -176
  129. package/src/test/tools.test.ts +0 -64
  130. package/src/tool/agentChat.ts +0 -203
  131. package/src/tool/agentMain.ts +0 -180
  132. package/src/tool/chatMain.ts +0 -621
  133. package/src/tool/commandPrompt.ts +0 -264
  134. package/src/tool/files.ts +0 -82
  135. package/src/tool/main.ts +0 -25
  136. package/src/tool/nodePlatform.ts +0 -73
  137. package/src/tool/options.ts +0 -144
  138. package/src/tool/prompt.ts +0 -101
  139. package/test_data/background_test_profile.json +0 -6
  140. package/test_data/background_test_script.json +0 -11
  141. package/test_data/dummyllm_script_crash.json +0 -32
  142. package/test_data/dummyllm_script_image_gen.json +0 -19
  143. package/test_data/dummyllm_script_image_gen_fe.json +0 -29
  144. package/test_data/dummyllm_script_invoke_image_gen_tool.json +0 -37
  145. package/test_data/dummyllm_script_render_tool.json +0 -29
  146. package/test_data/dummyllm_script_simplecalc.json +0 -28
  147. package/test_data/dummyllm_script_test_auto_approve.json +0 -81
  148. package/test_data/dummyllm_script_test_simplecalc_addition.json +0 -29
  149. package/test_data/frog.png +0 -0
  150. package/test_data/frog.png.b64 +0 -1
  151. package/test_data/git_message_profile.json +0 -4
  152. package/test_data/git_wip_system.txt +0 -5
  153. package/test_data/image_gen_test_profile.json +0 -5
  154. package/test_data/pr_message_profile.json +0 -4
  155. package/test_data/pr_review_profile.json +0 -4
  156. package/test_data/prompt_simplecalc.txt +0 -1
  157. package/test_data/simplecalc_profile.json +0 -4
  158. package/test_data/sudomcp_import_profile.json +0 -4
  159. package/test_data/test_script_profile.json +0 -8
  160. package/tsconfig.json +0 -13
  161. package/vitest.config.ts +0 -39
@@ -1,574 +0,0 @@
1
- import { Tool, Resource } from "@modelcontextprotocol/sdk/types.js";
2
- import { strict as assert } from "assert";
3
- import { v4 as uuidv4 } from "uuid";
4
-
5
- import {
6
- AgentProfile,
7
- McpServerBrief,
8
- SavedAgentProfile,
9
- getLogger,
10
- } from "@xalia/xmcp/sdk";
11
-
12
- import { ISkillManager } from "../../agent/sudoMcpServerManager";
13
- import {
14
- McpServerInfo,
15
- McpServerInfoRW,
16
- ResourceContent,
17
- } from "../../agent/mcpServerManager";
18
- import { IConversation } from "../../agent/agent";
19
- import { MessageParam } from "../../agent/llm";
20
-
21
- import {
22
- ClientSessionMessage,
23
- ClientSessionMessageData,
24
- ServerSessionScopedMessage,
25
- ClientSetWorkspace,
26
- ClientToServer,
27
- isServerSessionFileMessage,
28
- } from "../protocol/messages";
29
- import { SessionParticipantMap } from "../data/dataModels";
30
- import { ISessionMessageSender } from "./interfaces";
31
- import { IMessageSender } from "./connection";
32
- import { SessionFiles } from "./sessionFiles";
33
- import { SessionFileDescriptor } from "../../agent/tools/fileManager";
34
- import { ResponseAwaiter } from "../utils/responseAwaiter";
35
-
36
- const logger = getLogger();
37
-
38
- class RemoteSudoMcpServerManager implements ISkillManager {
39
- private sender: ISessionMessageSender;
40
- private briefs: McpServerBrief[];
41
- private briefsMap = new Map<string, McpServerBrief>();
42
- private mcpServers = new Map<string, McpServerInfoRW>();
43
-
44
- constructor(sender: ISessionMessageSender, briefs: McpServerBrief[]) {
45
- this.sender = sender;
46
- this.briefs = briefs;
47
- briefs.forEach((b) => {
48
- this.briefsMap.set(b.name, b);
49
- });
50
- }
51
-
52
- hasMcpServer(mcpServerName: string): boolean {
53
- return this.mcpServers.has(mcpServerName);
54
- }
55
-
56
- getMcpServerNames(): string[] {
57
- return Array.from(this.mcpServers.keys());
58
- }
59
-
60
- getMcpServer(mcpServerName: string): McpServerInfo {
61
- const server = this.mcpServers.get(mcpServerName);
62
- if (server) {
63
- return server;
64
- }
65
- throw Error(`[getMcpServer] unknown server ${mcpServerName}`);
66
- }
67
-
68
- // eslint-disable-next-line @typescript-eslint/require-await
69
- async removeMcpServer(mcpServerName: string): Promise<void> {
70
- if (!this.mcpServers.has(mcpServerName)) {
71
- logger.info(
72
- `[removeMcpServer] mcpServers: ${JSON.stringify(this.mcpServers)}`
73
- );
74
- throw Error(`no server ${mcpServerName} (removeMcpServer)`);
75
- }
76
-
77
- this.sender.sendSessionMessage({
78
- type: "remove_mcp_server",
79
- server_name: mcpServerName,
80
- });
81
- }
82
-
83
- enableAllTools(mcpServerName: string): void {
84
- if (!this.mcpServers.has(mcpServerName)) {
85
- throw Error(`no server ${mcpServerName} (enableAllTools)`);
86
- }
87
-
88
- this.sender.sendSessionMessage({
89
- type: "enable_all_mcp_server_tools",
90
- server_name: mcpServerName,
91
- });
92
- }
93
-
94
- disableAllTools(mcpServerName: string): void {
95
- if (!this.mcpServers.has(mcpServerName)) {
96
- throw Error(`no server ${mcpServerName} (disableAllTools)`);
97
- }
98
-
99
- this.sender.sendSessionMessage({
100
- type: "disable_all_mcp_server_tools",
101
- server_name: mcpServerName,
102
- });
103
- }
104
-
105
- enableTool(mcpServerName: string, toolName: string): void {
106
- const server = this.mcpServers.get(mcpServerName);
107
- if (!server) {
108
- throw Error(`no server ${mcpServerName} (enableTool)`);
109
- }
110
- const tools = server.getTool(toolName);
111
- if (!tools) {
112
- throw Error(`no tool ${toolName} on server ${mcpServerName}`);
113
- }
114
-
115
- this.sender.sendSessionMessage({
116
- type: "enable_mcp_server_tool",
117
- server_name: mcpServerName,
118
- tool: toolName,
119
- });
120
- }
121
-
122
- disableTool(mcpServerName: string, toolName: string): void {
123
- const server = this.mcpServers.get(mcpServerName);
124
- if (!server) {
125
- throw Error(`no server ${mcpServerName} (disableTool)`);
126
- }
127
- const tools = server.getTool(toolName);
128
- if (!tools) {
129
- throw Error(`no tool ${toolName} on server ${mcpServerName}`);
130
- }
131
-
132
- this.sender.sendSessionMessage({
133
- type: "disable_mcp_server_tool",
134
- server_name: mcpServerName,
135
- tool: toolName,
136
- });
137
- }
138
-
139
- onMcpServerAdded(
140
- mcpServerName: string,
141
- tools: Tool[],
142
- enabled_tools: string[],
143
- resources: Resource[]
144
- ) {
145
- logger.debug(
146
- `[onMcpServerAdded]: ${mcpServerName}, tools: ${JSON.stringify(tools)}` +
147
- `, enabled: ${JSON.stringify(enabled_tools)}`
148
- );
149
-
150
- const mcpServerInfo = new McpServerInfoRW(mcpServerName, tools, resources);
151
- for (const tool of enabled_tools) {
152
- mcpServerInfo.enableTool(tool);
153
- }
154
- this.mcpServers.set(mcpServerName, mcpServerInfo);
155
- }
156
-
157
- onMcpServerRemoved(mcpServerName: string) {
158
- this.mcpServers.delete(mcpServerName);
159
- }
160
-
161
- onMcpServerToolEnabled(mcpServerName: string, toolName: string) {
162
- const server = this.mcpServers.get(mcpServerName);
163
- if (!server) {
164
- throw Error(`no server ${mcpServerName} (onMcpServerToolEnabled)`);
165
- }
166
- const tools = server.getTool(toolName);
167
- if (!tools) {
168
- throw Error(`no tool ${toolName} on server ${mcpServerName}`);
169
- }
170
-
171
- server.enableTool(toolName);
172
- }
173
-
174
- onMcpServerToolDisabled(mcpServerName: string, toolName: string) {
175
- const server = this.mcpServers.get(mcpServerName);
176
- if (!server) {
177
- throw Error(`no server ${mcpServerName} (onMcpServerToolDisabled)`);
178
- }
179
- const tools = server.getTool(toolName);
180
- if (!tools) {
181
- throw Error(`no tool ${toolName} on server ${mcpServerName}`);
182
- }
183
-
184
- server.disableTool(toolName);
185
- }
186
-
187
- getServerBriefs(): McpServerBrief[] {
188
- return this.briefs;
189
- }
190
-
191
- // eslint-disable-next-line @typescript-eslint/require-await
192
- async addMcpServer(server_name: string, enable_all: boolean): Promise<void> {
193
- if (!this.briefsMap.has(server_name)) {
194
- throw Error(`no such server ${server_name} (addMcpServer)`);
195
- }
196
-
197
- this.sender.sendSessionMessage({
198
- type: "add_mcp_server",
199
- server_name,
200
- enable_all,
201
- });
202
- }
203
-
204
- hasServer(server_name: string): boolean {
205
- return this.briefsMap.has(server_name);
206
- }
207
-
208
- async shutdown(): Promise<void> {}
209
- }
210
-
211
- type ServerResponseMessages = Extract<
212
- ServerSessionScopedMessage,
213
- { client_message_id?: string }
214
- >;
215
-
216
- export class SessionClient implements ISessionMessageSender, IConversation {
217
- private readonly sessionUUID: string;
218
- private readonly savedAgentProfile: SavedAgentProfile;
219
- private readonly sender: IMessageSender<ClientToServer>;
220
- private readonly smsm: RemoteSudoMcpServerManager;
221
- private readonly sessionFiles: SessionFiles;
222
- private readonly responseHandler: ResponseAwaiter<ServerResponseMessages>;
223
- private participants: SessionParticipantMap;
224
- private systemPrompt: string;
225
- private model: string;
226
- private agentPaused: boolean;
227
-
228
- public constructor(
229
- sessionUUID: string,
230
- savedAgentProfile: SavedAgentProfile,
231
- sender: IMessageSender<ClientToServer>,
232
- serverBriefs: McpServerBrief[],
233
- participants: SessionParticipantMap,
234
- agentPaused: boolean
235
- ) {
236
- const fileList: SessionFileDescriptor[] = []; // Pass in?
237
- this.sessionUUID = sessionUUID;
238
- this.savedAgentProfile = savedAgentProfile;
239
- this.sender = sender;
240
- this.smsm = new RemoteSudoMcpServerManager(this, serverBriefs);
241
- this.sessionFiles = new SessionFiles(sessionUUID, fileList, sender);
242
- this.responseHandler = ResponseAwaiter.init(
243
- "session_error",
244
- (msg: ServerResponseMessages) => msg.client_message_id
245
- );
246
- this.participants = participants;
247
- this.systemPrompt = "";
248
- this.model = "";
249
- this.agentPaused = agentPaused;
250
- }
251
-
252
- public getSessionUUID(): string {
253
- return this.sessionUUID;
254
- }
255
-
256
- /// This object can be queried to upload or download files, or get the
257
- /// latest list. See `SessionFiles` for full usage.
258
- public getSessionFiles(): SessionFiles {
259
- return this.sessionFiles;
260
- }
261
-
262
- public getSudoMcpServerManager(): ISkillManager {
263
- return this.smsm;
264
- }
265
-
266
- public getConversation(): MessageParam[] {
267
- throw new Error("unimpl: getConversation");
268
- }
269
-
270
- public getAgentProfile(): AgentProfile {
271
- return this.savedAgentProfile.profile;
272
- }
273
-
274
- public getAgentUuid(): string {
275
- return this.savedAgentProfile.uuid;
276
- }
277
-
278
- getSavedAgentProfile(): SavedAgentProfile {
279
- return this.savedAgentProfile;
280
- }
281
-
282
- getSystemPrompt(): string {
283
- return this.systemPrompt;
284
- }
285
-
286
- setSystemPrompt(system_prompt: string): void {
287
- // Don't set system prompt here. Wait until we get confirmation from the
288
- // server.
289
- this.sendSessionMessage({ type: "set_system_prompt", system_prompt });
290
- }
291
-
292
- getModel(): string {
293
- return this.model;
294
- }
295
-
296
- setModel(model: string): void {
297
- // Don't set model here. Wait until we get confirmation from the server.
298
- this.sendSessionMessage({ type: "set_model", model });
299
- }
300
-
301
- getAgentPaused(): boolean {
302
- return this.agentPaused;
303
- }
304
-
305
- setAgentPaused(paused: boolean) {
306
- this.sendSessionMessage({
307
- type: "set_agent_paused",
308
- paused,
309
- });
310
- }
311
-
312
- async getMcpResource(
313
- server_name: string,
314
- uri: string
315
- ): Promise<ResourceContent[]> {
316
- const client_message_id = uuidv4();
317
- const resourceP = this.responseHandler.waitForResponse(client_message_id);
318
- this.sender.send({
319
- type: "get_mcp_resource",
320
- session_id: this.sessionUUID,
321
- client_message_id,
322
- server_name,
323
- uri,
324
- });
325
-
326
- const resourceMsg = await resourceP;
327
- if (resourceMsg.type !== "mcp_resource") {
328
- throw new Error(
329
- `unexpected response to resource req: ${JSON.stringify(resourceMsg)}`
330
- );
331
- }
332
- return resourceMsg.contents;
333
- }
334
-
335
- userMessage(
336
- msg?: string,
337
- imageB64?: string,
338
- attachedFiles?: { name: string; data_url: string }[]
339
- ): void {
340
- assert(
341
- msg || imageB64 || (attachedFiles && attachedFiles.length > 0),
342
- "Either message, image, or attachedFiles must be provided"
343
- );
344
-
345
- this.sendSessionMessage({
346
- type: "msg",
347
- message: msg,
348
- imageB64,
349
- attachedFiles,
350
- });
351
- }
352
-
353
- userMessageRaceMode(model: string, msg?: string, imageB64?: string): void {
354
- assert(msg || imageB64, "Either message or image must be provided");
355
-
356
- this.sendSessionMessage({
357
- type: "msg",
358
- message: msg,
359
- imageB64,
360
- race_mode: model,
361
- });
362
- }
363
-
364
- raceModeResult(result: string, message_id: string): void {
365
- this.sendSessionMessage({
366
- type: "race_mode_result",
367
- result,
368
- message_id,
369
- });
370
- }
371
-
372
- stop(): void {
373
- this.sendSessionMessage({
374
- type: "stop",
375
- });
376
- }
377
-
378
- resetConversation(): void {
379
- throw new Error("resetConversation not implemented for ChatClient");
380
- }
381
-
382
- shutdown(): Promise<void> {
383
- throw new Error("shutdown not implemented for ChatClient");
384
- }
385
-
386
- getParticipants(): SessionParticipantMap {
387
- return this.participants;
388
- }
389
-
390
- /**
391
- * Sets auto-approval preference for a specific tool.
392
- */
393
- setAutoApproval(
394
- serverName: string,
395
- toolName: string,
396
- autoApprove: boolean
397
- ): void {
398
- const smsm = this.getSudoMcpServerManager();
399
- if (!smsm.hasMcpServer(serverName)) {
400
- throw Error(`server ${serverName} not added (setAutoApproval)`);
401
- }
402
- const server = smsm.getMcpServer(serverName);
403
- const tool = server.getTool(toolName);
404
- if (!tool) {
405
- throw Error(`no tool ${toolName} on server ${serverName}`);
406
- }
407
-
408
- this.sendSessionMessage({
409
- type: "set_auto_approval",
410
- server_name: serverName,
411
- tool: toolName,
412
- auto_approve: autoApprove,
413
- });
414
- }
415
-
416
- /**
417
- * Send a generic message (for compatibility)
418
- */
419
- public sendMessage(message: { type: string; message: string }): void {
420
- throw new Error(`Unsupported message type: ${message.type}`);
421
- }
422
-
423
- /**
424
- * The caller sees a message `ServerApproveToolCall`, at which point it
425
- * should prompt the user for permission to run the tool. The result of
426
- * that prompt should be passed here.
427
- */
428
- toolCallApprovalResult(
429
- id: string,
430
- result: boolean,
431
- auto_approve: boolean
432
- ): void {
433
- this.sendSessionMessage({
434
- type: "tool_call_approval_result",
435
- id,
436
- result,
437
- auto_approve,
438
- });
439
- }
440
-
441
- // Implementation of IMessageSender interface
442
- public sendSessionMessage(message: ClientSessionMessageData): void {
443
- const enrichedMessage: ClientSessionMessage = {
444
- ...message,
445
- client_message_id: uuidv4(),
446
- session_id: this.sessionUUID,
447
- };
448
-
449
- this.sender.send(enrichedMessage);
450
- }
451
-
452
- public setWorkspace(
453
- message: string | undefined,
454
- imageB64: string | undefined
455
- ): void {
456
- const msg: ClientSetWorkspace = {
457
- type: "set_workspace",
458
- message,
459
- imageB64,
460
- };
461
- this.sendSessionMessage(msg);
462
- }
463
-
464
- /**
465
- * Set MCP server configuration.
466
- */
467
- public setMcpServerConfig(
468
- server_name: string,
469
- config: Record<string, string>
470
- ): void {
471
- this.sendSessionMessage({
472
- type: "set_mcp_server_config",
473
- server_name,
474
- config,
475
- });
476
- }
477
-
478
- public deleteMcpServerConfig(server_name: string): void {
479
- this.sendSessionMessage({
480
- type: "set_mcp_server_config",
481
- server_name,
482
- });
483
- }
484
-
485
- public async shareSession(): Promise<string> {
486
- const msg: ClientToServer = {
487
- type: "share_session",
488
- client_message_id: uuidv4(),
489
- session_id: this.sessionUUID,
490
- };
491
-
492
- this.sender.send(msg);
493
- const response = await this.responseHandler.waitForResponse(
494
- msg.client_message_id
495
- );
496
- if (response.type === "session_shared") {
497
- return response.access_token;
498
- }
499
-
500
- throw new Error(`unexpected response to "share"session": ${response.type}`);
501
- }
502
-
503
- handleMessage(message: ServerSessionScopedMessage): void {
504
- if (isServerSessionFileMessage(message)) {
505
- this.sessionFiles.onMessage(message);
506
- return;
507
- }
508
-
509
- switch (message.type) {
510
- //
511
- // State updates
512
- //
513
-
514
- case "mcp_server_added":
515
- this.smsm.onMcpServerAdded(
516
- message.server_name,
517
- message.tools,
518
- message.enabled_tools,
519
- message.resources
520
- );
521
- break;
522
- case "mcp_server_removed":
523
- this.smsm.onMcpServerRemoved(message.server_name);
524
- break;
525
- case "mcp_server_tool_enabled":
526
- this.smsm.onMcpServerToolEnabled(message.server_name, message.tool);
527
- break;
528
- case "mcp_server_tool_disabled":
529
- this.smsm.onMcpServerToolDisabled(message.server_name, message.tool);
530
- break;
531
- case "system_prompt_updated":
532
- this.systemPrompt = message.system_prompt;
533
- break;
534
- case "model_updated":
535
- this.model = message.model;
536
- break;
537
- case "session_info":
538
- // This is handled in the layer above, in the chatClient
539
- break;
540
- case "user_added":
541
- this.participants.set(message.user_uuid, {
542
- user_uuid: message.user_uuid,
543
- nickname: message.nickname,
544
- email: message.email,
545
- role: message.role,
546
- });
547
- break;
548
- case "user_removed":
549
- this.participants.delete(message.user_uuid);
550
- break;
551
-
552
- // Responses to requests which can be awaited
553
- case "mcp_resource":
554
- case "session_shared":
555
- this.responseHandler.onMessage(message);
556
- break;
557
-
558
- case "agent_paused":
559
- this.agentPaused = message.paused;
560
- break;
561
-
562
- //
563
- // Ignore other messages - the owner (the UI layer) can handle them at
564
- // its discretion.
565
- //
566
-
567
- default:
568
- logger.debug(
569
- `[handleMessageInternal]: ignoring message: ${message.type}`
570
- );
571
- break;
572
- }
573
- }
574
- }
@@ -1,142 +0,0 @@
1
- import { v4 as uuidv4 } from "uuid";
2
- import { strict as assert } from "assert";
3
-
4
- import { getLogger } from "@xalia/xmcp/sdk";
5
- import { SessionFileDescriptor } from "../../agent/tools/fileManager";
6
- import {
7
- ClientToServer,
8
- ServerSessionFileContent,
9
- ServerSessionFileMessage,
10
- } from "../protocol/messages";
11
- import { IMessageSender } from "./connection";
12
- import { ResponseAwaiter } from "../utils/responseAwaiter";
13
-
14
- const logger = getLogger();
15
-
16
- /// Object for the UI to use to interact with the FileManager. If the UI
17
- /// receives ServerSessionFileChanged or ServerSessionFileDeleted then it
18
- /// should call `listFiles()` to get the new list of files, and optionally
19
- /// request the latest content via `getFileContent`.
20
- export class SessionFiles {
21
- readonly sessionUUID: string;
22
- readonly descriptors: Map<string, SessionFileDescriptor>;
23
- readonly responseHandler: ResponseAwaiter<ServerSessionFileContent>;
24
- readonly sender: IMessageSender<ClientToServer>;
25
-
26
- constructor(
27
- sessionUUID: string,
28
- initialFileList: SessionFileDescriptor[],
29
- sender: IMessageSender<ClientToServer>
30
- ) {
31
- this.sessionUUID = sessionUUID;
32
- this.descriptors = new Map(initialFileList.map((d) => [d.name, d]));
33
- this.responseHandler = ResponseAwaiter.init(
34
- undefined,
35
- (msg) => msg.client_message_id
36
- );
37
- this.sender = sender;
38
- }
39
-
40
- listFiles(): SessionFileDescriptor[] {
41
- return Array.from(this.descriptors.values());
42
- }
43
-
44
- /**
45
- * Retrieve file contents.
46
- */
47
- async getFileContent(name: string): Promise<string> {
48
- const msg: ClientToServer = {
49
- type: "session_file_get_content",
50
- session_id: this.sessionUUID,
51
- name,
52
- client_message_id: uuidv4(),
53
- };
54
- this.sender.send(msg);
55
- const response = await this.responseHandler.waitForResponse(
56
- msg.client_message_id
57
- );
58
- if (response.name !== name) {
59
- throw new Error(
60
- `invalid name for file ${name}: ${JSON.stringify(response)}`
61
- );
62
- }
63
- return response.data_url;
64
- }
65
-
66
- deleteFile(name: string): void {
67
- const msg: ClientToServer = {
68
- type: "session_file_delete",
69
- session_id: this.sessionUUID,
70
- name,
71
- client_message_id: uuidv4(),
72
- };
73
- this.sender.send(msg);
74
- }
75
-
76
- putFileContent(
77
- name: string | undefined,
78
- summary: string | undefined,
79
- data_url: string
80
- ): void {
81
- // TODO: eventually, we could wait for a response which includes an AI
82
- // assigned name.
83
- assert(name, "for now, uploaded content must have a name");
84
-
85
- const msg: ClientToServer = {
86
- type: "session_file_put_content",
87
- session_id: this.sessionUUID,
88
- name,
89
- summary,
90
- data_url,
91
- client_message_id: uuidv4(),
92
- };
93
- this.sender.send(msg);
94
- }
95
-
96
- onMessage(msg: ServerSessionFileMessage) {
97
- logger.debug(`[SessionFiles.onMessage]: msg: ${JSON.stringify(msg)}`);
98
- switch (msg.type) {
99
- case "session_file_changed":
100
- this.descriptors.set(msg.descriptor.name, msg.descriptor);
101
- break;
102
-
103
- case "session_file_deleted":
104
- this.descriptors.delete(msg.name);
105
- break;
106
-
107
- case "session_file_content":
108
- this.responseHandler.onMessage(msg);
109
- break;
110
-
111
- default: {
112
- const _: never = msg;
113
- const msgStr = JSON.stringify(msg);
114
- throw new Error(`[SessionFiles.onMessage] invalid message: ${msgStr}`);
115
- }
116
- }
117
- }
118
-
119
- decodeSessionFileUrl(url: string): {
120
- session_uuid: string;
121
- name: string;
122
- } {
123
- const u = new URL(url);
124
- if (u.protocol !== "file+session:") {
125
- throw new Error(`unexpected protocol ${u.protocol} (file+session)`);
126
- }
127
- if (u.port || u.search || u.hash) {
128
- throw new Error("badly formed session file url: ${url}");
129
- }
130
- return {
131
- session_uuid: u.host || this.sessionUUID,
132
- name: removeLeadingSlashes(u.pathname),
133
- };
134
- }
135
- }
136
-
137
- function removeLeadingSlashes(name: string): string {
138
- while (name.startsWith("/")) {
139
- name = name.slice(1);
140
- }
141
- return name;
142
- }