@xalia/agent 0.6.1 → 0.6.3

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 (127) hide show
  1. package/dist/agent/src/agent/agent.js +109 -57
  2. package/dist/agent/src/agent/agentUtils.js +24 -26
  3. package/dist/agent/src/agent/compressingContextManager.js +3 -2
  4. package/dist/agent/src/agent/dummyLLM.js +1 -3
  5. package/dist/agent/src/agent/imageGenLLM.js +67 -0
  6. package/dist/agent/src/agent/imageGenerator.js +43 -0
  7. package/dist/agent/src/agent/llm.js +27 -0
  8. package/dist/agent/src/agent/mcpServerManager.js +18 -6
  9. package/dist/agent/src/agent/nullAgentEventHandler.js +6 -0
  10. package/dist/agent/src/agent/openAILLM.js +3 -3
  11. package/dist/agent/src/agent/openAILLMStreaming.js +41 -6
  12. package/dist/agent/src/chat/client/chatClient.js +154 -235
  13. package/dist/agent/src/chat/client/constants.js +1 -2
  14. package/dist/agent/src/chat/client/sessionClient.js +47 -15
  15. package/dist/agent/src/chat/client/sessionFiles.js +102 -0
  16. package/dist/agent/src/chat/data/apiKeyManager.js +38 -7
  17. package/dist/agent/src/chat/data/database.js +83 -70
  18. package/dist/agent/src/chat/data/dbSessionFileModels.js +49 -0
  19. package/dist/agent/src/chat/data/dbSessionFiles.js +76 -0
  20. package/dist/agent/src/chat/data/dbSessionMessages.js +57 -0
  21. package/dist/agent/src/chat/data/mimeTypes.js +44 -0
  22. package/dist/agent/src/chat/protocol/messages.js +21 -1
  23. package/dist/agent/src/chat/server/chatContextManager.js +19 -16
  24. package/dist/agent/src/chat/server/connectionManager.js +14 -36
  25. package/dist/agent/src/chat/server/connectionManager.test.js +3 -16
  26. package/dist/agent/src/chat/server/conversation.js +73 -44
  27. package/dist/agent/src/chat/server/imageGeneratorTools.js +111 -0
  28. package/dist/agent/src/chat/server/openSession.js +398 -233
  29. package/dist/agent/src/chat/server/openSessionMessageSender.js +2 -0
  30. package/dist/agent/src/chat/server/server.js +5 -8
  31. package/dist/agent/src/chat/server/sessionFileManager.js +171 -38
  32. package/dist/agent/src/chat/server/sessionRegistry.js +214 -42
  33. package/dist/agent/src/chat/server/test-utils/mockFactories.js +12 -11
  34. package/dist/agent/src/chat/server/tools.js +27 -6
  35. package/dist/agent/src/chat/utils/approvalManager.js +82 -64
  36. package/dist/agent/src/chat/utils/multiAsyncQueue.js +9 -1
  37. package/dist/agent/src/chat/{client/responseHandler.js → utils/responseAwaiter.js} +41 -18
  38. package/dist/agent/src/test/agent.test.js +104 -63
  39. package/dist/agent/src/test/approvalManager.test.js +79 -35
  40. package/dist/agent/src/test/chatContextManager.test.js +16 -17
  41. package/dist/agent/src/test/clientServerConnection.test.js +2 -2
  42. package/dist/agent/src/test/db.test.js +33 -70
  43. package/dist/agent/src/test/dbSessionFiles.test.js +179 -0
  44. package/dist/agent/src/test/dbSessionMessages.test.js +67 -0
  45. package/dist/agent/src/test/dbTestTools.js +6 -5
  46. package/dist/agent/src/test/imageLoad.test.js +1 -1
  47. package/dist/agent/src/test/mcpServerManager.test.js +1 -1
  48. package/dist/agent/src/test/multiAsyncQueue.test.js +50 -0
  49. package/dist/agent/src/test/responseAwaiter.test.js +74 -0
  50. package/dist/agent/src/test/testTools.js +12 -0
  51. package/dist/agent/src/tool/agentChat.js +25 -6
  52. package/dist/agent/src/tool/agentMain.js +1 -1
  53. package/dist/agent/src/tool/chatMain.js +115 -6
  54. package/dist/agent/src/tool/commandPrompt.js +7 -3
  55. package/dist/agent/src/tool/files.js +23 -15
  56. package/dist/agent/src/tool/options.js +2 -2
  57. package/package.json +1 -1
  58. package/scripts/setup_chat +2 -2
  59. package/scripts/test_chat +95 -36
  60. package/src/agent/agent.ts +152 -41
  61. package/src/agent/agentUtils.ts +34 -41
  62. package/src/agent/compressingContextManager.ts +5 -4
  63. package/src/agent/context.ts +1 -1
  64. package/src/agent/dummyLLM.ts +1 -3
  65. package/src/agent/iAgentEventHandler.ts +15 -2
  66. package/src/agent/imageGenLLM.ts +99 -0
  67. package/src/agent/imageGenerator.ts +60 -0
  68. package/src/agent/llm.ts +128 -4
  69. package/src/agent/mcpServerManager.ts +26 -7
  70. package/src/agent/nullAgentEventHandler.ts +6 -0
  71. package/src/agent/openAILLM.ts +3 -8
  72. package/src/agent/openAILLMStreaming.ts +60 -14
  73. package/src/chat/client/chatClient.ts +262 -286
  74. package/src/chat/client/constants.ts +0 -2
  75. package/src/chat/client/sessionClient.ts +82 -20
  76. package/src/chat/client/sessionFiles.ts +142 -0
  77. package/src/chat/data/apiKeyManager.ts +55 -7
  78. package/src/chat/data/dataModels.ts +17 -7
  79. package/src/chat/data/database.ts +107 -92
  80. package/src/chat/data/dbSessionFileModels.ts +91 -0
  81. package/src/chat/data/dbSessionFiles.ts +99 -0
  82. package/src/chat/data/dbSessionMessages.ts +68 -0
  83. package/src/chat/data/mimeTypes.ts +58 -0
  84. package/src/chat/protocol/messages.ts +136 -25
  85. package/src/chat/server/chatContextManager.ts +42 -24
  86. package/src/chat/server/connectionManager.test.ts +2 -22
  87. package/src/chat/server/connectionManager.ts +18 -53
  88. package/src/chat/server/conversation.ts +106 -59
  89. package/src/chat/server/imageGeneratorTools.ts +138 -0
  90. package/src/chat/server/openSession.ts +606 -325
  91. package/src/chat/server/openSessionMessageSender.ts +4 -0
  92. package/src/chat/server/server.ts +5 -11
  93. package/src/chat/server/sessionFileManager.ts +223 -63
  94. package/src/chat/server/sessionRegistry.ts +317 -52
  95. package/src/chat/server/test-utils/mockFactories.ts +13 -13
  96. package/src/chat/server/tools.ts +43 -8
  97. package/src/chat/utils/agentSessionMap.ts +2 -2
  98. package/src/chat/utils/approvalManager.ts +153 -81
  99. package/src/chat/utils/multiAsyncQueue.ts +11 -1
  100. package/src/chat/{client/responseHandler.ts → utils/responseAwaiter.ts} +73 -23
  101. package/src/test/agent.test.ts +152 -75
  102. package/src/test/approvalManager.test.ts +108 -40
  103. package/src/test/chatContextManager.test.ts +26 -22
  104. package/src/test/clientServerConnection.test.ts +3 -3
  105. package/src/test/compressingContextManager.test.ts +1 -1
  106. package/src/test/context.test.ts +2 -1
  107. package/src/test/conversation.test.ts +1 -1
  108. package/src/test/db.test.ts +41 -83
  109. package/src/test/dbSessionFiles.test.ts +258 -0
  110. package/src/test/dbSessionMessages.test.ts +85 -0
  111. package/src/test/dbTestTools.ts +9 -5
  112. package/src/test/imageLoad.test.ts +2 -2
  113. package/src/test/mcpServerManager.test.ts +3 -1
  114. package/src/test/multiAsyncQueue.test.ts +58 -0
  115. package/src/test/responseAwaiter.test.ts +103 -0
  116. package/src/test/testTools.ts +15 -1
  117. package/src/tool/agentChat.ts +36 -8
  118. package/src/tool/agentMain.ts +7 -7
  119. package/src/tool/chatMain.ts +128 -7
  120. package/src/tool/commandPrompt.ts +10 -5
  121. package/src/tool/files.ts +30 -13
  122. package/src/tool/options.ts +1 -1
  123. package/test_data/dummyllm_script_image_gen.json +19 -0
  124. package/test_data/dummyllm_script_invoke_image_gen_tool.json +30 -0
  125. package/test_data/image_gen_test_profile.json +5 -0
  126. package/dist/agent/src/test/responseHandler.test.js +0 -61
  127. package/src/test/responseHandler.test.ts +0 -78
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OpenAILLMStreaming = void 0;
4
4
  exports.initializeCompletion = initializeCompletion;
