@xalia/agent 0.6.2 → 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 +8 -5
- package/dist/agent/src/agent/agentUtils.js +9 -12
- package/dist/agent/src/chat/client/chatClient.js +88 -240
- package/dist/agent/src/chat/client/constants.js +1 -2
- package/dist/agent/src/chat/client/sessionClient.js +4 -13
- package/dist/agent/src/chat/client/sessionFiles.js +3 -3
- package/dist/agent/src/chat/protocol/messages.js +0 -1
- package/dist/agent/src/chat/server/chatContextManager.js +5 -9
- package/dist/agent/src/chat/server/connectionManager.test.js +1 -0
- package/dist/agent/src/chat/server/conversation.js +9 -4
- package/dist/agent/src/chat/server/openSession.js +241 -238
- package/dist/agent/src/chat/server/openSessionMessageSender.js +2 -0
- package/dist/agent/src/chat/server/sessionRegistry.js +17 -12
- package/dist/agent/src/chat/utils/approvalManager.js +82 -64
- package/dist/agent/src/chat/{client/responseHandler.js → utils/responseAwaiter.js} +41 -18
- package/dist/agent/src/test/agent.test.js +90 -53
- package/dist/agent/src/test/approvalManager.test.js +79 -35
- package/dist/agent/src/test/chatContextManager.test.js +12 -17
- package/dist/agent/src/test/responseAwaiter.test.js +74 -0
- package/dist/agent/src/tool/agentChat.js +1 -1
- package/dist/agent/src/tool/chatMain.js +2 -2
- package/package.json +1 -1
- package/scripts/setup_chat +2 -2
- package/scripts/test_chat +61 -60
- package/src/agent/agent.ts +9 -5
- package/src/agent/agentUtils.ts +14 -27
- package/src/chat/client/chatClient.ts +167 -296
- package/src/chat/client/constants.ts +0 -2
- package/src/chat/client/sessionClient.ts +15 -19
- package/src/chat/client/sessionFiles.ts +9 -12
- package/src/chat/data/dataModels.ts +1 -0
- package/src/chat/protocol/messages.ts +9 -12
- package/src/chat/server/chatContextManager.ts +7 -12
- package/src/chat/server/connectionManager.test.ts +1 -0
- package/src/chat/server/conversation.ts +19 -11
- package/src/chat/server/openSession.ts +383 -340
- package/src/chat/server/openSessionMessageSender.ts +4 -0
- package/src/chat/server/sessionRegistry.ts +33 -12
- package/src/chat/utils/approvalManager.ts +153 -81
- package/src/chat/{client/responseHandler.ts → utils/responseAwaiter.ts} +73 -23
- package/src/test/agent.test.ts +130 -62
- package/src/test/approvalManager.test.ts +108 -40
- package/src/test/chatContextManager.test.ts +19 -20
- package/src/test/responseAwaiter.test.ts +103 -0
- package/src/tool/agentChat.ts +2 -2
- package/src/tool/chatMain.ts +2 -2
- package/dist/agent/src/test/responseHandler.test.js +0 -61
- package/src/test/responseHandler.test.ts +0 -78
|
@@ -85,12 +85,15 @@ class Agent {
|
|
|
85
85
|
// `toolCallResults`.
|
|
86
86
|
const result = await this.doToolCall(toolCall);
|
|
87
87
|
toolCallResults.push([context.length, result]);
|
|
88
|
-
|
|
88
|
+
const toolResult = {
|
|
89
89
|
role: "tool",
|
|
90
90
|
tool_call_id: toolCall.id,
|
|
91
91
|
content: result.response,
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
};
|
|
93
|
+
if (result.metadata) {
|
|
94
|
+
toolResult.metadata = result.metadata;
|
|
95
|
+
}
|
|
96
|
+
context.push(toolResult);
|
|
94
97
|
// If the tool call requested that its args be redacted, this can be
|
|
95
98
|
// done now - before the next LLM invocation.
|
|
96
99
|
if (result.overwriteArgs) {
|
|
@@ -221,14 +224,14 @@ class Agent {
|
|
|
221
224
|
result = { response: "User denied tool request." };
|
|
222
225
|
}
|
|
223
226
|
else {
|
|
224
|
-
const args = JSON.parse(toolCall.function.arguments);
|
|
227
|
+
const args = JSON.parse(toolCall.function.arguments || "{}");
|
|
225
228
|
result = await agentTool.handler(this, args);
|
|
226
229
|
}
|
|
227
230
|
}
|
|
228
231
|
else {
|
|
229
232
|
// McpServer tool call (agentTool === undefined). Sanity check the
|
|
230
233
|
// tool call data, get approval, and then invoke.
|
|
231
|
-
const args = JSON.parse(toolCall.function.arguments);
|
|
234
|
+
const args = JSON.parse(toolCall.function.arguments || "{}");
|
|
232
235
|
const tc = this.mcpServerManager.verifyToolCall(toolName, args);
|
|
233
236
|
if (!(await this.eventHandler.onToolCall(toolCall, false))) {
|
|
234
237
|
result = { response: "User denied tool request." };
|
|
@@ -17,16 +17,14 @@ const repeatLLM_1 = require("./repeatLLM");
|
|
|
17
17
|
const context_1 = require("./context");
|
|
18
18
|
const imageGenLLM_1 = require("./imageGenLLM");
|
|
19
19
|
const logger = (0, sdk_1.getLogger)();
|
|
20
|
-
async function createAgentWithoutSkills(llmUrl,
|
|
20
|
+
async function createAgentWithoutSkills(llmUrl, model, eventHandler, platform, contextManager, llmApiKey, sudomcpConfig, authorizedUrl, stream = false) {
|
|
21
21
|
// Init SudoMcpServerManager
|
|
22
22
|
logger.debug("[createAgentWithSkills] creating SudoMcpServerManager.");
|
|
23
23
|
const sudoMcpServerManager = await sudoMcpServerManager_1.SkillManager.initialize((url, authResultP, displayName) => {
|
|
24
24
|
platform.openUrl(url, authResultP, displayName);
|
|
25
25
|
}, sudomcpConfig.backend_url, sudomcpConfig.api_key, authorizedUrl);
|
|
26
|
-
logger.debug("[createAgentWithoutSkills] restore mcp settings:" +
|
|
27
|
-
JSON.stringify(agentProfile.mcp_settings));
|
|
28
26
|
// Create agent using the event handler
|
|
29
|
-
const agent = await createAgentFromSkillManager(llmUrl,
|
|
27
|
+
const agent = await createAgentFromSkillManager(llmUrl, model, eventHandler, platform, contextManager, llmApiKey, sudoMcpServerManager, stream);
|
|
30
28
|
return [agent, sudoMcpServerManager];
|
|
31
29
|
}
|
|
32
30
|
/**
|
|
@@ -34,17 +32,16 @@ async function createAgentWithoutSkills(llmUrl, agentProfile, defaultModel, even
|
|
|
34
32
|
* IAgentEventHandler interface. This is the preferred way to create
|
|
35
33
|
* agents.
|
|
36
34
|
*/
|
|
37
|
-
async function createAgentWithSkills(llmUrl,
|
|
38
|
-
const [agent, sudoMcpServerManager] = await createAgentWithoutSkills(llmUrl,
|
|
39
|
-
logger.debug(
|
|
40
|
-
await sudoMcpServerManager.restoreMcpSettings(
|
|
35
|
+
async function createAgentWithSkills(llmUrl, model, eventHandler, platform, contextManager, llmApiKey, sudomcpConfig, mcpSettings, authorizedUrl, stream = false) {
|
|
36
|
+
const [agent, sudoMcpServerManager] = await createAgentWithoutSkills(llmUrl, model, eventHandler, platform, contextManager, llmApiKey, sudomcpConfig, authorizedUrl, stream);
|
|
37
|
+
logger.debug(`[createAgentWithSkills] skilles: ${JSON.stringify(mcpSettings)}`);
|
|
38
|
+
await sudoMcpServerManager.restoreMcpSettings(mcpSettings);
|
|
41
39
|
return [agent, sudoMcpServerManager];
|
|
42
40
|
}
|
|
43
|
-
async function createAgentFromSkillManager(llmUrl,
|
|
41
|
+
async function createAgentFromSkillManager(llmUrl, model, eventHandler, platform, contextManager, llmApiKey, skillManager, stream = false) {
|
|
44
42
|
// Create agent
|
|
45
43
|
logger.debug("[createAgentFromSkillManager] creating agent ...");
|
|
46
|
-
const llm = await createLLM(llmUrl, llmApiKey,
|
|
47
|
-
contextManager.setAgentPrompt(agentProfile.system_prompt);
|
|
44
|
+
const llm = await createLLM(llmUrl, llmApiKey, model, stream, platform);
|
|
48
45
|
const agent = agent_1.Agent.initializeWithLLM(eventHandler, llm, contextManager, skillManager);
|
|
49
46
|
logger.debug("[createAgentFromSkillManager] done");
|
|
50
47
|
return agent;
|
|
@@ -101,7 +98,7 @@ async function createNonInteractiveAgent(url, agentProfile, defaultModel, conver
|
|
|
101
98
|
onToolCallResult: () => { },
|
|
102
99
|
};
|
|
103
100
|
const contextManager = new context_1.ContextManager(agentProfile.system_prompt, conversation || []);
|
|
104
|
-
const [agent, _] = await createAgentWithSkills(url, agentProfile
|
|
101
|
+
const [agent, _] = await createAgentWithSkills(url, agentProfile.model || defaultModel, eventHandler, platform, contextManager, openaiApiKey, sudomcpConfig, agentProfile.mcp_settings, undefined);
|
|
105
102
|
return agent;
|
|
106
103
|
}
|
|
107
104
|
/**
|
|
@@ -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,41 +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(),
|
|
19
|
-
// Whether this client is in guest mode
|
|
20
|
-
isGuestMode = false, closed = false, currentSessionId = undefined,
|
|
21
|
-
// note: currentTeamId will not be reset when swtiching to a user session.
|
|
22
|
-
currentTeamId = undefined,
|
|
23
|
-
// session_uuid -> session client
|
|
24
|
-
activeSessions = new Map(),
|
|
25
|
-
// session_uuid -> true if there is a new message.
|
|
26
|
-
newMessage = new Map(),
|
|
27
|
-
// Track pending session join request, this should be a singleton
|
|
28
|
-
sessionJoinOrCreateRes = undefined) {
|
|
19
|
+
constructor(connection, eventHandler, userAgentSessionMap, teams, isGuestMode) {
|
|
29
20
|
this.connection = connection;
|
|
30
21
|
this.eventHandler = eventHandler;
|
|
31
22
|
this.userAgentSessionMap = userAgentSessionMap;
|
|
32
23
|
this.teams = teams;
|
|
33
24
|
this.isGuestMode = isGuestMode;
|
|
34
|
-
this.closed =
|
|
35
|
-
this.currentSessionId =
|
|
36
|
-
this.currentTeamId =
|
|
37
|
-
this.activeSessions =
|
|
38
|
-
this.newMessage =
|
|
39
|
-
this.
|
|
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));
|
|
40
31
|
}
|
|
41
32
|
/**
|
|
42
33
|
* Connect to server and create ChatClient instance
|
|
43
34
|
*/
|
|
44
35
|
static async init(url, token, eventHandler) {
|
|
45
36
|
// Determine if this is a guest token (format: guest_<hash>_<session-id>)
|
|
46
|
-
const isGuestMode = token.startsWith(
|
|
37
|
+
const isGuestMode = token.startsWith("guest_");
|
|
47
38
|
const connection = new connection_1.Connection({
|
|
48
39
|
url,
|
|
49
40
|
token,
|
|
@@ -217,70 +208,26 @@ class ChatClient {
|
|
|
217
208
|
this.teams.clear();
|
|
218
209
|
this.currentSessionId = undefined;
|
|
219
210
|
this.currentTeamId = undefined;
|
|
220
|
-
this.sessionJoinOrCreateRes = undefined;
|
|
221
211
|
}
|
|
222
212
|
async createNewSession(newTitle, agentProfileId, teamId) {
|
|
223
213
|
if (this.closed) {
|
|
224
214
|
throw new Error("ChatClient is closed");
|
|
225
215
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const sessionId = this.sessionJoinOrCreateRes?.sessionId;
|
|
242
|
-
if (!sessionId) {
|
|
243
|
-
throw new Error("Session id is not set in resolving.");
|
|
244
|
-
}
|
|
245
|
-
if (sessionId !== sessionInfo.session_id) {
|
|
246
|
-
throw new Error("SessionInfo id mismatch");
|
|
247
|
-
}
|
|
248
|
-
clearTimeout(timeoutId);
|
|
249
|
-
this.activeSessions.set(sessionId, sessionClient);
|
|
250
|
-
this.currentSessionId = sessionId;
|
|
251
|
-
if (sessionInfo.team_uuid) {
|
|
252
|
-
this.currentTeamId = sessionInfo.team_uuid;
|
|
253
|
-
}
|
|
254
|
-
this.sessionJoinOrCreateRes = undefined;
|
|
255
|
-
this.addSessionToAgentSessionMap(sessionInfo);
|
|
256
|
-
resolve(sessionClient);
|
|
257
|
-
},
|
|
258
|
-
reject: (error) => {
|
|
259
|
-
clearTimeout(timeoutId);
|
|
260
|
-
this.sessionJoinOrCreateRes = undefined;
|
|
261
|
-
reject(error);
|
|
262
|
-
},
|
|
263
|
-
sessionId: "",
|
|
264
|
-
clientMessageId,
|
|
265
|
-
};
|
|
266
|
-
// Send session_create message
|
|
267
|
-
try {
|
|
268
|
-
this.connection.send({
|
|
269
|
-
type: "control_session_create",
|
|
270
|
-
client_message_id: clientMessageId,
|
|
271
|
-
title: newTitle,
|
|
272
|
-
agent_profile_id: agentProfileId,
|
|
273
|
-
team_id: teamId,
|
|
274
|
-
});
|
|
275
|
-
logger.info(`[ChatClient] Sent session_create for session ${newTitle}` +
|
|
276
|
-
` client message id: ${clientMessageId}`);
|
|
277
|
-
}
|
|
278
|
-
catch (error) {
|
|
279
|
-
this.sessionJoinOrCreateRes = undefined;
|
|
280
|
-
clearTimeout(timeoutId);
|
|
281
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
282
|
-
}
|
|
283
|
-
});
|
|
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;
|
|
284
231
|
}
|
|
285
232
|
/**
|
|
286
233
|
* Connect to an existing session by sending session_join message
|
|
@@ -290,58 +237,22 @@ class ChatClient {
|
|
|
290
237
|
if (this.closed) {
|
|
291
238
|
throw new Error("ChatClient is closed");
|
|
292
239
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
return new Promise((resolve, reject) => {
|
|
300
|
-
const clientMessageId = (0, uuid_1.v4)();
|
|
301
|
-
// Set up timeout for the request
|
|
302
|
-
const timeoutId = setTimeout(() => {
|
|
303
|
-
this.sessionJoinOrCreateRes = undefined;
|
|
304
|
-
reject(new Error(`Session join timeout for session ${sessionId}`));
|
|
305
|
-
}, constants_1.SESSION_JOIN_TIMEOUT);
|
|
306
|
-
// Track this pending request
|
|
307
|
-
this.sessionJoinOrCreateRes = {
|
|
308
|
-
resolve: (sessionClient, sessionInfo) => {
|
|
309
|
-
clearTimeout(timeoutId);
|
|
310
|
-
this.activeSessions.set(sessionId, sessionClient);
|
|
311
|
-
this.currentSessionId = sessionId;
|
|
312
|
-
if (sessionInfo.team_uuid) {
|
|
313
|
-
this.currentTeamId = sessionInfo.team_uuid;
|
|
314
|
-
}
|
|
315
|
-
this.sessionJoinOrCreateRes = undefined;
|
|
316
|
-
this.updateSessionInfo(sessionInfo);
|
|
317
|
-
logger.info(`[ChatClient] joined session ${sessionId}`);
|
|
318
|
-
resolve(sessionClient);
|
|
319
|
-
},
|
|
320
|
-
reject: (error) => {
|
|
321
|
-
clearTimeout(timeoutId);
|
|
322
|
-
this.sessionJoinOrCreateRes = undefined;
|
|
323
|
-
logger.error(`[ChatClient] failed to join session` +
|
|
324
|
-
` ${sessionId}: ${error.message}`);
|
|
325
|
-
reject(error);
|
|
326
|
-
},
|
|
327
|
-
sessionId,
|
|
328
|
-
clientMessageId,
|
|
329
|
-
};
|
|
330
|
-
// Send session_join message
|
|
331
|
-
try {
|
|
332
|
-
this.connection.send({
|
|
333
|
-
type: "control_session_join",
|
|
334
|
-
client_message_id: clientMessageId,
|
|
335
|
-
target_session_id: sessionId,
|
|
336
|
-
});
|
|
337
|
-
logger.debug(`[ChatClient] Sent session_join for session ${sessionId}`);
|
|
338
|
-
}
|
|
339
|
-
catch (error) {
|
|
340
|
-
this.sessionJoinOrCreateRes = undefined;
|
|
341
|
-
clearTimeout(timeoutId);
|
|
342
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
343
|
-
}
|
|
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,
|
|
344
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;
|
|
345
256
|
}
|
|
346
257
|
/**
|
|
347
258
|
* Fetch the session list from the server
|
|
@@ -649,97 +560,28 @@ class ChatClient {
|
|
|
649
560
|
* Handle session_info message which can be for:
|
|
650
561
|
* 1. A pending session join/create request
|
|
651
562
|
* 2. An update to an existing active session
|
|
563
|
+
* 3. A new session notification
|
|
652
564
|
* We update the session list here as well.
|
|
653
565
|
*/
|
|
654
566
|
handleSessionInfoMessage(msg) {
|
|
655
|
-
|
|
656
|
-
//
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
logger.info(`[ChatClient] msg: ${JSON.stringify(msg)}`);
|
|
663
|
-
const sessionClient = new sessionClient_1.SessionClient(sessionId, msg.saved_agent_profile, this.connection, msg.mcp_server_briefs, (0, database_1.createSessionParticipantMap)(msg.participants), msg.agent_paused);
|
|
664
|
-
// we need to pass the session id if this is a new session
|
|
665
|
-
if (this.sessionJoinOrCreateRes.sessionId === "") {
|
|
666
|
-
this.sessionJoinOrCreateRes.sessionId = sessionId;
|
|
667
|
-
}
|
|
668
|
-
else if (this.sessionJoinOrCreateRes.sessionId !== sessionId) {
|
|
669
|
-
throw new Error(`[ChatClient] session id mismatch: ` +
|
|
670
|
-
`${this.sessionJoinOrCreateRes.sessionId} !== ${sessionId}`);
|
|
671
|
-
}
|
|
672
|
-
resolve(sessionClient, msg);
|
|
673
|
-
}
|
|
674
|
-
catch (error) {
|
|
675
|
-
const errorMsg = error instanceof Error ? error : new Error(String(error));
|
|
676
|
-
logger.error(`[ChatClient] Failed to create SessionClient: ${errorMsg.message}`);
|
|
677
|
-
reject(errorMsg);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
else {
|
|
681
|
-
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;
|
|
682
574
|
}
|
|
575
|
+
logger.debug(`[ChatClient.handleSessionInfoMessage] not handled by awaiter - updating`);
|
|
576
|
+
this.upsertSessionToAgentSessionMap(msg);
|
|
683
577
|
}
|
|
684
578
|
handleControlError(msg) {
|
|
685
|
-
|
|
686
|
-
if (this.sessionJoinOrCreateRes &&
|
|
687
|
-
msg.client_message_id === this.sessionJoinOrCreateRes.clientMessageId) {
|
|
688
|
-
this.sessionJoinOrCreateRes.reject(new Error(msg.message));
|
|
689
|
-
}
|
|
690
|
-
this.eventHandler.onError(`Server error: ${msg.message}`);
|
|
691
|
-
}
|
|
692
|
-
/**
|
|
693
|
-
* Update the session list. This is called when a session is updated.
|
|
694
|
-
* An error is thrown if the session id is not found.
|
|
695
|
-
* TODO: we might want to resync session map, for now, throw error to
|
|
696
|
-
* expose problems in the code.
|
|
697
|
-
*/
|
|
698
|
-
updateAgentSessionMap(sessionInfo) {
|
|
699
|
-
const sessionId = sessionInfo.session_id;
|
|
700
|
-
// Skip agent session map updates for guest sessions since they don't
|
|
701
|
-
// need session organization
|
|
702
|
-
if (this.isGuestMode) {
|
|
703
|
-
logger.info(`[ChatClient] Skipping agent session map update for ` +
|
|
704
|
-
`guest session ${sessionId}`);
|
|
579
|
+
if (this.sessionJoinAwaiter.onMessage(msg)) {
|
|
705
580
|
return;
|
|
706
581
|
}
|
|
707
|
-
|
|
708
|
-
const teamInfo = this.teams.get(sessionInfo.team_uuid);
|
|
709
|
-
if (!teamInfo) {
|
|
710
|
-
throw new Error(`Team ${sessionInfo.team_uuid} not found in team list`);
|
|
711
|
-
}
|
|
712
|
-
const agentSessionMap = teamInfo.agentSessionMap;
|
|
713
|
-
if (!doUpdateAgentSessionMap(agentSessionMap, sessionInfo)) {
|
|
714
|
-
throw new Error(`[updateAgentSessionMap] team session ${sessionId}` +
|
|
715
|
-
` not found in agent session map`);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
else {
|
|
719
|
-
const agentSessionMap = this.userAgentSessionMap;
|
|
720
|
-
if (!doUpdateAgentSessionMap(agentSessionMap, sessionInfo)) {
|
|
721
|
-
throw new Error(`[updateAgentSessionMap] user session ${sessionId}` +
|
|
722
|
-
` not found in agent session map`);
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
/**
|
|
727
|
-
* Update the SessionDescriptor info for an existing session, given a full
|
|
728
|
-
* ServerSessionInfo. This also passes the session info to the session
|
|
729
|
-
* client. An error is thrown if the session client is not found.
|
|
730
|
-
*/
|
|
731
|
-
updateSessionInfo(msg) {
|
|
732
|
-
const sessionId = msg.session_id;
|
|
733
|
-
const sessionClient = this.activeSessions.get(sessionId);
|
|
734
|
-
this.updateAgentSessionMap(msg);
|
|
735
|
-
if (sessionClient) {
|
|
736
|
-
sessionClient.updateSessionInfo(msg);
|
|
737
|
-
}
|
|
738
|
-
else {
|
|
739
|
-
throw new Error(`Session client not found for session ${sessionId}`);
|
|
740
|
-
}
|
|
582
|
+
this.eventHandler.onError(`Server error: ${msg.message}`);
|
|
741
583
|
}
|
|
742
|
-
|
|
584
|
+
upsertSessionToAgentSessionMap(msg) {
|
|
743
585
|
// Skip agent session map updates for guest sessions since they don't
|
|
744
586
|
// need session organization
|
|
745
587
|
if (this.isGuestMode) {
|
|
@@ -761,9 +603,40 @@ class ChatClient {
|
|
|
761
603
|
throw new Error(`[addSessionToAgentSessionMap] Agent ${agentUuid}` +
|
|
762
604
|
` not found in agent session map`);
|
|
763
605
|
}
|
|
764
|
-
|
|
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
|
+
}
|
|
765
620
|
agentSession.updated_at = new Date(msg.updated_at).getTime();
|
|
766
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
|
+
}
|
|
767
640
|
}
|
|
768
641
|
exports.ChatClient = ChatClient;
|
|
769
642
|
function sessionInfoToSessionDescriptor(msg) {
|
|
@@ -777,28 +650,3 @@ function sessionInfoToSessionDescriptor(msg) {
|
|
|
777
650
|
agent_paused: msg.agent_paused,
|
|
778
651
|
};
|
|
779
652
|
}
|
|
780
|
-
/**
|
|
781
|
-
* Update the agent session map
|
|
782
|
-
* @param agentSessionMap - the agent session map
|
|
783
|
-
* @param sessionInfo - the session info
|
|
784
|
-
* @returns true if the session is updated, false otherwise
|
|
785
|
-
*/
|
|
786
|
-
function doUpdateAgentSessionMap(agentSessionMap, sessionInfo) {
|
|
787
|
-
const sessionId = sessionInfo.session_id;
|
|
788
|
-
const agentUuid = sessionInfo.saved_agent_profile.uuid;
|
|
789
|
-
const agent = agentSessionMap.get(agentUuid);
|
|
790
|
-
if (!agent) {
|
|
791
|
-
return false;
|
|
792
|
-
}
|
|
793
|
-
let updated = false;
|
|
794
|
-
agent.sessions.map((session) => {
|
|
795
|
-
if (session.session_uuid === sessionId) {
|
|
796
|
-
updated = true;
|
|
797
|
-
return sessionInfoToSessionDescriptor(sessionInfo);
|
|
798
|
-
}
|
|
799
|
-
else {
|
|
800
|
-
return session;
|
|
801
|
-
}
|
|
802
|
-
});
|
|
803
|
-
return updated;
|
|
804
|
-
}
|
|
@@ -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
|
|
@@ -6,9 +6,8 @@ const uuid_1 = require("uuid");
|
|
|
6
6
|
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
7
7
|
const mcpServerManager_1 = require("../../agent/mcpServerManager");
|
|
8
8
|
const messages_1 = require("../protocol/messages");
|
|
9
|
-
const database_1 = require("../data/database");
|
|
10
9
|
const sessionFiles_1 = require("./sessionFiles");
|
|
11
|
-
const
|
|
10
|
+
const responseAwaiter_1 = require("../utils/responseAwaiter");
|
|
12
11
|
const logger = (0, sdk_1.getLogger)();
|
|
13
12
|
class RemoteSudoMcpServerManager {
|
|
14
13
|
constructor(sender, briefs) {
|
|
@@ -153,7 +152,7 @@ class SessionClient {
|
|
|
153
152
|
this.sender = sender;
|
|
154
153
|
this.smsm = new RemoteSudoMcpServerManager(this, serverBriefs);
|
|
155
154
|
this.sessionFiles = new sessionFiles_1.SessionFiles(sessionUUID, fileList, sender);
|
|
156
|
-
this.responseHandler =
|
|
155
|
+
this.responseHandler = responseAwaiter_1.ResponseAwaiter.init("session_error", (msg) => msg.client_message_id);
|
|
157
156
|
this.participants = participants;
|
|
158
157
|
this.systemPrompt = "";
|
|
159
158
|
this.model = "";
|
|
@@ -267,11 +266,6 @@ class SessionClient {
|
|
|
267
266
|
};
|
|
268
267
|
this.sender.send(enrichedMessage);
|
|
269
268
|
}
|
|
270
|
-
updateSessionInfo(sessionInfo) {
|
|
271
|
-
// TODO: Determine the correct approach. Cache the workspace?
|
|
272
|
-
const infoStr = JSON.stringify(sessionInfo.workspace);
|
|
273
|
-
logger.debug(`[SessionClient] ignoring session info: ${infoStr}`);
|
|
274
|
-
}
|
|
275
269
|
setWorkspace(message, imageB64) {
|
|
276
270
|
const msg = {
|
|
277
271
|
type: "set_workspace",
|
|
@@ -303,7 +297,7 @@ class SessionClient {
|
|
|
303
297
|
session_id: this.sessionUUID,
|
|
304
298
|
};
|
|
305
299
|
this.sender.send(msg);
|
|
306
|
-
const response = await this.responseHandler.waitForResponse(msg);
|
|
300
|
+
const response = await this.responseHandler.waitForResponse(msg.client_message_id);
|
|
307
301
|
if (response.type === "session_shared") {
|
|
308
302
|
return response.access_token;
|
|
309
303
|
}
|
|
@@ -337,10 +331,7 @@ class SessionClient {
|
|
|
337
331
|
this.model = message.model;
|
|
338
332
|
break;
|
|
339
333
|
case "session_info":
|
|
340
|
-
//
|
|
341
|
-
if ("participants" in message) {
|
|
342
|
-
this.participants = (0, database_1.createSessionParticipantMap)(message.participants);
|
|
343
|
-
}
|
|
334
|
+
// This is handled in the layer above, in the chatClient
|
|
344
335
|
break;
|
|
345
336
|
case "user_added":
|
|
346
337
|
this.participants.set(message.user_uuid, {
|
|
@@ -4,7 +4,7 @@ exports.SessionFiles = void 0;
|
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
5
|
const assert_1 = require("assert");
|
|
6
6
|
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
7
|
-
const
|
|
7
|
+
const responseAwaiter_1 = require("../utils/responseAwaiter");
|
|
8
8
|
const logger = (0, sdk_1.getLogger)();
|
|
9
9
|
/// Object for the UI to use to interact with the FileManager. If the UI
|
|
10
10
|
/// receives ServerSessionFileChanged or ServerSessionFileDeleted then it
|
|
@@ -14,7 +14,7 @@ class SessionFiles {
|
|
|
14
14
|
constructor(sessionUUID, initialFileList, sender) {
|
|
15
15
|
this.sessionUUID = sessionUUID;
|
|
16
16
|
this.descriptors = new Map(initialFileList.map((d) => [d.name, d]));
|
|
17
|
-
this.responseHandler =
|
|
17
|
+
this.responseHandler = responseAwaiter_1.ResponseAwaiter.init(undefined, (msg) => msg.client_message_id);
|
|
18
18
|
this.sender = sender;
|
|
19
19
|
}
|
|
20
20
|
listFiles() {
|
|
@@ -31,7 +31,7 @@ class SessionFiles {
|
|
|
31
31
|
client_message_id: (0, uuid_1.v4)(),
|
|
32
32
|
};
|
|
33
33
|
this.sender.send(msg);
|
|
34
|
-
const response = await this.responseHandler.waitForResponse(msg);
|
|
34
|
+
const response = await this.responseHandler.waitForResponse(msg.client_message_id);
|
|
35
35
|
if (response.name !== name) {
|
|
36
36
|
throw new Error(`invalid name for file ${name}: ${JSON.stringify(response)}`);
|
|
37
37
|
}
|
|
@@ -99,7 +99,7 @@ class ChatContextManager {
|
|
|
99
99
|
getConversationMessages() {
|
|
100
100
|
return this.conversationMessages.concat(this.pendingUserMessages);
|
|
101
101
|
}
|
|
102
|
-
processUserMessage(msg,
|
|
102
|
+
processUserMessage(msg, from_uuid, from_nickname) {
|
|
103
103
|
// TODO: maintain a queue internally instead of relying on the caller to
|
|
104
104
|
// pass in our generated messages back into `startAgentResponse`.
|
|
105
105
|
// Filter out null messages immediately.
|
|
@@ -112,7 +112,8 @@ class ChatContextManager {
|
|
|
112
112
|
session_id: this.sessionUUID,
|
|
113
113
|
message_idx,
|
|
114
114
|
message: msg.message,
|
|
115
|
-
user_uuid:
|
|
115
|
+
user_uuid: from_uuid,
|
|
116
|
+
user_nickname: from_nickname,
|
|
116
117
|
};
|
|
117
118
|
if (msg.imageB64) {
|
|
118
119
|
userMessage.imageB64 = msg.imageB64;
|
|
@@ -149,7 +150,7 @@ class ChatContextManager {
|
|
|
149
150
|
// Compute the new llm messages
|
|
150
151
|
const llmUserMessages = [];
|
|
151
152
|
for (const msg of pendingUserMessages) {
|
|
152
|
-
const userMsg = (0, agent_1.createUserMessage)(msg.message, msg.imageB64, msg.
|
|
153
|
+
const userMsg = (0, agent_1.createUserMessage)(msg.message, msg.imageB64, msg.user_nickname);
|
|
153
154
|
if (userMsg) {
|
|
154
155
|
llmUserMessages.push(userMsg);
|
|
155
156
|
}
|
|
@@ -200,7 +201,7 @@ class ChatContextManager {
|
|
|
200
201
|
}
|
|
201
202
|
if (JSON.stringify(sMsg.content) !== JSON.stringify(lMsg)) {
|
|
202
203
|
messageListError(`newSessionMessages[${String(i)}].content !== ` +
|
|
203
|
-
`newLLMMessages[${String(i)}]
|
|
204
|
+
`newLLMMessages[${String(i)}]`);
|
|
204
205
|
}
|
|
205
206
|
if (sMsg.message_idx !== pMsg.message_idx) {
|
|
206
207
|
messageListError(`newSessionMessages[${String(i)}].message_idx !== ` +
|
|
@@ -244,11 +245,6 @@ class ChatContextManager {
|
|
|
244
245
|
this.startingLLMContextLength = undefined;
|
|
245
246
|
this.pendingMessages = undefined;
|
|
246
247
|
this.curAgentMsgIdx = undefined;
|
|
247
|
-
return {
|
|
248
|
-
type: "session_error",
|
|
249
|
-
session_id: this.sessionUUID,
|
|
250
|
-
message: errMsg,
|
|
251
|
-
};
|
|
252
248
|
}
|
|
253
249
|
processAgentMessage(msg, end) {
|
|
254
250
|
(0, assert_1.strict)(typeof this.startingLLMContextLength !== "undefined");
|