@xalia/agent 0.6.9 → 0.6.11

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 (199) hide show
  1. package/README.md +11 -0
  2. package/dist/agent/src/agent/agent.js +77 -18
  3. package/dist/agent/src/agent/agentUtils.js +3 -2
  4. package/dist/agent/src/agent/documentSummarizer.js +126 -0
  5. package/dist/agent/src/agent/dummyLLM.js +25 -22
  6. package/dist/agent/src/agent/imageGenLLM.js +22 -19
  7. package/dist/agent/src/agent/llm.js +1 -1
  8. package/dist/agent/src/agent/openAILLM.js +15 -12
  9. package/dist/agent/src/agent/openAILLMStreaming.js +68 -37
  10. package/dist/agent/src/agent/repeatLLM.js +16 -7
  11. package/dist/agent/src/agent/tokenCounter.js +390 -0
  12. package/dist/agent/src/agent/tokenCounter.test.js +206 -0
  13. package/dist/agent/src/agent/toolSettings.js +17 -0
  14. package/dist/agent/src/agent/tools/calculatorTool.js +45 -0
  15. package/dist/agent/src/agent/tools/contentExtractors/pdfToText.js +55 -0
  16. package/dist/agent/src/agent/tools/datetimeTool.js +38 -0
  17. package/dist/agent/src/agent/tools/fileManager/fileManagerTool.js +156 -0
  18. package/dist/agent/src/agent/tools/fileManager/index.js +31 -0
  19. package/dist/agent/src/agent/tools/fileManager/memoryFileManager.js +102 -0
  20. package/dist/agent/src/{chat/data → agent/tools/fileManager}/mimeTypes.js +3 -1
  21. package/dist/agent/src/agent/tools/fileManager/prompt.js +33 -0
  22. package/dist/agent/src/{chat/data/dbSessionFileModels.js → agent/tools/fileManager/types.js} +7 -0
  23. package/dist/agent/src/agent/tools/index.js +64 -0
  24. package/dist/agent/src/agent/tools/openUrlTool.js +57 -0
  25. package/dist/agent/src/agent/tools/renderTool.js +89 -0
  26. package/dist/agent/src/agent/tools/utils.js +61 -0
  27. package/dist/agent/src/{chat/utils/search.js → agent/tools/webSearch.js} +1 -2
  28. package/dist/agent/src/agent/tools/webSearchTool.js +40 -0
  29. package/dist/agent/src/chat/client/chatClient.js +28 -0
  30. package/dist/agent/src/chat/client/index.js +4 -1
  31. package/dist/agent/src/chat/client/sessionClient.js +28 -2
  32. package/dist/agent/src/chat/constants.js +8 -0
  33. package/dist/agent/src/chat/data/dbSessionFiles.js +11 -6
  34. package/dist/agent/src/chat/protocol/messages.js +5 -0
  35. package/dist/agent/src/chat/server/chatContextManager.js +45 -25
  36. package/dist/agent/src/chat/server/conversation.js +3 -0
  37. package/dist/agent/src/chat/server/imageGeneratorTools.js +20 -8
  38. package/dist/agent/src/chat/server/openAIRouterLLM.js +0 -3
  39. package/dist/agent/src/chat/server/openSession.js +218 -55
  40. package/dist/agent/src/chat/server/promptRefiner.js +86 -0
  41. package/dist/agent/src/chat/server/server.js +5 -1
  42. package/dist/agent/src/chat/server/sessionFileManager.js +22 -221
  43. package/dist/agent/src/chat/server/sessionRegistry.js +87 -0
  44. package/dist/agent/src/chat/server/titleGenerator.js +112 -0
  45. package/dist/agent/src/chat/server/titleGenerator.test.js +113 -0
  46. package/dist/agent/src/chat/server/tools.js +63 -287
  47. package/dist/agent/src/chat/utils/approvalManager.js +6 -3
  48. package/dist/agent/src/chat/utils/multiAsyncQueue.js +3 -0
  49. package/dist/agent/src/test/agent.test.js +16 -17
  50. package/dist/agent/src/test/chatContextManager.test.js +15 -3
  51. package/dist/agent/src/test/dbMcpServerConfigs.test.js +4 -4
  52. package/dist/agent/src/test/dbSessionFiles.test.js +17 -17
  53. package/dist/agent/src/test/testTools.js +6 -1
  54. package/dist/agent/src/test/tools.test.js +27 -9
  55. package/dist/agent/src/tool/agentChat.js +5 -2
  56. package/dist/agent/src/tool/chatMain.js +34 -7
  57. package/dist/agent/src/tool/commandPrompt.js +2 -2
  58. package/dist/agent/src/tool/files.js +7 -8
  59. package/package.json +8 -2
  60. package/.env.development +0 -1
  61. package/.prettierrc.json +0 -11
  62. package/dist/agent/src/agent/tools.js +0 -44
  63. package/eslint.config.mjs +0 -38
  64. package/scripts/chat_server +0 -8
  65. package/scripts/git_message +0 -31
  66. package/scripts/git_wip +0 -21
  67. package/scripts/pr_message +0 -18
  68. package/scripts/pr_review +0 -16
  69. package/scripts/setup_chat +0 -90
  70. package/scripts/shutdown_chat_server +0 -42
  71. package/scripts/start_chat_server +0 -24
  72. package/scripts/sudomcp_import +0 -23
  73. package/scripts/test_chat +0 -308
  74. package/src/agent/agent.ts +0 -624
  75. package/src/agent/agentUtils.ts +0 -285
  76. package/src/agent/compressingContextManager.ts +0 -129
  77. package/src/agent/context.ts +0 -265
  78. package/src/agent/contextWithWorkspace.ts +0 -162
  79. package/src/agent/dummyLLM.ts +0 -126
  80. package/src/agent/iAgentEventHandler.ts +0 -64
  81. package/src/agent/imageGenLLM.ts +0 -97
  82. package/src/agent/imageGenerator.ts +0 -45
  83. package/src/agent/iplatform.ts +0 -18
  84. package/src/agent/llm.ts +0 -74
  85. package/src/agent/mcpServerManager.ts +0 -541
  86. package/src/agent/nullAgentEventHandler.ts +0 -26
  87. package/src/agent/nullPlatform.ts +0 -13
  88. package/src/agent/openAI.ts +0 -123
  89. package/src/agent/openAILLM.ts +0 -95
  90. package/src/agent/openAILLMStreaming.ts +0 -609
  91. package/src/agent/promptProvider.ts +0 -87
  92. package/src/agent/repeatLLM.ts +0 -50
  93. package/src/agent/sudoMcpServerManager.ts +0 -361
  94. package/src/agent/tokenAuth.ts +0 -50
  95. package/src/agent/tools.ts +0 -57
  96. package/src/chat/client/chatClient.ts +0 -922
  97. package/src/chat/client/connection.test.ts +0 -241
  98. package/src/chat/client/connection.ts +0 -286
  99. package/src/chat/client/constants.ts +0 -1
  100. package/src/chat/client/index.ts +0 -18
  101. package/src/chat/client/interfaces.ts +0 -34
  102. package/src/chat/client/sessionClient.ts +0 -537
  103. package/src/chat/client/sessionFiles.ts +0 -142
  104. package/src/chat/client/teamManager.ts +0 -29
  105. package/src/chat/data/apiKeyManager.ts +0 -76
  106. package/src/chat/data/dataModels.ts +0 -101
  107. package/src/chat/data/database.ts +0 -997
  108. package/src/chat/data/dbMcpServerConfigs.ts +0 -59
  109. package/src/chat/data/dbSessionFileModels.ts +0 -113
  110. package/src/chat/data/dbSessionFiles.ts +0 -99
  111. package/src/chat/data/dbSessionMessages.ts +0 -102
  112. package/src/chat/data/mimeTypes.ts +0 -58
  113. package/src/chat/protocol/connectionMessages.ts +0 -49
  114. package/src/chat/protocol/constants.ts +0 -55
  115. package/src/chat/protocol/errors.ts +0 -16
  116. package/src/chat/protocol/messages.ts +0 -846
  117. package/src/chat/server/README.md +0 -127
  118. package/src/chat/server/chatContextManager.ts +0 -639
  119. package/src/chat/server/connectionManager.test.ts +0 -246
  120. package/src/chat/server/connectionManager.ts +0 -506
  121. package/src/chat/server/conversation.ts +0 -316
  122. package/src/chat/server/errorUtils.ts +0 -28
  123. package/src/chat/server/imageGeneratorTools.ts +0 -160
  124. package/src/chat/server/openAIRouterLLM.ts +0 -171
  125. package/src/chat/server/openSession.ts +0 -1689
  126. package/src/chat/server/openSessionMessageSender.ts +0 -4
  127. package/src/chat/server/server.ts +0 -175
  128. package/src/chat/server/sessionFileManager.ts +0 -422
  129. package/src/chat/server/sessionRegistry.test.ts +0 -137
  130. package/src/chat/server/sessionRegistry.ts +0 -1425
  131. package/src/chat/server/test-utils/mockFactories.ts +0 -422
  132. package/src/chat/server/tools.ts +0 -397
  133. package/src/chat/utils/agentSessionMap.ts +0 -76
  134. package/src/chat/utils/approvalManager.ts +0 -183
  135. package/src/chat/utils/asyncLock.ts +0 -43
  136. package/src/chat/utils/asyncQueue.ts +0 -62
  137. package/src/chat/utils/htmlToText.ts +0 -61
  138. package/src/chat/utils/multiAsyncQueue.ts +0 -62
  139. package/src/chat/utils/responseAwaiter.ts +0 -181
  140. package/src/chat/utils/search.ts +0 -139
  141. package/src/chat/utils/userResolver.ts +0 -48
  142. package/src/chat/utils/websocket.ts +0 -16
  143. package/src/index.ts +0 -0
  144. package/src/test/agent.test.ts +0 -590
  145. package/src/test/approvalManager.test.ts +0 -141
  146. package/src/test/chatContextManager.test.ts +0 -527
  147. package/src/test/clientServerConnection.test.ts +0 -205
  148. package/src/test/compressingContextManager.test.ts +0 -77
  149. package/src/test/context.test.ts +0 -150
  150. package/src/test/contextTestTools.ts +0 -95
  151. package/src/test/conversation.test.ts +0 -109
  152. package/src/test/db.test.ts +0 -363
  153. package/src/test/dbMcpServerConfigs.test.ts +0 -112
  154. package/src/test/dbSessionFiles.test.ts +0 -258
  155. package/src/test/dbSessionMessages.test.ts +0 -85
  156. package/src/test/dbTestTools.ts +0 -157
  157. package/src/test/imageLoad.test.ts +0 -15
  158. package/src/test/mcpServerManager.test.ts +0 -114
  159. package/src/test/multiAsyncQueue.test.ts +0 -183
  160. package/src/test/openaiStreaming.test.ts +0 -177
  161. package/src/test/prompt.test.ts +0 -27
  162. package/src/test/promptProvider.test.ts +0 -33
  163. package/src/test/responseAwaiter.test.ts +0 -103
  164. package/src/test/sudoMcpServerManager.test.ts +0 -63
  165. package/src/test/testTools.ts +0 -171
  166. package/src/test/tools.test.ts +0 -39
  167. package/src/tool/agentChat.ts +0 -194
  168. package/src/tool/agentMain.ts +0 -180
  169. package/src/tool/chatMain.ts +0 -594
  170. package/src/tool/commandPrompt.ts +0 -264
  171. package/src/tool/files.ts +0 -84
  172. package/src/tool/main.ts +0 -25
  173. package/src/tool/nodePlatform.ts +0 -73
  174. package/src/tool/options.ts +0 -144
  175. package/src/tool/prompt.ts +0 -101
  176. package/test_data/background_test_profile.json +0 -6
  177. package/test_data/background_test_script.json +0 -11
  178. package/test_data/dummyllm_script_crash.json +0 -32
  179. package/test_data/dummyllm_script_image_gen.json +0 -19
  180. package/test_data/dummyllm_script_image_gen_fe.json +0 -29
  181. package/test_data/dummyllm_script_invoke_image_gen_tool.json +0 -37
  182. package/test_data/dummyllm_script_render_tool.json +0 -29
  183. package/test_data/dummyllm_script_simplecalc.json +0 -28
  184. package/test_data/dummyllm_script_test_auto_approve.json +0 -81
  185. package/test_data/dummyllm_script_test_simplecalc_addition.json +0 -29
  186. package/test_data/frog.png +0 -0
  187. package/test_data/frog.png.b64 +0 -1
  188. package/test_data/git_message_profile.json +0 -4
  189. package/test_data/git_wip_system.txt +0 -5
  190. package/test_data/image_gen_test_profile.json +0 -5
  191. package/test_data/pr_message_profile.json +0 -4
  192. package/test_data/pr_review_profile.json +0 -4
  193. package/test_data/prompt_simplecalc.txt +0 -1
  194. package/test_data/simplecalc_profile.json +0 -4
  195. package/test_data/sudomcp_import_profile.json +0 -4
  196. package/test_data/test_script_profile.json +0 -8
  197. package/tsconfig.json +0 -13
  198. package/vitest.config.ts +0 -39
  199. /package/dist/agent/src/{chat/utils → agent/tools/contentExtractors}/htmlToText.js +0 -0
