@xalia/agent 0.5.8 → 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.
Files changed (185) hide show
  1. package/README.md +23 -8
  2. package/dist/agent/src/agent/agent.js +173 -96
  3. package/dist/agent/src/agent/agentUtils.js +82 -53
  4. package/dist/agent/src/agent/compressingContextManager.js +102 -0
  5. package/dist/agent/src/agent/context.js +189 -0
  6. package/dist/agent/src/agent/dummyLLM.js +46 -5
  7. package/dist/agent/src/agent/iAgentEventHandler.js +2 -0
  8. package/dist/agent/src/agent/mcpServerManager.js +22 -23
  9. package/dist/agent/src/agent/nullAgentEventHandler.js +21 -0
  10. package/dist/agent/src/agent/nullPlatform.js +14 -0
  11. package/dist/agent/src/agent/openAILLMStreaming.js +12 -7
  12. package/dist/agent/src/agent/promptProvider.js +63 -0
  13. package/dist/agent/src/agent/repeatLLM.js +5 -5
  14. package/dist/agent/src/agent/sudoMcpServerManager.js +11 -9
  15. package/dist/agent/src/agent/tokenAuth.js +7 -7
  16. package/dist/agent/src/agent/tools.js +1 -1
  17. package/dist/agent/src/chat/client/chatClient.js +733 -0
  18. package/dist/agent/src/chat/client/connection.js +209 -0
  19. package/dist/agent/src/chat/client/connection.test.js +188 -0
  20. package/dist/agent/src/chat/client/constants.js +5 -0
  21. package/dist/agent/src/chat/client/index.js +15 -0
  22. package/dist/agent/src/chat/client/interfaces.js +2 -0
  23. package/dist/agent/src/chat/client/responseHandler.js +105 -0
  24. package/dist/agent/src/chat/client/sessionClient.js +331 -0
  25. package/dist/agent/src/chat/client/teamManager.js +2 -0
  26. package/dist/agent/src/chat/{apiKeyManager.js → data/apiKeyManager.js} +4 -0
  27. package/dist/agent/src/chat/data/dataModels.js +2 -0
  28. package/dist/agent/src/chat/data/database.js +749 -0
  29. package/dist/agent/src/chat/data/dbMcpServerConfigs.js +47 -0
  30. package/dist/agent/src/chat/protocol/connectionMessages.js +5 -0
  31. package/dist/agent/src/chat/protocol/constants.js +50 -0
  32. package/dist/agent/src/chat/protocol/errors.js +22 -0
  33. package/dist/agent/src/chat/protocol/messages.js +110 -0
  34. package/dist/agent/src/chat/server/chatContextManager.js +405 -0
  35. package/dist/agent/src/chat/server/connectionManager.js +352 -0
  36. package/dist/agent/src/chat/server/connectionManager.test.js +159 -0
  37. package/dist/agent/src/chat/server/conversation.js +198 -0
  38. package/dist/agent/src/chat/server/errorUtils.js +23 -0
  39. package/dist/agent/src/chat/server/openSession.js +869 -0
  40. package/dist/agent/src/chat/server/server.js +177 -0
  41. package/dist/agent/src/chat/server/sessionFileManager.js +161 -0
  42. package/dist/agent/src/chat/server/sessionRegistry.js +700 -0
  43. package/dist/agent/src/chat/server/sessionRegistry.test.js +97 -0
  44. package/dist/agent/src/chat/server/test-utils/mockFactories.js +307 -0
  45. package/dist/agent/src/chat/server/tools.js +243 -0
  46. package/dist/agent/src/chat/utils/agentSessionMap.js +66 -0
  47. package/dist/agent/src/chat/utils/approvalManager.js +85 -0
  48. package/dist/agent/src/{utils → chat/utils}/asyncLock.js +3 -3
  49. package/dist/agent/src/chat/{asyncQueue.js → utils/asyncQueue.js} +12 -2
  50. package/dist/agent/src/chat/utils/htmlToText.js +84 -0
  51. package/dist/agent/src/chat/utils/multiAsyncQueue.js +42 -0
  52. package/dist/agent/src/chat/utils/search.js +145 -0
  53. package/dist/agent/src/chat/utils/userResolver.js +46 -0
  54. package/dist/agent/src/chat/{websocket.js → utils/websocket.js} +2 -0
  55. package/dist/agent/src/test/agent.test.js +332 -0
  56. package/dist/agent/src/test/approvalManager.test.js +58 -0
  57. package/dist/agent/src/test/chatContextManager.test.js +392 -0
  58. package/dist/agent/src/test/clientServerConnection.test.js +158 -0
  59. package/dist/agent/src/test/compressingContextManager.test.js +65 -0
  60. package/dist/agent/src/test/context.test.js +83 -0
  61. package/dist/agent/src/test/conversation.test.js +89 -0
  62. package/dist/agent/src/test/db.test.js +262 -90
  63. package/dist/agent/src/test/dbMcpServerConfigs.test.js +72 -0
  64. package/dist/agent/src/test/dbTestTools.js +99 -0
  65. package/dist/agent/src/test/imageLoad.test.js +8 -7
  66. package/dist/agent/src/test/mcpServerManager.test.js +21 -18
  67. package/dist/agent/src/test/multiAsyncQueue.test.js +101 -0
  68. package/dist/agent/src/test/openaiStreaming.test.js +12 -11
  69. package/dist/agent/src/test/prompt.test.js +5 -4
  70. package/dist/agent/src/test/promptProvider.test.js +28 -0
  71. package/dist/agent/src/test/responseHandler.test.js +61 -0
  72. package/dist/agent/src/test/sudoMcpServerManager.test.js +14 -12
  73. package/dist/agent/src/test/testTools.js +109 -0
  74. package/dist/agent/src/test/tools.test.js +31 -0
  75. package/dist/agent/src/tool/agentChat.js +21 -10
  76. package/dist/agent/src/tool/agentMain.js +1 -1
  77. package/dist/agent/src/tool/chatMain.js +235 -58
  78. package/dist/agent/src/tool/commandPrompt.js +15 -9
  79. package/dist/agent/src/tool/files.js +20 -16
  80. package/dist/agent/src/tool/nodePlatform.js +47 -3
  81. package/dist/agent/src/tool/options.js +4 -4
  82. package/dist/agent/src/tool/prompt.js +19 -13
  83. package/eslint.config.mjs +14 -1
  84. package/package.json +14 -6
  85. package/scripts/chat_server +8 -0
  86. package/scripts/setup_chat +7 -2
  87. package/scripts/shutdown_chat_server +3 -0
  88. package/scripts/test_chat +135 -17
  89. package/src/agent/agent.ts +270 -135
  90. package/src/agent/agentUtils.ts +136 -95
  91. package/src/agent/compressingContextManager.ts +164 -0
  92. package/src/agent/context.ts +268 -0
  93. package/src/agent/dummyLLM.ts +76 -8
  94. package/src/agent/iAgentEventHandler.ts +54 -0
  95. package/src/agent/iplatform.ts +1 -0
  96. package/src/agent/mcpServerManager.ts +32 -30
  97. package/src/agent/nullAgentEventHandler.ts +20 -0
  98. package/src/agent/nullPlatform.ts +13 -0
  99. package/src/agent/openAILLMStreaming.ts +12 -6
  100. package/src/agent/promptProvider.ts +87 -0
  101. package/src/agent/repeatLLM.ts +5 -5
  102. package/src/agent/sudoMcpServerManager.ts +13 -11
  103. package/src/agent/tokenAuth.ts +7 -7
  104. package/src/agent/tools.ts +3 -1
  105. package/src/chat/client/chatClient.ts +900 -0
  106. package/src/chat/client/connection.test.ts +241 -0
  107. package/src/chat/client/connection.ts +276 -0
  108. package/src/chat/client/constants.ts +3 -0
  109. package/src/chat/client/index.ts +18 -0
  110. package/src/chat/client/interfaces.ts +34 -0
  111. package/src/chat/client/responseHandler.ts +131 -0
  112. package/src/chat/client/sessionClient.ts +443 -0
  113. package/src/chat/client/teamManager.ts +29 -0
  114. package/src/chat/{apiKeyManager.ts → data/apiKeyManager.ts} +6 -2
  115. package/src/chat/data/dataModels.ts +85 -0
  116. package/src/chat/data/database.ts +982 -0
  117. package/src/chat/data/dbMcpServerConfigs.ts +59 -0
  118. package/src/chat/protocol/connectionMessages.ts +49 -0
  119. package/src/chat/protocol/constants.ts +55 -0
  120. package/src/chat/protocol/errors.ts +16 -0
  121. package/src/chat/protocol/messages.ts +682 -0
  122. package/src/chat/server/README.md +127 -0
  123. package/src/chat/server/chatContextManager.ts +612 -0
  124. package/src/chat/server/connectionManager.test.ts +266 -0
  125. package/src/chat/server/connectionManager.ts +541 -0
  126. package/src/chat/server/conversation.ts +269 -0
  127. package/src/chat/server/errorUtils.ts +28 -0
  128. package/src/chat/server/openSession.ts +1332 -0
  129. package/src/chat/server/server.ts +177 -0
  130. package/src/chat/server/sessionFileManager.ts +239 -0
  131. package/src/chat/server/sessionRegistry.test.ts +138 -0
  132. package/src/chat/server/sessionRegistry.ts +1064 -0
  133. package/src/chat/server/test-utils/mockFactories.ts +422 -0
  134. package/src/chat/server/tools.ts +265 -0
  135. package/src/chat/utils/agentSessionMap.ts +76 -0
  136. package/src/chat/utils/approvalManager.ts +111 -0
  137. package/src/{utils → chat/utils}/asyncLock.ts +3 -3
  138. package/src/chat/{asyncQueue.ts → utils/asyncQueue.ts} +14 -3
  139. package/src/chat/utils/htmlToText.ts +61 -0
  140. package/src/chat/utils/multiAsyncQueue.ts +52 -0
  141. package/src/chat/utils/search.ts +139 -0
  142. package/src/chat/utils/userResolver.ts +48 -0
  143. package/src/chat/{websocket.ts → utils/websocket.ts} +2 -0
  144. package/src/test/agent.test.ts +487 -0
  145. package/src/test/approvalManager.test.ts +73 -0
  146. package/src/test/chatContextManager.test.ts +521 -0
  147. package/src/test/clientServerConnection.test.ts +207 -0
  148. package/src/test/compressingContextManager.test.ts +82 -0
  149. package/src/test/context.test.ts +105 -0
  150. package/src/test/conversation.test.ts +109 -0
  151. package/src/test/db.test.ts +351 -103
  152. package/src/test/dbMcpServerConfigs.test.ts +112 -0
  153. package/src/test/dbTestTools.ts +153 -0
  154. package/src/test/imageLoad.test.ts +7 -6
  155. package/src/test/mcpServerManager.test.ts +19 -14
  156. package/src/test/multiAsyncQueue.test.ts +125 -0
  157. package/src/test/openaiStreaming.test.ts +11 -10
  158. package/src/test/prompt.test.ts +4 -3
  159. package/src/test/promptProvider.test.ts +33 -0
  160. package/src/test/responseHandler.test.ts +78 -0
  161. package/src/test/sudoMcpServerManager.test.ts +22 -15
  162. package/src/test/testTools.ts +146 -0
  163. package/src/test/tools.test.ts +39 -0
  164. package/src/tool/agentChat.ts +26 -12
  165. package/src/tool/agentMain.ts +1 -1
  166. package/src/tool/chatMain.ts +283 -100
  167. package/src/tool/commandPrompt.ts +25 -9
  168. package/src/tool/files.ts +25 -19
  169. package/src/tool/nodePlatform.ts +52 -3
  170. package/src/tool/options.ts +4 -2
  171. package/src/tool/prompt.ts +22 -15
  172. package/test_data/dummyllm_script_crash.json +32 -0
  173. package/test_data/frog.png.b64 +1 -0
  174. package/vitest.config.ts +39 -0
  175. package/dist/agent/src/chat/client.js +0 -310
  176. package/dist/agent/src/chat/conversationManager.js +0 -502
  177. package/dist/agent/src/chat/db.js +0 -218
  178. package/dist/agent/src/chat/messages.js +0 -29
  179. package/dist/agent/src/chat/server.js +0 -158
  180. package/src/chat/client.ts +0 -445
  181. package/src/chat/conversationManager.ts +0 -730
  182. package/src/chat/db.ts +0 -304
  183. package/src/chat/messages.ts +0 -266
  184. package/src/chat/server.ts +0 -177
  185. /package/{frog.png → test_data/frog.png} +0 -0
