@xalia/agent 0.5.7 → 0.6.0
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/README.md +23 -8
- package/dist/agent/src/agent/agent.js +176 -96
- package/dist/agent/src/agent/agentUtils.js +82 -59
- package/dist/agent/src/agent/compressingContextManager.js +102 -0
- package/dist/agent/src/agent/context.js +189 -0
- package/dist/agent/src/agent/dummyLLM.js +46 -5
- package/dist/agent/src/agent/mcpServerManager.js +23 -24
- package/dist/agent/src/agent/nullAgentEventHandler.js +21 -0
- package/dist/agent/src/agent/nullPlatform.js +14 -0
- package/dist/agent/src/agent/openAILLMStreaming.js +26 -14
- package/dist/agent/src/agent/promptProvider.js +63 -0
- package/dist/agent/src/agent/repeatLLM.js +5 -5
- package/dist/agent/src/agent/sudoMcpServerManager.js +23 -21
- package/dist/agent/src/agent/tokenAuth.js +7 -7
- package/dist/agent/src/agent/tools.js +1 -1
- package/dist/agent/src/chat/client/chatClient.js +733 -0
- package/dist/agent/src/chat/client/connection.js +209 -0
- package/dist/agent/src/chat/client/connection.test.js +188 -0
- package/dist/agent/src/chat/client/constants.js +5 -0
- package/dist/agent/src/chat/client/index.js +15 -0
- package/dist/agent/src/chat/client/interfaces.js +2 -0
- package/dist/agent/src/chat/client/responseHandler.js +105 -0
- package/dist/agent/src/chat/client/sessionClient.js +331 -0
- package/dist/agent/src/chat/client/teamManager.js +2 -0
- package/dist/agent/src/chat/{apiKeyManager.js → data/apiKeyManager.js} +4 -0
- package/dist/agent/src/chat/data/dataModels.js +2 -0
- package/dist/agent/src/chat/data/database.js +749 -0
- package/dist/agent/src/chat/data/dbMcpServerConfigs.js +47 -0
- package/dist/agent/src/chat/protocol/connectionMessages.js +5 -0
- package/dist/agent/src/chat/protocol/constants.js +50 -0
- package/dist/agent/src/chat/protocol/errors.js +22 -0
- package/dist/agent/src/chat/protocol/messages.js +110 -0
- package/dist/agent/src/chat/server/chatContextManager.js +405 -0
- package/dist/agent/src/chat/server/connectionManager.js +352 -0
- package/dist/agent/src/chat/server/connectionManager.test.js +159 -0
- package/dist/agent/src/chat/server/conversation.js +198 -0
- package/dist/agent/src/chat/server/errorUtils.js +23 -0
- package/dist/agent/src/chat/server/openSession.js +869 -0
- package/dist/agent/src/chat/server/server.js +177 -0
- package/dist/agent/src/chat/server/sessionFileManager.js +161 -0
- package/dist/agent/src/chat/server/sessionRegistry.js +700 -0
- package/dist/agent/src/chat/server/sessionRegistry.test.js +97 -0
- package/dist/agent/src/chat/server/test-utils/mockFactories.js +307 -0
- package/dist/agent/src/chat/server/tools.js +243 -0
- package/dist/agent/src/chat/utils/agentSessionMap.js +66 -0
- package/dist/agent/src/chat/utils/approvalManager.js +85 -0
- package/dist/agent/src/{utils → chat/utils}/asyncLock.js +3 -3
- package/dist/agent/src/chat/{asyncQueue.js → utils/asyncQueue.js} +12 -2
- package/dist/agent/src/chat/utils/htmlToText.js +84 -0
- package/dist/agent/src/chat/utils/multiAsyncQueue.js +42 -0
- package/dist/agent/src/chat/utils/search.js +145 -0
- package/dist/agent/src/chat/utils/userResolver.js +46 -0
- package/dist/agent/src/chat/utils/websocket.js +16 -0
- package/dist/agent/src/test/agent.test.js +332 -0
- package/dist/agent/src/test/approvalManager.test.js +58 -0
- package/dist/agent/src/test/chatContextManager.test.js +392 -0
- package/dist/agent/src/test/clientServerConnection.test.js +158 -0
- package/dist/agent/src/test/compressingContextManager.test.js +65 -0
- package/dist/agent/src/test/context.test.js +83 -0
- package/dist/agent/src/test/conversation.test.js +89 -0
- package/dist/agent/src/test/db.test.js +271 -83
- package/dist/agent/src/test/dbMcpServerConfigs.test.js +72 -0
- package/dist/agent/src/test/dbTestTools.js +99 -0
- package/dist/agent/src/test/imageLoad.test.js +8 -7
- package/dist/agent/src/test/mcpServerManager.test.js +23 -20
- package/dist/agent/src/test/multiAsyncQueue.test.js +101 -0
- package/dist/agent/src/test/openaiStreaming.test.js +64 -35
- package/dist/agent/src/test/prompt.test.js +5 -4
- package/dist/agent/src/test/promptProvider.test.js +28 -0
- package/dist/agent/src/test/responseHandler.test.js +61 -0
- package/dist/agent/src/test/sudoMcpServerManager.test.js +24 -25
- package/dist/agent/src/test/testTools.js +109 -0
- package/dist/agent/src/test/tools.test.js +31 -0
- package/dist/agent/src/tool/agentChat.js +21 -10
- package/dist/agent/src/tool/agentMain.js +1 -1
- package/dist/agent/src/tool/chatMain.js +241 -58
- package/dist/agent/src/tool/commandPrompt.js +22 -17
- package/dist/agent/src/tool/files.js +20 -16
- package/dist/agent/src/tool/nodePlatform.js +47 -3
- package/dist/agent/src/tool/options.js +4 -4
- package/dist/agent/src/tool/prompt.js +19 -13
- package/eslint.config.mjs +14 -1
- package/package.json +14 -6
- package/scripts/chat_server +8 -0
- package/scripts/setup_chat +7 -2
- package/scripts/shutdown_chat_server +3 -0
- package/scripts/test_chat +135 -17
- package/src/agent/agent.ts +283 -138
- package/src/agent/agentUtils.ts +143 -108
- package/src/agent/compressingContextManager.ts +164 -0
- package/src/agent/context.ts +268 -0
- package/src/agent/dummyLLM.ts +76 -8
- package/src/agent/iAgentEventHandler.ts +54 -0
- package/src/agent/iplatform.ts +1 -0
- package/src/agent/mcpServerManager.ts +35 -31
- package/src/agent/nullAgentEventHandler.ts +20 -0
- package/src/agent/nullPlatform.ts +13 -0
- package/src/agent/openAILLMStreaming.ts +26 -13
- package/src/agent/promptProvider.ts +87 -0
- package/src/agent/repeatLLM.ts +5 -5
- package/src/agent/sudoMcpServerManager.ts +30 -29
- package/src/agent/tokenAuth.ts +7 -7
- package/src/agent/tools.ts +3 -1
- package/src/chat/client/chatClient.ts +900 -0
- package/src/chat/client/connection.test.ts +241 -0
- package/src/chat/client/connection.ts +276 -0
- package/src/chat/client/constants.ts +3 -0
- package/src/chat/client/index.ts +18 -0
- package/src/chat/client/interfaces.ts +34 -0
- package/src/chat/client/responseHandler.ts +131 -0
- package/src/chat/client/sessionClient.ts +443 -0
- package/src/chat/client/teamManager.ts +29 -0
- package/src/chat/{apiKeyManager.ts → data/apiKeyManager.ts} +6 -2
- package/src/chat/data/dataModels.ts +85 -0
- package/src/chat/data/database.ts +982 -0
- package/src/chat/data/dbMcpServerConfigs.ts +59 -0
- package/src/chat/protocol/connectionMessages.ts +49 -0
- package/src/chat/protocol/constants.ts +55 -0
- package/src/chat/protocol/errors.ts +16 -0
- package/src/chat/protocol/messages.ts +682 -0
- package/src/chat/server/README.md +127 -0
- package/src/chat/server/chatContextManager.ts +612 -0
- package/src/chat/server/connectionManager.test.ts +266 -0
- package/src/chat/server/connectionManager.ts +541 -0
- package/src/chat/server/conversation.ts +269 -0
- package/src/chat/server/errorUtils.ts +28 -0
- package/src/chat/server/openSession.ts +1332 -0
- package/src/chat/server/server.ts +177 -0
- package/src/chat/server/sessionFileManager.ts +239 -0
- package/src/chat/server/sessionRegistry.test.ts +138 -0
- package/src/chat/server/sessionRegistry.ts +1064 -0
- package/src/chat/server/test-utils/mockFactories.ts +422 -0
- package/src/chat/server/tools.ts +265 -0
- package/src/chat/utils/agentSessionMap.ts +76 -0
- package/src/chat/utils/approvalManager.ts +111 -0
- package/src/{utils → chat/utils}/asyncLock.ts +3 -3
- package/src/chat/{asyncQueue.ts → utils/asyncQueue.ts} +14 -3
- package/src/chat/utils/htmlToText.ts +61 -0
- package/src/chat/utils/multiAsyncQueue.ts +52 -0
- package/src/chat/utils/search.ts +139 -0
- package/src/chat/utils/userResolver.ts +48 -0
- package/src/chat/utils/websocket.ts +16 -0
- package/src/test/agent.test.ts +487 -0
- package/src/test/approvalManager.test.ts +73 -0
- package/src/test/chatContextManager.test.ts +521 -0
- package/src/test/clientServerConnection.test.ts +207 -0
- package/src/test/compressingContextManager.test.ts +82 -0
- package/src/test/context.test.ts +105 -0
- package/src/test/conversation.test.ts +109 -0
- package/src/test/db.test.ts +358 -89
- package/src/test/dbMcpServerConfigs.test.ts +112 -0
- package/src/test/dbTestTools.ts +153 -0
- package/src/test/imageLoad.test.ts +7 -6
- package/src/test/mcpServerManager.test.ts +21 -16
- package/src/test/multiAsyncQueue.test.ts +125 -0
- package/src/test/openaiStreaming.test.ts +71 -36
- package/src/test/prompt.test.ts +4 -3
- package/src/test/promptProvider.test.ts +33 -0
- package/src/test/responseHandler.test.ts +78 -0
- package/src/test/sudoMcpServerManager.test.ts +32 -30
- package/src/test/testTools.ts +146 -0
- package/src/test/tools.test.ts +39 -0
- package/src/tool/agentChat.ts +26 -12
- package/src/tool/agentMain.ts +1 -1
- package/src/tool/chatMain.ts +292 -100
- package/src/tool/commandPrompt.ts +28 -19
- package/src/tool/files.ts +25 -19
- package/src/tool/nodePlatform.ts +52 -3
- package/src/tool/options.ts +4 -2
- package/src/tool/prompt.ts +22 -15
- package/test_data/dummyllm_script_crash.json +32 -0
- package/test_data/frog.png.b64 +1 -0
- package/vitest.config.ts +39 -0
- package/dist/agent/src/chat/client.js +0 -349
- package/dist/agent/src/chat/conversationManager.js +0 -392
- package/dist/agent/src/chat/db.js +0 -209
- package/dist/agent/src/chat/frontendClient.js +0 -74
- package/dist/agent/src/chat/server.js +0 -158
- package/src/chat/client.ts +0 -455
- package/src/chat/conversationManager.ts +0 -595
- package/src/chat/db.ts +0 -290
- package/src/chat/frontendClient.ts +0 -123
- package/src/chat/messages.ts +0 -235
- package/src/chat/server.ts +0 -177
- /package/dist/agent/src/{chat/messages.js → agent/iAgentEventHandler.js} +0 -0
- /package/{frog.png → test_data/frog.png} +0 -0
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionRegistry = void 0;
|
|
4
|
+
exports.userSessionDataCreate = userSessionDataCreate;
|
|
5
|
+
exports.teamSessionDataCreate = teamSessionDataCreate;
|
|
6
|
+
const uuid_1 = require("uuid");
|
|
7
|
+
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
8
|
+
const messages_1 = require("../protocol/messages");
|
|
9
|
+
const openSession_1 = require("./openSession");
|
|
10
|
+
const database_1 = require("../data/database");
|
|
11
|
+
const userResolver_1 = require("../utils/userResolver");
|
|
12
|
+
const errors_1 = require("../protocol/errors");
|
|
13
|
+
const agentUtils_1 = require("../../agent/agentUtils");
|
|
14
|
+
const errorUtils_1 = require("./errorUtils");
|
|
15
|
+
const logger = (0, sdk_1.getLogger)();
|
|
16
|
+
class SessionRegistry {
|
|
17
|
+
constructor(db, connectionManager, llmUrl, xmcpUrl) {
|
|
18
|
+
this.db = db;
|
|
19
|
+
this.connectionManager = connectionManager;
|
|
20
|
+
this.llmUrl = llmUrl;
|
|
21
|
+
this.xmcpUrl = xmcpUrl;
|
|
22
|
+
// In memory session-user/user-session mappings
|
|
23
|
+
// Note: this mappings ONLY trackes online users and will
|
|
24
|
+
// be cleaned up when the user disconnects.
|
|
25
|
+
// sessionId -> userIds
|
|
26
|
+
this.sessionUsers = new Map();
|
|
27
|
+
// userId -> sessionIds
|
|
28
|
+
this.userSessions = new Map();
|
|
29
|
+
// Session instances
|
|
30
|
+
// sessionId -> OpenSession
|
|
31
|
+
this.openSessions = new Map();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Add user to session membership.
|
|
35
|
+
* Creates session tracking if it doesn't exist.
|
|
36
|
+
*/
|
|
37
|
+
addUserToSessionMemory(userId, sessionId) {
|
|
38
|
+
logger.debug(`[SessionRegistry] Adding user ${userId} to session ${sessionId}`);
|
|
39
|
+
// Add to sessionUsers
|
|
40
|
+
if (!this.sessionUsers.has(sessionId)) {
|
|
41
|
+
this.sessionUsers.set(sessionId, new Set());
|
|
42
|
+
}
|
|
43
|
+
const sessionUserSet = this.sessionUsers.get(sessionId);
|
|
44
|
+
if (sessionUserSet) {
|
|
45
|
+
sessionUserSet.add(userId);
|
|
46
|
+
}
|
|
47
|
+
// Add to userSessions
|
|
48
|
+
if (!this.userSessions.has(userId)) {
|
|
49
|
+
this.userSessions.set(userId, new Set());
|
|
50
|
+
}
|
|
51
|
+
const userSessionSet = this.userSessions.get(userId);
|
|
52
|
+
if (userSessionSet) {
|
|
53
|
+
userSessionSet.add(sessionId);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Remove user from session membership.
|
|
58
|
+
* Cleans up empty sessions and triggers session cleanup if needed.
|
|
59
|
+
*/
|
|
60
|
+
removeUserFromSessionMemory(userId, sessionId) {
|
|
61
|
+
logger.debug(`[SessionRegistry] Removing user ${userId} from session ${sessionId}`);
|
|
62
|
+
// Remove from sessionUsers
|
|
63
|
+
const sessionUserSet = this.sessionUsers.get(sessionId);
|
|
64
|
+
if (sessionUserSet) {
|
|
65
|
+
sessionUserSet.delete(userId);
|
|
66
|
+
if (sessionUserSet.size === 0) {
|
|
67
|
+
this.sessionUsers.delete(sessionId);
|
|
68
|
+
// Clean up session instance when empty
|
|
69
|
+
const session = this.openSessions.get(sessionId);
|
|
70
|
+
if (session) {
|
|
71
|
+
logger.debug(`[SessionRegistry] Triggering cleanup for empty session ` +
|
|
72
|
+
sessionId);
|
|
73
|
+
// The onEmpty callback will remove from openSessions map
|
|
74
|
+
logger.debug(`[SessionRegistry] Session ${sessionId} empty. Removing`);
|
|
75
|
+
session.onEmpty();
|
|
76
|
+
this.openSessions.delete(sessionId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Remove from userSessions
|
|
81
|
+
const userSessionSet = this.userSessions.get(userId);
|
|
82
|
+
if (userSessionSet) {
|
|
83
|
+
userSessionSet.delete(sessionId);
|
|
84
|
+
if (userSessionSet.size === 0) {
|
|
85
|
+
this.userSessions.delete(userId);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
logger.info(`[SessionRegistry] User ${userId} removed from session ${sessionId}`);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get all users in a session.
|
|
92
|
+
*/
|
|
93
|
+
async getSessionUsers(sessionId) {
|
|
94
|
+
const users = await this.db.sessionGetParticipants(sessionId);
|
|
95
|
+
return new Set(users.map((u) => u.user_uuid));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get all users in a session.
|
|
99
|
+
*/
|
|
100
|
+
getInMemorySessionUsers(sessionId) {
|
|
101
|
+
return this.sessionUsers.get(sessionId) || new Set();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get all sessions a user belongs to.
|
|
105
|
+
*/
|
|
106
|
+
getUserSessions(userId) {
|
|
107
|
+
return this.userSessions.get(userId) || new Set();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get all sessions a user belongs to in memory.
|
|
111
|
+
*/
|
|
112
|
+
getInMemoryUserSessions(userId) {
|
|
113
|
+
return this.userSessions.get(userId) || new Set();
|
|
114
|
+
}
|
|
115
|
+
async processMessage(connectionId, userId, message) {
|
|
116
|
+
if ((0, messages_1.isClientControlMessage)(message)) {
|
|
117
|
+
// handle connection level message
|
|
118
|
+
await this.processClientControlMessage(connectionId, userId, message);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// handle session level message
|
|
122
|
+
this.processSessionMessage(userId, message);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
sendControlError(connectionId, clientMsgId, errorMessage) {
|
|
126
|
+
// TODO: Why is this managed at the transport level?
|
|
127
|
+
const errorMsg = {
|
|
128
|
+
type: "control_error",
|
|
129
|
+
message: errorMessage,
|
|
130
|
+
client_message_id: clientMsgId,
|
|
131
|
+
};
|
|
132
|
+
this.connectionManager.sendToConnection(connectionId, errorMsg);
|
|
133
|
+
}
|
|
134
|
+
async processClientControlMessage(connectionId, userId, message) {
|
|
135
|
+
switch (message.type) {
|
|
136
|
+
case "control_agent_profile_create":
|
|
137
|
+
await this.handleAgentProfileCreate(message, userId);
|
|
138
|
+
break;
|
|
139
|
+
case "control_agent_profile_delete":
|
|
140
|
+
// TODO:
|
|
141
|
+
throw new Error("not implemented yet");
|
|
142
|
+
case "control_get_session_list":
|
|
143
|
+
await this.handleGetSessionList(connectionId, userId, message);
|
|
144
|
+
break;
|
|
145
|
+
case "control_session_create":
|
|
146
|
+
await this.handleSessionCreate(connectionId, userId, message);
|
|
147
|
+
break;
|
|
148
|
+
case "control_session_join":
|
|
149
|
+
await this.handleSessionJoin(connectionId, userId, message);
|
|
150
|
+
break;
|
|
151
|
+
case "control_session_delete":
|
|
152
|
+
await this.handleSessionDelete(connectionId, userId, message);
|
|
153
|
+
break;
|
|
154
|
+
case "control_team_create":
|
|
155
|
+
await this.handleTeamCreate(connectionId, userId, message);
|
|
156
|
+
break;
|
|
157
|
+
case "control_add_team_user":
|
|
158
|
+
await this.handleAddTeamUser(connectionId, userId, message);
|
|
159
|
+
break;
|
|
160
|
+
case "control_remove_team_user":
|
|
161
|
+
await this.handleRemoveTeamUser(connectionId, userId, message);
|
|
162
|
+
break;
|
|
163
|
+
default: {
|
|
164
|
+
const exhaustive = message;
|
|
165
|
+
// unknown connection type should be a fatal error
|
|
166
|
+
throw new errors_1.ChatFatalError(`Unknown connection message type: ${String(exhaustive)}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Handle session_list request
|
|
172
|
+
*/
|
|
173
|
+
async handleGetSessionList(connectionId, userId, message) {
|
|
174
|
+
try {
|
|
175
|
+
const [userSessions, teamSessions, userAgents] = await this.getAllAgentsAndSessionsByUser(userId);
|
|
176
|
+
const response = {
|
|
177
|
+
type: "control_session_list",
|
|
178
|
+
user_sessions: userSessions,
|
|
179
|
+
team_sessions: teamSessions,
|
|
180
|
+
user_agents: userAgents,
|
|
181
|
+
client_message_id: message.client_message_id,
|
|
182
|
+
};
|
|
183
|
+
this.connectionManager.sendToConnection(connectionId, response);
|
|
184
|
+
logger.info(`[ConnectionManager] Sent ` +
|
|
185
|
+
`${String(userSessions.length)} user sessions and ` +
|
|
186
|
+
`${String(teamSessions.length)} team sessions to user ${userId}`);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
logger.error(`[ConnectionManager] Failed to get session list:`, error);
|
|
190
|
+
this.sendControlError(connectionId, message.client_message_id, JSON.stringify(error));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async handleSessionDelete(connectionId, userId, message) {
|
|
194
|
+
const sessionId = message.target_session_id;
|
|
195
|
+
try {
|
|
196
|
+
// validate the session access
|
|
197
|
+
await this.validateSessionAccess(sessionId, userId, "owner");
|
|
198
|
+
// get users/team-uuid before deletion
|
|
199
|
+
const [users, teamUuid, sessionData] = await Promise.all([
|
|
200
|
+
this.getSessionUsers(sessionId),
|
|
201
|
+
this.db.sessionGetTeamUuid(sessionId),
|
|
202
|
+
this.db.sessionGetById(sessionId),
|
|
203
|
+
]);
|
|
204
|
+
if (!sessionData) {
|
|
205
|
+
throw new errors_1.ChatFatalError(`No such session: ${sessionId}`);
|
|
206
|
+
}
|
|
207
|
+
// delete the session from database
|
|
208
|
+
await this.db.sessionDeleteById(sessionId);
|
|
209
|
+
// remove the session from memory (should be no thrown from here)
|
|
210
|
+
const session = this.openSessions.get(sessionId);
|
|
211
|
+
if (session) {
|
|
212
|
+
const users = this.getInMemorySessionUsers(sessionId);
|
|
213
|
+
for (const user of users) {
|
|
214
|
+
this.removeUserFromSessionMemory(user, sessionId);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
this.openSessions.delete(sessionId);
|
|
218
|
+
// send confirmation to the client
|
|
219
|
+
const response = {
|
|
220
|
+
type: "control_session_deleted",
|
|
221
|
+
session_id: sessionId,
|
|
222
|
+
team_uuid: teamUuid,
|
|
223
|
+
agent_profile_uuid: sessionData.agent_profile_uuid,
|
|
224
|
+
client_message_id: message.client_message_id,
|
|
225
|
+
};
|
|
226
|
+
this.connectionManager.sendToUsers(users, response);
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
logger.error(`[SessionRegistry] Error delete session ${sessionId}:`, error);
|
|
230
|
+
this.sendControlError(connectionId, message.client_message_id, String(error));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Handle team_create_request message - creates a new team.
|
|
235
|
+
* @param connectionId connection id
|
|
236
|
+
* @param userId user id
|
|
237
|
+
* @param message team create request message
|
|
238
|
+
*/
|
|
239
|
+
async handleTeamCreate(connectionId, userId, message) {
|
|
240
|
+
try {
|
|
241
|
+
// Resolve all initial members in parallel
|
|
242
|
+
// this contains undefined to mark failed lookups
|
|
243
|
+
const resolvedMemberIds = await Promise.all(message.initial_members.map((member) => (0, userResolver_1.resolveUserIdentifier)(this.db, member)));
|
|
244
|
+
const failedLookups = [];
|
|
245
|
+
const validMemberIds = [];
|
|
246
|
+
const participants = [];
|
|
247
|
+
// filter out the owner and extract failed lookups
|
|
248
|
+
resolvedMemberIds.forEach((user, index) => {
|
|
249
|
+
if (user && user.uuid !== userId) {
|
|
250
|
+
validMemberIds.push(user.uuid);
|
|
251
|
+
participants.push({
|
|
252
|
+
user_uuid: user.uuid,
|
|
253
|
+
nickname: user.nickname || "",
|
|
254
|
+
email: user.email,
|
|
255
|
+
role: "participant",
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
else if (user === undefined) {
|
|
259
|
+
failedLookups.push(message.initial_members[index]);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
// Create the team with initial participants
|
|
263
|
+
const teamUuid = await this.db.createTeamWithParticipants(message.team_name, userId, validMemberIds);
|
|
264
|
+
const response = {
|
|
265
|
+
type: "control_team_created",
|
|
266
|
+
team_uuid: teamUuid,
|
|
267
|
+
team_owner_uuid: userId,
|
|
268
|
+
team_name: message.team_name,
|
|
269
|
+
members: participants,
|
|
270
|
+
failed_lookups: failedLookups,
|
|
271
|
+
};
|
|
272
|
+
const members = new Set(validMemberIds);
|
|
273
|
+
members.add(userId);
|
|
274
|
+
this.connectionManager.sendToUsers(members, response);
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
logger.error(`[SessionRegistry] Error creating team: ${String(error)}`);
|
|
278
|
+
this.sendControlError(connectionId, message.client_message_id, String(error));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Process session-scoped client message.
|
|
283
|
+
* Handles membership messages here, others to OpenSession.
|
|
284
|
+
*/
|
|
285
|
+
processSessionMessage(userId, message) {
|
|
286
|
+
const sessionId = message.session_id;
|
|
287
|
+
logger.info(`[SessionRegistry] Processing message from user ${userId} in session ` +
|
|
288
|
+
sessionId);
|
|
289
|
+
const session = this.openSessions.get(sessionId);
|
|
290
|
+
if (!session) {
|
|
291
|
+
throw new errors_1.ChatFatalError(`Internal error: No such session ${sessionId}`);
|
|
292
|
+
}
|
|
293
|
+
// Forward all other messages to OpenSession
|
|
294
|
+
session.onClientSessionMessage(userId, message);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Handle add_user message - adds user to team.
|
|
298
|
+
*/
|
|
299
|
+
async handleAddTeamUser(connectionId, fromUserId, message) {
|
|
300
|
+
// Validate permissions - only owner can add users
|
|
301
|
+
const access = await this.validateTeamAccess(message.target_team_id, fromUserId, "owner");
|
|
302
|
+
if (!access) {
|
|
303
|
+
this.sendControlError(connectionId, message.client_message_id, "Only team owner can add users");
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
// Resolve user identifier
|
|
307
|
+
const user = await (0, userResolver_1.resolveUserIdentifier)(this.db, message.user_uuid_or_email);
|
|
308
|
+
if (!user) {
|
|
309
|
+
this.sendControlError(connectionId, message.client_message_id, "User not found");
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
// Check if user is already a participant
|
|
313
|
+
const participants = await this.db.teamGetMembers(message.target_team_id);
|
|
314
|
+
if (participants.some((p) => p.user_uuid === user.uuid)) {
|
|
315
|
+
this.sendControlError(connectionId, message.client_message_id, "User is already a participant");
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
// Update database
|
|
319
|
+
try {
|
|
320
|
+
await this.db.teamAddMember(message.target_team_id, user.uuid);
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
this.sendControlError(connectionId, message.client_message_id, "Server Internal Error: cannot add user.");
|
|
324
|
+
logger.error(`[SessionRegistry] Error adding user ${user.uuid}` +
|
|
325
|
+
` to team ${message.target_team_id}:`, error);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
// add user to related active sessions
|
|
329
|
+
const sessions = await this.db.teamGetSessions(message.target_team_id);
|
|
330
|
+
for (const sessionData of sessions) {
|
|
331
|
+
const session = this.openSessions.get(sessionData.session_uuid);
|
|
332
|
+
if (session) {
|
|
333
|
+
session.addParticipant(user.uuid, {
|
|
334
|
+
user_uuid: user.uuid,
|
|
335
|
+
nickname: user.nickname || "",
|
|
336
|
+
email: user.email,
|
|
337
|
+
role: "participant",
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
logger.info(`[SessionRegistry] User ${user.uuid}` +
|
|
342
|
+
`added to team ${message.target_team_id}`);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Handle remove_user message - removes user from session membership.
|
|
346
|
+
* Only session owner can remove users.
|
|
347
|
+
*/
|
|
348
|
+
async handleRemoveTeamUser(connectionId, fromUserId, message) {
|
|
349
|
+
// Validate permissions - only owner can remove users
|
|
350
|
+
const access = await this.validateTeamAccess(message.target_team_id, fromUserId, "owner");
|
|
351
|
+
if (!access) {
|
|
352
|
+
this.sendControlError(connectionId, message.client_message_id, "Only team owner can remove users");
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
// Resolve user identifier
|
|
356
|
+
const user = await (0, userResolver_1.resolveUserIdentifier)(this.db, message.user_uuid_or_email);
|
|
357
|
+
if (!user) {
|
|
358
|
+
this.sendControlError(connectionId, message.client_message_id, "User not found");
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
// owner cannot remove her/himself
|
|
362
|
+
if (user.uuid === fromUserId) {
|
|
363
|
+
this.sendControlError(connectionId, message.client_message_id, "Owner cannot remove herself/himself");
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
// Check if user is actually a participant
|
|
367
|
+
const participants = await this.db.teamGetMembers(message.target_team_id);
|
|
368
|
+
if (!participants.some((p) => p.user_uuid === user.uuid)) {
|
|
369
|
+
this.sendControlError(connectionId, message.client_message_id, "User is not a participant");
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
// Remove from database
|
|
373
|
+
try {
|
|
374
|
+
await this.db.teamRemoveMember(message.target_team_id, user.uuid);
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
this.sendControlError(connectionId, message.client_message_id, "Server Internal Error: cannot remove user.");
|
|
378
|
+
logger.error(`[SessionRegistry] Error removing user ${user.uuid}` +
|
|
379
|
+
` from team ${message.target_team_id}:`, error);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
// Update OpenSession's participant map and in memory tracking
|
|
383
|
+
const sessions = await this.db.teamGetSessions(message.target_team_id);
|
|
384
|
+
for (const sessionData of sessions) {
|
|
385
|
+
const session = this.openSessions.get(sessionData.session_uuid);
|
|
386
|
+
if (session) {
|
|
387
|
+
session.removeParticipant(user.uuid);
|
|
388
|
+
this.removeUserFromSessionMemory(user.uuid, sessionData.session_uuid);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
logger.info(`[SessionRegistry] User ${user.uuid} ` +
|
|
392
|
+
`removed from team ${message.target_team_id}`);
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Get session instance, if the session has not been initialized,
|
|
396
|
+
* it will be.
|
|
397
|
+
*/
|
|
398
|
+
async getAndActivateSession(sessionId) {
|
|
399
|
+
if (this.openSessions.has(sessionId)) {
|
|
400
|
+
logger.info(`[SessionRegistry] Session ${sessionId} already exists`);
|
|
401
|
+
const openSession = this.openSessions.get(sessionId);
|
|
402
|
+
if (!openSession) {
|
|
403
|
+
throw new errors_1.ChatFatalError(`Internal error: No such session: ${sessionId}`);
|
|
404
|
+
}
|
|
405
|
+
return openSession;
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
logger.info(`[SessionRegistry] loading session ${sessionId}`);
|
|
409
|
+
return openSession_1.OpenSession.initWithExistingSession(this.db, sessionId, this.llmUrl, this.xmcpUrl, this.connectionManager);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Handle user joining a session.
|
|
414
|
+
* Activates the session if not already active.
|
|
415
|
+
* return the session info to the client joining the session.
|
|
416
|
+
*/
|
|
417
|
+
async handleSessionJoin(connectionId, userId, message) {
|
|
418
|
+
const sessionId = message.target_session_id;
|
|
419
|
+
logger.info(`[SessionRegistry] Joining session ${sessionId} for user ${userId}`);
|
|
420
|
+
try {
|
|
421
|
+
// Validate session access permissions
|
|
422
|
+
const access = await this.validateSessionAccess(sessionId, userId);
|
|
423
|
+
if (!access) {
|
|
424
|
+
throw new errors_1.ChatFatalError(`User ${userId} is not authorized to ` + `join session ${sessionId}`);
|
|
425
|
+
}
|
|
426
|
+
// get or create the session
|
|
427
|
+
const session = await this.getAndActivateSession(sessionId);
|
|
428
|
+
if (!session) {
|
|
429
|
+
// this in theory should not happen
|
|
430
|
+
// since we have validated the access
|
|
431
|
+
throw new errors_1.ChatFatalError(`Server internal error: ` + `failed to load session ${sessionId}`);
|
|
432
|
+
}
|
|
433
|
+
// To this point, there should be no error thrown.
|
|
434
|
+
// Update in-memory session-user/user-session mappings
|
|
435
|
+
this.addUserToSessionMemory(userId, sessionId);
|
|
436
|
+
// Register session immediately
|
|
437
|
+
if (!this.openSessions.has(sessionId)) {
|
|
438
|
+
this.openSessions.set(sessionId, session);
|
|
439
|
+
}
|
|
440
|
+
// pass the message to the session to handle the rest
|
|
441
|
+
session.sendSessionData(connectionId, message.client_message_id);
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
logger.error(`[SessionRegistry] Error handling user join: ${String(error)}`);
|
|
445
|
+
this.sendControlError(connectionId, message.client_message_id, String(error));
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
async handleAgentProfileCreate(message, from) {
|
|
449
|
+
// get agent profile from template
|
|
450
|
+
let agentProfileFromTemplate = undefined;
|
|
451
|
+
if (message.template_name) {
|
|
452
|
+
const template = await this.db.agentTemplateGetByName(message.template_name);
|
|
453
|
+
if (!template) {
|
|
454
|
+
throw new Error(`template ${message.template_name} not found`);
|
|
455
|
+
}
|
|
456
|
+
agentProfileFromTemplate = template.agent_profile;
|
|
457
|
+
}
|
|
458
|
+
const newAgentProfile = agentProfileFromTemplate || {
|
|
459
|
+
model: agentUtils_1.DEFAULT_LLM_MODEL,
|
|
460
|
+
system_prompt: sdk_1.DEFAULT_AGENT_PROFILE_SYSTEM_PROMPT,
|
|
461
|
+
mcp_settings: {},
|
|
462
|
+
};
|
|
463
|
+
const team_uuid = message.team_uuid || undefined;
|
|
464
|
+
if (team_uuid) {
|
|
465
|
+
// TODO: should be able to reconstruct the full SavedAgentProfile in one
|
|
466
|
+
// call.
|
|
467
|
+
const savedAgentProfile = await this.db.createAgentProfile(undefined, team_uuid, message.title, newAgentProfile);
|
|
468
|
+
if (!savedAgentProfile) {
|
|
469
|
+
throw new Error("failed creating team agent profile (createAgentProfile)");
|
|
470
|
+
}
|
|
471
|
+
// Broadcast the new AgentProfile to all participants
|
|
472
|
+
const participants = await this.db.teamGetMembers(team_uuid);
|
|
473
|
+
this.connectionManager.sendToUsers(new Set(participants.map((p) => p.user_uuid)), { type: "control_agent_profile_created", profile: savedAgentProfile });
|
|
474
|
+
return savedAgentProfile.uuid;
|
|
475
|
+
}
|
|
476
|
+
// User AgentProfile
|
|
477
|
+
const savedAgentProfile = await this.db.createAgentProfile(from, undefined, message.title, newAgentProfile);
|
|
478
|
+
if (!savedAgentProfile) {
|
|
479
|
+
throw new Error("failed creating agent profile (createAgentProfile)");
|
|
480
|
+
}
|
|
481
|
+
// Send the new AgentProfile to the user
|
|
482
|
+
this.connectionManager.sendToUsers(new Set([from]), {
|
|
483
|
+
type: "control_agent_profile_created",
|
|
484
|
+
profile: savedAgentProfile,
|
|
485
|
+
});
|
|
486
|
+
return savedAgentProfile.uuid;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Create a new session for a user.
|
|
490
|
+
* - create session in database (via `newSession`)
|
|
491
|
+
* - create an OpenSession instance
|
|
492
|
+
* - return the session info
|
|
493
|
+
*/
|
|
494
|
+
async handleSessionCreate(connectionId, fromUserId, message) {
|
|
495
|
+
try {
|
|
496
|
+
// If agent not given, create one and inform the client
|
|
497
|
+
if (!message.agent_profile_id) {
|
|
498
|
+
logger.info("[handleSessionCreate] creating new AgentProfile");
|
|
499
|
+
// Create AgentProfile and inform the client
|
|
500
|
+
message.agent_profile_id = await this.handleAgentProfileCreate({
|
|
501
|
+
type: "control_agent_profile_create",
|
|
502
|
+
title: "New Agent " + (0, uuid_1.v4)(),
|
|
503
|
+
user_uuid: fromUserId,
|
|
504
|
+
team_uuid: message.team_id,
|
|
505
|
+
}, fromUserId);
|
|
506
|
+
}
|
|
507
|
+
// Create new session and get its instance
|
|
508
|
+
const { openSession, sessionId } = message.team_id
|
|
509
|
+
? await this.newTeamSession(fromUserId, message.team_id, message.title, message.agent_profile_id)
|
|
510
|
+
: await this.newUserSession(fromUserId, message.title, message.agent_profile_id);
|
|
511
|
+
// there should be no further error thrown from now.
|
|
512
|
+
// Register session immediately
|
|
513
|
+
this.openSessions.set(sessionId, openSession);
|
|
514
|
+
// add owner to session memory
|
|
515
|
+
this.addUserToSessionMemory(fromUserId, sessionId);
|
|
516
|
+
// send session info to the connection
|
|
517
|
+
openSession.sendSessionData(connectionId, message.client_message_id);
|
|
518
|
+
logger.info(`[SessionRegistry] new session ${sessionId}:` +
|
|
519
|
+
` ${message.title} for ${fromUserId}`);
|
|
520
|
+
}
|
|
521
|
+
catch (error) {
|
|
522
|
+
const errStr = (0, errorUtils_1.getErrorString)(error);
|
|
523
|
+
logger.error(`[SessionRegistry] Error in session create: ${errStr}`);
|
|
524
|
+
this.sendControlError(connectionId, message.client_message_id, errStr);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Create a new user session.
|
|
529
|
+
*/
|
|
530
|
+
async newUserSession(ownerId, title, agentProfileId) {
|
|
531
|
+
// validate the agent profile
|
|
532
|
+
await this.validateSavedAgentProfile(agentProfileId);
|
|
533
|
+
const newSessionData = {
|
|
534
|
+
...userSessionDataCreate(ownerId, title, agentProfileId),
|
|
535
|
+
updated_at: new Date().toISOString(),
|
|
536
|
+
};
|
|
537
|
+
const openSession = await openSession_1.OpenSession.initWithEmptySession(this.db, newSessionData, this.llmUrl, this.xmcpUrl, this.connectionManager);
|
|
538
|
+
return { sessionId: newSessionData.session_uuid, openSession };
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Create a new team session.
|
|
542
|
+
*/
|
|
543
|
+
async newTeamSession(fromUserId, teamId, title, agentProfileId) {
|
|
544
|
+
// validate agent profile and team access
|
|
545
|
+
const [_savedAgentProfile, access] = await Promise.all([
|
|
546
|
+
this.validateSavedAgentProfile(agentProfileId),
|
|
547
|
+
this.validateTeamAccess(teamId, fromUserId),
|
|
548
|
+
]);
|
|
549
|
+
if (!access) {
|
|
550
|
+
throw new errors_1.ChatFatalError(`User ${fromUserId} is not a participant of team ${teamId}`);
|
|
551
|
+
}
|
|
552
|
+
const newSessionData = {
|
|
553
|
+
...teamSessionDataCreate(teamId, fromUserId, title, agentProfileId),
|
|
554
|
+
updated_at: new Date().toISOString(),
|
|
555
|
+
};
|
|
556
|
+
// initialize the open session
|
|
557
|
+
const openSession = await openSession_1.OpenSession.initWithEmptySession(this.db, newSessionData, this.llmUrl, this.xmcpUrl, this.connectionManager);
|
|
558
|
+
return { sessionId: newSessionData.session_uuid, openSession };
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Gracefully shutdown all sessions and clean up resources.
|
|
562
|
+
*/
|
|
563
|
+
shutdown() {
|
|
564
|
+
logger.info(`[SessionRegistry] Shutting down ` +
|
|
565
|
+
String(this.openSessions.size) +
|
|
566
|
+
` sessions`);
|
|
567
|
+
// Create list of sessions to avoid concurrent modification
|
|
568
|
+
const sessionsToClose = Array.from(this.openSessions.keys());
|
|
569
|
+
for (const sessionId of sessionsToClose) {
|
|
570
|
+
const session = this.openSessions.get(sessionId);
|
|
571
|
+
if (session) {
|
|
572
|
+
logger.debug(`[SessionRegistry] Closing session ${sessionId}`);
|
|
573
|
+
try {
|
|
574
|
+
// Trigger the session's cleanup
|
|
575
|
+
session.onEmpty();
|
|
576
|
+
}
|
|
577
|
+
catch (error) {
|
|
578
|
+
logger.error(`[SessionRegistry] Error closing session ${sessionId}:`, error);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
// Clear all maps
|
|
583
|
+
this.sessionUsers.clear();
|
|
584
|
+
this.userSessions.clear();
|
|
585
|
+
this.openSessions.clear();
|
|
586
|
+
logger.info(`[SessionRegistry] Shutdown complete`);
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Handle user disconnect - clean up from all sessions.
|
|
590
|
+
* Called when a connection is closed to ensure proper cleanup.
|
|
591
|
+
*/
|
|
592
|
+
handleUserDisconnect(userId) {
|
|
593
|
+
logger.info(`[SessionRegistry] Handling disconnect for user ${userId}`);
|
|
594
|
+
// Get all sessions the user is in (copy to avoid concurrent modification)
|
|
595
|
+
const userSessionIds = this.getInMemoryUserSessions(userId);
|
|
596
|
+
const sessionsToLeave = Array.from(userSessionIds);
|
|
597
|
+
// Remove user from each session
|
|
598
|
+
for (const sessionId of sessionsToLeave) {
|
|
599
|
+
this.removeUserFromSessionMemory(userId, sessionId);
|
|
600
|
+
}
|
|
601
|
+
logger.info(`[SessionRegistry] User ${userId} removed` +
|
|
602
|
+
` from ${String(sessionsToLeave.length)} sessions`);
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Get all sessions for a user,
|
|
606
|
+
* including user solo sessions and team sessions.
|
|
607
|
+
* This will also create a default agent profile if none exists.
|
|
608
|
+
*/
|
|
609
|
+
async getAllAgentsAndSessionsByUser(userId) {
|
|
610
|
+
return Promise.all([
|
|
611
|
+
this.db.getUserSessions(userId),
|
|
612
|
+
this.db.getTeamInfosByUser(userId),
|
|
613
|
+
this.agentProfilesGetByUserOrDefault(userId),
|
|
614
|
+
]);
|
|
615
|
+
}
|
|
616
|
+
async agentProfilesGetByUserOrDefault(userId) {
|
|
617
|
+
const agentProfiles = await this.db.agentProfilesGetByUser(userId);
|
|
618
|
+
if (agentProfiles.length === 0) {
|
|
619
|
+
const profileName = sdk_1.DEFAULT_AGENT_PROFILE_NAME;
|
|
620
|
+
const profile = await this.db.createAgentProfile(userId, undefined, profileName, sdk_1.DEFAULT_AGENT_PROFILE);
|
|
621
|
+
if (!profile) {
|
|
622
|
+
throw new Error(`No such agent profile: ${profileName}`);
|
|
623
|
+
}
|
|
624
|
+
return [profile];
|
|
625
|
+
}
|
|
626
|
+
return agentProfiles;
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Validates that an agent profile exists in the database.
|
|
630
|
+
*/
|
|
631
|
+
async validateSavedAgentProfile(agentProfileId) {
|
|
632
|
+
const savedAgentProfile = await this.db.getSavedAgentProfileById(agentProfileId);
|
|
633
|
+
if (!savedAgentProfile) {
|
|
634
|
+
throw new errors_1.ChatFatalError(`No such agent profile: ${agentProfileId}`);
|
|
635
|
+
}
|
|
636
|
+
return savedAgentProfile;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Validates that a user has permission to access a session.
|
|
640
|
+
*/
|
|
641
|
+
async validateSessionAccess(sessionId, userId, accessType) {
|
|
642
|
+
const session = this.openSessions.get(sessionId);
|
|
643
|
+
if (session) {
|
|
644
|
+
// in memory session
|
|
645
|
+
const participants = session.getParticipants();
|
|
646
|
+
const role = participants.get(userId);
|
|
647
|
+
if (!role) {
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
return !accessType || role.role === accessType;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
// fetch the session from database
|
|
656
|
+
const participants = await this.db.sessionGetParticipants(sessionId);
|
|
657
|
+
const role = participants.find((p) => p.user_uuid === userId)?.role;
|
|
658
|
+
if (!role) {
|
|
659
|
+
return false;
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
return !accessType || role === accessType;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Validates that a user has permission to access a team.
|
|
668
|
+
*/
|
|
669
|
+
async validateTeamAccess(teamId, userId, accessType) {
|
|
670
|
+
const participants = await this.db.teamGetMembers(teamId);
|
|
671
|
+
const role = participants.find((p) => p.user_uuid === userId)?.role;
|
|
672
|
+
if (!role) {
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
else {
|
|
676
|
+
return !accessType || role === accessType;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
exports.SessionRegistry = SessionRegistry;
|
|
681
|
+
function userSessionDataCreate(ownerId, title, agentProfileId) {
|
|
682
|
+
return {
|
|
683
|
+
session_uuid: database_1.Database.sessionNewUUID(),
|
|
684
|
+
title,
|
|
685
|
+
team_uuid: undefined,
|
|
686
|
+
agent_profile_uuid: agentProfileId,
|
|
687
|
+
workspace: undefined,
|
|
688
|
+
user_uuid: ownerId,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
function teamSessionDataCreate(teamId, ownerId, title, agentProfileId) {
|
|
692
|
+
return {
|
|
693
|
+
session_uuid: database_1.Database.sessionNewUUID(),
|
|
694
|
+
title,
|
|
695
|
+
team_uuid: teamId,
|
|
696
|
+
agent_profile_uuid: agentProfileId,
|
|
697
|
+
workspace: undefined,
|
|
698
|
+
user_uuid: ownerId,
|
|
699
|
+
};
|
|
700
|
+
}
|