@@ -1,997 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
- /* eslint-disable @typescript-eslint/no-unsafe-return */
3
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
4
-
5
- import {
6
- TeamParticipant,
7
- TeamRole,
8
- SessionParticipantMap,
9
- TeamInfo,
10
- SessionCheckpoint,
11
- UserMessageData,
12
- SessionCreateData,
13
- SessionDescriptor,
14
- SessionData,
15
- } from "./dataModels";
16
- import { createClient, SupabaseClient } from "@supabase/supabase-js";
17
- import type * as supabase from "../../../../supabase/database.types";
18
- import {
19
- AgentPreferences,
20
- AgentProfile,
21
- AgentTemplate,
22
- ApiKey,
23
- getLogger,
24
- SavedAgentProfile,
25
- } from "@xalia/xmcp/sdk";
26
-
27
- const logger = getLogger();
28
-
29
- export const SESSION_ALLOWED_PARTICIPANTS = ["owner", "participant"];
30
-
31
- export const SUPABASE_LOCAL_URL = "http://127.0.0.1:54321";
32
- export const SUPABASE_LOCAL_KEY =
33
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiw" +
34
- "icm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJz" +
35
- "dJsyH-qQwv8Hdp7fsn3W0YpN81IU";
36
-
37
- export function createSessionParticipantMap(
38
- participants: TeamParticipant[]
39
- ): SessionParticipantMap {
40
- const pmap: SessionParticipantMap = new Map();
41
- participants.forEach((p) => {
42
- pmap.set(p.user_uuid, p);
43
- });
44
-
45
- return pmap;
46
- }
47
-
48
- /**
49
- * 'name' -> 'name'
50
- * 'space/name' -> ['space', 'name']
51
- */
52
- export function resolveCompoundName(name: string): string | [string, string] {
53
- const components = name.split("/");
54
- if (components.length === 1) {
55
- return name;
56
- }
57
- if (components.length !== 2) {
58
- throw new Error("invalid compound name");
59
- }
60
- return components as [string, string];
61
- }
62
-
63
- export type UserData = {
64
- uuid: supabase.Tables<"api_keys">["user_uuid"];
65
- nickname: supabase.Tables<"users">["nickname"];
66
- email: supabase.Tables<"users">["email"];
67
- timezone: supabase.Tables<"users">["timezone"];
68
- };
69
-
70
- export class DbClientBase {
71
- constructor(protected client: SupabaseClient) {}
72
- }
73
-
74
- export class Database {
75
- private client: SupabaseClient;
76
-
77
- constructor(supabaseUrl: string, supabaseKey: string) {
78
- this.client = createClient(supabaseUrl, supabaseKey);
79
- }
80
-
81
- /**
82
- * Get the underlying Supabase client for testing purposes only.
83
- * DO NOT use in production code - use Database methods instead.
84
- */
85
- getClientForTesting(): SupabaseClient {
86
- return this.client;
87
- }
88
-
89
- async getUserDataFromApiKey(apiKey: string): Promise<UserData | undefined> {
90
- const { data, error } = await this.client
91
- .from("api_keys")
92
- .select("user_uuid, users ( nickname, timezone, email )")
93
- .eq("api_key", apiKey)
94
- .maybeSingle<{
95
- user_uuid: supabase.Tables<"api_keys">["user_uuid"];
96
- users: {
97
- nickname: supabase.Tables<"users">["nickname"];
98
- timezone: supabase.Tables<"users">["timezone"];
99
- email: supabase.Tables<"users">["email"];
100
- };
101
- }>();
102
-
103
- logger.debug(
104
- `[getUserDataFromApiKey]: got ${JSON.stringify({ data, error })}`
105
- );
106
-
107
- if (error) {
108
- throw error;
109
- }
110
-
111
- if (data === null) {
112
- return undefined;
113
- }
114
-
115
- return {
116
- uuid: data.user_uuid,
117
- email: data.users.email,
118
- nickname: data.users.nickname || `user ${data.user_uuid}`,
119
- timezone: data.users.timezone || "UTC",
120
- };
121
- }
122
-
123
- async getUserFromUuid(user_uuid: string): Promise<UserData | undefined> {
124
- const { data, error } = await this.client
125
- .from("users")
126
- .select("*")
127
- .eq("uuid", user_uuid)
128
- .maybeSingle();
129
- if (error) {
130
- throw error;
131
- }
132
-
133
- return data;
134
- }
135
-
136
- async getUserByEmail(email: string): Promise<UserData | undefined> {
137
- const { data, error } = await this.client
138
- .from("users")
139
- .select("*")
140
- .eq("email", email)
141
- .maybeSingle();
142
- if (error) {
143
- throw error;
144
- }
145
-
146
- return data;
147
- }
148
-
149
- async createUser(
150
- user_uuid: string,
151
- email: string,
152
- nickname: string,
153
- timezone?: string
154
- ): Promise<void> {
155
- const payload: supabase.TablesInsert<"users"> = {
156
- uuid: user_uuid,
157
- email,
158
- nickname,
159
- timezone: timezone || "UTC",
160
- };
161
-
162
- const { error } = await this.client.from("users").insert(payload);
163
- if (error) {
164
- throw error;
165
- }
166
- }
167
-
168
- async deleteUser(user_uuid: string): Promise<void> {
169
- const { error } = await this.client
170
- .from("users")
171
- .delete()
172
- .eq("uuid", user_uuid);
173
- if (error) {
174
- throw error;
175
- }
176
- }
177
-
178
- async addApiKey(
179
- user_uuid: string,
180
- api_key: string,
181
- name: string,
182
- scopes: string[],
183
- is_default: boolean = false
184
- ): Promise<ApiKey> {
185
- const payload: supabase.TablesInsert<"api_keys"> = {
186
- user_uuid,
187
- api_key,
188
- name,
189
- scopes,
190
- is_default,
191
- };
192
- const { data, error } = await this.client
193
- .from("api_keys")
194
- .insert(payload)
195
- .select("*")
196
- .maybeSingle();
197
- if (error) {
198
- throw error;
199
- }
200
-
201
- return data;
202
- }
203
-
204
- /**
205
- * Get user's default API key from user UUID
206
- * NOTE: this should only be used for accessing
207
- * the session owner's API key.
208
- * */
209
- async getUserApiKey(user_uuid: string): Promise<string | undefined> {
210
- const { data, error } = await this.client
211
- .from("api_keys")
212
- .select("api_key")
213
- .eq("user_uuid", user_uuid)
214
- .eq("is_default", true)
215
- .maybeSingle();
216
-
217
- if (error) {
218
- throw error;
219
- }
220
- return data?.api_key;
221
- }
222
-
223
- async getSavedAgentProfileById(
224
- agentProfileId: string
225
- ): Promise<SavedAgentProfile | undefined> {
226
- const { data, error } = await this.client
227
- .from("agent_profiles")
228
- .select("*")
229
- .eq("uuid", agentProfileId)
230
- .maybeSingle<supabase.Tables<"agent_profiles">>();
231
- if (error) {
232
- throw error;
233
- }
234
-
235
- return data
236
- ? SavedAgentProfile.fromJSONObj(data as Record<string, unknown>)
237
- : undefined;
238
- }
239
-
240
- async getSavedAgentProfileByName(
241
- user_uuid: string,
242
- agentProfileName: string
243
- ): Promise<SavedAgentProfile | undefined> {
244
- const { data, error } = await this.client
245
- .from("agent_profiles")
246
- .select("*")
247
- .eq("user_uuid", user_uuid)
248
- .eq("profile_name", agentProfileName)
249
- .maybeSingle<supabase.Tables<"agent_profiles">>();
250
- if (error) {
251
- throw error;
252
- }
253
-
254
- return data
255
- ? SavedAgentProfile.fromJSONObj(data as Record<string, unknown>)
256
- : undefined;
257
- }
258
-
259
- async getAgentProfileById(
260
- agentProfileId: string
261
- ): Promise<AgentProfile | undefined> {
262
- const { data, error } = await this.client
263
- .from("agent_profiles")
264
- .select("profile")
265
- .eq("uuid", agentProfileId)
266
- .maybeSingle<{
267
- profile: supabase.Tables<"agent_profiles">["profile"];
268
- }>();
269
- if (error) {
270
- throw error;
271
- }
272
- return data
273
- ? AgentProfile.fromJSONObj(data.profile as Record<string, unknown>)
274
- : undefined;
275
- }
276
-
277
- async createAgentProfile(
278
- user_uuid: string | undefined,
279
- team_uuid: string | undefined,
280
- profileName: string,
281
- profile: AgentProfile
282
- ): Promise<SavedAgentProfile | undefined> {
283
- const payload: supabase.TablesInsert<"agent_profiles"> = {
284
- profile: profile as unknown as supabase.Json,
285
- user_uuid,
286
- team_uuid,
287
- profile_name: profileName,
288
- };
289
- const { data, error } = await this.client
290
- .from("agent_profiles")
291
- .upsert(payload)
292
- .select("*");
293
- if (error) {
294
- throw error;
295
- }
296
-
297
- return data[0]
298
- ? SavedAgentProfile.fromJSONObj(data[0] as Record<string, unknown>)
299
- : undefined;
300
- }
301
-
302
- async updateAgentProfile(uuid: string, profile: AgentProfile): Promise<void> {
303
- const payload: supabase.TablesUpdate<"agent_profiles"> = {
304
- profile: profile as unknown as supabase.Json,
305
- };
306
- const { error } = await this.client
307
- .from("agent_profiles")
308
- .update(payload)
309
- .eq("uuid", uuid);
310
- if (error) {
311
- throw error;
312
- }
313
- }
314
-
315
- async getAgentProfilePreferences(
316
- agentProfileId: string
317
- ): Promise<AgentPreferences | undefined> {
318
- const { data, error } = await this.client
319
- .from("agent_profiles")
320
- .select("preferences")
321
- .eq("uuid", agentProfileId)
322
- .maybeSingle<{
323
- preferences: supabase.Tables<"agent_profiles">["preferences"];
324
- }>();
325
- if (error) {
326
- throw error;
327
- }
328
-
329
- return data ? (data.preferences as AgentPreferences) : undefined;
330
- }
331
-
332
- async updateAgentProfilePreferences(
333
- agentProfileId: string,
334
- preferences: AgentPreferences
335
- ): Promise<void> {
336
- const payload: supabase.TablesUpdate<"agent_profiles"> = { preferences };
337
- const { error } = await this.client
338
- .from("agent_profiles")
339
- .update(payload)
340
- .eq("uuid", agentProfileId);
341
- if (error) {
342
- throw error;
343
- }
344
- }
345
-
346
- async clearAgentProfiles(): Promise<void> {
347
- await this.client.from("agent_profiles").delete().neq("uuid", "");
348
- }
349
-
350
- async deleteAgentProfile(agentProfileUuid: string): Promise<void> {
351
- const { error } = await this.client
352
- .from("agent_profiles")
353
- .delete()
354
- .eq("uuid", agentProfileUuid);
355
- if (error) {
356
- throw error;
357
- }
358
- }
359
-
360
- //
361
- // sessions
362
- //
363
-
364
- static sessionNewUUID(): string {
365
- const bytes = new Uint8Array(16);
366
- crypto.getRandomValues(bytes);
367
-
368
- return Array.from(bytes)
369
- .map((b) => b.toString(16).padStart(2, "0"))
370
- .join("")
371
- .padStart(32, "0"); // in case of leading zeros
372
- }
373
-
374
- async sessionsGet(): Promise<supabase.Tables<"sessions">[]> {
375
- const { data, error } = await this.client.from("sessions").select("*");
376
-
377
- if (error) {
378
- throw error;
379
- }
380
- return data;
381
- }
382
-
383
- async sessionGetById(session_uuid: string): Promise<SessionData | undefined> {
384
- const { data, error } = await this.client
385
- .from("sessions")
386
- .select("*")
387
- .eq("uuid", session_uuid)
388
- .maybeSingle();
389
-
390
- if (error) {
391
- throw error;
392
- }
393
-
394
- if (!data) {
395
- return undefined;
396
- }
397
-
398
- return {
399
- ...data,
400
- workspace: data.workspace || undefined,
401
- owner_uuid: data.user_uuid,
402
- session_uuid: data.uuid,
403
- updated_at: data.updated_at || new Date().toISOString(),
404
- };
405
- }
406
-
407
- async sessionGetDescriptorById(
408
- session_uuid: string
409
- ): Promise<SessionDescriptor | undefined> {
410
- const { data, error } = await this.client
411
- .from("sessions")
412
- .select(
413
- // eslint-disable-next-line max-len
414
- "uuid,title,agent_profile_uuid,user_uuid,team_uuid,access_token,updated_at,agent_paused"
415
- )
416
- .eq("uuid", session_uuid)
417
- .maybeSingle();
418
-
419
- if (error) {
420
- throw error;
421
- }
422
-
423
- if (!data) {
424
- return undefined;
425
- }
426
-
427
- return {
428
- ...data,
429
- access_token: data.access_token || undefined,
430
- session_uuid: data.uuid,
431
- updated_at: data.updated_at || new Date().toISOString(),
432
- };
433
- }
434
-
435
- async sessionGetDescriptorByName(
436
- user_uuid: string,
437
- session_name: string
438
- ): Promise<SessionDescriptor | undefined> {
439
- const { data, error } = await this.client
440
- .from("sessions")
441
- .select(
442
- // eslint-disable-next-line max-len
443
- "uuid,title,agent_profile_uuid,user_uuid,team_uuid,access_token,updated_at,agent_paused"
444
- )
445
- .eq("user_uuid", user_uuid)
446
- .eq("title", session_name)
447
- .maybeSingle();
448
-
449
- if (error) {
450
- logger.error(`[getSessionByName] error: ${JSON.stringify(error)}`);
451
- throw error;
452
- }
453
-
454
- if (!data) {
455
- return undefined;
456
- }
457
-
458
- return {
459
- ...data,
460
- session_uuid: data.uuid,
461
- access_token: data.access_token || undefined,
462
- updated_at: data.updated_at || new Date().toISOString(),
463
- };
464
- }
465
-
466
- async sessionCreate(session_data: SessionCreateData): Promise<void> {
467
- const payload: supabase.TablesInsert<"sessions"> = {
468
- uuid: session_data.session_uuid,
469
- title: session_data.title,
470
- agent_profile_uuid: session_data.agent_profile_uuid,
471
- user_uuid: session_data.user_uuid,
472
- team_uuid: session_data.team_uuid,
473
- access_token: session_data.access_token,
474
- };
475
- const { error } = await this.client.from("sessions").insert(payload);
476
- if (error) {
477
- throw error;
478
- }
479
- }
480
-
481
- async sessionUpdateTitle(session_uuid: string, title: string): Promise<void> {
482
- const payload: supabase.TablesUpdate<"sessions"> = { title };
483
- const { error } = await this.client
484
- .from("sessions")
485
- .update(payload)
486
- .eq("uuid", session_uuid);
487
- if (error) {
488
- throw error;
489
- }
490
- }
491
-
492
- async sessionUpdateWorkspace(
493
- session_uuid: string,
494
- workspace: UserMessageData | undefined
495
- ): Promise<void> {
496
- const payload: supabase.TablesUpdate<"sessions"> = {
497
- workspace: workspace || null,
498
- };
499
- const { error } = await this.client
500
- .from("sessions")
501
- .update(payload)
502
- .eq("uuid", session_uuid);
503
- if (error) {
504
- throw error;
505
- }
506
- }
507
-
508
- async sessionUpdateAccessToken(
509
- session_uuid: string,
510
- access_token: string | undefined
511
- ): Promise<void> {
512
- const payload: supabase.TablesUpdate<"sessions"> = {
513
- access_token: access_token || null,
514
- };
515
- const { error } = await this.client
516
- .from("sessions")
517
- .update(payload)
518
- .eq("uuid", session_uuid);
519
- if (error) {
520
- throw error;
521
- }
522
- }
523
-
524
- async sessionSetAgentPaused(
525
- session_uuid: string,
526
- paused: boolean
527
- ): Promise<void> {
528
- const payload: supabase.TablesUpdate<"sessions"> = { agent_paused: paused };
529
- const { error } = await this.client
530
- .from("sessions")
531
- .update(payload)
532
- .eq("uuid", session_uuid);
533
- if (error) {
534
- throw error;
535
- }
536
- }
537
-
538
- async sessionDeleteById(session_uuid: string): Promise<void> {
539
- await this.client.from("sessions").delete().eq("uuid", session_uuid);
540
- }
541
-
542
- async clearSessions(): Promise<void> {
543
- await this.client.from("sessions").delete().neq("uuid", "");
544
- }
545
-
546
- /**
547
- * Get all user sessions (not including team sessions) for a user.
548
- * @param user_uuid
549
- * @returns SessionData[]
550
- */
551
- async getUserSessions(user_uuid: string): Promise<SessionDescriptor[]> {
552
- const { data: userSessions, error: userSessionsError } = await this.client
553
- .from("sessions")
554
- .select(
555
- // eslint-disable-next-line max-len
556
- "uuid,title,agent_profile_uuid,updated_at,user_uuid,access_token,agent_paused"
557
- )
558
- .eq("user_uuid", user_uuid)
559
- .is("team_uuid", null);
560
-
561
- if (userSessionsError) {
562
- throw userSessionsError;
563
- }
564
-
565
- return userSessions.map((s) => ({
566
- session_uuid: s.uuid,
567
- title: s.title,
568
- team_uuid: undefined,
569
- agent_profile_uuid: s.agent_profile_uuid,
570
- access_token: s.access_token || undefined,
571
- updated_at: s.updated_at,
572
- user_uuid: s.user_uuid,
573
- agent_paused: s.agent_paused,
574
- }));
575
- }
576
-
577
- async sessionGetParticipants(
578
- session_uuid: string
579
- ): Promise<TeamParticipant[]> {
580
- // check if session is a team session
581
- const { data, error } = await this.client
582
- .from("sessions")
583
- .select("team_uuid, user_uuid")
584
- .eq("uuid", session_uuid)
585
- .maybeSingle();
586
-
587
- if (error || !data) {
588
- throw error || new Error("Session not found");
589
- }
590
-
591
- if (data.team_uuid) {
592
- return this.teamGetMembers(data.team_uuid as string);
593
- } else {
594
- const userData = await this.getUserFromUuid(data.user_uuid as string);
595
- if (!userData) {
596
- throw new Error("Cannot find user data");
597
- }
598
- return [
599
- {
600
- user_uuid: data.user_uuid,
601
- nickname: userData.nickname || "",
602
- email: userData.email,
603
- role: "owner",
604
- },
605
- ];
606
- }
607
- }
608
-
609
- async sessionGetTeamUuid(session_uuid: string): Promise<string | undefined> {
610
- const data = await this.sessionGetDescriptorById(session_uuid);
611
- if (data) {
612
- return data.team_uuid;
613
- }
614
- return undefined;
615
- }
616
-
617
- //
618
- // session_checkpoints
619
- //
620
-
621
- async sessionCheckpointDelete(session_uuid: string): Promise<void> {
622
- const { error } = await this.client
623
- .from("session_checkpoints")
624
- .delete()
625
- .neq("session_uuid", session_uuid);
626
- if (error) {
627
- throw error;
628
- }
629
- }
630
-
631
- async sessionCheckpointGet(
632
- session_uuid: string
633
- ): Promise<SessionCheckpoint | undefined> {
634
- const { error, data } = await this.client
635
- .from("session_checkpoints")
636
- .select("message_idx,summary")
637
- .eq("session_uuid", session_uuid)
638
- .maybeSingle();
639
-
640
- if (error) {
641
- throw error;
642
- }
643
-
644
- if (!data) {
645
- return undefined;
646
- }
647
-
648
- return {
649
- message_idx: data.message_idx,
650
- summary: data.summary,
651
- };
652
- }
653
-
654
- async sessionCheckpointSet(
655
- session_uuid: string,
656
- checkpoint: SessionCheckpoint
657
- ): Promise<void> {
658
- const payload: supabase.TablesUpdate<"session_checkpoints"> = {
659
- session_uuid,
660
- message_idx: checkpoint.message_idx,
661
- summary: checkpoint.summary,
662
- };
663
-
664
- const { error } = await this.client
665
- .from("session_checkpoints")
666
- .upsert([payload], { onConflict: "session_uuid" });
667
- if (error) {
668
- throw error;
669
- }
670
- }
671
-
672
- //
673
- // agent_profiles
674
- //
675
-
676
- /**
677
- * Get all agents belonging to a user.
678
- * @param userUuid - UUID of the user
679
- * @returns Array of agent profiles
680
- */
681
- async agentProfilesGetByUser(userUuid: string): Promise<SavedAgentProfile[]> {
682
- const { data, error } = await this.client
683
- .from("agent_profiles")
684
- .select("*")
685
- .eq("user_uuid", userUuid);
686
-
687
- if (error) {
688
- throw error;
689
- }
690
-
691
- return data.map((agent) =>
692
- SavedAgentProfile.fromJSONObj(agent as Record<string, unknown>)
693
- );
694
- }
695
-
696
- /**
697
- * Get all agents belonging to a team.
698
- * @param teamUuid - UUID of the team
699
- * @returns Array of agent profiles
700
- */
701
- async agentProfilesGetByTeam(teamUuid: string): Promise<SavedAgentProfile[]> {
702
- const { data, error } = await this.client
703
- .from("agent_profiles")
704
- .select("*")
705
- .eq("team_uuid", teamUuid);
706
-
707
- if (error) {
708
- throw error;
709
- }
710
-
711
- return data.map((agent) =>
712
- SavedAgentProfile.fromJSONObj(agent as Record<string, unknown>)
713
- );
714
- }
715
-
716
- async agentTemplateGetByName(
717
- templateName: string
718
- ): Promise<AgentTemplate | undefined> {
719
- const { data, error } = await this.client
720
- .from("agent_templates")
721
- .select("*")
722
- .eq("name", templateName)
723
- .maybeSingle();
724
- if (error) {
725
- throw error;
726
- }
727
- return data
728
- ? AgentTemplate.fromJSONObj(data as Record<string, unknown>)
729
- : undefined;
730
- }
731
- //
732
- // teams
733
- //
734
-
735
- async createTeam(teamName: string, owner_uuid: string): Promise<string> {
736
- const { data, error } = await this.client.rpc("create_team_with_owner", {
737
- p_owner_uuid: owner_uuid,
738
- p_name: teamName,
739
- });
740
- if (error) {
741
- throw error;
742
- }
743
- // The SQL function returns a TEXT (team UUID)
744
- return data as string;
745
- }
746
-
747
- /**
748
- * Creates a team with initial participants.
749
- * The owner is automatically added as a team member with 'owner' role.
750
- * @param teamName - Name of the team to create
751
- * @param ownerUuid - UUID of the team owner
752
- * @param initialParticipants - Array of user UUIDs to add as initial members
753
- * @returns The UUID of the created team
754
- */
755
- async createTeamWithParticipants(
756
- teamName: string,
757
- ownerUuid: string,
758
- initialParticipants: string[]
759
- ): Promise<string> {
760
- // Create the team first
761
- const teamUuid = await this.createTeam(teamName, ownerUuid);
762
-
763
- // If there are initial participants, add them to the team
764
- if (initialParticipants.length > 0) {
765
- // Filter out the owner if they're in the initial participants list
766
- const participantsToAdd = initialParticipants.filter(
767
- (uuid) => uuid !== ownerUuid
768
- );
769
-
770
- if (participantsToAdd.length > 0) {
771
- // Add all participants to the
772
- const teamMemberInserts = participantsToAdd.map((userUuid) => ({
773
- team_uuid: teamUuid,
774
- user_uuid: userUuid,
775
- role: "participant" as const,
776
- }));
777
-
778
- const { error: membersError } = await this.client
779
- .from("team_members")
780
- .insert(teamMemberInserts);
781
-
782
- if (membersError) {
783
- // If adding members fails, we should clean up the team
784
- logger.error(
785
- `Failed to add members to team ${teamUuid}: ${membersError.message}`
786
- );
787
- // Optionally delete the team if member addition fails
788
- await this.deleteTeam(teamUuid);
789
- throw membersError;
790
- }
791
- }
792
- }
793
-
794
- logger.info(
795
- `Created team ${teamUuid}: ${String(initialParticipants.length)} members`
796
- );
797
-
798
- return teamUuid;
799
- }
800
-
801
- /**
802
- * Deletes a team and all associated data.
803
- * This will also remove all team members and any team sessions.
804
- * @param teamUuid - UUID of the team to delete
805
- */
806
- async deleteTeam(teamUuid: string): Promise<void> {
807
- try {
808
- // Delete the team - this should cascade to team_members and team_sessions
809
- // due to foreign key constraints with ON DELETE CASCADE
810
- const { error } = await this.client
811
- .from("teams")
812
- .delete()
813
- .eq("uuid", teamUuid);
814
-
815
- if (error) {
816
- throw error;
817
- }
818
-
819
- logger.info(`Deleted team ${teamUuid}`);
820
- } catch (error) {
821
- logger.error(`Failed to delete team ${teamUuid}:`, error);
822
- throw new Error(`Failed to delete team: ${String(error)}`);
823
- }
824
- }
825
-
826
- async getTeamInfosByUser(user_uuid: string): Promise<TeamInfo[]> {
827
- // Get all teams the user is a member of (including as owner)
828
- const { data: teamMemberships, error: teamError } = await this.client
829
- .from("team_members")
830
- .select(
831
- `
832
- team_uuid,
833
- role,
834
- teams!inner (
835
- uuid,
836
- name,
837
- owner_uuid
838
- )
839
- `
840
- )
841
- .eq("user_uuid", user_uuid);
842
-
843
- if (teamError) {
844
- throw teamError;
845
- }
846
-
847
- if (teamMemberships.length === 0) {
848
- return [];
849
- }
850
-
851
- // Process all team memberships in parallel
852
- const teamSessionPromises = teamMemberships.map(async (membership) => {
853
- const teamUuid: string = membership.team_uuid;
854
- const teamData = membership.teams as unknown as {
855
- uuid: string;
856
- name: string;
857
- owner_uuid: string;
858
- };
859
-
860
- // Use the role from team_members, sanity check against teams.owner_uuid
861
- const memberRole = membership.role as TeamRole;
862
- const isActualOwner = teamData.owner_uuid === user_uuid;
863
-
864
- // Sanity check: if roles are inconsistent, throw an error
865
- if (
866
- (memberRole === "owner" && !isActualOwner) ||
867
- (memberRole !== "owner" && isActualOwner)
868
- ) {
869
- throw new Error(
870
- `Data inconsistency: user ${user_uuid} has role '${memberRole}' ` +
871
- `in team ${teamUuid}, but owner is ${teamData.owner_uuid}`
872
- );
873
- }
874
-
875
- // Fetch team members and sessions in parallel using helper methods
876
- const [participants, sessions, agents] = await Promise.all([
877
- this.teamGetMembers(teamUuid),
878
- this.teamGetSessions(teamUuid),
879
- this.agentProfilesGetByTeam(teamUuid),
880
- ]);
881
-
882
- return {
883
- team_uuid: teamUuid,
884
- team_name: teamData.name,
885
- owner_uuid: teamData.owner_uuid,
886
- participants,
887
- sessions,
888
- agents,
889
- };
890
- });
891
-
892
- return await Promise.all(teamSessionPromises);
893
- }
894
-
895
- /**
896
- * Get all members of a team.
897
- * @param teamUuid - UUID of the team
898
- * @returns Array of team members with their roles
899
- */
900
- async teamGetMembers(teamUuid: string): Promise<TeamParticipant[]> {
901
- const { data, error } = await this.client
902
- .from("team_members")
903
- .select("user_uuid, role")
904
- .eq("team_uuid", teamUuid);
905
-
906
- if (error) {
907
- throw error;
908
- }
909
-
910
- // get users' data in parallel
911
- const usersData = await Promise.all(
912
- data.map((member) => this.getUserFromUuid(member.user_uuid as string))
913
- );
914
-
915
- usersData.forEach((user) => {
916
- if (!user) {
917
- throw new Error("Cannot find user data");
918
- }
919
- });
920
-
921
- const result = data.map((member, index) => ({
922
- user_uuid: member.user_uuid,
923
- nickname: usersData[index]?.nickname || "",
924
- email: usersData[index]?.email || "",
925
- role: member.role as TeamRole,
926
- }));
927
-
928
- return result;
929
- }
930
-
931
- /**
932
- * Add a member to a team as a participant.
933
- * @param teamUuid - UUID of the team
934
- * @param userUuid - UUID of the user to add
935
- */
936
- async teamAddMember(teamUuid: string, userUuid: string): Promise<void> {
937
- const { error } = await this.client.from("team_members").insert({
938
- team_uuid: teamUuid,
939
- user_uuid: userUuid,
940
- role: "participant",
941
- });
942
-
943
- if (error) {
944
- throw error;
945
- }
946
- }
947
-
948
- /**
949
- * Remove a member from a team.
950
- * @param teamUuid - UUID of the team
951
- * @param userUuid - UUID of the user to remove
952
- */
953
- async teamRemoveMember(teamUuid: string, userUuid: string): Promise<void> {
954
- const { error } = await this.client
955
- .from("team_members")
956
- .delete()
957
- .eq("team_uuid", teamUuid)
958
- .eq("user_uuid", userUuid)
959
- .eq("role", "participant");
960
-
961
- if (error) {
962
- throw error;
963
- }
964
- }
965
-
966
- /**
967
- * Get all sessions belonging to a team.
968
- * @param teamUuid - UUID of the team
969
- * @returns Array of session data
970
- */
971
- async teamGetSessions(teamUuid: string): Promise<SessionDescriptor[]> {
972
- const { data, error } = await this.client
973
- .from("sessions")
974
- .select("uuid,title,agent_profile_uuid,updated_at,user_uuid,agent_paused")
975
- .eq("team_uuid", teamUuid);
976
-
977
- if (error) {
978
- throw error;
979
- }
980
-
981
- return data.map((session) => ({
982
- session_uuid: session.uuid,
983
- title: session.title,
984
- team_uuid: teamUuid,
985
- agent_profile_uuid: session.agent_profile_uuid,
986
- updated_at: session.updated_at || new Date().toISOString(),
987
- user_uuid: session.user_uuid,
988
- agent_paused: session.agent_paused,
989
- }));
990
- }
991
-
992
- createTypedClient<T extends DbClientBase>(
993
- ctor: new (client: SupabaseClient) => T
994
- ): T {
995
- return new ctor(this.client);
996
- }
997
- }