@meistrari/chat-nuxt 1.11.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +15 -0
  3. package/dist/runtime/components/chat/conversation-item.d.vue.ts +6 -1
  4. package/dist/runtime/components/chat/conversation-item.vue +30 -141
  5. package/dist/runtime/components/chat/conversation-item.vue.d.ts +6 -1
  6. package/dist/runtime/components/chat/conversation-list.d.vue.ts +1 -0
  7. package/dist/runtime/components/chat/conversation-list.vue +300 -72
  8. package/dist/runtime/components/chat/conversation-list.vue.d.ts +1 -0
  9. package/dist/runtime/components/chat/conversation-new-group-button.d.vue.ts +7 -0
  10. package/dist/runtime/components/chat/conversation-new-group-button.vue +90 -0
  11. package/dist/runtime/components/chat/conversation-new-group-button.vue.d.ts +7 -0
  12. package/dist/runtime/components/chat/conversation-new-group-popover-content.d.vue.ts +23 -0
  13. package/dist/runtime/components/chat/conversation-new-group-popover-content.vue +75 -0
  14. package/dist/runtime/components/chat/conversation-new-group-popover-content.vue.d.ts +23 -0
  15. package/dist/runtime/components/chat/mobile/shell/drawer.d.vue.ts +2 -2
  16. package/dist/runtime/components/chat/mobile/shell/drawer.vue +370 -41
  17. package/dist/runtime/components/chat/mobile/shell/drawer.vue.d.ts +2 -2
  18. package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +6 -6
  19. package/dist/runtime/components/chat/mobile/shell/shell.vue +1 -1
  20. package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +6 -6
  21. package/dist/runtime/components/chat/topbar.d.vue.ts +2 -2
  22. package/dist/runtime/components/chat/topbar.vue.d.ts +2 -2
  23. package/dist/runtime/composables/useConversationGroups.d.ts +12 -0
  24. package/dist/runtime/composables/useConversationGroups.js +160 -0
  25. package/dist/runtime/composables/useConversationItem.d.ts +48 -0
  26. package/dist/runtime/composables/useConversationItem.js +176 -0
  27. package/dist/runtime/composables/useConversationList.d.ts +128 -0
  28. package/dist/runtime/composables/useConversationList.js +553 -0
  29. package/dist/runtime/composables/useConversations.d.ts +2 -0
  30. package/dist/runtime/composables/useConversations.js +45 -1
  31. package/dist/runtime/composables/usePendingConversationGroup.d.ts +5 -0
  32. package/dist/runtime/composables/usePendingConversationGroup.js +33 -0
  33. package/dist/runtime/embed/components/ChatConfigurationModal.d.vue.ts +1 -1
  34. package/dist/runtime/embed/components/ChatConfigurationModal.vue.d.ts +1 -1
  35. package/dist/runtime/embed/components/ChatEmbedInner.vue +24 -5
  36. package/dist/runtime/server/api/chat/conversations/[id]/group.patch.d.ts +1 -0
  37. package/dist/runtime/server/api/chat/conversations/[id]/group.patch.js +1 -0
  38. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.d.ts +1 -0
  39. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.js +1 -0
  40. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.d.ts +1 -0
  41. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.js +1 -0
  42. package/dist/runtime/server/api/chat/conversations/groups/index.get.d.ts +1 -0
  43. package/dist/runtime/server/api/chat/conversations/groups/index.get.js +1 -0
  44. package/dist/runtime/server/api/chat/conversations/groups/index.post.d.ts +1 -0
  45. package/dist/runtime/server/api/chat/conversations/groups/index.post.js +1 -0
  46. package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +1 -0
  47. package/dist/runtime/server/api/conversations/[id]/group.patch.d.ts +2 -0
  48. package/dist/runtime/server/api/conversations/[id]/group.patch.js +46 -0
  49. package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.d.ts +4 -0
  50. package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.js +25 -0
  51. package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.d.ts +2 -0
  52. package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.js +30 -0
  53. package/dist/runtime/server/api/conversations/groups/index.get.d.ts +2 -0
  54. package/dist/runtime/server/api/conversations/groups/index.get.js +13 -0
  55. package/dist/runtime/server/api/conversations/groups/index.post.d.ts +2 -0
  56. package/dist/runtime/server/api/conversations/groups/index.post.js +26 -0
  57. package/dist/runtime/server/api/conversations/index.post.js +1 -0
  58. package/dist/runtime/server/db/schema/conversation-groups.d.ts +143 -0
  59. package/dist/runtime/server/db/schema/conversation-groups.js +20 -0
  60. package/dist/runtime/server/db/schema/conversations.d.ts +17 -0
  61. package/dist/runtime/server/db/schema/conversations.js +3 -0
  62. package/dist/runtime/server/db/schema/index.d.ts +1 -0
  63. package/dist/runtime/server/db/schema/index.js +1 -0
  64. package/dist/runtime/server/utils/conversation-group.d.ts +4 -0
  65. package/dist/runtime/server/utils/conversation-group.js +48 -0
  66. package/dist/runtime/types/chat.d.ts +2 -1
  67. package/dist/runtime/utils/conversation-groups.d.ts +18 -0
  68. package/dist/runtime/utils/conversation-groups.js +30 -0
  69. package/drizzle/0016_dry_aaron_stack.sql +16 -0
  70. package/drizzle/meta/0016_snapshot.json +887 -0
  71. package/drizzle/meta/_journal.json +7 -0
  72. package/package.json +1 -1
