@xalia/agent 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/src/agent/agent.js +109 -57
- package/dist/agent/src/agent/agentUtils.js +24 -26
- package/dist/agent/src/agent/compressingContextManager.js +3 -2
- package/dist/agent/src/agent/dummyLLM.js +1 -3
- package/dist/agent/src/agent/imageGenLLM.js +67 -0
- package/dist/agent/src/agent/imageGenerator.js +43 -0
- package/dist/agent/src/agent/llm.js +27 -0
- package/dist/agent/src/agent/mcpServerManager.js +18 -6
- package/dist/agent/src/agent/nullAgentEventHandler.js +6 -0
- package/dist/agent/src/agent/openAILLM.js +3 -3
- package/dist/agent/src/agent/openAILLMStreaming.js +41 -6
- package/dist/agent/src/chat/client/chatClient.js +154 -235
- package/dist/agent/src/chat/client/constants.js +1 -2
- package/dist/agent/src/chat/client/sessionClient.js +47 -15
- package/dist/agent/src/chat/client/sessionFiles.js +102 -0
- package/dist/agent/src/chat/data/apiKeyManager.js +38 -7
- package/dist/agent/src/chat/data/database.js +83 -70
- package/dist/agent/src/chat/data/dbSessionFileModels.js +49 -0
- package/dist/agent/src/chat/data/dbSessionFiles.js +76 -0
- package/dist/agent/src/chat/data/dbSessionMessages.js +57 -0
- package/dist/agent/src/chat/data/mimeTypes.js +44 -0
- package/dist/agent/src/chat/protocol/messages.js +21 -1
- package/dist/agent/src/chat/server/chatContextManager.js +19 -16
- package/dist/agent/src/chat/server/connectionManager.js +14 -36
- package/dist/agent/src/chat/server/connectionManager.test.js +3 -16
- package/dist/agent/src/chat/server/conversation.js +73 -44
- package/dist/agent/src/chat/server/imageGeneratorTools.js +111 -0
- package/dist/agent/src/chat/server/openSession.js +398 -233
- package/dist/agent/src/chat/server/openSessionMessageSender.js +2 -0
- package/dist/agent/src/chat/server/server.js +5 -8
- package/dist/agent/src/chat/server/sessionFileManager.js +171 -38
- package/dist/agent/src/chat/server/sessionRegistry.js +214 -42
- package/dist/agent/src/chat/server/test-utils/mockFactories.js +12 -11
- package/dist/agent/src/chat/server/tools.js +27 -6
- package/dist/agent/src/chat/utils/approvalManager.js +82 -64
- package/dist/agent/src/chat/utils/multiAsyncQueue.js +9 -1
- package/dist/agent/src/chat/{client/responseHandler.js → utils/responseAwaiter.js} +41 -18
- package/dist/agent/src/test/agent.test.js +104 -63
- package/dist/agent/src/test/approvalManager.test.js +79 -35
- package/dist/agent/src/test/chatContextManager.test.js +16 -17
- package/dist/agent/src/test/clientServerConnection.test.js +2 -2
- package/dist/agent/src/test/db.test.js +33 -70
- package/dist/agent/src/test/dbSessionFiles.test.js +179 -0
- package/dist/agent/src/test/dbSessionMessages.test.js +67 -0
- package/dist/agent/src/test/dbTestTools.js +6 -5
- package/dist/agent/src/test/imageLoad.test.js +1 -1
- package/dist/agent/src/test/mcpServerManager.test.js +1 -1
- package/dist/agent/src/test/multiAsyncQueue.test.js +50 -0
- package/dist/agent/src/test/responseAwaiter.test.js +74 -0
- package/dist/agent/src/test/testTools.js +12 -0
- package/dist/agent/src/tool/agentChat.js +25 -6
- package/dist/agent/src/tool/agentMain.js +1 -1
- package/dist/agent/src/tool/chatMain.js +115 -6
- package/dist/agent/src/tool/commandPrompt.js +7 -3
- package/dist/agent/src/tool/files.js +23 -15
- package/dist/agent/src/tool/options.js +2 -2
- package/package.json +1 -1
- package/scripts/setup_chat +2 -2
- package/scripts/test_chat +95 -36
- package/src/agent/agent.ts +152 -41
- package/src/agent/agentUtils.ts +34 -41
- package/src/agent/compressingContextManager.ts +5 -4
- package/src/agent/context.ts +1 -1
- package/src/agent/dummyLLM.ts +1 -3
- package/src/agent/iAgentEventHandler.ts +15 -2
- package/src/agent/imageGenLLM.ts +99 -0
- package/src/agent/imageGenerator.ts +60 -0
- package/src/agent/llm.ts +128 -4
- package/src/agent/mcpServerManager.ts +26 -7
- package/src/agent/nullAgentEventHandler.ts +6 -0
- package/src/agent/openAILLM.ts +3 -8
- package/src/agent/openAILLMStreaming.ts +60 -14
- package/src/chat/client/chatClient.ts +262 -286
- package/src/chat/client/constants.ts +0 -2
- package/src/chat/client/sessionClient.ts +82 -20
- package/src/chat/client/sessionFiles.ts +142 -0
- package/src/chat/data/apiKeyManager.ts +55 -7
- package/src/chat/data/dataModels.ts +17 -7
- package/src/chat/data/database.ts +107 -92
- package/src/chat/data/dbSessionFileModels.ts +91 -0
- package/src/chat/data/dbSessionFiles.ts +99 -0
- package/src/chat/data/dbSessionMessages.ts +68 -0
- package/src/chat/data/mimeTypes.ts +58 -0
- package/src/chat/protocol/messages.ts +136 -25
- package/src/chat/server/chatContextManager.ts +42 -24
- package/src/chat/server/connectionManager.test.ts +2 -22
- package/src/chat/server/connectionManager.ts +18 -53
- package/src/chat/server/conversation.ts +106 -59
- package/src/chat/server/imageGeneratorTools.ts +138 -0
- package/src/chat/server/openSession.ts +606 -325
- package/src/chat/server/openSessionMessageSender.ts +4 -0
- package/src/chat/server/server.ts +5 -11
- package/src/chat/server/sessionFileManager.ts +223 -63
- package/src/chat/server/sessionRegistry.ts +317 -52
- package/src/chat/server/test-utils/mockFactories.ts +13 -13
- package/src/chat/server/tools.ts +43 -8
- package/src/chat/utils/agentSessionMap.ts +2 -2
- package/src/chat/utils/approvalManager.ts +153 -81
- package/src/chat/utils/multiAsyncQueue.ts +11 -1
- package/src/chat/{client/responseHandler.ts → utils/responseAwaiter.ts} +73 -23
- package/src/test/agent.test.ts +152 -75
- package/src/test/approvalManager.test.ts +108 -40
- package/src/test/chatContextManager.test.ts +26 -22
- package/src/test/clientServerConnection.test.ts +3 -3
- package/src/test/compressingContextManager.test.ts +1 -1
- package/src/test/context.test.ts +2 -1
- package/src/test/conversation.test.ts +1 -1
- package/src/test/db.test.ts +41 -83
- package/src/test/dbSessionFiles.test.ts +258 -0
- package/src/test/dbSessionMessages.test.ts +85 -0
- package/src/test/dbTestTools.ts +9 -5
- package/src/test/imageLoad.test.ts +2 -2
- package/src/test/mcpServerManager.test.ts +3 -1
- package/src/test/multiAsyncQueue.test.ts +58 -0
- package/src/test/responseAwaiter.test.ts +103 -0
- package/src/test/testTools.ts +15 -1
- package/src/tool/agentChat.ts +36 -8
- package/src/tool/agentMain.ts +7 -7
- package/src/tool/chatMain.ts +128 -7
- package/src/tool/commandPrompt.ts +10 -5
- package/src/tool/files.ts +30 -13
- package/src/tool/options.ts +1 -1
- package/test_data/dummyllm_script_image_gen.json +19 -0
- package/test_data/dummyllm_script_invoke_image_gen_tool.json +30 -0
- package/test_data/image_gen_test_profile.json +5 -0
- package/dist/agent/src/test/responseHandler.test.js +0 -61
- package/src/test/responseHandler.test.ts +0 -78
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
1
2
|
import { strict as assert } from "assert";
|
|
2
3
|
import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
3
4
|
|
|
@@ -6,25 +7,19 @@ import {
|
|
|
6
7
|
Configuration,
|
|
7
8
|
getLogger,
|
|
8
9
|
McpServerSettings,
|
|
9
|
-
prefsGetAutoApprove,
|
|
10
|
-
prefsSetAutoApprove,
|
|
11
10
|
SavedAgentProfile,
|
|
12
11
|
} from "@xalia/xmcp/sdk";
|
|
13
12
|
|
|
13
|
+
import { Agent, createUserMessage } from "../../agent/agent";
|
|
14
14
|
import {
|
|
15
|
-
Agent,
|
|
16
15
|
ChatCompletionAssistantMessageParam,
|
|
17
16
|
ChatCompletionMessageToolCall,
|
|
18
17
|
ChatCompletionToolMessageParam,
|
|
19
|
-
|
|
20
|
-
} from "../../agent/agent";
|
|
18
|
+
} from "../../agent/llm";
|
|
21
19
|
import { SkillManager } from "../../agent/sudoMcpServerManager";
|
|
22
20
|
import { McpServerInfo } from "../../agent/mcpServerManager";
|
|
23
21
|
import { IAgentEventHandler } from "../../agent/iAgentEventHandler";
|
|
24
|
-
import {
|
|
25
|
-
createAgentWithoutSkills,
|
|
26
|
-
DEFAULT_LLM_MODEL,
|
|
27
|
-
} from "../../agent/agentUtils";
|
|
22
|
+
import { createAgentWithoutSkills } from "../../agent/agentUtils";
|
|
28
23
|
import { IPlatform } from "../../agent/iplatform";
|
|
29
24
|
|
|
30
25
|
import type {
|
|
@@ -37,15 +32,18 @@ import type {
|
|
|
37
32
|
ServerModelUpdated,
|
|
38
33
|
ServerUserMessage,
|
|
39
34
|
ClientUserMessage,
|
|
40
|
-
ServerToolAutoApprovalSet,
|
|
41
35
|
ClientSessionMessage,
|
|
42
36
|
ServerSessionError,
|
|
43
37
|
ServerUserAdded,
|
|
44
38
|
ServerUserRemoved,
|
|
45
39
|
ServerSessionInfo,
|
|
46
|
-
ServerSessionUpdate,
|
|
47
40
|
ClientSetWorkspace,
|
|
41
|
+
ClientToServer,
|
|
42
|
+
ServerSessionFileContent,
|
|
48
43
|
ClientSetMcpServerConfig,
|
|
44
|
+
ClientShareSession,
|
|
45
|
+
ClientSessionMessageBase,
|
|
46
|
+
ServerAgentPaused,
|
|
49
47
|
} from "../protocol/messages";
|
|
50
48
|
import { AsyncQueue } from "../utils/asyncQueue";
|
|
51
49
|
import { MultiAsyncQueue } from "../utils/multiAsyncQueue";
|
|
@@ -57,13 +55,17 @@ import {
|
|
|
57
55
|
TeamParticipant,
|
|
58
56
|
SessionCreateData,
|
|
59
57
|
UserMessageData,
|
|
58
|
+
SessionDescriptor,
|
|
60
59
|
} from "../data/dataModels";
|
|
61
60
|
import {
|
|
62
61
|
Database,
|
|
63
62
|
createSessionParticipantMap,
|
|
64
63
|
UserData,
|
|
65
64
|
} from "../data/database";
|
|
66
|
-
import {
|
|
65
|
+
import {
|
|
66
|
+
DbAgentPreferencesWriter,
|
|
67
|
+
ToolApprovalManager,
|
|
68
|
+
} from "../utils/approvalManager";
|
|
67
69
|
import { ChatErrorMessage, ChatFatalError } from "../protocol/errors";
|
|
68
70
|
import { addDefaultChatTools } from "./tools";
|
|
69
71
|
import { ChatContextManager, ICheckpointWriter } from "./chatContextManager";
|
|
@@ -71,17 +73,33 @@ import {
|
|
|
71
73
|
llmUserMessageToUserMessageData,
|
|
72
74
|
MESSAGE_INDEX_START_VALUE,
|
|
73
75
|
} from "./conversation";
|
|
74
|
-
import {
|
|
76
|
+
import {
|
|
77
|
+
ChatSessionFileManager,
|
|
78
|
+
ISessionFileManager,
|
|
79
|
+
ISessionFileManagerEventHandler,
|
|
80
|
+
} from "./sessionFileManager";
|
|
75
81
|
import { IUserConnectionManager } from "./connectionManager";
|
|
76
82
|
import { getErrorString } from "./errorUtils";
|
|
77
83
|
import { NODE_PLATFORM } from "../../tool/nodePlatform";
|
|
78
84
|
import { DbMcpServerConfigs } from "../data/dbMcpServerConfigs";
|
|
85
|
+
import { SessionFileEntry } from "../data/dbSessionFileModels";
|
|
86
|
+
import { ApiKeyManager } from "../data/apiKeyManager";
|
|
87
|
+
import { DbSessionMessages } from "../data/dbSessionMessages";
|
|
88
|
+
import { ISessionMessageSender } from "./openSessionMessageSender";
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The model to use when the AgentProfile does not specify one.
|
|
92
|
+
*/
|
|
93
|
+
const DEFAULT_CHAT_LLM_MODEL =
|
|
94
|
+
process.env["DEFAULT_LLM_MODEL"] || "anthropic/claude-sonnet-4.5";
|
|
79
95
|
|
|
80
96
|
/**
|
|
81
97
|
* Num messages to load at conversation startup.
|
|
82
98
|
*/
|
|
83
99
|
export const DEFAULT_NUM_MESSGAES = 500;
|
|
84
100
|
|
|
101
|
+
export const GUEST_TOKEN_PREFIX = "guest";
|
|
102
|
+
|
|
85
103
|
const logger = getLogger();
|
|
86
104
|
|
|
87
105
|
type QueuedClientMessage<
|
|
@@ -109,6 +127,170 @@ class DBCheckpointWriter implements ICheckpointWriter {
|
|
|
109
127
|
}
|
|
110
128
|
}
|
|
111
129
|
|
|
130
|
+
export class ChatSessionMessageSender
|
|
131
|
+
implements ISessionMessageSender<ServerToClient>
|
|
132
|
+
{
|
|
133
|
+
constructor(
|
|
134
|
+
public readonly connectionManager: IUserConnectionManager<ServerToClient>,
|
|
135
|
+
public readonly sessionParticipants: SessionParticipantMap
|
|
136
|
+
) {}
|
|
137
|
+
|
|
138
|
+
broadcast(msg: ServerToClient): void {
|
|
139
|
+
const users: Set<string> = new Set(this.sessionParticipants.keys());
|
|
140
|
+
this.connectionManager.sendToUsers(users, msg);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
sendTo(userUUID: string, msg: ServerToClient): void {
|
|
144
|
+
this.connectionManager.sendToUsers(new Set([userUUID]), msg);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
class ChatSessionPlatform {
|
|
149
|
+
constructor(
|
|
150
|
+
private sender: ISessionMessageSender<ServerToClient>,
|
|
151
|
+
private sessionUUID: string,
|
|
152
|
+
private ownerUUID: string
|
|
153
|
+
) {}
|
|
154
|
+
|
|
155
|
+
// IPlatform.openUrl
|
|
156
|
+
openUrl(url: string, authResultP: Promise<boolean>, display_name: string) {
|
|
157
|
+
// These requests are always passed to the original owner, since it is
|
|
158
|
+
// their settings that will be used for all MCP servers.
|
|
159
|
+
this.sender.broadcast({
|
|
160
|
+
type: "authentication_started",
|
|
161
|
+
session_id: this.sessionUUID,
|
|
162
|
+
url,
|
|
163
|
+
});
|
|
164
|
+
this.sender.sendTo(this.ownerUUID, {
|
|
165
|
+
type: "authenticate",
|
|
166
|
+
session_id: this.sessionUUID,
|
|
167
|
+
url,
|
|
168
|
+
display_name,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// TODO: auth timeout
|
|
172
|
+
// Don't stall this function waiting for authentication
|
|
173
|
+
void authResultP.then((result) => {
|
|
174
|
+
this.sender.broadcast({
|
|
175
|
+
type: "authentication_finished",
|
|
176
|
+
session_id: this.sessionUUID,
|
|
177
|
+
url,
|
|
178
|
+
result,
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// IPlatform.load
|
|
184
|
+
load(filename: string): Promise<string> {
|
|
185
|
+
if (process.env.DEVELOPMENT === "1") {
|
|
186
|
+
return NODE_PLATFORM.load(filename);
|
|
187
|
+
}
|
|
188
|
+
throw new ChatErrorMessage("Platform.load not implemented");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// IPlatform.renderHTML
|
|
192
|
+
renderHTML(html: string): Promise<void> {
|
|
193
|
+
return new Promise<void>((r) => {
|
|
194
|
+
this.sender.broadcast({
|
|
195
|
+
type: "render_html",
|
|
196
|
+
html,
|
|
197
|
+
session_id: this.sessionUUID,
|
|
198
|
+
});
|
|
199
|
+
r();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export class ChatSessionAgentEventHandler implements IAgentEventHandler {
|
|
205
|
+
constructor(
|
|
206
|
+
private readonly sessionUUID: string,
|
|
207
|
+
private readonly sender: ISessionMessageSender<ServerToClient>,
|
|
208
|
+
private readonly approvalManager: ToolApprovalManager,
|
|
209
|
+
private readonly contextManager: ChatContextManager
|
|
210
|
+
) {}
|
|
211
|
+
|
|
212
|
+
onCompletion(result: ChatCompletionAssistantMessageParam): void {
|
|
213
|
+
logger.debug(`[OpenSession.onCompletion] : ${JSON.stringify(result)}`);
|
|
214
|
+
// Nothing to broadcast. Caller will receive this via onAgentMessage.
|
|
215
|
+
this.contextManager.processAgentResponse(result);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
onImage(image: OpenAI.Chat.Completions.ChatCompletionContentPartImage): void {
|
|
219
|
+
logger.debug(`[OpenSession.onImage] : ${image.image_url.url}`);
|
|
220
|
+
throw new Error("[OpenSession.onImage] unimplemented");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
onToolCallResult(result: ChatCompletionToolMessageParam): void {
|
|
224
|
+
logger.debug(`[onToolCallResult] : ${JSON.stringify(result)}`);
|
|
225
|
+
const toolCallMessage = this.contextManager.processToolCallResult(result);
|
|
226
|
+
this.sender.broadcast(toolCallMessage);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async onToolCall(
|
|
230
|
+
toolCall: ChatCompletionMessageToolCall,
|
|
231
|
+
agentTool: boolean
|
|
232
|
+
): Promise<boolean> {
|
|
233
|
+
if (agentTool) {
|
|
234
|
+
// "Agent" tools are considered internal to the agent, and are always
|
|
235
|
+
// permitted. Inform all clients and immediately approve.
|
|
236
|
+
this.sender.broadcast({
|
|
237
|
+
type: "tool_call",
|
|
238
|
+
tool_call: toolCall,
|
|
239
|
+
session_id: this.sessionUUID,
|
|
240
|
+
});
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// TODO: Need a proper mapping to/from MCP calls to tool names
|
|
245
|
+
|
|
246
|
+
const [serverName, tool] = toolCall.function.name.split("__");
|
|
247
|
+
const { approved, requested } = await this.approvalManager.getApproval(
|
|
248
|
+
serverName,
|
|
249
|
+
tool,
|
|
250
|
+
toolCall
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
// For now, the frontend uses the tool_call data in the
|
|
254
|
+
// "approve_tool_call" request to display the tool call data. If approval
|
|
255
|
+
// was requested in this way, don't send the "tool_call" message as well.
|
|
256
|
+
|
|
257
|
+
if (approved && !requested) {
|
|
258
|
+
this.sender.broadcast({
|
|
259
|
+
type: "tool_call",
|
|
260
|
+
tool_call: toolCall,
|
|
261
|
+
session_id: this.sessionUUID,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return approved;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
onAgentMessage(msg: string, end: boolean): Promise<void> {
|
|
269
|
+
logger.debug(
|
|
270
|
+
`[OpenSession.onAgentMessage] msg: ${msg}, end: ${String(end)}`
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
// Inform the contextManager and broadcast the ServerAgentMessageChunk
|
|
274
|
+
const agentMsgChunk = this.contextManager.processAgentMessage(msg, end);
|
|
275
|
+
this.sender.broadcast(agentMsgChunk);
|
|
276
|
+
return Promise.resolve();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
onReasoning(reasoning: string): Promise<void> {
|
|
280
|
+
return new Promise<void>((r) => {
|
|
281
|
+
logger.debug(`[OpenSession.onReasoning]${reasoning}`);
|
|
282
|
+
if (reasoning.length > 0) {
|
|
283
|
+
this.sender.broadcast({
|
|
284
|
+
type: "agent_reasoning_chunk",
|
|
285
|
+
reasoning,
|
|
286
|
+
session_id: this.sessionUUID,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
r();
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
112
294
|
/**
|
|
113
295
|
* Describes a Session (conversation) with connected participants.
|
|
114
296
|
*
|
|
@@ -119,7 +301,7 @@ class DBCheckpointWriter implements ICheckpointWriter {
|
|
|
119
301
|
* be seen until user messages had been fully processed, which could block
|
|
120
302
|
* tool approvals and other interactions).
|
|
121
303
|
*/
|
|
122
|
-
export class OpenSession implements
|
|
304
|
+
export class OpenSession implements ISessionFileManagerEventHandler {
|
|
123
305
|
private readonly db: Database;
|
|
124
306
|
private /* readonly */ agent: Agent;
|
|
125
307
|
private readonly sessionUUID: string;
|
|
@@ -129,15 +311,18 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
129
311
|
private readonly sessionParticipants: SessionParticipantMap;
|
|
130
312
|
private readonly agentProfilePreferences: AgentPreferences;
|
|
131
313
|
private /* readonly */ skillManager: SkillManager;
|
|
132
|
-
private readonly
|
|
314
|
+
private readonly sender: ChatSessionMessageSender;
|
|
133
315
|
private readonly messageQueue: AsyncQueue<QueuedClientMessage>;
|
|
134
316
|
private readonly userMessageQueue: MultiAsyncQueue<ServerUserMessage>;
|
|
135
317
|
private readonly contextManager: ChatContextManager;
|
|
136
|
-
private readonly approvalManager:
|
|
318
|
+
private readonly approvalManager: ToolApprovalManager;
|
|
137
319
|
private readonly savedAgentProfile: SavedAgentProfile;
|
|
320
|
+
private readonly sessionFileManager: ISessionFileManager;
|
|
138
321
|
private isPersisted: boolean;
|
|
322
|
+
private accessToken: string | undefined;
|
|
139
323
|
private sessionTitle: string;
|
|
140
324
|
private sessionUpdatedAt: string;
|
|
325
|
+
private agentPaused: boolean;
|
|
141
326
|
|
|
142
327
|
private constructor(
|
|
143
328
|
db: Database,
|
|
@@ -149,8 +334,9 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
149
334
|
agentProfilePreferences: AgentPreferences,
|
|
150
335
|
skillManager: SkillManager,
|
|
151
336
|
contextManager: ChatContextManager,
|
|
152
|
-
|
|
153
|
-
approvalManager:
|
|
337
|
+
sender: ChatSessionMessageSender,
|
|
338
|
+
approvalManager: ToolApprovalManager,
|
|
339
|
+
fileManager: ISessionFileManager
|
|
154
340
|
) {
|
|
155
341
|
this.db = db;
|
|
156
342
|
this.agent = agent;
|
|
@@ -158,10 +344,11 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
158
344
|
this.teamUUID = sessionData.team_uuid;
|
|
159
345
|
this.userUUID = sessionData.user_uuid;
|
|
160
346
|
this.agentProfileUUID = sessionData.agent_profile_uuid;
|
|
347
|
+
this.accessToken = sessionData.access_token;
|
|
161
348
|
this.sessionParticipants = sessionParticipants;
|
|
162
349
|
this.agentProfilePreferences = agentProfilePreferences;
|
|
163
350
|
this.skillManager = skillManager;
|
|
164
|
-
this.
|
|
351
|
+
this.sender = sender;
|
|
165
352
|
this.messageQueue = new AsyncQueue<QueuedClientMessage>((m) =>
|
|
166
353
|
this.processMessage(m)
|
|
167
354
|
);
|
|
@@ -172,13 +359,18 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
172
359
|
this.contextManager = contextManager;
|
|
173
360
|
this.approvalManager = approvalManager;
|
|
174
361
|
this.savedAgentProfile = savedAgentProfile;
|
|
362
|
+
this.sessionFileManager = fileManager;
|
|
175
363
|
this.isPersisted = isPersisted;
|
|
176
364
|
this.sessionTitle = sessionData.title;
|
|
177
365
|
this.sessionUpdatedAt = sessionData.updated_at;
|
|
366
|
+
this.agentPaused = sessionData.agent_paused;
|
|
367
|
+
|
|
368
|
+
fileManager.addEventHandler(this);
|
|
178
369
|
}
|
|
179
370
|
|
|
180
371
|
private static async init(
|
|
181
372
|
db: Database,
|
|
373
|
+
isPersisted: boolean,
|
|
182
374
|
sessionData: SessionData,
|
|
183
375
|
savedAgentProfile: SavedAgentProfile,
|
|
184
376
|
sessionMessages: SessionMessage[],
|
|
@@ -191,61 +383,54 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
191
383
|
connectionManager: IUserConnectionManager<ServerToClient>
|
|
192
384
|
): Promise<OpenSession> {
|
|
193
385
|
const sessionId = sessionData.session_uuid;
|
|
194
|
-
|
|
195
|
-
const
|
|
386
|
+
|
|
387
|
+
const fileManager = await ChatSessionFileManager.init(db, sessionId);
|
|
388
|
+
const sender = new ChatSessionMessageSender(
|
|
389
|
+
connectionManager,
|
|
390
|
+
sessionParticipants
|
|
391
|
+
);
|
|
392
|
+
const platform = new ChatSessionPlatform(sender, sessionId, ownerData.uuid);
|
|
393
|
+
const toolApprovalManager = new ToolApprovalManager(
|
|
394
|
+
sessionData.session_uuid,
|
|
395
|
+
savedAgentProfile.uuid,
|
|
396
|
+
savedAgentProfile.preferences,
|
|
397
|
+
sender,
|
|
398
|
+
new DbAgentPreferencesWriter(db)
|
|
399
|
+
);
|
|
400
|
+
const { agent, skillManager, contextManager } = await createContextAndAgent(
|
|
401
|
+
sessionId,
|
|
196
402
|
savedAgentProfile.profile.system_prompt,
|
|
403
|
+
savedAgentProfile.profile.model || DEFAULT_CHAT_LLM_MODEL,
|
|
197
404
|
sessionMessages,
|
|
198
|
-
|
|
199
|
-
ownerData.uuid,
|
|
405
|
+
sessionData.workspace,
|
|
200
406
|
sessionCheckpoint,
|
|
201
|
-
|
|
202
|
-
savedAgentProfile.profile.model || DEFAULT_LLM_MODEL,
|
|
407
|
+
ownerData,
|
|
203
408
|
ownerApiKey,
|
|
204
|
-
|
|
205
|
-
|
|
409
|
+
llmUrl,
|
|
410
|
+
xmcpUrl,
|
|
411
|
+
fileManager,
|
|
412
|
+
sender,
|
|
413
|
+
platform,
|
|
414
|
+
toolApprovalManager
|
|
206
415
|
);
|
|
207
|
-
if (sessionData.workspace) {
|
|
208
|
-
const ws = sessionData.workspace;
|
|
209
|
-
contextManager.setWorkspace(createUserMessage(ws.message, ws.imageB64));
|
|
210
|
-
}
|
|
211
416
|
|
|
212
417
|
const openSession = new OpenSession(
|
|
213
418
|
db,
|
|
214
|
-
|
|
419
|
+
agent,
|
|
215
420
|
sessionData,
|
|
216
421
|
savedAgentProfile,
|
|
217
|
-
|
|
422
|
+
isPersisted,
|
|
218
423
|
sessionParticipants,
|
|
219
424
|
savedAgentProfile.preferences,
|
|
220
|
-
|
|
221
|
-
contextManager,
|
|
222
|
-
connectionManager
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
// Initialize an empty agent (to ensure there are no callbacks before
|
|
226
|
-
// the OpenSession and client are fully set up).
|
|
227
|
-
|
|
228
|
-
const xmcpConfig = Configuration.new(ownerApiKey, xmcpUrl, false);
|
|
229
|
-
const [agent, skillManager] = await createAgentWithoutSkills(
|
|
230
|
-
llmUrl,
|
|
231
|
-
savedAgentProfile.profile,
|
|
232
|
-
openSession,
|
|
233
|
-
openSession,
|
|
425
|
+
skillManager,
|
|
234
426
|
contextManager,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
true
|
|
427
|
+
sender,
|
|
428
|
+
toolApprovalManager,
|
|
429
|
+
fileManager
|
|
239
430
|
);
|
|
240
|
-
await addDefaultChatTools(agent, ownerData.timezone, openSession);
|
|
241
431
|
|
|
242
|
-
//
|
|
243
|
-
openSession.agent = agent;
|
|
244
|
-
openSession.skillManager = skillManager;
|
|
432
|
+
// Note, MCP servers have not been enabled yet
|
|
245
433
|
|
|
246
|
-
await openSession.restoreMcpSettings(
|
|
247
|
-
savedAgentProfile.profile.mcp_settings
|
|
248
|
-
);
|
|
249
434
|
return openSession;
|
|
250
435
|
}
|
|
251
436
|
|
|
@@ -270,15 +455,22 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
270
455
|
}
|
|
271
456
|
|
|
272
457
|
const sessionParticipants = new Map<string, TeamParticipant>();
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
458
|
+
if (sessionData.participants && sessionData.participants.length > 0) {
|
|
459
|
+
sessionData.participants.forEach((p) => {
|
|
460
|
+
sessionParticipants.set(p.user_uuid, p);
|
|
461
|
+
});
|
|
462
|
+
} else {
|
|
463
|
+
sessionParticipants.set(sessionData.user_uuid, {
|
|
464
|
+
user_uuid: sessionData.user_uuid,
|
|
465
|
+
nickname: ownerData.nickname || "",
|
|
466
|
+
email: ownerData.email,
|
|
467
|
+
role: "owner",
|
|
468
|
+
});
|
|
469
|
+
}
|
|
279
470
|
|
|
280
471
|
return OpenSession.init(
|
|
281
472
|
db,
|
|
473
|
+
false /* isPersisted */,
|
|
282
474
|
sessionData,
|
|
283
475
|
savedAgentProfile,
|
|
284
476
|
sessionMessages,
|
|
@@ -311,6 +503,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
311
503
|
} = await loadSessionData(db, sessionId);
|
|
312
504
|
return OpenSession.init(
|
|
313
505
|
db,
|
|
506
|
+
true /* isPersisted */,
|
|
314
507
|
sessionData,
|
|
315
508
|
savedAgentProfile,
|
|
316
509
|
sessionMessages,
|
|
@@ -328,22 +521,66 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
328
521
|
logger.info(`[OpenSession.onEmpty] session ${this.sessionUUID}`);
|
|
329
522
|
}
|
|
330
523
|
|
|
524
|
+
getDescriptor(): SessionDescriptor {
|
|
525
|
+
assert(
|
|
526
|
+
typeof this.accessToken === "string" ||
|
|
527
|
+
typeof this.accessToken === "undefined"
|
|
528
|
+
);
|
|
529
|
+
return {
|
|
530
|
+
session_uuid: this.sessionUUID,
|
|
531
|
+
title: this.sessionTitle,
|
|
532
|
+
agent_profile_uuid: this.agentProfileUUID,
|
|
533
|
+
user_uuid: this.userUUID,
|
|
534
|
+
team_uuid: this.teamUUID,
|
|
535
|
+
access_token: this.accessToken,
|
|
536
|
+
updated_at: this.sessionUpdatedAt,
|
|
537
|
+
agent_paused: this.agentPaused,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
331
541
|
getParticipants(): SessionParticipantMap {
|
|
332
542
|
return this.sessionParticipants;
|
|
333
543
|
}
|
|
334
544
|
|
|
335
|
-
sendSessionData(
|
|
545
|
+
async sendSessionData(
|
|
546
|
+
connectionId: string,
|
|
547
|
+
clientMessageId: string,
|
|
548
|
+
restoreMcpState: boolean
|
|
549
|
+
): Promise<void> {
|
|
336
550
|
logger.info(
|
|
337
551
|
`[SessionRegistry] sending session data for session ${this.sessionUUID}`
|
|
338
552
|
);
|
|
339
553
|
|
|
340
554
|
const sessionInfo = this.serverSessionInfo(clientMessageId);
|
|
341
|
-
this.connectionManager
|
|
555
|
+
const connMgr = this.sender.connectionManager;
|
|
556
|
+
connMgr.sendToConnection(connectionId, sessionInfo);
|
|
557
|
+
|
|
558
|
+
// This could be cleaner. If the session has just been created, we must
|
|
559
|
+
// restore the mcp servers. However, we cannot do that until the client
|
|
560
|
+
// has initialized itself (in case we need auth messages), hence it must
|
|
561
|
+
// happen at this stage, BEFORE we call sendMcpSettings below.
|
|
562
|
+
|
|
563
|
+
if (restoreMcpState) {
|
|
564
|
+
await this.restoreMcpSettings(
|
|
565
|
+
this.savedAgentProfile.profile.mcp_settings
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// send session file info
|
|
570
|
+
const fileDescriptors = this.sessionFileManager.listFiles();
|
|
571
|
+
fileDescriptors.forEach((descriptor) => {
|
|
572
|
+
connMgr.sendToConnection(connectionId, {
|
|
573
|
+
type: "session_file_changed",
|
|
574
|
+
session_id: this.sessionUUID,
|
|
575
|
+
descriptor,
|
|
576
|
+
new_file: false,
|
|
577
|
+
});
|
|
578
|
+
});
|
|
342
579
|
|
|
343
580
|
// send conversation history
|
|
344
581
|
const conversationMessages = this.contextManager.getConversationMessages();
|
|
345
582
|
conversationMessages.forEach((message) => {
|
|
346
|
-
|
|
583
|
+
connMgr.sendToConnection(connectionId, message);
|
|
347
584
|
});
|
|
348
585
|
|
|
349
586
|
// add MCP settings
|
|
@@ -351,12 +588,12 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
351
588
|
|
|
352
589
|
// add system prompt and model
|
|
353
590
|
const agentProfile = this.agent.getAgentProfile();
|
|
354
|
-
|
|
591
|
+
connMgr.sendToConnection(connectionId, {
|
|
355
592
|
type: "system_prompt_updated",
|
|
356
593
|
system_prompt: agentProfile.system_prompt,
|
|
357
594
|
session_id: this.sessionUUID,
|
|
358
595
|
});
|
|
359
|
-
|
|
596
|
+
connMgr.sendToConnection(connectionId, {
|
|
360
597
|
type: "model_updated",
|
|
361
598
|
model: agentProfile.model || "",
|
|
362
599
|
session_id: this.sessionUUID,
|
|
@@ -407,7 +644,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
407
644
|
skillManager.enableTool(server_name, enabled_tool);
|
|
408
645
|
}
|
|
409
646
|
} catch (e) {
|
|
410
|
-
this.broadcast({
|
|
647
|
+
this.sender.broadcast({
|
|
411
648
|
type: "session_error",
|
|
412
649
|
message: `Error adding MCP server ${server_name}: ${String(e)}`,
|
|
413
650
|
session_id: this.sessionUUID,
|
|
@@ -426,9 +663,9 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
426
663
|
private handleError(err: unknown, from?: string): boolean {
|
|
427
664
|
const sendError = (msg: ServerSessionError) => {
|
|
428
665
|
if (from) {
|
|
429
|
-
this.sendTo(from, msg);
|
|
666
|
+
this.sender.sendTo(from, msg);
|
|
430
667
|
} else {
|
|
431
|
-
this.broadcast(msg);
|
|
668
|
+
this.sender.broadcast(msg);
|
|
432
669
|
}
|
|
433
670
|
};
|
|
434
671
|
|
|
@@ -458,162 +695,29 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
458
695
|
return true;
|
|
459
696
|
}
|
|
460
697
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
this.
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
sendTo(user_uuid: string, msg: ServerToClient): void {
|
|
467
|
-
this.connectionManager.sendToUsers(new Set([user_uuid]), msg);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// IPlatform.openUrl
|
|
471
|
-
openUrl(url: string, authResultP: Promise<boolean>, display_name: string) {
|
|
472
|
-
// These requests are always passed to the original owner, since it is
|
|
473
|
-
// their settings that will be used for all MCP servers.
|
|
474
|
-
this.broadcast({
|
|
475
|
-
type: "authentication_started",
|
|
476
|
-
session_id: this.sessionUUID,
|
|
477
|
-
url,
|
|
478
|
-
});
|
|
479
|
-
this.sendTo(this.userUUID, {
|
|
480
|
-
type: "authenticate",
|
|
698
|
+
// ISessionFileManagerEventHandler.onFileDeleted
|
|
699
|
+
onFileDeleted(name: string): void {
|
|
700
|
+
this.sender.broadcast({
|
|
701
|
+
type: "session_file_deleted",
|
|
481
702
|
session_id: this.sessionUUID,
|
|
482
|
-
|
|
483
|
-
display_name,
|
|
703
|
+
name,
|
|
484
704
|
});
|
|
485
|
-
|
|
486
|
-
// TODO: auth timeout
|
|
487
|
-
// Don't stall this function waiting for authentication
|
|
488
|
-
void authResultP.then((result) => {
|
|
489
|
-
this.sendTo(this.userUUID, {
|
|
490
|
-
type: "authentication_finished",
|
|
491
|
-
session_id: this.sessionUUID,
|
|
492
|
-
url,
|
|
493
|
-
result,
|
|
494
|
-
});
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// IPlatform.load
|
|
499
|
-
load(filename: string): Promise<string> {
|
|
500
|
-
if (process.env.DEVELOPMENT === "1") {
|
|
501
|
-
return NODE_PLATFORM.load(filename);
|
|
502
|
-
}
|
|
503
|
-
throw new ChatErrorMessage("Platform.load not implemented");
|
|
504
705
|
}
|
|
505
706
|
|
|
506
|
-
//
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
707
|
+
// ISessionFileManagerEventHandler.onFileChanged
|
|
708
|
+
onFileChanged(entry: SessionFileEntry, new_file: boolean): void {
|
|
709
|
+
this.sender.broadcast({
|
|
710
|
+
type: "session_file_changed",
|
|
711
|
+
session_id: this.sessionUUID,
|
|
712
|
+
descriptor: {
|
|
713
|
+
name: entry.name,
|
|
714
|
+
mime_type: entry.mime_type,
|
|
715
|
+
summary: entry.summary,
|
|
716
|
+
},
|
|
717
|
+
new_file,
|
|
515
718
|
});
|
|
516
719
|
}
|
|
517
720
|
|
|
518
|
-
// IAgentEventHandler.onCompletion
|
|
519
|
-
onCompletion(result: ChatCompletionAssistantMessageParam): void {
|
|
520
|
-
logger.debug(`[OpenSession.onCompletion] : ${JSON.stringify(result)}`);
|
|
521
|
-
// Nothing to broadcast. Caller will receive this via onAgentMessage.
|
|
522
|
-
this.contextManager.processAgentResponse(result);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// IAgentEventHandler.onToolCallResult
|
|
526
|
-
onToolCallResult(result: ChatCompletionToolMessageParam): void {
|
|
527
|
-
logger.debug(`[onToolCallResult] : ${JSON.stringify(result)}`);
|
|
528
|
-
const toolCallMessage = this.contextManager.processToolCallResult(result);
|
|
529
|
-
this.broadcast(toolCallMessage);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// IAgentEventHandler.onToolCall
|
|
533
|
-
async onToolCall(
|
|
534
|
-
toolCall: ChatCompletionMessageToolCall,
|
|
535
|
-
agentTool: boolean
|
|
536
|
-
): Promise<boolean> {
|
|
537
|
-
if (agentTool) {
|
|
538
|
-
// "Agent" tools are considered internal to the agent, and are always
|
|
539
|
-
// permitted. Inform all clients and immediately approve.
|
|
540
|
-
this.broadcast({
|
|
541
|
-
type: "tool_call",
|
|
542
|
-
tool_call: toolCall,
|
|
543
|
-
session_id: this.sessionUUID,
|
|
544
|
-
});
|
|
545
|
-
return true;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// TODO: Need a proper mapping to/from MCP calls to tool names
|
|
549
|
-
|
|
550
|
-
const [serverName, tool] = toolCall.function.name.split("__");
|
|
551
|
-
const autoApproved = prefsGetAutoApprove(
|
|
552
|
-
this.agentProfilePreferences,
|
|
553
|
-
serverName,
|
|
554
|
-
tool
|
|
555
|
-
);
|
|
556
|
-
if (!autoApproved) {
|
|
557
|
-
const { id, resultP } = this.approvalManager.startApproval(
|
|
558
|
-
toolCall.function.name
|
|
559
|
-
);
|
|
560
|
-
this.broadcast({
|
|
561
|
-
type: "approve_tool_call",
|
|
562
|
-
id,
|
|
563
|
-
tool_call: toolCall,
|
|
564
|
-
session_id: this.sessionUUID,
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
try {
|
|
568
|
-
logger.debug(`[OpenSession.onToolCall] awaiting approval ${id}`);
|
|
569
|
-
const { approved, auto_approve } = await resultP;
|
|
570
|
-
logger.debug(
|
|
571
|
-
`[OpenSession.onToolCall] approval ${id}: ${String(approved)}`
|
|
572
|
-
);
|
|
573
|
-
if (auto_approve) {
|
|
574
|
-
logger.debug(
|
|
575
|
-
"[OpenSession.onToolCall] auto_approve set. updated preferences"
|
|
576
|
-
);
|
|
577
|
-
const autoApprovalMsg = await this.onSetAutoApproval(
|
|
578
|
-
serverName,
|
|
579
|
-
tool,
|
|
580
|
-
true
|
|
581
|
-
);
|
|
582
|
-
if (autoApprovalMsg) {
|
|
583
|
-
this.broadcast(autoApprovalMsg);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
return approved;
|
|
588
|
-
} catch (e) {
|
|
589
|
-
logger.debug(
|
|
590
|
-
`[OpenSession.onToolCall] error waiting for approval ${id}: ` +
|
|
591
|
-
String(e)
|
|
592
|
-
);
|
|
593
|
-
return false;
|
|
594
|
-
}
|
|
595
|
-
} else {
|
|
596
|
-
this.broadcast({
|
|
597
|
-
type: "tool_call",
|
|
598
|
-
tool_call: toolCall,
|
|
599
|
-
session_id: this.sessionUUID,
|
|
600
|
-
});
|
|
601
|
-
return true;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// IAgentEventHandler.onAgentMessage
|
|
606
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
607
|
-
async onAgentMessage(msg: string, end: boolean): Promise<void> {
|
|
608
|
-
logger.debug(
|
|
609
|
-
`[OpenSession.onAgentMessage] msg: ${msg}, end: ${String(end)}`
|
|
610
|
-
);
|
|
611
|
-
|
|
612
|
-
// Inform the contextManager and broadcast the ServerAgentMessageChunk
|
|
613
|
-
const agentMsgChunk = this.contextManager.processAgentMessage(msg, end);
|
|
614
|
-
this.broadcast(agentMsgChunk);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
721
|
/**
|
|
618
722
|
* Handle incoming session-related message from client.
|
|
619
723
|
* These messages include tool calls, MCP changes, etc.
|
|
@@ -630,7 +734,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
630
734
|
logger.warn(
|
|
631
735
|
`User ${from} not in session ${this.sessionUUID} - ignoring message`
|
|
632
736
|
);
|
|
633
|
-
this.sendTo(from, {
|
|
737
|
+
this.sender.sendTo(from, {
|
|
634
738
|
type: "session_error",
|
|
635
739
|
message: "You are not a participant in this session",
|
|
636
740
|
session_id: this.sessionUUID,
|
|
@@ -641,7 +745,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
641
745
|
|
|
642
746
|
// Enqueue message for processing
|
|
643
747
|
if (!this.messageQueue.tryEnqueue({ msg: message, from })) {
|
|
644
|
-
this.sendTo(
|
|
748
|
+
this.sender.sendTo(
|
|
645
749
|
from,
|
|
646
750
|
this.addSessionContext({
|
|
647
751
|
type: "session_error",
|
|
@@ -662,7 +766,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
662
766
|
const mcpServer = this.skillManager.getMcpServer(server_name);
|
|
663
767
|
const tools = mcpServer.getTools();
|
|
664
768
|
const enabled_tools = Array.from(mcpServer.getEnabledTools().keys());
|
|
665
|
-
this.connectionManager.sendToConnection(connectionId, {
|
|
769
|
+
this.sender.connectionManager.sendToConnection(connectionId, {
|
|
666
770
|
type: "mcp_server_added",
|
|
667
771
|
server_name,
|
|
668
772
|
tools,
|
|
@@ -693,70 +797,76 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
693
797
|
undefined;
|
|
694
798
|
switch (msg.type) {
|
|
695
799
|
case "msg":
|
|
696
|
-
broadcastMsg = this.
|
|
800
|
+
broadcastMsg = await this.handleUserMessage(msg, queuedMessage.from);
|
|
697
801
|
break;
|
|
698
802
|
case "add_mcp_server":
|
|
699
|
-
broadcastMsg = await this.
|
|
803
|
+
broadcastMsg = await this.handleAddMcpServer(
|
|
700
804
|
msg.server_name,
|
|
701
805
|
msg.enable_all
|
|
702
806
|
);
|
|
703
807
|
break;
|
|
704
808
|
case "remove_mcp_server":
|
|
705
|
-
broadcastMsg = await this.
|
|
809
|
+
broadcastMsg = await this.handleRemoveMcpServer(msg.server_name);
|
|
706
810
|
break;
|
|
707
811
|
case "enable_mcp_server_tool":
|
|
708
|
-
broadcastMsg = await this.
|
|
812
|
+
broadcastMsg = await this.handleEnableMcpServerTool(
|
|
709
813
|
msg.server_name,
|
|
710
814
|
msg.tool
|
|
711
815
|
);
|
|
712
816
|
break;
|
|
713
817
|
case "disable_mcp_server_tool":
|
|
714
|
-
broadcastMsg = await this.
|
|
818
|
+
broadcastMsg = await this.handleDisableMcpServerTool(
|
|
715
819
|
msg.server_name,
|
|
716
820
|
msg.tool
|
|
717
821
|
);
|
|
718
822
|
break;
|
|
719
823
|
case "enable_all_mcp_server_tools":
|
|
720
|
-
broadcastMsg = await this.
|
|
824
|
+
broadcastMsg = await this.handleEnableAllMcpServerTools(
|
|
825
|
+
msg.server_name
|
|
826
|
+
);
|
|
721
827
|
break;
|
|
722
828
|
case "disable_all_mcp_server_tools":
|
|
723
|
-
broadcastMsg = await this.
|
|
829
|
+
broadcastMsg = await this.handleDisableAllMcpServerTools(
|
|
830
|
+
msg.server_name
|
|
831
|
+
);
|
|
724
832
|
break;
|
|
725
833
|
case "tool_call_approval_result":
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
)
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
result: msg.result,
|
|
737
|
-
session_id: this.sessionUUID,
|
|
738
|
-
};
|
|
739
|
-
}
|
|
834
|
+
this.approvalManager.onApprovalResult(msg);
|
|
835
|
+
break;
|
|
836
|
+
case "session_file_get_content":
|
|
837
|
+
void this.handleSessionFileGetContent(msg, queuedMessage.from);
|
|
838
|
+
break;
|
|
839
|
+
case "session_file_delete":
|
|
840
|
+
await this.handleSessionFileDelete(msg);
|
|
841
|
+
break;
|
|
842
|
+
case "session_file_put_content":
|
|
843
|
+
await this.handleSessionFilePutContent(msg);
|
|
740
844
|
break;
|
|
741
845
|
case "set_auto_approval":
|
|
742
|
-
broadcastMsg = await this.
|
|
846
|
+
broadcastMsg = await this.approvalManager.setAutoApprove(
|
|
743
847
|
msg.server_name,
|
|
744
848
|
msg.tool,
|
|
745
849
|
msg.auto_approve
|
|
746
850
|
);
|
|
747
851
|
break;
|
|
748
852
|
case "set_system_prompt":
|
|
749
|
-
broadcastMsg = await this.
|
|
853
|
+
broadcastMsg = await this.handleSetSystemPrompt(msg.system_prompt);
|
|
750
854
|
break;
|
|
751
855
|
case "set_model":
|
|
752
|
-
broadcastMsg = await this.
|
|
856
|
+
broadcastMsg = await this.handleSetModel(msg.model);
|
|
857
|
+
break;
|
|
858
|
+
case "set_agent_paused":
|
|
859
|
+
broadcastMsg = await this.handleSetAgentPaused(msg.paused);
|
|
753
860
|
break;
|
|
754
861
|
case "set_workspace":
|
|
755
|
-
await this.
|
|
862
|
+
await this.handleSetWorkspace(msg, queuedMessage.from);
|
|
756
863
|
break;
|
|
757
864
|
case "set_mcp_server_config":
|
|
758
865
|
await this.handleSetMcpServerConfig(msg, queuedMessage.from);
|
|
759
866
|
break;
|
|
867
|
+
case "share_session":
|
|
868
|
+
await this.handleShareSession(msg, queuedMessage.from);
|
|
869
|
+
break;
|
|
760
870
|
default: {
|
|
761
871
|
const exhaustive: never = msg; // Error => non-exhaustive switch-case.
|
|
762
872
|
return exhaustive;
|
|
@@ -766,10 +876,10 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
766
876
|
if (broadcastMsg) {
|
|
767
877
|
if (broadcastMsg instanceof Array) {
|
|
768
878
|
broadcastMsg.map((msg) => {
|
|
769
|
-
this.broadcast(msg);
|
|
879
|
+
this.sender.broadcast(msg);
|
|
770
880
|
});
|
|
771
881
|
} else {
|
|
772
|
-
this.broadcast(broadcastMsg);
|
|
882
|
+
this.sender.broadcast(broadcastMsg);
|
|
773
883
|
}
|
|
774
884
|
}
|
|
775
885
|
} catch (err: unknown) {
|
|
@@ -779,7 +889,28 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
779
889
|
}
|
|
780
890
|
}
|
|
781
891
|
|
|
782
|
-
private async
|
|
892
|
+
private async handleShareSession(
|
|
893
|
+
msg: ClientShareSession & ClientSessionMessageBase,
|
|
894
|
+
from: string
|
|
895
|
+
) {
|
|
896
|
+
if (!this.accessToken) {
|
|
897
|
+
const accessToken = ApiKeyManager.createApiKeyWithPayload(
|
|
898
|
+
GUEST_TOKEN_PREFIX,
|
|
899
|
+
this.sessionUUID
|
|
900
|
+
);
|
|
901
|
+
await this.db.sessionUpdateAccessToken(this.sessionUUID, accessToken);
|
|
902
|
+
this.accessToken = accessToken;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
this.sender.sendTo(from, {
|
|
906
|
+
type: "session_shared",
|
|
907
|
+
access_token: this.accessToken,
|
|
908
|
+
client_message_id: msg.client_message_id,
|
|
909
|
+
session_id: this.sessionUUID,
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
private async handleSetWorkspace(
|
|
783
914
|
msg: ClientSetWorkspace,
|
|
784
915
|
sender: string
|
|
785
916
|
): Promise<void> {
|
|
@@ -799,18 +930,28 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
799
930
|
assert(this.isPersisted);
|
|
800
931
|
}
|
|
801
932
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
933
|
+
/**
|
|
934
|
+
* `processUserMessage` logic when agent is paused. Trigger the context,
|
|
935
|
+
* add the user messages and then extract the new DB messages.
|
|
936
|
+
*/
|
|
937
|
+
private processUserMessagePaused(
|
|
938
|
+
msgs: ServerUserMessage[]
|
|
939
|
+
): SessionMessage[] {
|
|
940
|
+
const { llmUserMessages } = this.contextManager.startAgentResponse(msgs);
|
|
941
|
+
|
|
942
|
+
// Just send the user LLM messages direct to the ContextManager, so they
|
|
943
|
+
// are available to the LLM once it is restarted.
|
|
807
944
|
|
|
808
|
-
|
|
945
|
+
this.contextManager.addMessages(llmUserMessages);
|
|
946
|
+
return this.contextManager.endAgentResponse();
|
|
947
|
+
}
|
|
809
948
|
|
|
949
|
+
private async processUserMessagesActive(
|
|
950
|
+
msgs: ServerUserMessage[]
|
|
951
|
+
): Promise<SessionMessage[]> {
|
|
810
952
|
const { llmUserMessages, agentFirstChunk } =
|
|
811
953
|
this.contextManager.startAgentResponse(msgs);
|
|
812
|
-
this.broadcast(agentFirstChunk);
|
|
813
|
-
|
|
954
|
+
this.sender.broadcast(agentFirstChunk);
|
|
814
955
|
try {
|
|
815
956
|
await this.agent.userMessagesRaw(llmUserMessages);
|
|
816
957
|
} catch (e) {
|
|
@@ -821,22 +962,26 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
821
962
|
// Errors during agent replies must be turned into messages.
|
|
822
963
|
|
|
823
964
|
const errMsg = `error from LLM: ${String(e)}`;
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
this.broadcast(err);
|
|
827
|
-
return;
|
|
965
|
+
this.contextManager.revertAgentResponse(errMsg);
|
|
966
|
+
throw new Error(errMsg);
|
|
828
967
|
}
|
|
968
|
+
return this.contextManager.endAgentResponse();
|
|
969
|
+
}
|
|
829
970
|
|
|
830
|
-
|
|
971
|
+
private async processUserMessages(msgs: ServerUserMessage[]): Promise<void> {
|
|
972
|
+
try {
|
|
973
|
+
const newSessionMessages = this.agentPaused
|
|
974
|
+
? this.processUserMessagePaused(msgs)
|
|
975
|
+
: await this.processUserMessagesActive(msgs);
|
|
831
976
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
977
|
+
logger.debug(
|
|
978
|
+
"[processUserMessages] newSessionMessages: " +
|
|
979
|
+
JSON.stringify(newSessionMessages)
|
|
980
|
+
);
|
|
836
981
|
|
|
837
|
-
try {
|
|
838
982
|
// Append to in-memory conversation and write to the DB
|
|
839
|
-
|
|
983
|
+
const dbsm = this.db.createTypedClient(DbSessionMessages);
|
|
984
|
+
await dbsm.append(this.sessionUUID, newSessionMessages);
|
|
840
985
|
} catch (e) {
|
|
841
986
|
if (!this.handleError(e)) {
|
|
842
987
|
throw e;
|
|
@@ -844,10 +989,10 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
844
989
|
}
|
|
845
990
|
}
|
|
846
991
|
|
|
847
|
-
private
|
|
992
|
+
private async handleUserMessage(
|
|
848
993
|
msg: ClientUserMessage,
|
|
849
994
|
from: string
|
|
850
|
-
): ServerUserMessage | undefined {
|
|
995
|
+
): Promise<ServerUserMessage | undefined> {
|
|
851
996
|
// Return a ServerUserMessage for broadcast. The actual message is places
|
|
852
997
|
// on a queue to be dealt with in another loop. This allows Agent
|
|
853
998
|
// processing of user messages to depend on other messages.
|
|
@@ -857,13 +1002,23 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
857
1002
|
|
|
858
1003
|
// Assign the user message_idx and attempt to enqueue.
|
|
859
1004
|
|
|
860
|
-
const
|
|
861
|
-
|
|
1005
|
+
const user = this.sessionParticipants.get(from);
|
|
1006
|
+
if (!user) {
|
|
1007
|
+
throw new Error(`unrecognized user ${from}`);
|
|
1008
|
+
}
|
|
1009
|
+
const userMessage = this.contextManager.processUserMessage(
|
|
1010
|
+
msg,
|
|
1011
|
+
from,
|
|
1012
|
+
user.nickname
|
|
1013
|
+
);
|
|
1014
|
+
if (!userMessage) {
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
862
1017
|
// Special case for the first message of the session
|
|
863
1018
|
|
|
864
1019
|
if (userMessage.message_idx === MESSAGE_INDEX_START_VALUE) {
|
|
865
1020
|
// No need to wait for this to complete before broadcasting.
|
|
866
|
-
|
|
1021
|
+
await this.onFirstMessage(userMessage);
|
|
867
1022
|
}
|
|
868
1023
|
|
|
869
1024
|
if (!this.userMessageQueue.tryEnqueue(userMessage)) {
|
|
@@ -874,7 +1029,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
874
1029
|
|
|
875
1030
|
this.contextManager.unprocessUserMessage(userMessage);
|
|
876
1031
|
|
|
877
|
-
this.sendTo(from, {
|
|
1032
|
+
this.sender.sendTo(from, {
|
|
878
1033
|
type: "session_error",
|
|
879
1034
|
message: "failed to queue message. try again later.",
|
|
880
1035
|
session_id: this.sessionUUID,
|
|
@@ -893,14 +1048,20 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
893
1048
|
title: this.sessionTitle,
|
|
894
1049
|
team_uuid: this.teamUUID,
|
|
895
1050
|
agent_profile_uuid: this.agentProfileUUID,
|
|
896
|
-
workspace: this.workspaceUserMessageData(),
|
|
897
1051
|
user_uuid: this.userUUID,
|
|
1052
|
+
agent_paused: false,
|
|
898
1053
|
};
|
|
899
1054
|
logger.info(
|
|
900
1055
|
`[OpenSession.onFirstMessage] writing session ${this.sessionUUID}`
|
|
901
1056
|
);
|
|
902
1057
|
|
|
903
1058
|
await this.db.sessionCreate(sessionCreateData);
|
|
1059
|
+
|
|
1060
|
+
const workspace = this.workspaceUserMessageData();
|
|
1061
|
+
if (workspace) {
|
|
1062
|
+
await this.db.sessionUpdateWorkspace(this.sessionUUID, workspace);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
904
1065
|
this.isPersisted = true;
|
|
905
1066
|
}
|
|
906
1067
|
|
|
@@ -919,17 +1080,42 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
919
1080
|
}
|
|
920
1081
|
assert(this.isPersisted);
|
|
921
1082
|
|
|
922
|
-
//
|
|
1083
|
+
// Send session created notification
|
|
1084
|
+
const sessionInfo = this.serverSessionInfo("");
|
|
1085
|
+
|
|
1086
|
+
if (this.teamUUID) {
|
|
1087
|
+
// Team session: notify all members about the new session
|
|
1088
|
+
try {
|
|
1089
|
+
const teamMembers = await this.db.teamGetMembers(this.teamUUID);
|
|
1090
|
+
const teamMemberIds = new Set(teamMembers.map((m) => m.user_uuid));
|
|
923
1091
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1092
|
+
this.sender.connectionManager.sendToUsers(teamMemberIds, sessionInfo);
|
|
1093
|
+
|
|
1094
|
+
logger.info(
|
|
1095
|
+
`[OpenSession] notified ${String(teamMemberIds.size)} team members` +
|
|
1096
|
+
` about new session ${this.sessionUUID} in team ${this.teamUUID}`
|
|
1097
|
+
);
|
|
1098
|
+
} catch (error) {
|
|
1099
|
+
logger.error(
|
|
1100
|
+
"[OpenSession] Error notifying team members about session" +
|
|
1101
|
+
`${this.sessionUUID}: ${String(error)}`
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
} else {
|
|
1105
|
+
// If this is a user session, notify the session owner
|
|
1106
|
+
this.sender.connectionManager.sendToUsers(
|
|
1107
|
+
new Set([this.userUUID]),
|
|
1108
|
+
sessionInfo
|
|
1109
|
+
);
|
|
1110
|
+
|
|
1111
|
+
logger.info(
|
|
1112
|
+
`[OpenSession] notified session owner ${this.userUUID} about ` +
|
|
1113
|
+
`new session ${this.sessionUUID}`
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
930
1116
|
}
|
|
931
1117
|
|
|
932
|
-
private async
|
|
1118
|
+
private async handleAddMcpServer(
|
|
933
1119
|
serverName: string,
|
|
934
1120
|
enableAll: boolean
|
|
935
1121
|
): Promise<ServerMcpServerAdded> {
|
|
@@ -967,7 +1153,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
967
1153
|
};
|
|
968
1154
|
}
|
|
969
1155
|
|
|
970
|
-
private async
|
|
1156
|
+
private async handleRemoveMcpServer(
|
|
971
1157
|
server_name: string
|
|
972
1158
|
): Promise<ServerMcpServerRemoved> {
|
|
973
1159
|
logger.info(`[onRemoveMcpServer]: Removing server ${server_name}`);
|
|
@@ -986,7 +1172,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
986
1172
|
};
|
|
987
1173
|
}
|
|
988
1174
|
|
|
989
|
-
private async
|
|
1175
|
+
private async handleEnableMcpServerTool(
|
|
990
1176
|
server_name: string,
|
|
991
1177
|
tool: string
|
|
992
1178
|
): Promise<ServerMcpServerToolEnabled> {
|
|
@@ -1003,7 +1189,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
1003
1189
|
};
|
|
1004
1190
|
}
|
|
1005
1191
|
|
|
1006
|
-
private async
|
|
1192
|
+
private async handleDisableMcpServerTool(
|
|
1007
1193
|
server_name: string,
|
|
1008
1194
|
tool: string
|
|
1009
1195
|
): Promise<ServerMcpServerToolDisabled> {
|
|
@@ -1020,7 +1206,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
1020
1206
|
};
|
|
1021
1207
|
}
|
|
1022
1208
|
|
|
1023
|
-
private async
|
|
1209
|
+
private async handleEnableAllMcpServerTools(
|
|
1024
1210
|
server_name: string
|
|
1025
1211
|
): Promise<ServerMcpServerToolEnabled[]> {
|
|
1026
1212
|
// We reimplement the logic to enable any disabled tools so we can
|
|
@@ -1046,7 +1232,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
1046
1232
|
return msgs;
|
|
1047
1233
|
}
|
|
1048
1234
|
|
|
1049
|
-
private async
|
|
1235
|
+
private async handleDisableAllMcpServerTools(
|
|
1050
1236
|
server_name: string
|
|
1051
1237
|
): Promise<ServerMcpServerToolDisabled[]> {
|
|
1052
1238
|
// We reimplement the logic to disable all enabled tools so we can
|
|
@@ -1070,7 +1256,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
1070
1256
|
return msgs;
|
|
1071
1257
|
}
|
|
1072
1258
|
|
|
1073
|
-
private async
|
|
1259
|
+
private async handleSetSystemPrompt(
|
|
1074
1260
|
system_prompt: string
|
|
1075
1261
|
): Promise<ServerSystemPromptUpdated> {
|
|
1076
1262
|
this.agent.setSystemPrompt(system_prompt);
|
|
@@ -1082,12 +1268,20 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
1082
1268
|
};
|
|
1083
1269
|
}
|
|
1084
1270
|
|
|
1085
|
-
private async
|
|
1271
|
+
private async handleSetModel(model: string): Promise<ServerModelUpdated> {
|
|
1086
1272
|
this.agent.setModel(model);
|
|
1087
1273
|
await this.updateAgentProfile();
|
|
1088
1274
|
return { type: "model_updated", model, session_id: this.sessionUUID };
|
|
1089
1275
|
}
|
|
1090
1276
|
|
|
1277
|
+
private async handleSetAgentPaused(
|
|
1278
|
+
paused: boolean
|
|
1279
|
+
): Promise<ServerAgentPaused> {
|
|
1280
|
+
this.agentPaused = paused;
|
|
1281
|
+
await this.db.sessionSetAgentPaused(this.sessionUUID, paused);
|
|
1282
|
+
return { type: "agent_paused", session_id: this.sessionUUID, paused };
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1091
1285
|
private async handleSetMcpServerConfig(
|
|
1092
1286
|
msg: ClientSetMcpServerConfig,
|
|
1093
1287
|
from: string
|
|
@@ -1120,32 +1314,48 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
1120
1314
|
// TODO: Do we want to braodcast an "mcp_server_config_updated" message?
|
|
1121
1315
|
}
|
|
1122
1316
|
|
|
1123
|
-
private async
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
)
|
|
1136
|
-
|
|
1137
|
-
this.agentProfileUUID,
|
|
1138
|
-
this.agentProfilePreferences
|
|
1139
|
-
);
|
|
1317
|
+
private async handleSessionFileGetContent(
|
|
1318
|
+
msg: Extract<ClientToServer, { type: "session_file_get_content" }>,
|
|
1319
|
+
from: string
|
|
1320
|
+
): Promise<void> {
|
|
1321
|
+
const data_url = await this.sessionFileManager.getFileContent(msg.name);
|
|
1322
|
+
const contentMsg: ServerSessionFileContent = {
|
|
1323
|
+
type: "session_file_content",
|
|
1324
|
+
session_id: this.sessionUUID,
|
|
1325
|
+
client_message_id: msg.client_message_id,
|
|
1326
|
+
name: msg.name,
|
|
1327
|
+
data_url,
|
|
1328
|
+
};
|
|
1329
|
+
this.sender.sendTo(from, contentMsg);
|
|
1330
|
+
}
|
|
1140
1331
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1332
|
+
private async handleSessionFileDelete(
|
|
1333
|
+
msg: Extract<ClientToServer, { type: "session_file_delete" }>
|
|
1334
|
+
): Promise<void> {
|
|
1335
|
+
await this.sessionFileManager.deleteFile(msg.name);
|
|
1336
|
+
// Note, the SessionFileManager will call us back via
|
|
1337
|
+
// `ISessionFileManagerEventHandler.onFileDeleted` if the deletion
|
|
1338
|
+
// succeeds. We broadcast in that callback.
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
private async handleSessionFilePutContent(
|
|
1342
|
+
msg: Extract<ClientToServer, { type: "session_file_put_content" }>
|
|
1343
|
+
): Promise<void> {
|
|
1344
|
+
// If the session hasn't been persisted, it must be written to the DB,
|
|
1345
|
+
// (otherwise the sessionId in the SessionFiles entry will not be
|
|
1346
|
+
// considered valid byt he DB).
|
|
1347
|
+
|
|
1348
|
+
if (!this.isPersisted) {
|
|
1349
|
+
await this.createSessionInDB();
|
|
1148
1350
|
}
|
|
1351
|
+
await this.sessionFileManager.putFileContent(
|
|
1352
|
+
msg.name,
|
|
1353
|
+
msg.summary,
|
|
1354
|
+
msg.data_url
|
|
1355
|
+
);
|
|
1356
|
+
// Note, the SessionFileManager will call us back via
|
|
1357
|
+
// `ISessionFileManagerEventHandler.onFileChanged` if the deletion
|
|
1358
|
+
// succeeds. We broadcast in that callback.
|
|
1149
1359
|
}
|
|
1150
1360
|
|
|
1151
1361
|
private ensureMcpServer(serverName: string): McpServerInfo {
|
|
@@ -1175,19 +1385,19 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
1175
1385
|
* This only updates the local participant map - actual membership
|
|
1176
1386
|
* tracking is handled by SessionRegistry.
|
|
1177
1387
|
*/
|
|
1178
|
-
addParticipant(userId: string,
|
|
1179
|
-
this.sessionParticipants.set(userId,
|
|
1388
|
+
addParticipant(userId: string, participant: TeamParticipant): void {
|
|
1389
|
+
this.sessionParticipants.set(userId, participant);
|
|
1180
1390
|
// Broadcast result to all session participants
|
|
1181
1391
|
const broadcastMessage: ServerUserAdded = {
|
|
1182
1392
|
type: "user_added",
|
|
1183
1393
|
user_uuid: userId,
|
|
1184
1394
|
role: "participant",
|
|
1185
|
-
nickname:
|
|
1186
|
-
email:
|
|
1395
|
+
nickname: participant.nickname,
|
|
1396
|
+
email: participant.email,
|
|
1187
1397
|
session_id: this.sessionUUID,
|
|
1188
1398
|
};
|
|
1189
1399
|
|
|
1190
|
-
this.broadcast(broadcastMessage);
|
|
1400
|
+
this.sender.broadcast(broadcastMessage);
|
|
1191
1401
|
}
|
|
1192
1402
|
|
|
1193
1403
|
/**
|
|
@@ -1204,7 +1414,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
1204
1414
|
session_id: this.sessionUUID,
|
|
1205
1415
|
};
|
|
1206
1416
|
|
|
1207
|
-
this.broadcast(broadcastMessage);
|
|
1417
|
+
this.sender.broadcast(broadcastMessage);
|
|
1208
1418
|
}
|
|
1209
1419
|
|
|
1210
1420
|
private getSessionParticipants(): TeamParticipant[] {
|
|
@@ -1239,6 +1449,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
|
|
|
1239
1449
|
client_message_id: clientMessageId,
|
|
1240
1450
|
session_id: this.sessionUUID,
|
|
1241
1451
|
team_uuid: this.teamUUID,
|
|
1452
|
+
agent_paused: this.agentPaused,
|
|
1242
1453
|
};
|
|
1243
1454
|
}
|
|
1244
1455
|
|
|
@@ -1302,10 +1513,11 @@ async function loadSessionData(
|
|
|
1302
1513
|
ownerApiKey: string;
|
|
1303
1514
|
sessionParticipants: SessionParticipantMap;
|
|
1304
1515
|
}> {
|
|
1516
|
+
const dbsm = db.createTypedClient(DbSessionMessages);
|
|
1305
1517
|
const [sessionData, sessionMessages, sessionParticipants, sessionCheckpoint] =
|
|
1306
1518
|
await Promise.all([
|
|
1307
|
-
db.
|
|
1308
|
-
|
|
1519
|
+
db.sessionGetDescriptorById(sessionId),
|
|
1520
|
+
dbsm.getConversation(sessionId, DEFAULT_NUM_MESSGAES, 0),
|
|
1309
1521
|
db.sessionGetParticipants(sessionId),
|
|
1310
1522
|
db.sessionCheckpointGet(sessionId),
|
|
1311
1523
|
]);
|
|
@@ -1330,3 +1542,72 @@ async function loadSessionData(
|
|
|
1330
1542
|
sessionParticipants: createSessionParticipantMap(sessionParticipants),
|
|
1331
1543
|
};
|
|
1332
1544
|
}
|
|
1545
|
+
|
|
1546
|
+
async function createContextAndAgent(
|
|
1547
|
+
sessionUUID: string,
|
|
1548
|
+
systemPrompt: string,
|
|
1549
|
+
model: string,
|
|
1550
|
+
sessionMessages: SessionMessage[],
|
|
1551
|
+
workspace: UserMessageData | undefined,
|
|
1552
|
+
sessionCheckpoint: SessionCheckpoint | undefined,
|
|
1553
|
+
ownerData: UserData,
|
|
1554
|
+
ownerApiKey: string,
|
|
1555
|
+
llmUrl: string,
|
|
1556
|
+
xmcpUrl: string,
|
|
1557
|
+
fileManager: ChatSessionFileManager,
|
|
1558
|
+
sender: ISessionMessageSender<ServerToClient>,
|
|
1559
|
+
platform: IPlatform,
|
|
1560
|
+
approvalManager: ToolApprovalManager
|
|
1561
|
+
): Promise<{
|
|
1562
|
+
agent: Agent;
|
|
1563
|
+
skillManager: SkillManager;
|
|
1564
|
+
contextManager: ChatContextManager;
|
|
1565
|
+
}> {
|
|
1566
|
+
const contextManager = new ChatContextManager(
|
|
1567
|
+
systemPrompt,
|
|
1568
|
+
sessionMessages,
|
|
1569
|
+
sessionUUID,
|
|
1570
|
+
ownerData.uuid,
|
|
1571
|
+
sessionCheckpoint,
|
|
1572
|
+
llmUrl,
|
|
1573
|
+
model,
|
|
1574
|
+
ownerApiKey,
|
|
1575
|
+
undefined as unknown as DBCheckpointWriter, // TODO
|
|
1576
|
+
fileManager
|
|
1577
|
+
);
|
|
1578
|
+
if (workspace) {
|
|
1579
|
+
contextManager.setWorkspace(
|
|
1580
|
+
createUserMessage(workspace.message, workspace.imageB64)
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
const eventHandler = new ChatSessionAgentEventHandler(
|
|
1585
|
+
sessionUUID,
|
|
1586
|
+
sender,
|
|
1587
|
+
approvalManager,
|
|
1588
|
+
contextManager
|
|
1589
|
+
);
|
|
1590
|
+
|
|
1591
|
+
const xmcpConfig = Configuration.new(ownerApiKey, xmcpUrl, false);
|
|
1592
|
+
const [agent, skillManager] = await createAgentWithoutSkills(
|
|
1593
|
+
llmUrl,
|
|
1594
|
+
model,
|
|
1595
|
+
eventHandler,
|
|
1596
|
+
platform,
|
|
1597
|
+
contextManager,
|
|
1598
|
+
ownerApiKey,
|
|
1599
|
+
xmcpConfig,
|
|
1600
|
+
undefined,
|
|
1601
|
+
true
|
|
1602
|
+
);
|
|
1603
|
+
await addDefaultChatTools(
|
|
1604
|
+
agent,
|
|
1605
|
+
ownerData.timezone,
|
|
1606
|
+
platform,
|
|
1607
|
+
fileManager,
|
|
1608
|
+
llmUrl,
|
|
1609
|
+
ownerApiKey
|
|
1610
|
+
);
|
|
1611
|
+
|
|
1612
|
+
return { agent, skillManager, contextManager };
|
|
1613
|
+
}
|