@xalia/agent 0.6.0 → 0.6.2

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 (112) hide show
  1. package/dist/agent/src/agent/agent.js +103 -54
  2. package/dist/agent/src/agent/agentUtils.js +22 -21
  3. package/dist/agent/src/agent/compressingContextManager.js +3 -2
  4. package/dist/agent/src/agent/dummyLLM.js +1 -3
  5. package/dist/agent/src/agent/imageGenLLM.js +67 -0
  6. package/dist/agent/src/agent/imageGenerator.js +43 -0
  7. package/dist/agent/src/agent/llm.js +27 -0
  8. package/dist/agent/src/agent/mcpServerManager.js +18 -6
  9. package/dist/agent/src/agent/nullAgentEventHandler.js +6 -0
  10. package/dist/agent/src/agent/openAILLM.js +3 -3
  11. package/dist/agent/src/agent/openAILLMStreaming.js +41 -6
  12. package/dist/agent/src/chat/client/chatClient.js +84 -13
  13. package/dist/agent/src/chat/client/sessionClient.js +47 -6
  14. package/dist/agent/src/chat/client/sessionFiles.js +102 -0
  15. package/dist/agent/src/chat/data/apiKeyManager.js +38 -7
  16. package/dist/agent/src/chat/data/database.js +83 -70
  17. package/dist/agent/src/chat/data/dbSessionFileModels.js +49 -0
  18. package/dist/agent/src/chat/data/dbSessionFiles.js +76 -0
  19. package/dist/agent/src/chat/data/dbSessionMessages.js +57 -0
  20. package/dist/agent/src/chat/data/mimeTypes.js +44 -0
  21. package/dist/agent/src/chat/protocol/messages.js +21 -0
  22. package/dist/agent/src/chat/server/chatContextManager.js +14 -7
  23. package/dist/agent/src/chat/server/connectionManager.js +14 -36
  24. package/dist/agent/src/chat/server/connectionManager.test.js +2 -16
  25. package/dist/agent/src/chat/server/conversation.js +69 -45
  26. package/dist/agent/src/chat/server/imageGeneratorTools.js +111 -0
  27. package/dist/agent/src/chat/server/openSession.js +205 -43
  28. package/dist/agent/src/chat/server/server.js +5 -8
  29. package/dist/agent/src/chat/server/sessionFileManager.js +171 -38
  30. package/dist/agent/src/chat/server/sessionRegistry.js +199 -32
  31. package/dist/agent/src/chat/server/test-utils/mockFactories.js +12 -11
  32. package/dist/agent/src/chat/server/tools.js +27 -6
  33. package/dist/agent/src/chat/utils/multiAsyncQueue.js +9 -1
  34. package/dist/agent/src/test/agent.test.js +15 -11
  35. package/dist/agent/src/test/chatContextManager.test.js +4 -0
  36. package/dist/agent/src/test/clientServerConnection.test.js +2 -2
  37. package/dist/agent/src/test/db.test.js +33 -70
  38. package/dist/agent/src/test/dbSessionFiles.test.js +179 -0
  39. package/dist/agent/src/test/dbSessionMessages.test.js +67 -0
  40. package/dist/agent/src/test/dbTestTools.js +6 -5
  41. package/dist/agent/src/test/imageLoad.test.js +1 -1
  42. package/dist/agent/src/test/mcpServerManager.test.js +1 -1
  43. package/dist/agent/src/test/multiAsyncQueue.test.js +50 -0
  44. package/dist/agent/src/test/testTools.js +12 -0
  45. package/dist/agent/src/tool/agentChat.js +25 -6
  46. package/dist/agent/src/tool/agentMain.js +1 -1
  47. package/dist/agent/src/tool/chatMain.js +113 -4
  48. package/dist/agent/src/tool/commandPrompt.js +7 -3
  49. package/dist/agent/src/tool/files.js +23 -15
  50. package/dist/agent/src/tool/options.js +2 -2
  51. package/package.json +1 -1
  52. package/scripts/test_chat +124 -66
  53. package/src/agent/agent.ts +145 -38
  54. package/src/agent/agentUtils.ts +27 -21
  55. package/src/agent/compressingContextManager.ts +5 -4
  56. package/src/agent/context.ts +1 -1
  57. package/src/agent/dummyLLM.ts +1 -3
  58. package/src/agent/iAgentEventHandler.ts +15 -2
  59. package/src/agent/imageGenLLM.ts +99 -0
  60. package/src/agent/imageGenerator.ts +60 -0
  61. package/src/agent/llm.ts +128 -4
  62. package/src/agent/mcpServerManager.ts +26 -7
  63. package/src/agent/nullAgentEventHandler.ts +6 -0
  64. package/src/agent/openAILLM.ts +3 -8
  65. package/src/agent/openAILLMStreaming.ts +60 -14
  66. package/src/chat/client/chatClient.ts +119 -14
  67. package/src/chat/client/sessionClient.ts +75 -9
  68. package/src/chat/client/sessionFiles.ts +145 -0
  69. package/src/chat/data/apiKeyManager.ts +55 -7
  70. package/src/chat/data/dataModels.ts +16 -7
  71. package/src/chat/data/database.ts +107 -92
  72. package/src/chat/data/dbSessionFileModels.ts +91 -0
  73. package/src/chat/data/dbSessionFiles.ts +99 -0
  74. package/src/chat/data/dbSessionMessages.ts +68 -0
  75. package/src/chat/data/mimeTypes.ts +58 -0
  76. package/src/chat/protocol/messages.ts +127 -13
  77. package/src/chat/server/chatContextManager.ts +36 -13
  78. package/src/chat/server/connectionManager.test.ts +1 -22
  79. package/src/chat/server/connectionManager.ts +18 -53
  80. package/src/chat/server/conversation.ts +96 -57
  81. package/src/chat/server/imageGeneratorTools.ts +138 -0
  82. package/src/chat/server/openSession.ts +287 -49
  83. package/src/chat/server/server.ts +5 -11
  84. package/src/chat/server/sessionFileManager.ts +223 -63
  85. package/src/chat/server/sessionRegistry.ts +285 -41
  86. package/src/chat/server/test-utils/mockFactories.ts +13 -13
  87. package/src/chat/server/tools.ts +43 -8
  88. package/src/chat/utils/agentSessionMap.ts +2 -2
  89. package/src/chat/utils/multiAsyncQueue.ts +11 -1
  90. package/src/test/agent.test.ts +23 -14
  91. package/src/test/chatContextManager.test.ts +7 -2
  92. package/src/test/clientServerConnection.test.ts +3 -3
  93. package/src/test/compressingContextManager.test.ts +1 -1
  94. package/src/test/context.test.ts +2 -1
  95. package/src/test/conversation.test.ts +1 -1
  96. package/src/test/db.test.ts +41 -83
  97. package/src/test/dbSessionFiles.test.ts +258 -0
  98. package/src/test/dbSessionMessages.test.ts +85 -0
  99. package/src/test/dbTestTools.ts +9 -5
  100. package/src/test/imageLoad.test.ts +2 -2
  101. package/src/test/mcpServerManager.test.ts +3 -1
  102. package/src/test/multiAsyncQueue.test.ts +58 -0
  103. package/src/test/testTools.ts +15 -1
  104. package/src/tool/agentChat.ts +35 -7
  105. package/src/tool/agentMain.ts +7 -7
  106. package/src/tool/chatMain.ts +126 -5
  107. package/src/tool/commandPrompt.ts +10 -5
  108. package/src/tool/files.ts +30 -13
  109. package/src/tool/options.ts +1 -1
  110. package/test_data/dummyllm_script_image_gen.json +19 -0
  111. package/test_data/dummyllm_script_invoke_image_gen_tool.json +30 -0
  112. package/test_data/image_gen_test_profile.json +5 -0