@@ -25,7 +25,7 @@ const chatAction = useChatAction();
25
25
  const { user: authUser, activeOrganization, logout } = useChatAuth();
26
26
  const { getMember, getMemberImage } = useWorkspaceMembers();
27
27
  const statusToast = useStatusToast();
28
- const { conversations, loading: conversationsLoading, fetchConversations, createConversation, deleteConversation, renameConversation, duplicateConversation, updateConversationInList } = useConversations();
28
+ const { conversations, loading: conversationsLoading, fetchConversations, createConversation, deleteConversation, renameConversation, duplicateConversation, moveConversationToGroup, updateConversationInList } = useConversations();
29
29
  const { settings: workspaceSettingsState, fetchSettings } = useWorkspaceSettings();
30
30
  const { metadata: telaAgentMetadata, loading: telaAgentMetadataLoading, fetchAgentMetadata } = useTelaAgentMetadata();
31
31
  const isTelaAgentMode = computed(() => !!embedConfig?.telaAgentId.value);
@@ -46,6 +46,11 @@ const activeConversationId = ref(
46
46
  );
47
47
  const loadingConversationId = ref(null);
48
48
  const isReady = ref(false);
49
+ const {
50
+ pendingConversationGroupId,
51
+ setPendingConversationGroup,
52
+ clearPendingConversationGroup
53
+ } = usePendingConversationGroup();
49
54
  const resolvedFeatures = computed(() => resolveChatFeatures(props.features));
50
55
  const dropZoneRef = ref(null);
51
56
  const activeTab = ref("chat");