@@ -0,0 +1,869 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenSession = exports.DEFAULT_NUM_MESSGAES = void 0;
4
+ const assert_1 = require("assert");
5
+ const sdk_1 = require("@xalia/xmcp/sdk");
6
+ const agent_1 = require("../../agent/agent");
7
+ const agentUtils_1 = require("../../agent/agentUtils");
8
+ const asyncQueue_1 = require("../utils/asyncQueue");
9
+ const multiAsyncQueue_1 = require("../utils/multiAsyncQueue");
10
+ const database_1 = require("../data/database");
11
+ const approvalManager_1 = require("../utils/approvalManager");
12
+ const errors_1 = require("../protocol/errors");
13
+ const tools_1 = require("./tools");
14
+ const chatContextManager_1 = require("./chatContextManager");
15
+ const conversation_1 = require("./conversation");
16
+ const sessionFileManager_1 = require("./sessionFileManager");
17
+ const errorUtils_1 = require("./errorUtils");
18
+ const nodePlatform_1 = require("../../tool/nodePlatform");
19
+ const dbMcpServerConfigs_1 = require("../data/dbMcpServerConfigs");
20
+ /**
21
+ * Num messages to load at conversation startup.
22
+ */
23
+ exports.DEFAULT_NUM_MESSGAES = 500;
24
+ const logger = (0, sdk_1.getLogger)();
25
+ /**
26
+ * Implementation of ICheckpointWriter that writes into the DB.
27
+ */
28
+ class DBCheckpointWriter {
29
+ constructor(db, sessionId) {
30
+ this.db = db;
31
+ this.sessionId = sessionId;
32
+ }
33
+ writeCheckpoint(checkpoint) {
34
+ logger.info(`[DBCheckpointWriter] writing (session ${this.sessionId}):\n` +
35
+ JSON.stringify(checkpoint));
36
+ return this.db.sessionCheckpointSet(this.sessionId, checkpoint);
37
+ }
38
+ }
39
+ /**
40
+ * Describes a Session (conversation) with connected participants.
41
+ *
42
+ * We maintain 2 queues - one for all incoming messages from clients, and
43
+ * another for user messages (to be gathered and sent to the Agent). This
44
+ * allows processing of user messages to depend on messages from clients
45
+ * (using a single queue would not allow this, since later messages could not
46
+ * be seen until user messages had been fully processed, which could block
47
+ * tool approvals and other interactions).
48
+ */
49
+ class OpenSession {
50
+ constructor(db, agent, sessionData, savedAgentProfile, isPersisted, sessionParticipants, agentProfilePreferences, skillManager, contextManager, connectionManager, approvalManager = new approvalManager_1.ApprovalManager()) {
51
+ this.db = db;
52
+ this.agent = agent;
53
+ this.sessionUUID = sessionData.session_uuid;
54
+ this.teamUUID = sessionData.team_uuid;
55
+ this.userUUID = sessionData.user_uuid;
56
+ this.agentProfileUUID = sessionData.agent_profile_uuid;
57
+ this.sessionParticipants = sessionParticipants;
58
+ this.agentProfilePreferences = agentProfilePreferences;
59
+ this.skillManager = skillManager;
60
+ this.connectionManager = connectionManager;
61
+ this.messageQueue = new asyncQueue_1.AsyncQueue((m) => this.processMessage(m));
62
+ this.userMessageQueue = new multiAsyncQueue_1.MultiAsyncQueue((m) => this.processUserMessages(m));
63
+ this.contextManager = contextManager;
64
+ this.approvalManager = approvalManager;
65
+ this.savedAgentProfile = savedAgentProfile;
66
+ this.isPersisted = isPersisted;
67
+ this.sessionTitle = sessionData.title;
68
+ this.sessionUpdatedAt = sessionData.updated_at;
69
+ }
70
+ static async init(db, sessionData, savedAgentProfile, sessionMessages, sessionParticipants, ownerData, ownerApiKey, sessionCheckpoint, llmUrl, xmcpUrl, connectionManager) {
71
+ const sessionId = sessionData.session_uuid;
72
+ const fileManager = new sessionFileManager_1.ChatSessionFileManager(sessionId, db);
73
+ const contextManager = new chatContextManager_1.ChatContextManager(savedAgentProfile.profile.system_prompt, sessionMessages, sessionId, ownerData.uuid, sessionCheckpoint, llmUrl, savedAgentProfile.profile.model || agentUtils_1.DEFAULT_LLM_MODEL, ownerApiKey, new DBCheckpointWriter(db, sessionId), fileManager);
74
+ if (sessionData.workspace) {
75
+ const ws = sessionData.workspace;
76
+ contextManager.setWorkspace((0, agent_1.createUserMessage)(ws.message, ws.imageB64));
77
+ }
78
+ const openSession = new OpenSession(db, {}, // Placeholder - will be replaced after agent creation
79
+ sessionData, savedAgentProfile, false /* isPersisted */, sessionParticipants, savedAgentProfile.preferences, {}, // Placeholder - will be replaced after agent creation
80
+ contextManager, connectionManager);
81
+ // Initialize an empty agent (to ensure there are no callbacks before
82
+ // the OpenSession and client are fully set up).
83
+ const xmcpConfig = sdk_1.Configuration.new(ownerApiKey, xmcpUrl, false);
84
+ const [agent, skillManager] = await (0, agentUtils_1.createAgentWithoutSkills)(llmUrl, savedAgentProfile.profile, openSession, openSession, contextManager, ownerApiKey, xmcpConfig, undefined, true);
85
+ await (0, tools_1.addDefaultChatTools)(agent, ownerData.timezone, openSession);
86
+ // Update OpenSession with real agent and skillManager
87
+ openSession.agent = agent;
88
+ openSession.skillManager = skillManager;
89
+ await openSession.restoreMcpSettings(savedAgentProfile.profile.mcp_settings);
90
+ return openSession;
91
+ }
92
+ static async initWithEmptySession(db, sessionData, llmUrl, xmcpUrl, connectionManager) {
93
+ const sessionMessages = [];
94
+ const sessionCheckpoint = undefined;
95
+ const { savedAgentProfile, ownerData, ownerApiKey } = await loadDataForEmptySession(db, sessionData.agent_profile_uuid, sessionData.user_uuid);
96
+ if (!ownerApiKey) {
97
+ throw new errors_1.ChatErrorMessage("failed finding owners default api key");
98
+ }
99
+ const sessionParticipants = new Map();
100
+ sessionParticipants.set(sessionData.user_uuid, {
101
+ user_uuid: sessionData.user_uuid,
102
+ nickname: ownerData.nickname || "",
103
+ email: ownerData.email,
104
+ role: "owner",
105
+ });
106
+ return OpenSession.init(db, sessionData, savedAgentProfile, sessionMessages, sessionParticipants, ownerData, ownerApiKey, sessionCheckpoint, llmUrl, xmcpUrl, connectionManager);
107
+ }
108
+ static async initWithExistingSession(db, sessionId, llmUrl, xmcpUrl, connectionManager) {
109
+ // Load session data from database
110
+ const { sessionData, sessionMessages, sessionCheckpoint, savedAgentProfile, sessionParticipants, ownerData, ownerApiKey, } = await loadSessionData(db, sessionId);
111
+ return OpenSession.init(db, sessionData, savedAgentProfile, sessionMessages, sessionParticipants, ownerData, ownerApiKey, sessionCheckpoint, llmUrl, xmcpUrl, connectionManager);
112
+ }
113
+ onEmpty() {
114
+ logger.info(`[OpenSession.onEmpty] session ${this.sessionUUID}`);
115
+ }
116
+ getParticipants() {
117
+ return this.sessionParticipants;
118
+ }
119
+ sendSessionData(connectionId, clientMessageId) {
120
+ logger.info(`[SessionRegistry] sending session data for session ${this.sessionUUID}`);
121
+ const sessionInfo = this.serverSessionInfo(clientMessageId);
122
+ this.connectionManager.sendToConnection(connectionId, sessionInfo);
123
+ // send conversation history
124
+ const conversationMessages = this.contextManager.getConversationMessages();
125
+ conversationMessages.forEach((message) => {
126
+ this.connectionManager.sendToConnection(connectionId, message);
127
+ });
128
+ // add MCP settings
129
+ this.sendMcpSettings(connectionId);
130
+ // add system prompt and model
131
+ const agentProfile = this.agent.getAgentProfile();
132
+ this.connectionManager.sendToConnection(connectionId, {
133
+ type: "system_prompt_updated",
134
+ system_prompt: agentProfile.system_prompt,
135
+ session_id: this.sessionUUID,
136
+ });
137
+ this.connectionManager.sendToConnection(connectionId, {
138
+ type: "model_updated",
139
+ model: agentProfile.model || "",
140
+ session_id: this.sessionUUID,
141
+ });
142
+ }
143
+ /**
144
+ * Restore MCP settings from the database. We only broadcast when
145
+ * error occurs. The sending of MCP settings is handled by the
146
+ * the callers of `getMcpSettings` method.
147
+ * @param mcpServerSettings - MCP server settings from the database.
148
+ */
149
+ async restoreMcpSettings(mcpServerSettings) {
150
+ // TODO: SkillManager.restoreSessings could support a callback to handle
151
+ // the errors, then we don't need to reimplement the logic.
152
+ // Add each server. We handle any errors so that conversations may
153
+ // continue even if not all MCP servers can be enabled (e.g. if the owner
154
+ // is not present to refresh an oauth token).
155
+ logger.info(`[OpenSession] restoring MCP settings: ` +
156
+ JSON.stringify(mcpServerSettings));
157
+ const skillManager = this.skillManager;
158
+ for (const server_name in mcpServerSettings) {
159
+ try {
160
+ // TODO: the "[] => all-tools" logic is repeated here, in
161
+ // `sendSessionData` and in McpServerSettings. SkillManager could
162
+ // expose a method `expandMcpServerSettings` which removes missing
163
+ // elements and expands
164
+ // Handle the case where the mcp server no longer exists
165
+ if (!skillManager.hasServerBrief(server_name)) {
166
+ throw new Error(`unknown mcp server: ${server_name}`);
167
+ }
168
+ // Handle `[]` meaning "all tools"
169
+ let enabled_tools = mcpServerSettings[server_name];
170
+ if (enabled_tools.length === 0) {
171
+ const allTools = await skillManager.getServerTools(server_name);
172
+ enabled_tools = allTools.map((t) => t.name);
173
+ }
174
+ await skillManager.addMcpServer(server_name, false);
175
+ for (const enabled_tool of enabled_tools) {
176
+ skillManager.enableTool(server_name, enabled_tool);
177
+ }
178
+ }
179
+ catch (e) {
180
+ this.broadcast({
181
+ type: "session_error",
182
+ message: `Error adding MCP server ${server_name}: ${String(e)}`,
183
+ session_id: this.sessionUUID,
184
+ });
185
+ logger.error(`Error adding MCP server ${server_name}: ${String(e)}`);
186
+ }
187
+ }
188
+ }
189
+ /**
190
+ * Intended to be called within specific `catch` blocks, defining a
191
+ * consistent error handling approach. If the exception can be handled it
192
+ * is and `true` is returned. Otherwise false is returned (and the caller
193
+ * is expected to re-throw if they are unable to handle it).
194
+ */
195
+ handleError(err, from) {
196
+ const sendError = (msg) => {
197
+ if (from) {
198
+ this.sendTo(from, msg);
199
+ }
200
+ else {
201
+ this.broadcast(msg);
202
+ }
203
+ };
204
+ if (err instanceof errors_1.ChatFatalError) {
205
+ // ChatFatalError in session context should send error to specific user
206
+ // or broadcast if no specific user context
207
+ sendError({
208
+ type: "session_error",
209
+ message: err.message,
210
+ session_id: this.sessionUUID,
211
+ });
212
+ }
213
+ else if (err instanceof errors_1.ChatErrorMessage) {
214
+ sendError({
215
+ type: "session_error",
216
+ message: err.message,
217
+ session_id: this.sessionUUID,
218
+ });
219
+ }
220
+ else {
221
+ const errString = (0, errorUtils_1.getErrorString)(err);
222
+ sendError({
223
+ type: "session_error",
224
+ message: errString,
225
+ session_id: this.sessionUUID,
226
+ });
227
+ }
228
+ return true;
229
+ }
230
+ broadcast(msg) {
231
+ const users = new Set(this.sessionParticipants.keys());
232
+ this.connectionManager.sendToUsers(users, msg);
233
+ }
234
+ sendTo(user_uuid, msg) {
235
+ this.connectionManager.sendToUsers(new Set([user_uuid]), msg);
236
+ }
237
+ // IPlatform.openUrl
238
+ openUrl(url, authResultP, display_name) {
239
+ // These requests are always passed to the original owner, since it is
240
+ // their settings that will be used for all MCP servers.
241
+ this.broadcast({
242
+ type: "authentication_started",
243
+ session_id: this.sessionUUID,
244
+ url,
245
+ });
246
+ this.sendTo(this.userUUID, {
247
+ type: "authenticate",
248
+ session_id: this.sessionUUID,
249
+ url,
250
+ display_name,
251
+ });
252
+ // TODO: auth timeout
253
+ // Don't stall this function waiting for authentication
254
+ void authResultP.then((result) => {
255
+ this.sendTo(this.userUUID, {
256
+ type: "authentication_finished",
257
+ session_id: this.sessionUUID,
258
+ url,
259
+ result,
260
+ });
261
+ });
262
+ }
263
+ // IPlatform.load
264
+ load(filename) {
265
+ if (process.env.DEVELOPMENT === "1") {
266
+ return nodePlatform_1.NODE_PLATFORM.load(filename);
267
+ }
268
+ throw new errors_1.ChatErrorMessage("Platform.load not implemented");
269
+ }
270
+ // IPlatform.renderHTML
271
+ renderHTML(html) {
272
+ return new Promise((r) => {
273
+ this.broadcast({
274
+ type: "render_html",
275
+ html,
276
+ session_id: this.sessionUUID,
277
+ });
278
+ r();
279
+ });
280
+ }
281
+ // IAgentEventHandler.onCompletion
282
+ onCompletion(result) {
283
+ logger.debug(`[OpenSession.onCompletion] : ${JSON.stringify(result)}`);
284
+ // Nothing to broadcast. Caller will receive this via onAgentMessage.
285
+ this.contextManager.processAgentResponse(result);
286
+ }
287
+ // IAgentEventHandler.onToolCallResult
288
+ onToolCallResult(result) {
289
+ logger.debug(`[onToolCallResult] : ${JSON.stringify(result)}`);
290
+ const toolCallMessage = this.contextManager.processToolCallResult(result);
291
+ this.broadcast(toolCallMessage);
292
+ }
293
+ // IAgentEventHandler.onToolCall
294
+ async onToolCall(toolCall, agentTool) {
295
+ if (agentTool) {
296
+ // "Agent" tools are considered internal to the agent, and are always
297
+ // permitted. Inform all clients and immediately approve.
298
+ this.broadcast({
299
+ type: "tool_call",
300
+ tool_call: toolCall,
301
+ session_id: this.sessionUUID,
302
+ });
303
+ return true;
304
+ }
305
+ // TODO: Need a proper mapping to/from MCP calls to tool names
306
+ const [serverName, tool] = toolCall.function.name.split("__");
307
+ const autoApproved = (0, sdk_1.prefsGetAutoApprove)(this.agentProfilePreferences, serverName, tool);
308
+ if (!autoApproved) {
309
+ const { id, resultP } = this.approvalManager.startApproval(toolCall.function.name);
310
+ this.broadcast({
311
+ type: "approve_tool_call",
312
+ id,
313
+ tool_call: toolCall,
314
+ session_id: this.sessionUUID,
315
+ });
316
+ try {
317
+ logger.debug(`[OpenSession.onToolCall] awaiting approval ${id}`);
318
+ const { approved, auto_approve } = await resultP;
319
+ logger.debug(`[OpenSession.onToolCall] approval ${id}: ${String(approved)}`);
320
+ if (auto_approve) {
321
+ logger.debug("[OpenSession.onToolCall] auto_approve set. updated preferences");
322
+ const autoApprovalMsg = await this.onSetAutoApproval(serverName, tool, true);
323
+ if (autoApprovalMsg) {
324
+ this.broadcast(autoApprovalMsg);
325
+ }
326
+ }
327
+ return approved;
328
+ }
329
+ catch (e) {
330
+ logger.debug(`[OpenSession.onToolCall] error waiting for approval ${id}: ` +
331
+ String(e));
332
+ return false;
333
+ }
334
+ }
335
+ else {
336
+ this.broadcast({
337
+ type: "tool_call",
338
+ tool_call: toolCall,
339
+ session_id: this.sessionUUID,
340
+ });
341
+ return true;
342
+ }
343
+ }
344
+ // IAgentEventHandler.onAgentMessage
345
+ // eslint-disable-next-line @typescript-eslint/require-await
346
+ async onAgentMessage(msg, end) {
347
+ logger.debug(`[OpenSession.onAgentMessage] msg: ${msg}, end: ${String(end)}`);
348
+ // Inform the contextManager and broadcast the ServerAgentMessageChunk
349
+ const agentMsgChunk = this.contextManager.processAgentMessage(msg, end);
350
+ this.broadcast(agentMsgChunk);
351
+ }
352
+ /**
353
+ * Handle incoming session-related message from client.
354
+ * These messages include tool calls, MCP changes, etc.
355
+ * These messages are processed by `messageQueue`.
356
+ */
357
+ onClientSessionMessage(from, message) {
358
+ logger.info(`[OpenSession] Message from ${from} in session ` +
359
+ `${this.sessionUUID}: ${JSON.stringify(message)}`);
360
+ // Validate user is in session
361
+ if (!this.sessionParticipants.get(from)) {
362
+ logger.warn(`User ${from} not in session ${this.sessionUUID} - ignoring message`);
363
+ this.sendTo(from, {
364
+ type: "session_error",
365
+ message: "You are not a participant in this session",
366
+ session_id: this.sessionUUID,
367
+ client_message_id: message.client_message_id,
368
+ });
369
+ return;
370
+ }
371
+ // Enqueue message for processing
372
+ if (!this.messageQueue.tryEnqueue({ msg: message, from })) {
373
+ this.sendTo(from, this.addSessionContext({
374
+ type: "session_error",
375
+ message: "message queue full. try again later",
376
+ client_message_id: message.client_message_id,
377
+ }));
378
+ }
379
+ }
380
+ /**
381
+ * Get MCP settings in the form of `ServerToClient` messages.
382
+ * This is used to restore MCP settings when a user joins a session.
383
+ */
384
+ sendMcpSettings(connectionId) {
385
+ const servers = this.skillManager.getMcpServerNames();
386
+ servers.forEach((server_name) => {
387
+ const mcpServer = this.skillManager.getMcpServer(server_name);
388
+ const tools = mcpServer.getTools();
389
+ const enabled_tools = Array.from(mcpServer.getEnabledTools().keys());
390
+ this.connectionManager.sendToConnection(connectionId, {
391
+ type: "mcp_server_added",
392
+ server_name,
393
+ tools,
394
+ enabled_tools,
395
+ session_id: this.sessionUUID,
396
+ });
397
+ });
398
+ }
399
+ /**
400
+ * Called once for each message.
401
+ */
402
+ async processMessage(queuedMessage) {
403
+ logger.debug(`[onMessage]: processing (${queuedMessage.from}) ` +
404
+ JSON.stringify(queuedMessage.msg));
405
+ // In general, handlers return a message to be broadcast. Errors are
406
+ // handled by returning an error to just the sender. Handlers can
407
+ // also broadcast and send directly.
408
+ try {
409
+ const msg = queuedMessage.msg;
410
+ let broadcastMsg = undefined;
411
+ switch (msg.type) {
412
+ case "msg":
413
+ broadcastMsg = this.onUserMessage(msg, queuedMessage.from);
414
+ break;
415
+ case "add_mcp_server":
416
+ broadcastMsg = await this.onAddMcpServer(msg.server_name, msg.enable_all);
417
+ break;
418
+ case "remove_mcp_server":
419
+ broadcastMsg = await this.onRemoveMcpServer(msg.server_name);
420
+ break;
421
+ case "enable_mcp_server_tool":
422
+ broadcastMsg = await this.onEnableMcpServerTool(msg.server_name, msg.tool);
423
+ break;
424
+ case "disable_mcp_server_tool":
425
+ broadcastMsg = await this.onDisableMcpServerTool(msg.server_name, msg.tool);
426
+ break;
427
+ case "enable_all_mcp_server_tools":
428
+ broadcastMsg = await this.onEnableAllMcpServerTools(msg.server_name);
429
+ break;
430
+ case "disable_all_mcp_server_tools":
431
+ broadcastMsg = await this.onDisableAllMcpServerTools(msg.server_name);
432
+ break;
433
+ case "tool_call_approval_result":
434
+ if (this.approvalManager.approvalResult(msg.id, msg.result, msg.auto_approve)) {
435
+ broadcastMsg = {
436
+ type: "tool_call_approval_result",
437
+ id: msg.id,
438
+ result: msg.result,
439
+ session_id: this.sessionUUID,
440
+ };
441
+ }
442
+ break;
443
+ case "set_auto_approval":
444
+ broadcastMsg = await this.onSetAutoApproval(msg.server_name, msg.tool, msg.auto_approve);
445
+ break;
446
+ case "set_system_prompt":
447
+ broadcastMsg = await this.onSetSystemPrompt(msg.system_prompt);
448
+ break;
449
+ case "set_model":
450
+ broadcastMsg = await this.onSetModel(msg.model);
451
+ break;
452
+ case "set_workspace":
453
+ await this.onSetWorkspace(msg, queuedMessage.from);
454
+ break;
455
+ case "set_mcp_server_config":
456
+ await this.handleSetMcpServerConfig(msg, queuedMessage.from);
457
+ break;
458
+ default: {
459
+ const exhaustive = msg; // Error => non-exhaustive switch-case.
460
+ return exhaustive;
461
+ }
462
+ }
463
+ if (broadcastMsg) {
464
+ if (broadcastMsg instanceof Array) {
465
+ broadcastMsg.map((msg) => {
466
+ this.broadcast(msg);
467
+ });
468
+ }
469
+ else {
470
+ this.broadcast(broadcastMsg);
471
+ }
472
+ }
473
+ }
474
+ catch (err) {
475
+ if (!this.handleError(err, queuedMessage.from)) {
476
+ throw err;
477
+ }
478
+ }
479
+ }
480
+ async onSetWorkspace(msg, sender) {
481
+ this.contextManager.setWorkspace((0, agent_1.createUserMessage)(msg.message, msg.imageB64, sender));
482
+ const m = this.workspaceUserMessageData();
483
+ // It's possible that the session has not been written to the DB yet (if
484
+ // the workspace is updated before any messages are sent).
485
+ if (!this.isPersisted) {
486
+ await this.createSessionInDB();
487
+ }
488
+ else {
489
+ await this.db.sessionUpdateWorkspace(this.sessionUUID, m);
490
+ }
491
+ (0, assert_1.strict)(this.isPersisted);
492
+ }
493
+ async processUserMessages(msgs) {
494
+ if (msgs.length === 0) {
495
+ logger.debug("ignoring empty messages");
496
+ return;
497
+ }
498
+ // Begin an agent response using the accumulated user messages.
499
+ const { llmUserMessages, agentFirstChunk } = this.contextManager.startAgentResponse(msgs);
500
+ this.broadcast(agentFirstChunk);
501
+ try {
502
+ await this.agent.userMessagesRaw(llmUserMessages);
503
+ }
504
+ catch (e) {
505
+ logger.warn(`[OpenSession.processUserMessages] agent error: ${String(e)}`);
506
+ // Errors during agent replies must be turned into messages.
507
+ const errMsg = `error from LLM: ${String(e)}`;
508
+ await this.onAgentMessage(errMsg, true);
509
+ const err = this.contextManager.revertAgentResponse(errMsg);
510
+ this.broadcast(err);
511
+ return;
512
+ }
513
+ const newSessionMessages = this.contextManager.endAgentResponse();
514
+ logger.debug("[processUserMessages] newSessionMessages: " +
515
+ JSON.stringify(newSessionMessages));
516
+ try {
517
+ // Append to in-memory conversation and write to the DB
518
+ await this.db.sessionMessagesAppend(this.sessionUUID, newSessionMessages);
519
+ }
520
+ catch (e) {
521
+ if (!this.handleError(e)) {
522
+ throw e;
523
+ }
524
+ }
525
+ }
526
+ onUserMessage(msg, from) {
527
+ // Return a ServerUserMessage for broadcast. The actual message is places
528
+ // on a queue to be dealt with in another loop. This allows Agent
529
+ // processing of user messages to depend on other messages.
530
+ (0, assert_1.strict)(msg);
531
+ (0, assert_1.strict)(from);
532
+ // Assign the user message_idx and attempt to enqueue.
533
+ const userMessage = this.contextManager.processUserMessage(msg, from);
534
+ // Special case for the first message of the session
535
+ if (userMessage.message_idx === conversation_1.MESSAGE_INDEX_START_VALUE) {
536
+ // No need to wait for this to complete before broadcasting.
537
+ void this.onFirstMessage(userMessage);
538
+ }
539
+ if (!this.userMessageQueue.tryEnqueue(userMessage)) {
540
+ // We failed to enqueue - revert the `getNextMessageIdx`
541
+ // NOTE: Nothing should await between `getNextMessageIdx` and
542
+ // `freeMessageIdx` here.
543
+ this.contextManager.unprocessUserMessage(userMessage);
544
+ this.sendTo(from, {
545
+ type: "session_error",
546
+ message: "failed to queue message. try again later.",
547
+ session_id: this.sessionUUID,
548
+ });
549
+ return undefined;
550
+ }
551
+ // Message is enqueued. Broadcast to all participants.
552
+ return userMessage;
553
+ }
554
+ async createSessionInDB() {
555
+ (0, assert_1.strict)(!this.isPersisted);
556
+ const sessionCreateData = {
557
+ session_uuid: this.sessionUUID,
558
+ title: this.sessionTitle,
559
+ team_uuid: this.teamUUID,
560
+ agent_profile_uuid: this.agentProfileUUID,
561
+ workspace: this.workspaceUserMessageData(),
562
+ user_uuid: this.userUUID,
563
+ };
564
+ logger.info(`[OpenSession.onFirstMessage] writing session ${this.sessionUUID}`);
565
+ await this.db.sessionCreate(sessionCreateData);
566
+ this.isPersisted = true;
567
+ }
568
+ async onFirstMessage(userMsg) {
569
+ // Update title on the class before writing to the DB
570
+ this.sessionTitle = userMsg.message?.slice(0, 128) || "New Chat";
571
+ // The session may already have been saved (e.g. if the workspace is
572
+ // updated before any messages are sent).
573
+ if (!this.isPersisted) {
574
+ await this.createSessionInDB();
575
+ }
576
+ else {
577
+ await this.db.sessionUpdateTitle(this.sessionUUID, this.sessionTitle);
578
+ }
579
+ (0, assert_1.strict)(this.isPersisted);
580
+ // Broadcast the SessionUpdated message
581
+ const msg = {
582
+ type: "session_update",
583
+ session_id: this.sessionUUID,
584
+ title: this.sessionTitle,
585
+ };
586
+ this.broadcast(msg);
587
+ }
588
+ async onAddMcpServer(serverName, enableAll) {
589
+ logger.info(`[onAddMcpServer]: Adding server ${serverName} ` +
590
+ `(enable_all: ${String(enableAll)})`);
591
+ if (this.skillManager.hasMcpServer(serverName)) {
592
+ throw new errors_1.ChatErrorMessage(`${serverName} already added`);
593
+ }
594
+ if (!this.skillManager.hasServerBrief(serverName)) {
595
+ throw new errors_1.ChatErrorMessage(`no such server: ${serverName}`);
596
+ }
597
+ await this.skillManager.addMcpServer(serverName, enableAll);
598
+ this.skillManager.enableAllTools(serverName);
599
+ // Save changes to the AgentProfile
600
+ await this.updateAgentProfile();
601
+ // Broadcast the message to all participants.
602
+ const server = this.skillManager.getMcpServer(serverName);
603
+ const tools = server.getTools();
604
+ const enabled_tools = Array.from(server.getEnabledTools().keys());
605
+ return {
606
+ type: "mcp_server_added",
607
+ server_name: serverName,
608
+ tools,
609
+ enabled_tools,
610
+ session_id: this.sessionUUID,
611
+ };
612
+ }
613
+ async onRemoveMcpServer(server_name) {
614
+ logger.info(`[onRemoveMcpServer]: Removing server ${server_name}`);
615
+ if (!this.skillManager.hasMcpServer(server_name)) {
616
+ throw new errors_1.ChatErrorMessage(`${server_name} not enabled`);
617
+ }
618
+ await this.skillManager.removeMcpServer(server_name);
619
+ await this.updateAgentProfile();
620
+ return {
621
+ type: "mcp_server_removed",
622
+ server_name,
623
+ session_id: this.sessionUUID,
624
+ };
625
+ }
626
+ async onEnableMcpServerTool(server_name, tool) {
627
+ this.ensureMcpServer(server_name);
628
+ this.skillManager.enableTool(server_name, tool);
629
+ await this.updateAgentProfile();
630
+ return {
631
+ type: "mcp_server_tool_enabled",
632
+ server_name,
633
+ tool,
634
+ session_id: this.sessionUUID,
635
+ };
636
+ }
637
+ async onDisableMcpServerTool(server_name, tool) {
638
+ this.ensureMcpServerAndTool(server_name, tool);
639
+ this.skillManager.disableTool(server_name, tool);
640
+ await this.updateAgentProfile();
641
+ return {
642
+ type: "mcp_server_tool_disabled",
643
+ server_name,
644
+ tool,
645
+ session_id: this.sessionUUID,
646
+ };
647
+ }
648
+ async onEnableAllMcpServerTools(server_name) {
649
+ // We reimplement the logic to enable any disabled tools so we can
650
+ // construct messages along the way.
651
+ const server = this.ensureMcpServer(server_name);
652
+ const enabledTools = server.getEnabledTools();
653
+ const msgs = [];
654
+ for (const tool of server.getTools()) {
655
+ if (!enabledTools.get(tool.name)) {
656
+ this.skillManager.enableTool(server_name, tool.name);
657
+ msgs.push({
658
+ type: "mcp_server_tool_enabled",
659
+ server_name,
660
+ tool: tool.name,
661
+ session_id: this.sessionUUID,
662
+ });
663
+ }
664
+ }
665
+ await this.updateAgentProfile();
666
+ return msgs;
667
+ }
668
+ async onDisableAllMcpServerTools(server_name) {
669
+ // We reimplement the logic to disable all enabled tools so we can
670
+ // construct messages along the way.
671
+ const server = this.ensureMcpServer(server_name);
672
+ const enabledTools = server.getEnabledTools();
673
+ const msgs = [];
674
+ for (const tool of enabledTools.keys()) {
675
+ this.skillManager.disableTool(server_name, tool);
676
+ msgs.push({
677
+ type: "mcp_server_tool_disabled",
678
+ server_name,
679
+ tool,
680
+ session_id: this.sessionUUID,
681
+ });
682
+ }
683
+ await this.updateAgentProfile();
684
+ return msgs;
685
+ }
686
+ async onSetSystemPrompt(system_prompt) {
687
+ this.agent.setSystemPrompt(system_prompt);
688
+ await this.updateAgentProfile();
689
+ return {
690
+ type: "system_prompt_updated",
691
+ system_prompt,
692
+ session_id: this.sessionUUID,
693
+ };
694
+ }
695
+ async onSetModel(model) {
696
+ this.agent.setModel(model);
697
+ await this.updateAgentProfile();
698
+ return { type: "model_updated", model, session_id: this.sessionUUID };
699
+ }
700
+ async handleSetMcpServerConfig(msg, from) {
701
+ if (!this.skillManager.hasServerBrief(msg.server_name)) {
702
+ throw new Error(`unknown mcp server: ${msg.server_name}`);
703
+ }
704
+ const msc = this.db.createTypedClient(dbMcpServerConfigs_1.DbMcpServerConfigs);
705
+ if (msg.config) {
706
+ await msc.setConfigForUser(from, msg.server_name, msg.config);
707
+ }
708
+ else {
709
+ await msc.deleteConfigForUser(from, msg.server_name);
710
+ }
711
+ // If the mcp server was enabled, disable and re-enable to ensure the new
712
+ // settings are reflected. Note, we must track the set of enabled tools.
713
+ if (this.skillManager.hasMcpServer(msg.server_name)) {
714
+ const enabledTools = this.skillManager
715
+ .getMcpServer(msg.server_name)
716
+ .getEnabledTools();
717
+ await this.skillManager.removeMcpServer(msg.server_name);
718
+ await this.skillManager.addMcpServer(msg.server_name, false);
719
+ for (const tool of enabledTools.keys()) {
720
+ this.skillManager.enableTool(msg.server_name, tool);
721
+ }
722
+ }
723
+ // TODO: Do we want to braodcast an "mcp_server_config_updated" message?
724
+ }
725
+ async onSetAutoApproval(serverName, tool, autoApprove) {
726
+ if ((0, sdk_1.prefsSetAutoApprove)(this.agentProfilePreferences, serverName, tool, autoApprove)) {
727
+ await this.db.updateAgentProfilePreferences(this.agentProfileUUID, this.agentProfilePreferences);
728
+ return {
729
+ type: "tool_auto_approval_set",
730
+ server_name: serverName,
731
+ tool,
732
+ auto_approve: autoApprove,
733
+ session_id: this.sessionUUID,
734
+ };
735
+ }
736
+ }
737
+ ensureMcpServer(serverName) {
738
+ return this.skillManager.getMcpServer(serverName);
739
+ }
740
+ ensureMcpServerAndTool(serverName, toolName) {
741
+ const server = this.ensureMcpServer(serverName);
742
+ const tool = server.getTool(toolName);
743
+ if (!tool) {
744
+ throw new errors_1.ChatErrorMessage(`Tool ${toolName} on ${serverName} not found`);
745
+ }
746
+ return tool;
747
+ }
748
+ async updateAgentProfile() {
749
+ const profile = this.agent.getAgentProfile();
750
+ logger.debug(`[updateAgentProfile]: uuid: ${this.agentProfileUUID} profile: ` +
751
+ JSON.stringify(profile));
752
+ return this.db.updateAgentProfile(this.agentProfileUUID, profile);
753
+ }
754
+ /**
755
+ * Update participant in session (called by SessionRegistry).
756
+ * This only updates the local participant map - actual membership
757
+ * tracking is handled by SessionRegistry.
758
+ */
759
+ addParticipant(userId, role) {
760
+ this.sessionParticipants.set(userId, role);
761
+ // Broadcast result to all session participants
762
+ const broadcastMessage = {
763
+ type: "user_added",
764
+ user_uuid: userId,
765
+ role: "participant",
766
+ nickname: role.nickname,
767
+ email: role.email,
768
+ session_id: this.sessionUUID,
769
+ };
770
+ this.broadcast(broadcastMessage);
771
+ }
772
+ /**
773
+ * Remove participant from session (called by SessionRegistry).
774
+ * This only updates the local participant map - actual membership
775
+ * tracking is handled by SessionRegistry.
776
+ */
777
+ removeParticipant(userId) {
778
+ this.sessionParticipants.delete(userId);
779
+ // Broadcast result to all session participants
780
+ const broadcastMessage = {
781
+ type: "user_removed",
782
+ user_uuid: userId,
783
+ session_id: this.sessionUUID,
784
+ };
785
+ this.broadcast(broadcastMessage);
786
+ }
787
+ getSessionParticipants() {
788
+ return Array.from(this.sessionParticipants.entries()).map(([userId, user]) => ({
789
+ nickname: user.nickname,
790
+ email: user.email,
791
+ user_uuid: userId,
792
+ role: user.role,
793
+ }));
794
+ }
795
+ workspaceUserMessageData() {
796
+ const workspaceMsg = this.contextManager.getWorkspace();
797
+ return workspaceMsg
798
+ ? (0, conversation_1.llmUserMessageToUserMessageData)(workspaceMsg)
799
+ : undefined;
800
+ }
801
+ serverSessionInfo(clientMessageId) {
802
+ return {
803
+ type: "session_info",
804
+ owner_uuid: this.userUUID,
805
+ title: this.sessionTitle,
806
+ saved_agent_profile: this.savedAgentProfile,
807
+ workspace: this.workspaceUserMessageData(),
808
+ updated_at: this.sessionUpdatedAt,
809
+ participants: this.getSessionParticipants(),
810
+ mcp_server_briefs: this.skillManager.getServerBriefs(),
811
+ agent_preferences: this.agentProfilePreferences,
812
+ client_message_id: clientMessageId,
813
+ session_id: this.sessionUUID,
814
+ team_uuid: this.teamUUID,
815
+ };
816
+ }
817
+ // Helper method to add session context to messages
818
+ addSessionContext(msg) {
819
+ return {
820
+ ...msg,
821
+ session_id: this.sessionUUID,
822
+ };
823
+ }
824
+ }
825
+ exports.OpenSession = OpenSession;
826
+ async function loadDataForEmptySession(db, agent_profile_uuid, owner_uuid) {
827
+ const [savedAgentProfile, ownerData, ownerApiKey] = await Promise.all([
828
+ db.getSavedAgentProfileById(agent_profile_uuid),
829
+ db.getUserFromUuid(owner_uuid),
830
+ db.getUserApiKey(owner_uuid),
831
+ ]);
832
+ if (!savedAgentProfile) {
833
+ throw new errors_1.ChatFatalError(`No such agent profile: ${agent_profile_uuid}`);
834
+ }
835
+ if (!ownerData) {
836
+ throw new errors_1.ChatFatalError(`No owner ${owner_uuid}`);
837
+ }
838
+ if (!ownerData.nickname) {
839
+ throw new errors_1.ChatFatalError(`User ${owner_uuid} has no username - cannot create session`);
840
+ }
841
+ if (!ownerApiKey) {
842
+ throw new errors_1.ChatFatalError(`User ${owner_uuid} has no api keys - cannot create session`);
843
+ }
844
+ return { savedAgentProfile, ownerData, ownerApiKey };
845
+ }
846
+ /**
847
+ * Loads session data, agent profile, and owner information from the database.
848
+ */
849
+ async function loadSessionData(db, sessionId) {
850
+ const [sessionData, sessionMessages, sessionParticipants, sessionCheckpoint] = await Promise.all([
851
+ db.sessionGetById(sessionId),
852
+ db.sessionMessagesGetConversation(sessionId, exports.DEFAULT_NUM_MESSGAES, 0),
853
+ db.sessionGetParticipants(sessionId),
854
+ db.sessionCheckpointGet(sessionId),
855
+ ]);
856
+ if (!sessionData) {
857
+ throw new errors_1.ChatFatalError(`No such session: ${sessionId}`);
858
+ }
859
+ const { savedAgentProfile, ownerData, ownerApiKey } = await loadDataForEmptySession(db, sessionData.agent_profile_uuid, sessionData.user_uuid);
860
+ return {
861
+ sessionData,
862
+ sessionMessages,
863
+ sessionCheckpoint,
864
+ savedAgentProfile,
865
+ ownerData,
866
+ ownerApiKey,
867
+ sessionParticipants: (0, database_1.createSessionParticipantMap)(sessionParticipants),
868
+ };
869
+ }