@@ -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
 
@@ -11,20 +12,16 @@ import {
11
12
  SavedAgentProfile,
12
13
  } from "@xalia/xmcp/sdk";
13
14
 
15
+ import { Agent, createUserMessage } from "../../agent/agent";
14
16
  import {
15
- Agent,
16
17
  ChatCompletionAssistantMessageParam,
17
18
  ChatCompletionMessageToolCall,
18
19
  ChatCompletionToolMessageParam,
19
- createUserMessage,
20
- } from "../../agent/agent";
20
+ } from "../../agent/llm";
21
21
  import { SkillManager } from "../../agent/sudoMcpServerManager";
22
22
  import { McpServerInfo } from "../../agent/mcpServerManager";
23
23
  import { IAgentEventHandler } from "../../agent/iAgentEventHandler";
24
- import {
25
- createAgentWithoutSkills,
26
- DEFAULT_LLM_MODEL,
27
- } from "../../agent/agentUtils";
24
+ import { createAgentWithoutSkills } from "../../agent/agentUtils";
28
25
  import { IPlatform } from "../../agent/iplatform";
29
26
 
30
27
  import type {
@@ -45,7 +42,12 @@ import type {
45
42
  ServerSessionInfo,
46
43
  ServerSessionUpdate,
47
44
  ClientSetWorkspace,
45
+ ClientToServer,
46
+ ServerSessionFileContent,
48
47
  ClientSetMcpServerConfig,
48
+ ClientShareSession,
49
+ ClientSessionMessageBase,
50
+ ServerAgentPaused,
49
51
  } from "../protocol/messages";
50
52
  import { AsyncQueue } from "../utils/asyncQueue";
51
53
  import { MultiAsyncQueue } from "../utils/multiAsyncQueue";
@@ -57,6 +59,7 @@ import {
57
59
  TeamParticipant,
58
60
  SessionCreateData,
59
61
  UserMessageData,
62
+ SessionDescriptor,
60
63
  } from "../data/dataModels";
61
64
  import {
62
65
  Database,
@@ -71,17 +74,32 @@ import {
71
74
  llmUserMessageToUserMessageData,
72
75
  MESSAGE_INDEX_START_VALUE,
73
76
  } from "./conversation";
74
- import { ChatSessionFileManager } from "./sessionFileManager";
77
+ import {
78
+ ChatSessionFileManager,
79
+ ISessionFileManager,
80
+ ISessionFileManagerEventHandler,
81
+ } from "./sessionFileManager";
75
82
  import { IUserConnectionManager } from "./connectionManager";
76
83
  import { getErrorString } from "./errorUtils";
77
84
  import { NODE_PLATFORM } from "../../tool/nodePlatform";
78
85
  import { DbMcpServerConfigs } from "../data/dbMcpServerConfigs";
86
+ import { SessionFileEntry } from "../data/dbSessionFileModels";
87
+ import { ApiKeyManager } from "../data/apiKeyManager";
88
+ import { DbSessionMessages } from "../data/dbSessionMessages";
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<
@@ -119,7 +137,9 @@ class DBCheckpointWriter implements ICheckpointWriter {
119
137
  * be seen until user messages had been fully processed, which could block
120
138
  * tool approvals and other interactions).
121
139
  */
122
- export class OpenSession implements IAgentEventHandler, IPlatform {
140
+ export class OpenSession
141
+ implements IAgentEventHandler, ISessionFileManagerEventHandler, IPlatform
142
+ {
123
143
  private readonly db: Database;
124
144
  private /* readonly */ agent: Agent;
125
145
  private readonly sessionUUID: string;
@@ -135,9 +155,12 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
135
155
  private readonly contextManager: ChatContextManager;
136
156
  private readonly approvalManager: ApprovalManager;
137
157
  private readonly savedAgentProfile: SavedAgentProfile;
158
+ private readonly sessionFileManager: ISessionFileManager;
138
159
  private isPersisted: boolean;
160
+ private accessToken: string | undefined;
139
161
  private sessionTitle: string;
140
162
  private sessionUpdatedAt: string;
163
+ private agentPaused: boolean;
141
164
 
142
165
  private constructor(
143
166
  db: Database,
@@ -150,7 +173,8 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
150
173
  skillManager: SkillManager,
151
174
  contextManager: ChatContextManager,
152
175
  connectionManager: IUserConnectionManager<ServerToClient>,
153
- approvalManager: ApprovalManager = new ApprovalManager()
176
+ approvalManager: ApprovalManager,
177
+ fileManager: ISessionFileManager
154
178
  ) {
155
179
  this.db = db;
156
180
  this.agent = agent;
@@ -158,6 +182,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
158
182
  this.teamUUID = sessionData.team_uuid;
159
183
  this.userUUID = sessionData.user_uuid;
160
184
  this.agentProfileUUID = sessionData.agent_profile_uuid;
185
+ this.accessToken = sessionData.access_token;
161
186
  this.sessionParticipants = sessionParticipants;
162
187
  this.agentProfilePreferences = agentProfilePreferences;
163
188
  this.skillManager = skillManager;
@@ -172,13 +197,18 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
172
197
  this.contextManager = contextManager;
173
198
  this.approvalManager = approvalManager;
174
199
  this.savedAgentProfile = savedAgentProfile;
200
+ this.sessionFileManager = fileManager;
175
201
  this.isPersisted = isPersisted;
176
202
  this.sessionTitle = sessionData.title;
177
203
  this.sessionUpdatedAt = sessionData.updated_at;
204
+ this.agentPaused = sessionData.agent_paused;
205
+
206
+ fileManager.addEventHandler(this);
178
207
  }
179
208
 
180
209
  private static async init(
181
210
  db: Database,
211
+ isPersisted: boolean,
182
212
  sessionData: SessionData,
183
213
  savedAgentProfile: SavedAgentProfile,
184
214
  sessionMessages: SessionMessage[],
@@ -191,7 +221,8 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
191
221
  connectionManager: IUserConnectionManager<ServerToClient>
192
222
  ): Promise<OpenSession> {
193
223
  const sessionId = sessionData.session_uuid;
194
- const fileManager = new ChatSessionFileManager(sessionId, db);
224
+
225
+ const fileManager = await ChatSessionFileManager.init(db, sessionId);
195
226
  const contextManager = new ChatContextManager(
196
227
  savedAgentProfile.profile.system_prompt,
197
228
  sessionMessages,
@@ -199,7 +230,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
199
230
  ownerData.uuid,
200
231
  sessionCheckpoint,
201
232
  llmUrl,
202
- savedAgentProfile.profile.model || DEFAULT_LLM_MODEL,
233
+ savedAgentProfile.profile.model || DEFAULT_CHAT_LLM_MODEL,
203
234
  ownerApiKey,
204
235
  new DBCheckpointWriter(db, sessionId),
205
236
  fileManager
@@ -214,12 +245,14 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
214
245
  {} as Agent, // Placeholder - will be replaced after agent creation
215
246
  sessionData,
216
247
  savedAgentProfile,
217
- false /* isPersisted */,
248
+ isPersisted,
218
249
  sessionParticipants,
219
250
  savedAgentProfile.preferences,
220
251
  {} as SkillManager, // Placeholder - will be replaced after agent creation
221
252
  contextManager,
222
- connectionManager
253
+ connectionManager,
254
+ new ApprovalManager(),
255
+ fileManager
223
256
  );
224
257
 
225
258
  // Initialize an empty agent (to ensure there are no callbacks before
@@ -229,6 +262,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
229
262
  const [agent, skillManager] = await createAgentWithoutSkills(
230
263
  llmUrl,
231
264
  savedAgentProfile.profile,
265
+ DEFAULT_CHAT_LLM_MODEL,
232
266
  openSession,
233
267
  openSession,
234
268
  contextManager,
@@ -237,7 +271,14 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
237
271
  undefined,
238
272
  true
239
273
  );
240
- await addDefaultChatTools(agent, ownerData.timezone, openSession);
274
+ await addDefaultChatTools(
275
+ agent,
276
+ ownerData.timezone,
277
+ openSession,
278
+ fileManager,
279
+ llmUrl,
280
+ ownerApiKey
281
+ );
241
282
 
242
283
  // Update OpenSession with real agent and skillManager
243
284
  openSession.agent = agent;
@@ -279,6 +320,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
279
320
 
280
321
  return OpenSession.init(
281
322
  db,
323
+ false /* isPersisted */,
282
324
  sessionData,
283
325
  savedAgentProfile,
284
326
  sessionMessages,
@@ -311,6 +353,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
311
353
  } = await loadSessionData(db, sessionId);
312
354
  return OpenSession.init(
313
355
  db,
356
+ true /* isPersisted */,
314
357
  sessionData,
315
358
  savedAgentProfile,
316
359
  sessionMessages,
@@ -328,6 +371,23 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
328
371
  logger.info(`[OpenSession.onEmpty] session ${this.sessionUUID}`);
329
372
  }
330
373
 
374
+ getDescriptor(): SessionDescriptor {
375
+ assert(
376
+ typeof this.accessToken === "string" ||
377
+ typeof this.accessToken === "undefined"
378
+ );
379
+ return {
380
+ session_uuid: this.sessionUUID,
381
+ title: this.sessionTitle,
382
+ agent_profile_uuid: this.agentProfileUUID,
383
+ user_uuid: this.userUUID,
384
+ team_uuid: this.teamUUID,
385
+ access_token: this.accessToken,
386
+ updated_at: this.sessionUpdatedAt,
387
+ agent_paused: this.agentPaused,
388
+ };
389
+ }
390
+
331
391
  getParticipants(): SessionParticipantMap {
332
392
  return this.sessionParticipants;
333
393
  }
@@ -340,6 +400,17 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
340
400
  const sessionInfo = this.serverSessionInfo(clientMessageId);
341
401
  this.connectionManager.sendToConnection(connectionId, sessionInfo);
342
402
 
403
+ // send session file info
404
+ const fileDescriptors = this.sessionFileManager.listFiles();
405
+ fileDescriptors.forEach((descriptor) => {
406
+ this.connectionManager.sendToConnection(connectionId, {
407
+ type: "session_file_changed",
408
+ session_id: this.sessionUUID,
409
+ descriptor,
410
+ new_file: false,
411
+ });
412
+ });
413
+
343
414
  // send conversation history
344
415
  const conversationMessages = this.contextManager.getConversationMessages();
345
416
  conversationMessages.forEach((message) => {
@@ -522,6 +593,12 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
522
593
  this.contextManager.processAgentResponse(result);
523
594
  }
524
595
 
596
+ // IAgentEventHandler.onImage
597
+ onImage(image: OpenAI.Chat.Completions.ChatCompletionContentPartImage): void {
598
+ logger.debug(`[OpenSession.onImage] : ${image.image_url.url}`);
599
+ throw new Error("[OpenSession.onImage] unimplemented");
600
+ }
601
+
525
602
  // IAgentEventHandler.onToolCallResult
526
603
  onToolCallResult(result: ChatCompletionToolMessageParam): void {
527
604
  logger.debug(`[onToolCallResult] : ${JSON.stringify(result)}`);
@@ -614,6 +691,44 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
614
691
  this.broadcast(agentMsgChunk);
615
692
  }
616
693
 
694
+ // IAgentEventHandler.onReasoning
695
+ onReasoning(reasoning: string): Promise<void> {
696
+ return new Promise<void>((r) => {
697
+ logger.debug(`[OpenSession.onReasoning]${reasoning}`);
698
+ if (reasoning.length > 0) {
699
+ this.broadcast({
700
+ type: "agent_reasoning_chunk",
701
+ reasoning,
702
+ session_id: this.sessionUUID,
703
+ });
704
+ }
705
+ r();
706
+ });
707
+ }
708
+
709
+ // ISessionFileManagerEventHandler.onFileDeleted
710
+ onFileDeleted(name: string): void {
711
+ this.broadcast({
712
+ type: "session_file_deleted",
713
+ session_id: this.sessionUUID,
714
+ name,
715
+ });
716
+ }
717
+
718
+ // ISessionFileManagerEventHandler.onFileChanged
719
+ onFileChanged(entry: SessionFileEntry, new_file: boolean): void {
720
+ this.broadcast({
721
+ type: "session_file_changed",
722
+ session_id: this.sessionUUID,
723
+ descriptor: {
724
+ name: entry.name,
725
+ mime_type: entry.mime_type,
726
+ summary: entry.summary,
727
+ },
728
+ new_file,
729
+ });
730
+ }
731
+
617
732
  /**
618
733
  * Handle incoming session-related message from client.
619
734
  * These messages include tool calls, MCP changes, etc.
@@ -693,34 +808,38 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
693
808
  undefined;
694
809
  switch (msg.type) {
695
810
  case "msg":
696
- broadcastMsg = this.onUserMessage(msg, queuedMessage.from);
811
+ broadcastMsg = this.handleUserMessage(msg, queuedMessage.from);
697
812
  break;
698
813
  case "add_mcp_server":
699
- broadcastMsg = await this.onAddMcpServer(
814
+ broadcastMsg = await this.handleAddMcpServer(
700
815
  msg.server_name,
701
816
  msg.enable_all
702
817
  );
703
818
  break;
704
819
  case "remove_mcp_server":
705
- broadcastMsg = await this.onRemoveMcpServer(msg.server_name);
820
+ broadcastMsg = await this.handleRemoveMcpServer(msg.server_name);
706
821
  break;
707
822
  case "enable_mcp_server_tool":
708
- broadcastMsg = await this.onEnableMcpServerTool(
823
+ broadcastMsg = await this.handleEnableMcpServerTool(
709
824
  msg.server_name,
710
825
  msg.tool
711
826
  );
712
827
  break;
713
828
  case "disable_mcp_server_tool":
714
- broadcastMsg = await this.onDisableMcpServerTool(
829
+ broadcastMsg = await this.handleDisableMcpServerTool(
715
830
  msg.server_name,
716
831
  msg.tool
717
832
  );
718
833
  break;
719
834
  case "enable_all_mcp_server_tools":
720
- broadcastMsg = await this.onEnableAllMcpServerTools(msg.server_name);
835
+ broadcastMsg = await this.handleEnableAllMcpServerTools(
836
+ msg.server_name
837
+ );
721
838
  break;
722
839
  case "disable_all_mcp_server_tools":
723
- broadcastMsg = await this.onDisableAllMcpServerTools(msg.server_name);
840
+ broadcastMsg = await this.handleDisableAllMcpServerTools(
841
+ msg.server_name
842
+ );
724
843
  break;
725
844
  case "tool_call_approval_result":
726
845
  if (
@@ -738,6 +857,15 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
738
857
  };
739
858
  }
740
859
  break;
860
+ case "session_file_get_content":
861
+ void this.handleSessionFileGetContent(msg, queuedMessage.from);
862
+ break;
863
+ case "session_file_delete":
864
+ await this.handleSessionFileDelete(msg);
865
+ break;
866
+ case "session_file_put_content":
867
+ await this.handleSessionFilePutContent(msg);
868
+ break;
741
869
  case "set_auto_approval":
742
870
  broadcastMsg = await this.onSetAutoApproval(
743
871
  msg.server_name,
@@ -746,17 +874,23 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
746
874
  );
747
875
  break;
748
876
  case "set_system_prompt":
749
- broadcastMsg = await this.onSetSystemPrompt(msg.system_prompt);
877
+ broadcastMsg = await this.handleSetSystemPrompt(msg.system_prompt);
750
878
  break;
751
879
  case "set_model":
752
- broadcastMsg = await this.onSetModel(msg.model);
880
+ broadcastMsg = await this.handleSetModel(msg.model);
881
+ break;
882
+ case "set_agent_paused":
883
+ broadcastMsg = await this.handleSetAgentPaused(msg.paused);
753
884
  break;
754
885
  case "set_workspace":
755
- await this.onSetWorkspace(msg, queuedMessage.from);
886
+ await this.handleSetWorkspace(msg, queuedMessage.from);
756
887
  break;
757
888
  case "set_mcp_server_config":
758
889
  await this.handleSetMcpServerConfig(msg, queuedMessage.from);
759
890
  break;
891
+ case "share_session":
892
+ await this.handleShareSession(msg, queuedMessage.from);
893
+ break;
760
894
  default: {
761
895
  const exhaustive: never = msg; // Error => non-exhaustive switch-case.
762
896
  return exhaustive;
@@ -779,7 +913,28 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
779
913
  }
780
914
  }
781
915
 
782
- private async onSetWorkspace(
916
+ private async handleShareSession(
917
+ msg: ClientShareSession & ClientSessionMessageBase,
918
+ from: string
919
+ ) {
920
+ if (!this.accessToken) {
921
+ const accessToken = ApiKeyManager.createApiKeyWithPayload(
922
+ GUEST_TOKEN_PREFIX,
923
+ this.sessionUUID
924
+ );
925
+ await this.db.sessionUpdateAccessToken(this.sessionUUID, accessToken);
926
+ this.accessToken = accessToken;
927
+ }
928
+
929
+ this.sendTo(from, {
930
+ type: "session_shared",
931
+ access_token: this.accessToken,
932
+ client_message_id: msg.client_message_id,
933
+ session_id: this.sessionUUID,
934
+ });
935
+ }
936
+
937
+ private async handleSetWorkspace(
783
938
  msg: ClientSetWorkspace,
784
939
  sender: string
785
940
  ): Promise<void> {
@@ -799,18 +954,32 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
799
954
  assert(this.isPersisted);
800
955
  }
801
956
 
802
- private async processUserMessages(msgs: ServerUserMessage[]): Promise<void> {
803
- if (msgs.length === 0) {
804
- logger.debug("ignoring empty messages");
805
- return;
806
- }
957
+ /**
958
+ * `processUserMessage` logic when agent is paused. Trigger the context,
959
+ * add the user messages and then extract the new DB messages.
960
+ */
961
+ private processUserMessagePaused(
962
+ msgs: ServerUserMessage[]
963
+ ): SessionMessage[] {
964
+ const { llmUserMessages } = this.contextManager.startAgentResponse(msgs);
965
+
966
+ // Just send the user LLM messages direct to the ContextManager, so they
967
+ // are available to the LLM once it is restarted.
807
968
 
808
- // Begin an agent response using the accumulated user messages.
969
+ this.contextManager.addMessages(llmUserMessages);
970
+ return this.contextManager.endAgentResponse();
971
+ }
809
972
 
973
+ /**
974
+ * `processUserMessage` logic when agent is active. Start the Agent loop,
975
+ * adding all agent messages to the context. Extract the new DB messages.
976
+ */
977
+ private async processUserMessagesActive(
978
+ msgs: ServerUserMessage[]
979
+ ): Promise<SessionMessage[]> {
810
980
  const { llmUserMessages, agentFirstChunk } =
811
981
  this.contextManager.startAgentResponse(msgs);
812
982
  this.broadcast(agentFirstChunk);
813
-
814
983
  try {
815
984
  await this.agent.userMessagesRaw(llmUserMessages);
816
985
  } catch (e) {
@@ -824,10 +993,16 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
824
993
  await this.onAgentMessage(errMsg, true);
825
994
  const err = this.contextManager.revertAgentResponse(errMsg);
826
995
  this.broadcast(err);
827
- return;
996
+
997
+ // return await this.processuserMessagesActive(msgs);
828
998
  }
999
+ return this.contextManager.endAgentResponse();
1000
+ }
829
1001
 
830
- const newSessionMessages = this.contextManager.endAgentResponse();
1002
+ private async processUserMessages(msgs: ServerUserMessage[]): Promise<void> {
1003
+ const newSessionMessages = this.agentPaused
1004
+ ? this.processUserMessagePaused(msgs)
1005
+ : await this.processUserMessagesActive(msgs);
831
1006
 
832
1007
  logger.debug(
833
1008
  "[processUserMessages] newSessionMessages: " +
@@ -836,7 +1011,8 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
836
1011
 
837
1012
  try {
838
1013
  // Append to in-memory conversation and write to the DB
839
- await this.db.sessionMessagesAppend(this.sessionUUID, newSessionMessages);
1014
+ const dbsm = this.db.createTypedClient(DbSessionMessages);
1015
+ await dbsm.append(this.sessionUUID, newSessionMessages);
840
1016
  } catch (e) {
841
1017
  if (!this.handleError(e)) {
842
1018
  throw e;
@@ -844,7 +1020,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
844
1020
  }
845
1021
  }
846
1022
 
847
- private onUserMessage(
1023
+ private handleUserMessage(
848
1024
  msg: ClientUserMessage,
849
1025
  from: string
850
1026
  ): ServerUserMessage | undefined {
@@ -858,7 +1034,9 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
858
1034
  // Assign the user message_idx and attempt to enqueue.
859
1035
 
860
1036
  const userMessage = this.contextManager.processUserMessage(msg, from);
861
-
1037
+ if (!userMessage) {
1038
+ return;
1039
+ }
862
1040
  // Special case for the first message of the session
863
1041
 
864
1042
  if (userMessage.message_idx === MESSAGE_INDEX_START_VALUE) {
@@ -893,14 +1071,20 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
893
1071
  title: this.sessionTitle,
894
1072
  team_uuid: this.teamUUID,
895
1073
  agent_profile_uuid: this.agentProfileUUID,
896
- workspace: this.workspaceUserMessageData(),
897
1074
  user_uuid: this.userUUID,
1075
+ agent_paused: false,
898
1076
  };
899
1077
  logger.info(
900
1078
  `[OpenSession.onFirstMessage] writing session ${this.sessionUUID}`
901
1079
  );
902
1080
 
903
1081
  await this.db.sessionCreate(sessionCreateData);
1082
+
1083
+ const workspace = this.workspaceUserMessageData();
1084
+ if (workspace) {
1085
+ await this.db.sessionUpdateWorkspace(this.sessionUUID, workspace);
1086
+ }
1087
+
904
1088
  this.isPersisted = true;
905
1089
  }
906
1090
 
@@ -929,7 +1113,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
929
1113
  this.broadcast(msg);
930
1114
  }
931
1115
 
932
- private async onAddMcpServer(
1116
+ private async handleAddMcpServer(
933
1117
  serverName: string,
934
1118
  enableAll: boolean
935
1119
  ): Promise<ServerMcpServerAdded> {
@@ -967,7 +1151,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
967
1151
  };
968
1152
  }
969
1153
 
970
- private async onRemoveMcpServer(
1154
+ private async handleRemoveMcpServer(
971
1155
  server_name: string
972
1156
  ): Promise<ServerMcpServerRemoved> {
973
1157
  logger.info(`[onRemoveMcpServer]: Removing server ${server_name}`);
@@ -986,7 +1170,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
986
1170
  };
987
1171
  }
988
1172
 
989
- private async onEnableMcpServerTool(
1173
+ private async handleEnableMcpServerTool(
990
1174
  server_name: string,
991
1175
  tool: string
992
1176
  ): Promise<ServerMcpServerToolEnabled> {
@@ -1003,7 +1187,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
1003
1187
  };
1004
1188
  }
1005
1189
 
1006
- private async onDisableMcpServerTool(
1190
+ private async handleDisableMcpServerTool(
1007
1191
  server_name: string,
1008
1192
  tool: string
1009
1193
  ): Promise<ServerMcpServerToolDisabled> {
@@ -1020,7 +1204,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
1020
1204
  };
1021
1205
  }
1022
1206
 
1023
- private async onEnableAllMcpServerTools(
1207
+ private async handleEnableAllMcpServerTools(
1024
1208
  server_name: string
1025
1209
  ): Promise<ServerMcpServerToolEnabled[]> {
1026
1210
  // We reimplement the logic to enable any disabled tools so we can
@@ -1046,7 +1230,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
1046
1230
  return msgs;
1047
1231
  }
1048
1232
 
1049
- private async onDisableAllMcpServerTools(
1233
+ private async handleDisableAllMcpServerTools(
1050
1234
  server_name: string
1051
1235
  ): Promise<ServerMcpServerToolDisabled[]> {
1052
1236
  // We reimplement the logic to disable all enabled tools so we can
@@ -1070,7 +1254,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
1070
1254
  return msgs;
1071
1255
  }
1072
1256
 
1073
- private async onSetSystemPrompt(
1257
+ private async handleSetSystemPrompt(
1074
1258
  system_prompt: string
1075
1259
  ): Promise<ServerSystemPromptUpdated> {
1076
1260
  this.agent.setSystemPrompt(system_prompt);
@@ -1082,12 +1266,20 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
1082
1266
  };
1083
1267
  }
1084
1268
 
1085
- private async onSetModel(model: string): Promise<ServerModelUpdated> {
1269
+ private async handleSetModel(model: string): Promise<ServerModelUpdated> {
1086
1270
  this.agent.setModel(model);
1087
1271
  await this.updateAgentProfile();
1088
1272
  return { type: "model_updated", model, session_id: this.sessionUUID };
1089
1273
  }
1090
1274
 
1275
+ private async handleSetAgentPaused(
1276
+ paused: boolean
1277
+ ): Promise<ServerAgentPaused> {
1278
+ this.agentPaused = paused;
1279
+ await this.db.sessionSetAgentPaused(this.sessionUUID, paused);
1280
+ return { type: "agent_paused", session_id: this.sessionUUID, paused };
1281
+ }
1282
+
1091
1283
  private async handleSetMcpServerConfig(
1092
1284
  msg: ClientSetMcpServerConfig,
1093
1285
  from: string
@@ -1120,6 +1312,50 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
1120
1312
  // TODO: Do we want to braodcast an "mcp_server_config_updated" message?
1121
1313
  }
1122
1314
 
1315
+ private async handleSessionFileGetContent(
1316
+ msg: Extract<ClientToServer, { type: "session_file_get_content" }>,
1317
+ from: string
1318
+ ): Promise<void> {
1319
+ const data_url = await this.sessionFileManager.getFileContent(msg.name);
1320
+ const contentMsg: ServerSessionFileContent = {
1321
+ type: "session_file_content",
1322
+ session_id: this.sessionUUID,
1323
+ client_message_id: msg.client_message_id,
1324
+ name: msg.name,
1325
+ data_url,
1326
+ };
1327
+ this.sendTo(from, contentMsg);
1328
+ }
1329
+
1330
+ private async handleSessionFileDelete(
1331
+ msg: Extract<ClientToServer, { type: "session_file_delete" }>
1332
+ ): Promise<void> {
1333
+ await this.sessionFileManager.deleteFile(msg.name);
1334
+ // Note, the SessionFileManager will call us back via
1335
+ // `ISessionFileManagerEventHandler.onFileDeleted` if the deletion
1336
+ // succeeds. We broadcast in that callback.
1337
+ }
1338
+
1339
+ private async handleSessionFilePutContent(
1340
+ msg: Extract<ClientToServer, { type: "session_file_put_content" }>
1341
+ ): Promise<void> {
1342
+ // If the session hasn't been persisted, it must be written to the DB,
1343
+ // (otherwise the sessionId in the SessionFiles entry will not be
1344
+ // considered valid byt he DB).
1345
+
1346
+ if (!this.isPersisted) {
1347
+ await this.createSessionInDB();
1348
+ }
1349
+ await this.sessionFileManager.putFileContent(
1350
+ msg.name,
1351
+ msg.summary,
1352
+ msg.data_url
1353
+ );
1354
+ // Note, the SessionFileManager will call us back via
1355
+ // `ISessionFileManagerEventHandler.onFileChanged` if the deletion
1356
+ // succeeds. We broadcast in that callback.
1357
+ }
1358
+
1123
1359
  private async onSetAutoApproval(
1124
1360
  serverName: string,
1125
1361
  tool: string,
@@ -1239,6 +1475,7 @@ export class OpenSession implements IAgentEventHandler, IPlatform {
1239
1475
  client_message_id: clientMessageId,
1240
1476
  session_id: this.sessionUUID,
1241
1477
  team_uuid: this.teamUUID,
1478
+ agent_paused: this.agentPaused,
1242
1479
  };
1243
1480
  }
1244
1481
 
@@ -1302,10 +1539,11 @@ async function loadSessionData(
1302
1539
  ownerApiKey: string;
1303
1540
  sessionParticipants: SessionParticipantMap;
1304
1541
  }> {
1542
+ const dbsm = db.createTypedClient(DbSessionMessages);
1305
1543
  const [sessionData, sessionMessages, sessionParticipants, sessionCheckpoint] =
1306
1544
  await Promise.all([
1307
- db.sessionGetById(sessionId),
1308
- db.sessionMessagesGetConversation(sessionId, DEFAULT_NUM_MESSGAES, 0),
1545
+ db.sessionGetDescriptorById(sessionId),
1546
+ dbsm.getConversation(sessionId, DEFAULT_NUM_MESSGAES, 0),
1309
1547
  db.sessionGetParticipants(sessionId),
1310
1548
  db.sessionCheckpointGet(sessionId),
1311
1549
  ]);