@@ -154,6 +159,7 @@ async function openConversation(nextConversationId) {
154
159
  if (!normalizedConversationId) {
155
160
  return;
156
161
  }
162
+ clearPendingConversationGroup();
157
163
  if (activeConversationId.value !== normalizedConversationId) {
158
164
  closePreview();
159
165
  }
@@ -165,6 +171,9 @@ watch(() => props.conversationId, async (nextConversationId) => {
165
171
  return;
166
172
  }
167
173
  const normalizedConversationId = normalizeConversationId(nextConversationId);
174
+ if (normalizedConversationId) {
175
+ clearPendingConversationGroup();
176
+ }
168
177
  if (!isReady.value) {
169
178
  syncConversationId(normalizedConversationId);
170
179
  return;
@@ -272,7 +281,14 @@ async function handleSend(content, files, model, agentInputs) {
272
281
  const conversation = await createConversation(isTelaAgentMode.value ? void 0 : { model: model ?? selectedModel.value });
273
282
  if (!conversation)
274
283
  return;
275
- setConversation(conversation);
284
+ const groupId = pendingConversationGroupId.value;
285
+ clearPendingConversationGroup();
286
+ if (groupId && await moveConversationToGroup(conversation.id, groupId)) {
287
+ const groupedConversation = { ...conversation, groupId };
288
+ setConversation(groupedConversation);
289
+ } else {
290
+ setConversation(conversation);
291
+ }
276
292
  createdConversationId = conversation.id;
277
293
  }
278
294
  if (!currentConversation.value)
@@ -286,7 +302,8 @@ async function handleSend(content, files, model, agentInputs) {
286
302
  async function handleRetry(messageId) {
287
303
  await retryMessage(messageId);
288
304
  }
289
- async function handleNewConversation() {
305
+ async function handleNewConversation(groupId) {
306
+ setPendingConversationGroup(groupId);
290
307
  resetConversation();
291
308
  }
292
309
  const workspaceSheetOpen = ref(false);
@@ -407,10 +424,10 @@ async function handleDelete() {
407
424
  w-240px h-full flex="~ col" flex-shrink-0
408
425
  bg-neutral-50 b-r=".5px neutral-200"
409
426
  >
410
- <div px-12px pt-16px pb-8px flex="~ col" gap-8px>
427
+ <div px-12px pt-16px pb-8px flex gap-8px>
411
428
  <button
412
429
  flex items-center justify-center gap-6px
413
- h-32px px-12px w-full
430
+ h-32px px-12px flex-1 min-w-0
414
431
  bg-white b=".5px neutral-300" rounded-10px
415
432
  hover:bg-neutral-50 transition-colors cursor-pointer
416
433
  @click="handleNewConversation"
@@ -420,6 +437,7 @@ async function handleDelete() {
420
437
  Nova conversa
421
438
  </span>
422
439
  </button>
440
+ <ChatConversationNewGroupButton />
423
441
  </div>
424
442
 
425
443
  <div flex-1 overflow-y-auto px-4px>
@@ -429,6 +447,7 @@ async function handleDelete() {
429
447
  :get-member-image="getMemberImage"
430
448
  :select-conversation="openConversation"
431
449
  :reset-conversation="handleNewConversation"
450
+ :start-conversation="handleNewConversation"
432
451
  :loading="conversationsLoading"
433
452
  />
434
453
  </div>
@@ -0,0 +1 @@
1
+ export { default } from '../../../conversations/[id]/group.patch.js';
@@ -0,0 +1 @@
1
+ export { default } from "../../../conversations/[id]/group.patch.js";
@@ -0,0 +1 @@
1
+ export { default } from '../../../../conversations/groups/[groupId]/index.delete.js';
@@ -0,0 +1 @@
1
+ export { default } from "../../../../conversations/groups/[groupId]/index.delete.js";
@@ -0,0 +1 @@
1
+ export { default } from '../../../../conversations/groups/[groupId]/index.patch.js';
@@ -0,0 +1 @@
1
+ export { default } from "../../../../conversations/groups/[groupId]/index.patch.js";
@@ -0,0 +1 @@
1
+ export { default } from '../../../conversations/groups/index.get.js';
@@ -0,0 +1 @@
1
+ export { default } from "../../../conversations/groups/index.get.js";
@@ -0,0 +1 @@
1
+ export { default } from '../../../conversations/groups/index.post.js';
@@ -0,0 +1 @@
1
+ export { default } from "../../../conversations/groups/index.post.js";
@@ -26,6 +26,7 @@ export default defineEventHandler(async (event) => {
26
26
  const duplicated = await db.transaction(async (tx) => {
27
27
  const [newConversation] = await tx.insert(schema.conversations).values({
28
28
  workspaceId: context.workspaceId,
29
+ groupId: null,
29
30
  userId: context.user.id,
30
31
  title: `${original.title} (copy)`,
31
32
  createdBy: context.user.email,
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,46 @@
1
+ import { createError, defineEventHandler, getRouterParam, readBody } from "h3";
2
+ import { useDb, schema } from "#chat-runtime/server/db";
3
+ import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
4
+ import { resolveOptionalConversationGroupId, conversationGroupScopeConditions } from "#chat-runtime/server/utils/conversation-group";
5
+ import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
6
+ import { logger } from "#chat-runtime/server/utils/logger";
7
+ function hasGroupId(body) {
8
+ return !!body && typeof body === "object" && Object.prototype.hasOwnProperty.call(body, "groupId");
9
+ }
10
+ export default defineEventHandler(async (event) => {
11
+ const context = resolveChatRuntimeContext(event);
12
+ const db = useDb();
13
+ const id = getRouterParam(event, "id");
14
+ if (!id) {
15
+ throw createError({
16
+ statusCode: 400,
17
+ statusMessage: "Conversation ID is required"
18
+ });
19
+ }
20
+ const body = await readBody(event);
21
+ if (!hasGroupId(body)) {
22
+ throw createError({
23
+ statusCode: 400,
24
+ statusMessage: "Group ID is required"
25
+ });
26
+ }
27
+ const groupId = resolveOptionalConversationGroupId(body.groupId);
28
+ if (groupId) {
29
+ const [group] = await db.select({ id: schema.conversationGroups.id }).from(schema.conversationGroups).where(conversationGroupScopeConditions(context, groupId)).limit(1);
30
+ if (!group) {
31
+ throw createError({
32
+ statusCode: 404,
33
+ statusMessage: "Conversation group not found"
34
+ });
35
+ }
36
+ }
37
+ const [updated] = await db.update(schema.conversations).set({ groupId }).where(conversationScopeConditions(context, id)).returning();
38
+ if (!updated) {
39
+ throw createError({
40
+ statusCode: 404,
41
+ statusMessage: "Conversation not found"
42
+ });
43
+ }
44
+ logger.info({ conversationId: id, groupId, workspaceId: context.workspaceId }, "Conversation moved between groups");
45
+ return updated;
46
+ });
@@ -0,0 +1,4 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
2
+ success: boolean;
3
+ }>>;
4
+ export default _default;
@@ -0,0 +1,25 @@
1
+ import { createError, defineEventHandler, getRouterParam } from "h3";
2
+ import { useDb, schema } from "#chat-runtime/server/db";
3
+ import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
4
+ import { conversationGroupScopeConditions } from "#chat-runtime/server/utils/conversation-group";
5
+ import { logger } from "#chat-runtime/server/utils/logger";
6
+ export default defineEventHandler(async (event) => {
7
+ const context = resolveChatRuntimeContext(event);
8
+ const db = useDb();
9
+ const groupId = getRouterParam(event, "groupId");
10
+ if (!groupId) {
11
+ throw createError({
12
+ statusCode: 400,
13
+ statusMessage: "Group ID is required"
14
+ });
15
+ }
16
+ const [group] = await db.delete(schema.conversationGroups).where(conversationGroupScopeConditions(context, groupId)).returning();
17
+ if (!group) {
18
+ throw createError({
19
+ statusCode: 404,
20
+ statusMessage: "Conversation group not found"
21
+ });
22
+ }
23
+ logger.info({ groupId, workspaceId: context.workspaceId }, "Conversation group deleted");
24
+ return { success: true };
25
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,30 @@
1
+ import { createError, defineEventHandler, getRouterParam, readBody } from "h3";
2
+ import { useDb, schema } from "#chat-runtime/server/db";
3
+ import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
4
+ import { conversationGroupScopeConditions, resolveConversationGroupName } from "#chat-runtime/server/utils/conversation-group";
5
+ import { logger } from "#chat-runtime/server/utils/logger";
6
+ export default defineEventHandler(async (event) => {
7
+ const context = resolveChatRuntimeContext(event);
8
+ const db = useDb();
9
+ const groupId = getRouterParam(event, "groupId");
10
+ if (!groupId) {
11
+ throw createError({
12
+ statusCode: 400,
13
+ statusMessage: "Group ID is required"
14
+ });
15
+ }
16
+ const body = await readBody(event);
17
+ const name = resolveConversationGroupName(body?.name);
18
+ const [group] = await db.update(schema.conversationGroups).set({
19
+ name,
20
+ updatedAt: /* @__PURE__ */ new Date()
21
+ }).where(conversationGroupScopeConditions(context, groupId)).returning();
22
+ if (!group) {
23
+ throw createError({
24
+ statusCode: 404,
25
+ statusMessage: "Conversation group not found"
26
+ });
27
+ }
28
+ logger.info({ groupId, workspaceId: context.workspaceId }, "Conversation group renamed");
29
+ return group;
30
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,13 @@
1
+ import { defineEventHandler } from "h3";
2
+ import { asc } from "drizzle-orm";
3
+ import { useDb, schema } from "#chat-runtime/server/db";
4
+ import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
5
+ import { conversationGroupScopeConditions } from "#chat-runtime/server/utils/conversation-group";
6
+ import { logger } from "#chat-runtime/server/utils/logger";
7
+ export default defineEventHandler(async (event) => {
8
+ const context = resolveChatRuntimeContext(event);
9
+ const db = useDb();
10
+ const groups = await db.select().from(schema.conversationGroups).where(conversationGroupScopeConditions(context)).orderBy(asc(schema.conversationGroups.createdAt), asc(schema.conversationGroups.name));
11
+ logger.debug({ workspaceId: context.workspaceId, count: groups.length }, "Conversation groups listed");
12
+ return groups;
13
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,26 @@
1
+ import { createError, defineEventHandler, readBody } from "h3";
2
+ import { useDb, schema } from "#chat-runtime/server/db";
3
+ import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
4
+ import { resolveConversationGroupName } from "#chat-runtime/server/utils/conversation-group";
5
+ import { logger } from "#chat-runtime/server/utils/logger";
6
+ export default defineEventHandler(async (event) => {
7
+ const context = resolveChatRuntimeContext(event);
8
+ const db = useDb();
9
+ const body = await readBody(event);
10
+ const name = resolveConversationGroupName(body?.name);
11
+ const [group] = await db.insert(schema.conversationGroups).values({
12
+ workspaceId: context.workspaceId,
13
+ telaAgentId: context.telaAgentId,
14
+ conversationScope: context.conversationScope,
15
+ name,
16
+ createdBy: context.user.email
17
+ }).returning();
18
+ if (!group) {
19
+ throw createError({
20
+ statusCode: 500,
21
+ statusMessage: "Failed to create conversation group"
22
+ });
23
+ }
24
+ logger.info({ groupId: group.id, workspaceId: context.workspaceId }, "Conversation group created");
25
+ return group;
26
+ });
@@ -27,6 +27,7 @@ export default defineEventHandler(async (event) => {
27
27
  }
28
28
  const [conversation] = await db.insert(schema.conversations).values({
29
29
  workspaceId: context.workspaceId,
30
+ groupId: null,
30
31
  userId: context.user.id,
31
32
  createdBy: context.user.email,
32
33
  telaAgentId: context.telaAgentId,
@@ -0,0 +1,143 @@
1
+ export declare const conversationGroups: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
+ name: "conversation_groups";
3
+ schema: "chat";
4
+ columns: {
5
+ id: import("drizzle-orm/pg-core").PgColumn<{
6
+ name: "id";
7
+ tableName: "conversation_groups";
8
+ dataType: "string";
9
+ columnType: "PgUUID";
10
+ data: string;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: true;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: undefined;
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {}>;
22
+ workspaceId: import("drizzle-orm/pg-core").PgColumn<{
23
+ name: "workspace_id";
24
+ tableName: "conversation_groups";
25
+ dataType: "string";
26
+ columnType: "PgText";
27
+ data: string;
28
+ driverParam: string;
29
+ notNull: true;
30
+ hasDefault: false;
31
+ isPrimaryKey: false;
32
+ isAutoincrement: false;
33
+ hasRuntimeDefault: false;
34
+ enumValues: [string, ...string[]];
35
+ baseColumn: never;
36
+ identity: undefined;
37
+ generated: undefined;
38
+ }, {}, {}>;
39
+ telaAgentId: import("drizzle-orm/pg-core").PgColumn<{
40
+ name: "tela_agent_id";
41
+ tableName: "conversation_groups";
42
+ dataType: "string";
43
+ columnType: "PgText";
44
+ data: string;
45
+ driverParam: string;
46
+ notNull: false;
47
+ hasDefault: false;
48
+ isPrimaryKey: false;
49
+ isAutoincrement: false;
50
+ hasRuntimeDefault: false;
51
+ enumValues: [string, ...string[]];
52
+ baseColumn: never;
53
+ identity: undefined;
54
+ generated: undefined;
55
+ }, {}, {}>;
56
+ conversationScope: import("drizzle-orm/pg-core").PgColumn<{
57
+ name: "conversation_scope";
58
+ tableName: "conversation_groups";
59
+ dataType: "string";
60
+ columnType: "PgText";
61
+ data: string;
62
+ driverParam: string;
63
+ notNull: false;
64
+ hasDefault: false;
65
+ isPrimaryKey: false;
66
+ isAutoincrement: false;
67
+ hasRuntimeDefault: false;
68
+ enumValues: [string, ...string[]];
69
+ baseColumn: never;
70
+ identity: undefined;
71
+ generated: undefined;
72
+ }, {}, {}>;
73
+ name: import("drizzle-orm/pg-core").PgColumn<{
74
+ name: "name";
75
+ tableName: "conversation_groups";
76
+ dataType: "string";
77
+ columnType: "PgText";
78
+ data: string;
79
+ driverParam: string;
80
+ notNull: true;
81
+ hasDefault: false;
82
+ isPrimaryKey: false;
83
+ isAutoincrement: false;
84
+ hasRuntimeDefault: false;
85
+ enumValues: [string, ...string[]];
86
+ baseColumn: never;
87
+ identity: undefined;
88
+ generated: undefined;
89
+ }, {}, {}>;
90
+ createdBy: import("drizzle-orm/pg-core").PgColumn<{
91
+ name: "created_by";
92
+ tableName: "conversation_groups";
93
+ dataType: "string";
94
+ columnType: "PgText";
95
+ data: string;
96
+ driverParam: string;
97
+ notNull: false;
98
+ hasDefault: false;
99
+ isPrimaryKey: false;
100
+ isAutoincrement: false;
101
+ hasRuntimeDefault: false;
102
+ enumValues: [string, ...string[]];
103
+ baseColumn: never;
104
+ identity: undefined;
105
+ generated: undefined;
106
+ }, {}, {}>;
107
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
108
+ name: "created_at";
109
+ tableName: "conversation_groups";
110
+ dataType: "date";
111
+ columnType: "PgTimestamp";
112
+ data: Date;
113
+ driverParam: string;
114
+ notNull: true;
115
+ hasDefault: true;
116
+ isPrimaryKey: false;
117
+ isAutoincrement: false;
118
+ hasRuntimeDefault: false;
119
+ enumValues: undefined;
120
+ baseColumn: never;
121
+ identity: undefined;
122
+ generated: undefined;
123
+ }, {}, {}>;
124
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
125
+ name: "updated_at";
126
+ tableName: "conversation_groups";
127
+ dataType: "date";
128
+ columnType: "PgTimestamp";
129
+ data: Date;
130
+ driverParam: string;
131
+ notNull: true;
132
+ hasDefault: true;
133
+ isPrimaryKey: false;
134
+ isAutoincrement: false;
135
+ hasRuntimeDefault: false;
136
+ enumValues: undefined;
137
+ baseColumn: never;
138
+ identity: undefined;
139
+ generated: undefined;
140
+ }, {}, {}>;
141
+ };
142
+ dialect: "pg";
143
+ }>;
@@ -0,0 +1,20 @@
1
+ import { index, text, timestamp, uuid } from "drizzle-orm/pg-core";
2
+ import { chatSchema } from "./_schema.js";
3
+ export const conversationGroups = chatSchema.table("conversation_groups", {
4
+ id: uuid("id").primaryKey().defaultRandom(),
5
+ workspaceId: text("workspace_id").notNull(),
6
+ telaAgentId: text("tela_agent_id"),
7
+ conversationScope: text("conversation_scope"),
8
+ name: text("name").notNull(),
9
+ createdBy: text("created_by"),
10
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
11
+ updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
12
+ }, (table) => [
13
+ index("idx_conversation_groups_workspace").on(table.workspaceId),
14
+ index("idx_conversation_groups_runtime_created_at").on(
15
+ table.workspaceId,
16
+ table.telaAgentId,
17
+ table.conversationScope,
18
+ table.createdAt
19
+ )
20
+ ]);
@@ -37,6 +37,23 @@ export declare const conversations: import("drizzle-orm/pg-core").PgTableWithCol
37
37
  identity: undefined;
38
38
  generated: undefined;
39
39
  }, {}, {}>;
40
+ groupId: import("drizzle-orm/pg-core").PgColumn<{
41
+ name: "group_id";
42
+ tableName: "conversations";
43
+ dataType: "string";
44
+ columnType: "PgUUID";
45
+ data: string;
46
+ driverParam: string;
47
+ notNull: false;
48
+ hasDefault: false;
49
+ isPrimaryKey: false;
50
+ isAutoincrement: false;
51
+ hasRuntimeDefault: false;
52
+ enumValues: undefined;
53
+ baseColumn: never;
54
+ identity: undefined;
55
+ generated: undefined;
56
+ }, {}, {}>;
40
57
  userId: import("drizzle-orm/pg-core").PgColumn<{
41
58
  name: "user_id";
42
59
  tableName: "conversations";
@@ -1,8 +1,10 @@
1
1
  import { uuid, text, timestamp, index, jsonb } from "drizzle-orm/pg-core";
2
2
  import { chatSchema } from "./_schema.js";
3
+ import { conversationGroups } from "./conversation-groups.js";
3
4
  export const conversations = chatSchema.table("conversations", {
4
5
  id: uuid("id").primaryKey().defaultRandom(),
5
6
  workspaceId: text("workspace_id").notNull(),
7
+ groupId: uuid("group_id").references(() => conversationGroups.id, { onDelete: "set null" }),
6
8
  userId: text("user_id").notNull(),
7
9
  title: text("title").notNull().default("Nova conversa"),
8
10
  threadSessionId: text("thread_session_id"),
@@ -16,6 +18,7 @@ export const conversations = chatSchema.table("conversations", {
16
18
  updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
17
19
  }, (table) => [
18
20
  index("idx_conversations_workspace").on(table.workspaceId),
21
+ index("idx_conversations_group").on(table.groupId),
19
22
  index("idx_conversations_user").on(table.userId),
20
23
  index("idx_conversations_updated").on(table.updatedAt),
21
24
  index("idx_conversations_workspace_tela_agent_updated").on(table.workspaceId, table.telaAgentId, table.updatedAt)
@@ -1,4 +1,5 @@
1
1
  export { CHAT_SCHEMA_NAME, chatSchema } from './_schema.js';
2
+ export { conversationGroups } from './conversation-groups.js';
2
3
  export { conversationUsage, type ConversationUsageMetadata, type ModelUsageEntry } from './conversation-usage.js';
3
4
  export { conversations } from './conversations.js';
4
5
  export { type ExternalSkillInsert, type ExternalSkillRow, externalSkills, externalSkillsRelations } from './external-skills.js';
@@ -1,4 +1,5 @@
1
1
  export { CHAT_SCHEMA_NAME, chatSchema } from "./_schema.js";
2
+ export { conversationGroups } from "./conversation-groups.js";
2
3
  export { conversationUsage } from "./conversation-usage.js";
3
4
  export { conversations } from "./conversations.js";
4
5
  export { externalSkills, externalSkillsRelations } from "./external-skills.js";
@@ -0,0 +1,4 @@
1
+ import type { ChatRuntimeContext } from '#chat-runtime/server/utils/chat-context';
2
+ export declare function conversationGroupScopeConditions(context: ChatRuntimeContext, groupId?: string): import("drizzle-orm").SQL<unknown> | undefined;
3
+ export declare function resolveConversationGroupName(name: unknown): string;
4
+ export declare function resolveOptionalConversationGroupId(groupId: unknown): string | null;
@@ -0,0 +1,48 @@
1
+ import { and, eq, isNull } from "drizzle-orm";
2
+ import { createError } from "h3";
3
+ import { schema } from "#chat-runtime/server/db";
4
+ const MAX_GROUP_NAME_LENGTH = 80;
5
+ export function conversationGroupScopeConditions(context, groupId) {
6
+ const conditions = [
7
+ eq(schema.conversationGroups.workspaceId, context.workspaceId),
8
+ context.telaAgentId ? eq(schema.conversationGroups.telaAgentId, context.telaAgentId) : isNull(schema.conversationGroups.telaAgentId),
9
+ context.conversationScope ? eq(schema.conversationGroups.conversationScope, context.conversationScope) : isNull(schema.conversationGroups.conversationScope)
10
+ ];
11
+ if (groupId) {
12
+ conditions.unshift(eq(schema.conversationGroups.id, groupId));
13
+ }
14
+ return and(...conditions);
15
+ }
16
+ export function resolveConversationGroupName(name) {
17
+ if (typeof name !== "string") {
18
+ throw createError({
19
+ statusCode: 400,
20
+ statusMessage: "Group name is required"
21
+ });
22
+ }
23
+ const trimmedName = name.trim();
24
+ if (!trimmedName) {
25
+ throw createError({
26
+ statusCode: 400,
27
+ statusMessage: "Group name cannot be empty"
28
+ });
29
+ }
30
+ if (trimmedName.length > MAX_GROUP_NAME_LENGTH) {
31
+ throw createError({
32
+ statusCode: 400,
33
+ statusMessage: "Group name is too long"
34
+ });
35
+ }
36
+ return trimmedName;
37
+ }
38
+ export function resolveOptionalConversationGroupId(groupId) {
39
+ if (groupId === null)
40
+ return null;
41
+ if (typeof groupId !== "string" || !groupId.trim()) {
42
+ throw createError({
43
+ statusCode: 400,
44
+ statusMessage: "Invalid group ID"
45
+ });
46
+ }
47
+ return groupId.trim();
48
+ }
@@ -1,9 +1,10 @@
1
1
  import type { InferSelectModel } from 'drizzle-orm';
2
- import type { conversations, messages, ContextFile as SchemaContextFile, ExternalSkill as SchemaExternalSkill, MessageFile as SchemaMessageFile } from '#chat-runtime/server/db/schema';
2
+ import type { conversationGroups, conversations, messages, ContextFile as SchemaContextFile, ExternalSkill as SchemaExternalSkill, MessageFile as SchemaMessageFile } from '#chat-runtime/server/db/schema';
3
3
  export type ContextFile = SchemaContextFile;
4
4
  export type ExternalSkill = SchemaExternalSkill;
5
5
  export type MessageFile = SchemaMessageFile;
6
6
  export type Conversation = InferSelectModel<typeof conversations>;
7
+ export type ConversationGroup = InferSelectModel<typeof conversationGroups>;
7
8
  export type Message = InferSelectModel<typeof messages>;
8
9
  export type ConversationCreatorFilter = 'all' | 'mine';
9
10
  export type ConversationCreatorFilterCounts = Record<ConversationCreatorFilter, number>;
@@ -0,0 +1,18 @@
1
+ export type ConversationGroupLike = {
2
+ id: string;
3
+ name: string;
4
+ createdAt?: Date | string;
5
+ };
6
+ export type ConversationGroupItem = {
7
+ groupId?: string | null;
8
+ updatedAt: Date | string;
9
+ };
10
+ export type ConversationGroupBucket<TGroup extends ConversationGroupLike, TConversation extends ConversationGroupItem> = {
11
+ group: TGroup;
12
+ items: TConversation[];
13
+ };
14
+ export type GroupedConversations<TGroup extends ConversationGroupLike, TConversation extends ConversationGroupItem> = {
15
+ groups: ConversationGroupBucket<TGroup, TConversation>[];
16
+ unfiled: TConversation[];
17
+ };
18
+ export declare function groupConversationsByGroup<TConversation extends ConversationGroupItem, TGroup extends ConversationGroupLike>(conversations: readonly TConversation[], groups: readonly TGroup[]): GroupedConversations<TGroup, TConversation>;