5
5
  exports.updateCompletion = updateCompletion;
6
- const sdk_1 = require("@xalia/xmcp/sdk");
7
6
  const openai_1 = require("openai");
8
7
  const assert_1 = require("assert");
9
- const agentUtils_1 = require("./agentUtils");
8
+ const sdk_1 = require("@xalia/xmcp/sdk");
9
+ const llm_1 = require("./llm");
10
10
  const logger = (0, sdk_1.getLogger)();
11
11
  function initialToolCallFunction(deltaFn) {
12
12
  // export interface ChatCompletionChunk.Choice.Delta.ToolCall.Function {
@@ -152,6 +152,14 @@ function initializeCompletionMessage(delta) {
152
152
  tool_calls: toolCalls,
153
153
  };
154
154
  }
155
+ function updateReasoning(message, reasoning) {
156
+ if (!message.reasoning) {
157
+ message.reasoning = reasoning;
158
+ }
159
+ else {
160
+ message.reasoning += reasoning;
161
+ }
162
+ }
155
163
  function updateCompletionMessage(message, delta) {
156
164
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
157
165
  (0, assert_1.strict)(message.role === "assistant");
@@ -160,12 +168,20 @@ function updateCompletionMessage(message, delta) {
160
168
  (0, assert_1.strict)(!message.audio);
161
169
  (0, assert_1.strict)(message.tool_calls instanceof Array ||
162
170
  typeof message.tool_calls === "undefined");
163
- // export interface ChatCompletionChunk.Choice.Delta {
171
+ // export interface ChatCompletionChunkChoiceDeltaWithReasoning {
164
172
  // content?: string | null;
165
173
  // function_call?: Delta.FunctionCall;
166
174
  // refusal?: string | null;
167
175
  // role?: 'developer' | 'system' | 'user' | 'assistant' | 'tool';
168
176
  // tool_calls?: Array<Delta.ToolCall>;
177
+ //
178
+ // reasoning?: string;
179
+ // reasoning_details?: {
180
+ // type: "reasoning.text",
181
+ // text?: string,
182
+ // format?: string,
183
+ // index?:0
184
+ // }[]
169
185
  // }
170
186
  //
171
187
  // ->
@@ -178,6 +194,8 @@ function updateCompletionMessage(message, delta) {
178
194
  // audio?: ChatCompletionAudio | null;
179
195
  // function_call?: ChatCompletionMessage.FunctionCall | null;
180
196
  // tool_calls?: Array<ChatCompletionMessageToolCall>;
197
+ //
198
+ // reasoning?: string;
181
199
  // }
182
200
  if (delta.content) {
183
201
  if (message.content) {
@@ -203,6 +221,10 @@ function updateCompletionMessage(message, delta) {
203
221
  message.tool_calls = updateToolCalls(message.tool_calls, t);
204
222
  }
205
223
  }
224
+ const reasoning = (0, llm_1.choiceDeltaExtractReasoning)(delta);
225
+ if (reasoning) {
226
+ updateReasoning(message, reasoning);
227
+ }
206
228
  }
207
229
  function initializeCompletionChoice(chunkChoice) {
208
230
  // export interface ChatCompletionChunk.Choice {
@@ -377,9 +399,9 @@ class OpenAILLMStreaming {
377
399
  apiKey,
378
400
  baseURL: apiUrl,
379
401
  dangerouslyAllowBrowser: true,
380
- defaultHeaders: agentUtils_1.XALIA_APP_HEADER,
402
+ defaultHeaders: llm_1.XALIA_APP_HEADER,
381
403
  });
382
- this.model = model || agentUtils_1.DEFAULT_LLM_MODEL;
404
+ this.model = model;
383
405
  }
384
406
  setModel(model) {
385
407
  this.model = model;
@@ -390,7 +412,11 @@ class OpenAILLMStreaming {
390
412
  getUrl() {
391
413
  return this.openai.baseURL;
392
414
  }
393
- async getConversationResponse(messages, tools, onMessage) {
415
+ async getConversationResponse(messages, tools, onMessage, onReasoning) {
416
+ const reasoning = {
417
+ effort: "medium",
418
+ enabled: true,
419
+ };
394
420
  const chunks = await this.openai.chat.completions.create({
395
421
  model: this.model,
396
422
  messages,
@@ -399,6 +425,7 @@ class OpenAILLMStreaming {
399
425
  stream_options: {
400
426
  include_usage: true,
401
427
  },
428
+ extra_body: { reasoning },
402
429
  });
403
430
  // Check the type casting above
404
431
  if (!chunks.iterator) {
@@ -430,6 +457,14 @@ class OpenAILLMStreaming {
430
457
  await onMessage(delta.content, false);
431
458
  }
432
459
  }
460
+ if (onReasoning) {
461
+ const delta = chunk.choices[0]
462
+ ?.delta;
463
+ const reasoning = (0, llm_1.choiceDeltaExtractReasoning)(delta);
464
+ if (reasoning) {
465
+ await onReasoning(reasoning);
466
+ }
467
+ }
433
468
  }
434
469
  if (onMessage) {
435
470
  await onMessage("", true);
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ChatClient = void 0;
4
4
  const uuid_1 = require("uuid");
5
+ const assert_1 = require("assert");
5
6
  const sdk_1 = require("@xalia/xmcp/sdk");
6
7
  const connection_1 = require("./connection");
7
8
  const sessionClient_1 = require("./sessionClient");
@@ -9,36 +10,31 @@ const database_1 = require("../data/database");
9
10
  const messages_1 = require("../protocol/messages");
10
11
  const constants_1 = require("./constants");
11
12
  const agentSessionMap_1 = require("../utils/agentSessionMap");
13
+ const responseAwaiter_1 = require("../utils/responseAwaiter");
12
14
  const logger = (0, sdk_1.getLogger)();
15
+ function idExtractor(msg) {
16
+ return msg.client_message_id;
17
+ }
13
18
  class ChatClient {
14
- constructor(connection, eventHandler,
15
- // agent_uuid -> agent session data
16
- userAgentSessionMap = new Map(),
17
- // team_uuid -> team info
18
- teams = new Map(), closed = false, currentSessionId = undefined,
19
- // note: currentTeamId will not be reset when swtiching to a user session.
20
- currentTeamId = undefined,
21
- // session_uuid -> session client
22
- activeSessions = new Map(),
23
- // session_uuid -> true if there is a new message.
24
- newMessage = new Map(),
25
- // Track pending session join request, this should be a singleton
26
- sessionJoinOrCreateRes = undefined) {
19
+ constructor(connection, eventHandler, userAgentSessionMap, teams, isGuestMode) {
27
20
  this.connection = connection;
28
21
  this.eventHandler = eventHandler;
29
22
  this.userAgentSessionMap = userAgentSessionMap;
30
23
  this.teams = teams;
31
- this.closed = closed;
32
- this.currentSessionId = currentSessionId;
33
- this.currentTeamId = currentTeamId;
34
- this.activeSessions = activeSessions;
35
- this.newMessage = newMessage;
36
- this.sessionJoinOrCreateRes = sessionJoinOrCreateRes;
24
+ this.isGuestMode = isGuestMode;
25
+ this.closed = false;
26
+ this.currentSessionId = undefined;
27
+ this.currentTeamId = undefined;
28
+ this.activeSessions = new Map();
29
+ this.newMessage = new Map();
30
+ this.sessionJoinAwaiter = responseAwaiter_1.ResponseAwaiter.initWithImmediate("control_error", idExtractor, this.onJoinCreateResponse.bind(this), Math.max(constants_1.SESSION_JOIN_TIMEOUT));
37
31
  }
38
32
  /**
39
33
  * Connect to server and create ChatClient instance
40
34
  */
41
35
  static async init(url, token, eventHandler) {
36
+ // Determine if this is a guest token (format: guest_<hash>_<session-id>)
37
+ const isGuestMode = token.startsWith("guest_");
42
38
  const connection = new connection_1.Connection({
43
39
  url,
44
40
  token,
@@ -69,7 +65,7 @@ class ChatClient {
69
65
  });
70
66
  if (!client) {
71
67
  logger.info("Creating ChatClient");
72
- client = new ChatClient(connection, eventHandler, userAgentSessionMap, teams);
68
+ client = new ChatClient(connection, eventHandler, userAgentSessionMap, teams, isGuestMode);
73
69
  resolveClient(client);
74
70
  }
75
71
  else {
@@ -212,70 +208,26 @@ class ChatClient {
212
208
  this.teams.clear();
213
209
  this.currentSessionId = undefined;
214
210
  this.currentTeamId = undefined;
215
- this.sessionJoinOrCreateRes = undefined;
216
211
  }
217
212
  async createNewSession(newTitle, agentProfileId, teamId) {
218
213
  if (this.closed) {
219
214
  throw new Error("ChatClient is closed");
220
215
  }
221
- if (this.sessionJoinOrCreateRes) {
222
- logger.error(`[ChatClient] creating new session with ` +
223
- `profile id ${JSON.stringify(agentProfileId)}`);
224
- throw new Error("Session join/create is already in progress");
225
- }
226
- return new Promise((resolve, reject) => {
227
- const clientMessageId = (0, uuid_1.v4)();
228
- // Set up timeout for the request
229
- const timeoutId = setTimeout(() => {
230
- this.sessionJoinOrCreateRes = undefined;
231
- reject(new Error(`Session join timeout for creating new session`));
232
- }, constants_1.SESSION_CREATE_TIMEOUT);
233
- // Track this pending request
234
- this.sessionJoinOrCreateRes = {
235
- resolve: (sessionClient, sessionInfo) => {
236
- const sessionId = this.sessionJoinOrCreateRes?.sessionId;
237
- if (!sessionId) {
238
- throw new Error("Session id is not set in resolving.");
239
- }
240
- if (sessionId !== sessionInfo.session_id) {
241
- throw new Error("SessionInfo id mismatch");
242
- }
243
- clearTimeout(timeoutId);
244
- this.activeSessions.set(sessionId, sessionClient);
245
- this.currentSessionId = sessionId;
246
- if (sessionInfo.team_uuid) {
247
- this.currentTeamId = sessionInfo.team_uuid;
248
- }
249
- this.sessionJoinOrCreateRes = undefined;
250
- this.addSessionToAgentSessionMap(sessionInfo);
251
- resolve(sessionClient);
252
- },
253
- reject: (error) => {
254
- clearTimeout(timeoutId);
255
- this.sessionJoinOrCreateRes = undefined;
256
- reject(error);
257
- },
258
- sessionId: "",
259
- clientMessageId,
260
- };
261
- // Send session_create message
262
- try {
263
- this.connection.send({
264
- type: "control_session_create",
265
- client_message_id: clientMessageId,
266
- title: newTitle,
267
- agent_profile_id: agentProfileId,
268
- team_id: teamId,
269
- });
270
- logger.info(`[ChatClient] Sent session_create for session ${newTitle}` +
271
- ` client message id: ${clientMessageId}`);
272
- }
273
- catch (error) {
274
- this.sessionJoinOrCreateRes = undefined;
275
- clearTimeout(timeoutId);
276
- reject(error instanceof Error ? error : new Error(String(error)));
277
- }
278
- });
216
+ const clientMessageId = (0, uuid_1.v4)();
217
+ const createdSessionP = this.sessionJoinAwaiter.waitForResponse(clientMessageId);
218
+ const createMsg = {
219
+ type: "control_session_create",
220
+ client_message_id: clientMessageId,
221
+ title: newTitle,
222
+ agent_profile_id: agentProfileId,
223
+ team_id: teamId,
224
+ };
225
+ this.connection.send(createMsg);
226
+ logger.info(`[ChatClient] Sent ${JSON.stringify(createMsg)}`);
227
+ const createdSession = await createdSessionP;
228
+ const sessionId = createdSession.client.getSessionUUID();
229
+ logger.info(`[ChatClient] joined session ${sessionId}`);
230
+ return createdSession.client;
279
231
  }
280
232
  /**
281
233
  * Connect to an existing session by sending session_join message
@@ -285,58 +237,22 @@ class ChatClient {
285
237
  if (this.closed) {
286
238
  throw new Error("ChatClient is closed");
287
239
  }
288
- if (this.sessionJoinOrCreateRes) {
289
- logger.error(`[ChatClient] connecting to session ${sessionId}`);
290
- throw new Error("Session join/create is already in progress");
291
- }
292
- // TODO: we can directly return a session client if we cache conversation.
293
- // For now, we go through the join process.
294
- return new Promise((resolve, reject) => {
295
- const clientMessageId = (0, uuid_1.v4)();
296
- // Set up timeout for the request
297
- const timeoutId = setTimeout(() => {
298
- this.sessionJoinOrCreateRes = undefined;
299
- reject(new Error(`Session join timeout for session ${sessionId}`));
300
- }, constants_1.SESSION_JOIN_TIMEOUT);
301
- // Track this pending request
302
- this.sessionJoinOrCreateRes = {
303
- resolve: (sessionClient, sessionInfo) => {
304
- clearTimeout(timeoutId);
305
- this.activeSessions.set(sessionId, sessionClient);
306
- this.currentSessionId = sessionId;
307
- if (sessionInfo.team_uuid) {
308
- this.currentTeamId = sessionInfo.team_uuid;
309
- }
310
- this.sessionJoinOrCreateRes = undefined;
311
- this.updateSessionInfo(sessionInfo);
312
- logger.info(`[ChatClient] joined session ${sessionId}`);
313
- resolve(sessionClient);
314
- },
315
- reject: (error) => {
316
- clearTimeout(timeoutId);
317
- this.sessionJoinOrCreateRes = undefined;
318
- logger.error(`[ChatClient] failed to join session` +
319
- ` ${sessionId}: ${error.message}`);
320
- reject(error);
321
- },
322
- sessionId,
323
- clientMessageId,
324
- };
325
- // Send session_join message
326
- try {
327
- this.connection.send({
328
- type: "control_session_join",
329
- client_message_id: clientMessageId,
330
- target_session_id: sessionId,
331
- });
332
- logger.debug(`[ChatClient] Sent session_join for session ${sessionId}`);
333
- }
334
- catch (error) {
335
- this.sessionJoinOrCreateRes = undefined;
336
- clearTimeout(timeoutId);
337
- reject(error instanceof Error ? error : new Error(String(error)));
338
- }
240
+ const clientMessageId = (0, uuid_1.v4)();
241
+ const createdSessionP = this.sessionJoinAwaiter.waitForResponse(clientMessageId);
242
+ this.connection.send({
243
+ type: "control_session_join",
244
+ client_message_id: clientMessageId,
245
+ target_session_id: sessionId,
339
246
  });
247
+ logger.debug(`[ChatClient] Sent session_join for session ${sessionId}`);
248
+ // Await the response
249
+ const createdSession = await createdSessionP;
250
+ const newSessionId = createdSession.client.getSessionUUID();
251
+ if (newSessionId !== sessionId) {
252
+ throw new Error(`unexpected session id ${newSessionId}`);
253
+ }
254
+ logger.info(`[ChatClient] joined session ${sessionId}`);
255
+ return createdSession.client;
340
256
  }
341
257
  /**
342
258
  * Fetch the session list from the server
@@ -364,6 +280,22 @@ class ChatClient {
364
280
  });
365
281
  logger.debug(`[ChatClient] Sent session_delete_request for` + ` session ${sessionId}`);
366
282
  }
283
+ /**
284
+ * Delete an agent profile by sending control_agent_profile_delete message
285
+ * @param agentProfileUuid - The UUID of the agent profile to delete
286
+ * @returns void
287
+ */
288
+ deleteAgentProfile(agentProfileUuid) {
289
+ if (this.closed) {
290
+ throw new Error("ChatClient is closed");
291
+ }
292
+ this.connection.send({
293
+ type: "control_agent_profile_delete",
294
+ agent_profile_uuid: agentProfileUuid,
295
+ });
296
+ logger.debug(`[ChatClient] Sent control_agent_profile_delete for profile ` +
297
+ agentProfileUuid);
298
+ }
367
299
  /**
368
300
  * Create a new team with initial members
369
301
  * @param teamName
@@ -513,6 +445,9 @@ class ChatClient {
513
445
  case "control_team_created":
514
446
  this.handleTeamCreatedMessage(msg);
515
447
  break;
448
+ case "control_team_members_updated":
449
+ this.handleTeamMembersUpdated(msg);
450
+ break;
516
451
  default: {
517
452
  const _exhaustive = msg;
518
453
  throw new Error(`unexpected control msg: ${JSON.stringify(msg)}`);
@@ -541,8 +476,29 @@ class ChatClient {
541
476
  }
542
477
  void this.eventHandler.onMessage(msg, this);
543
478
  }
544
- handleAgentProfileDeleted(_msg) {
545
- throw new Error("handleAgentProfileDeleted not implemented");
479
+ handleAgentProfileDeleted(msg) {
480
+ logger.debug(`[ChatClient.handleAgentProfileDeleted] msg: ${JSON.stringify(msg)}`);
481
+ const profileUuid = msg.profile_uuid;
482
+ let found = false;
483
+ // Try to remove from user agent session map
484
+ if (this.userAgentSessionMap.has(profileUuid)) {
485
+ this.userAgentSessionMap.delete(profileUuid);
486
+ found = true;
487
+ }
488
+ else {
489
+ // Try to remove from team agent session maps
490
+ for (const [_teamId, team] of this.teams.entries()) {
491
+ if (team.agentSessionMap.has(profileUuid)) {
492
+ team.agentSessionMap.delete(profileUuid);
493
+ found = true;
494
+ break;
495
+ }
496
+ }
497
+ }
498
+ if (!found) {
499
+ logger.warn(`[ChatClient] Agent profile ${profileUuid} not found in any session map`);
500
+ }
501
+ void this.eventHandler.onMessage(msg, this);
546
502
  }
547
503
  handleSessionMessage(msg) {
548
504
  const sessionId = msg.session_id;
@@ -587,95 +543,52 @@ class ChatClient {
587
543
  void this.eventHandler.onMessage(msg, this);
588
544
  return;
589
545
  }
546
+ handleTeamMembersUpdated(msg) {
547
+ const team = this.teams.get(msg.team_uuid);
548
+ if (!team) {
549
+ logger.warn(`[ChatClient] Received team_members_updated for unknown team ` +
550
+ msg.team_uuid);
551
+ return;
552
+ }
553
+ // Update the participants map
554
+ team.participants = new Map(msg.members.map((participant) => [participant.user_uuid, participant]));
555
+ logger.info(`[ChatClient] Updated team members for team ${msg.team_uuid}, ` +
556
+ `now has ${String(msg.members.length)} members`);
557
+ void this.eventHandler.onMessage(msg, this);
558
+ }
590
559
  /**
591
560
  * Handle session_info message which can be for:
592
561
  * 1. A pending session join/create request
593
562
  * 2. An update to an existing active session
563
+ * 3. A new session notification
594
564
  * We update the session list here as well.
595
565
  */
596
566
  handleSessionInfoMessage(msg) {
597
- const sessionId = msg.session_id;
598
- // There is a pending request, check if this is the response
599
- if (this.sessionJoinOrCreateRes &&
600
- msg.client_message_id === this.sessionJoinOrCreateRes.clientMessageId) {
601
- const { resolve, reject } = this.sessionJoinOrCreateRes;
602
- try {
603
- logger.info(`[ChatClient] Creating SessionClient for session ${sessionId}`);
604
- logger.info(`[ChatClient] msg: ${JSON.stringify(msg)}`);
605
- const sessionClient = new sessionClient_1.SessionClient(sessionId, msg.saved_agent_profile, this.connection, msg.mcp_server_briefs, (0, database_1.createSessionParticipantMap)(msg.participants));
606
- // we need to pass the session id if this is a new session
607
- if (this.sessionJoinOrCreateRes.sessionId === "") {
608
- this.sessionJoinOrCreateRes.sessionId = sessionId;
609
- }
610
- else if (this.sessionJoinOrCreateRes.sessionId !== sessionId) {
611
- throw new Error(`[ChatClient] session id mismatch: ` +
612
- `${this.sessionJoinOrCreateRes.sessionId} !== ${sessionId}`);
613
- }
614
- resolve(sessionClient, msg);
615
- }
616
- catch (error) {
617
- const errorMsg = error instanceof Error ? error : new Error(String(error));
618
- logger.error(`[ChatClient] Failed to create SessionClient: ${errorMsg.message}`);
619
- reject(errorMsg);
620
- }
621
- }
622
- else {
623
- this.updateSessionInfo(msg);
567
+ // We intercept messages for th Awaiter here so that we can create and
568
+ // register the client immediately, before other messages for this session
569
+ // are handled. Relying on Awaiter alone (currently) schedules the resolv
570
+ // to be called later (potentially AFTER further messages for the session
571
+ // have been received).
572
+ if (this.sessionJoinAwaiter.onMessage(msg)) {
573
+ return;
624
574
  }
575
+ logger.debug(`[ChatClient.handleSessionInfoMessage] not handled by awaiter - updating`);
576
+ this.upsertSessionToAgentSessionMap(msg);
625
577
  }
626
578
  handleControlError(msg) {
627
- // reject the pending session join/create request if message id matches
628
- if (this.sessionJoinOrCreateRes &&
629
- msg.client_message_id === this.sessionJoinOrCreateRes.clientMessageId) {
630
- this.sessionJoinOrCreateRes.reject(new Error(msg.message));
579
+ if (this.sessionJoinAwaiter.onMessage(msg)) {
580
+ return;
631
581
  }
632
582
  this.eventHandler.onError(`Server error: ${msg.message}`);
633
583
  }
634
- /**
635
- * Update the session list. This is called when a session is updated.
636
- * An error is thrown if the session id is not found.
637
- * TODO: we might want to resync session map, for now, throw error to
638
- * expose problems in the code.
639
- */
640
- updateAgentSessionMap(sessionInfo) {
641
- const sessionId = sessionInfo.session_id;
642
- if (sessionInfo.team_uuid) {
643
- const teamInfo = this.teams.get(sessionInfo.team_uuid);
644
- if (!teamInfo) {
645
- throw new Error(`Team ${sessionInfo.team_uuid} not found in team list`);
646
- }
647
- //teamInfo.sessions.set(sessionId, ChatClient.toSessionData(sessionInfo));
648
- const agentSessionMap = teamInfo.agentSessionMap;
649
- if (!doUpdateAgentSessionMap(agentSessionMap, sessionInfo)) {
650
- throw new Error(`[updateAgentSessionMap] team session ${sessionId}` +
651
- ` not found in agent session map`);
652
- }
653
- }
654
- else {
655
- const agentSessionMap = this.userAgentSessionMap;
656
- if (!doUpdateAgentSessionMap(agentSessionMap, sessionInfo)) {
657
- throw new Error(`[updateAgentSessionMap] user session ${sessionId}` +
658
- ` not found in agent session map`);
659
- }
660
- }
661
- }
662
- /**
663
- * Update the session info for an existing session. This passes the session
664
- * info to the session client. An error is thrown if the session client is not
665
- * found.
666
- */
667
- updateSessionInfo(msg) {
668
- const sessionId = msg.session_id;
669
- const sessionClient = this.activeSessions.get(sessionId);
670
- this.updateAgentSessionMap(msg);
671
- if (sessionClient) {
672
- sessionClient.updateSessionInfo(msg);
673
- }
674
- else {
675
- throw new Error(`Session client not found for session ${sessionId}`);
584
+ upsertSessionToAgentSessionMap(msg) {
585
+ // Skip agent session map updates for guest sessions since they don't
586
+ // need session organization
587
+ if (this.isGuestMode) {
588
+ logger.info(`[ChatClient] Skipping agent session map add for ` +
589
+ `guest session ${msg.session_id}`);
590
+ return;
676
591
  }
677
- }
678
- addSessionToAgentSessionMap(msg) {
679
592
  // get the correct agent session map
680
593
  const agentSessionMap = msg.team_uuid
681
594
  ? this.teams.get(msg.team_uuid)?.agentSessionMap
@@ -690,44 +603,50 @@ class ChatClient {
690
603
  throw new Error(`[addSessionToAgentSessionMap] Agent ${agentUuid}` +
691
604
  ` not found in agent session map`);
692
605
  }
693
- agentSession.sessions.push(sessionInfoToSessionData(msg));
606
+ // Check if session already exists
607
+ const existingSessionIndex = agentSession.sessions.findIndex((session) => session.session_uuid === msg.session_id);
608
+ if (existingSessionIndex !== -1) {
609
+ // Update existing session
610
+ agentSession.sessions[existingSessionIndex] =
611
+ sessionInfoToSessionDescriptor(msg);
612
+ logger.info(`[ChatClient] Updated existing session ${msg.session_id} in ` +
613
+ `agent session map`);
614
+ }
615
+ else {
616
+ // Add new session
617
+ agentSession.sessions.push(sessionInfoToSessionDescriptor(msg));
618
+ logger.info(`[ChatClient] Added new session ${msg.session_id} to agent session map`);
619
+ }
694
620
  agentSession.updated_at = new Date(msg.updated_at).getTime();
695
621
  }
622
+ // This is the immediate callback of the ResponseAwaiter for session
623
+ // joining. It must create and register the client.
624
+ onJoinCreateResponse(msg) {
625
+ // Errors should not get through here.
626
+ (0, assert_1.strict)(msg.type === "session_info");
627
+ const sessionId = msg.session_id;
628
+ const client = new sessionClient_1.SessionClient(sessionId, msg.saved_agent_profile, this.connection, msg.mcp_server_briefs, (0, database_1.createSessionParticipantMap)(msg.participants), msg.agent_paused);
629
+ this.activeSessions.set(sessionId, client);
630
+ this.currentSessionId = sessionId;
631
+ if (msg.team_uuid) {
632
+ this.currentTeamId = msg.team_uuid;
633
+ }
634
+ this.upsertSessionToAgentSessionMap(msg);
635
+ return {
636
+ client,
637
+ sessionInfo: msg,
638
+ };
639
+ }
696
640
  }
697
641
  exports.ChatClient = ChatClient;
698
- function sessionInfoToSessionData(msg) {
642
+ function sessionInfoToSessionDescriptor(msg) {
699
643
  return {
700
644
  session_uuid: msg.session_id,
701
645
  title: msg.title,
702
646
  team_uuid: msg.team_uuid,
703
647
  agent_profile_uuid: msg.saved_agent_profile.uuid,
704
- workspace: msg.workspace,
705
648
  updated_at: msg.updated_at,
706
649
  user_uuid: msg.owner_uuid,
650
+ agent_paused: msg.agent_paused,
707
651
  };
708
652
  }
709
- /**
710
- * Update the agent session map
711
- * @param agentSessionMap - the agent session map
712
- * @param sessionInfo - the session info
713
- * @returns true if the session is updated, false otherwise
714
- */
715
- function doUpdateAgentSessionMap(agentSessionMap, sessionInfo) {
716
- const sessionId = sessionInfo.session_id;
717
- const agentUuid = sessionInfo.saved_agent_profile.uuid;
718
- const agent = agentSessionMap.get(agentUuid);
719
- if (!agent) {
720
- return false;
721
- }
722
- let updated = false;
723
- agent.sessions.map((session) => {
724
- if (session.session_uuid === sessionId) {
725
- updated = true;
726
- return sessionInfoToSessionData(sessionInfo);
727
- }
728
- else {
729
- return session;
730
- }
731
- });
732
- return updated;
733
- }
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SESSION_CREATE_TIMEOUT = exports.SESSION_JOIN_TIMEOUT = void 0;
3
+ exports.SESSION_JOIN_TIMEOUT = void 0;
4
4
  exports.SESSION_JOIN_TIMEOUT = 6000; // 6 seconds
5
- exports.SESSION_CREATE_TIMEOUT = 6000; // 6 seconds