@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.
- package/dist/agent/src/agent/agent.js +109 -57
- package/dist/agent/src/agent/agentUtils.js +24 -26
- package/dist/agent/src/agent/compressingContextManager.js +3 -2
- package/dist/agent/src/agent/dummyLLM.js +1 -3
- package/dist/agent/src/agent/imageGenLLM.js +67 -0
- package/dist/agent/src/agent/imageGenerator.js +43 -0
- package/dist/agent/src/agent/llm.js +27 -0
- package/dist/agent/src/agent/mcpServerManager.js +18 -6
- package/dist/agent/src/agent/nullAgentEventHandler.js +6 -0
- package/dist/agent/src/agent/openAILLM.js +3 -3
- package/dist/agent/src/agent/openAILLMStreaming.js +41 -6
- package/dist/agent/src/chat/client/chatClient.js +154 -235
- package/dist/agent/src/chat/client/constants.js +1 -2
- package/dist/agent/src/chat/client/sessionClient.js +47 -15
- package/dist/agent/src/chat/client/sessionFiles.js +102 -0
- package/dist/agent/src/chat/data/apiKeyManager.js +38 -7
- package/dist/agent/src/chat/data/database.js +83 -70
- package/dist/agent/src/chat/data/dbSessionFileModels.js +49 -0
- package/dist/agent/src/chat/data/dbSessionFiles.js +76 -0
- package/dist/agent/src/chat/data/dbSessionMessages.js +57 -0
- package/dist/agent/src/chat/data/mimeTypes.js +44 -0
- package/dist/agent/src/chat/protocol/messages.js +21 -1
- package/dist/agent/src/chat/server/chatContextManager.js +19 -16
- package/dist/agent/src/chat/server/connectionManager.js +14 -36
- package/dist/agent/src/chat/server/connectionManager.test.js +3 -16
- package/dist/agent/src/chat/server/conversation.js +73 -44
- package/dist/agent/src/chat/server/imageGeneratorTools.js +111 -0
- package/dist/agent/src/chat/server/openSession.js +398 -233
- package/dist/agent/src/chat/server/openSessionMessageSender.js +2 -0
- package/dist/agent/src/chat/server/server.js +5 -8
- package/dist/agent/src/chat/server/sessionFileManager.js +171 -38
- package/dist/agent/src/chat/server/sessionRegistry.js +214 -42
- package/dist/agent/src/chat/server/test-utils/mockFactories.js +12 -11
- package/dist/agent/src/chat/server/tools.js +27 -6
- package/dist/agent/src/chat/utils/approvalManager.js +82 -64
- package/dist/agent/src/chat/utils/multiAsyncQueue.js +9 -1
- package/dist/agent/src/chat/{client/responseHandler.js → utils/responseAwaiter.js} +41 -18
- package/dist/agent/src/test/agent.test.js +104 -63
- package/dist/agent/src/test/approvalManager.test.js +79 -35
- package/dist/agent/src/test/chatContextManager.test.js +16 -17
- package/dist/agent/src/test/clientServerConnection.test.js +2 -2
- package/dist/agent/src/test/db.test.js +33 -70
- package/dist/agent/src/test/dbSessionFiles.test.js +179 -0
- package/dist/agent/src/test/dbSessionMessages.test.js +67 -0
- package/dist/agent/src/test/dbTestTools.js +6 -5
- package/dist/agent/src/test/imageLoad.test.js +1 -1
- package/dist/agent/src/test/mcpServerManager.test.js +1 -1
- package/dist/agent/src/test/multiAsyncQueue.test.js +50 -0
- package/dist/agent/src/test/responseAwaiter.test.js +74 -0
- package/dist/agent/src/test/testTools.js +12 -0
- package/dist/agent/src/tool/agentChat.js +25 -6
- package/dist/agent/src/tool/agentMain.js +1 -1
- package/dist/agent/src/tool/chatMain.js +115 -6
- package/dist/agent/src/tool/commandPrompt.js +7 -3
- package/dist/agent/src/tool/files.js +23 -15
- package/dist/agent/src/tool/options.js +2 -2
- package/package.json +1 -1
- package/scripts/setup_chat +2 -2
- package/scripts/test_chat +95 -36
- package/src/agent/agent.ts +152 -41
- package/src/agent/agentUtils.ts +34 -41
- package/src/agent/compressingContextManager.ts +5 -4
- package/src/agent/context.ts +1 -1
- package/src/agent/dummyLLM.ts +1 -3
- package/src/agent/iAgentEventHandler.ts +15 -2
- package/src/agent/imageGenLLM.ts +99 -0
- package/src/agent/imageGenerator.ts +60 -0
- package/src/agent/llm.ts +128 -4
- package/src/agent/mcpServerManager.ts +26 -7
- package/src/agent/nullAgentEventHandler.ts +6 -0
- package/src/agent/openAILLM.ts +3 -8
- package/src/agent/openAILLMStreaming.ts +60 -14
- package/src/chat/client/chatClient.ts +262 -286
- package/src/chat/client/constants.ts +0 -2
- package/src/chat/client/sessionClient.ts +82 -20
- package/src/chat/client/sessionFiles.ts +142 -0
- package/src/chat/data/apiKeyManager.ts +55 -7
- package/src/chat/data/dataModels.ts +17 -7
- package/src/chat/data/database.ts +107 -92
- package/src/chat/data/dbSessionFileModels.ts +91 -0
- package/src/chat/data/dbSessionFiles.ts +99 -0
- package/src/chat/data/dbSessionMessages.ts +68 -0
- package/src/chat/data/mimeTypes.ts +58 -0
- package/src/chat/protocol/messages.ts +136 -25
- package/src/chat/server/chatContextManager.ts +42 -24
- package/src/chat/server/connectionManager.test.ts +2 -22
- package/src/chat/server/connectionManager.ts +18 -53
- package/src/chat/server/conversation.ts +106 -59
- package/src/chat/server/imageGeneratorTools.ts +138 -0
- package/src/chat/server/openSession.ts +606 -325
- package/src/chat/server/openSessionMessageSender.ts +4 -0
- package/src/chat/server/server.ts +5 -11
- package/src/chat/server/sessionFileManager.ts +223 -63
- package/src/chat/server/sessionRegistry.ts +317 -52
- package/src/chat/server/test-utils/mockFactories.ts +13 -13
- package/src/chat/server/tools.ts +43 -8
- package/src/chat/utils/agentSessionMap.ts +2 -2
- package/src/chat/utils/approvalManager.ts +153 -81
- package/src/chat/utils/multiAsyncQueue.ts +11 -1
- package/src/chat/{client/responseHandler.ts → utils/responseAwaiter.ts} +73 -23
- package/src/test/agent.test.ts +152 -75
- package/src/test/approvalManager.test.ts +108 -40
- package/src/test/chatContextManager.test.ts +26 -22
- package/src/test/clientServerConnection.test.ts +3 -3
- package/src/test/compressingContextManager.test.ts +1 -1
- package/src/test/context.test.ts +2 -1
- package/src/test/conversation.test.ts +1 -1
- package/src/test/db.test.ts +41 -83
- package/src/test/dbSessionFiles.test.ts +258 -0
- package/src/test/dbSessionMessages.test.ts +85 -0
- package/src/test/dbTestTools.ts +9 -5
- package/src/test/imageLoad.test.ts +2 -2
- package/src/test/mcpServerManager.test.ts +3 -1
- package/src/test/multiAsyncQueue.test.ts +58 -0
- package/src/test/responseAwaiter.test.ts +103 -0
- package/src/test/testTools.ts +15 -1
- package/src/tool/agentChat.ts +36 -8
- package/src/tool/agentMain.ts +7 -7
- package/src/tool/chatMain.ts +128 -7
- package/src/tool/commandPrompt.ts +10 -5
- package/src/tool/files.ts +30 -13
- package/src/tool/options.ts +1 -1
- package/test_data/dummyllm_script_image_gen.json +19 -0
- package/test_data/dummyllm_script_invoke_image_gen_tool.json +30 -0
- package/test_data/image_gen_test_profile.json +5 -0
- package/dist/agent/src/test/responseHandler.test.js +0 -61
- 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
|
|
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
|
|
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:
|
|
402
|
+
defaultHeaders: llm_1.XALIA_APP_HEADER,
|
|
381
403
|
});
|
|
382
|
-
this.model = 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.
|
|
32
|
-
this.
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
36
|
-
this.
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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(
|
|
545
|
-
|
|
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
|
-
|
|
598
|
-
//
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
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
|
-
|
|
628
|
-
|
|
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
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
3
|
+
exports.SESSION_JOIN_TIMEOUT = void 0;
|
|
4
4
|
exports.SESSION_JOIN_TIMEOUT = 6000; // 6 seconds
|
|
5
|
-
exports.SESSION_CREATE_TIMEOUT = 6000; // 6 seconds
|