@meistrari/chat-nuxt 1.11.0 → 1.13.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 (89) 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/reasoning-message.vue +4 -1
  22. package/dist/runtime/components/chat/topbar.d.vue.ts +2 -2
  23. package/dist/runtime/components/chat/topbar.vue.d.ts +2 -2
  24. package/dist/runtime/composables/useConversationGroups.d.ts +12 -0
  25. package/dist/runtime/composables/useConversationGroups.js +160 -0
  26. package/dist/runtime/composables/useConversationItem.d.ts +48 -0
  27. package/dist/runtime/composables/useConversationItem.js +176 -0
  28. package/dist/runtime/composables/useConversationList.d.ts +128 -0
  29. package/dist/runtime/composables/useConversationList.js +553 -0
  30. package/dist/runtime/composables/useConversations.d.ts +2 -0
  31. package/dist/runtime/composables/useConversations.js +45 -1
  32. package/dist/runtime/composables/usePendingConversationGroup.d.ts +5 -0
  33. package/dist/runtime/composables/usePendingConversationGroup.js +33 -0
  34. package/dist/runtime/composables/useVerbConjugation.d.ts +1 -0
  35. package/dist/runtime/composables/useVerbConjugation.js +14 -1
  36. package/dist/runtime/embed/components/ChatConfigurationModal.d.vue.ts +1 -1
  37. package/dist/runtime/embed/components/ChatConfigurationModal.vue.d.ts +1 -1
  38. package/dist/runtime/embed/components/ChatEmbedInner.vue +24 -5
  39. package/dist/runtime/server/api/chat/conversations/[id]/group.patch.d.ts +1 -0
  40. package/dist/runtime/server/api/chat/conversations/[id]/group.patch.js +1 -0
  41. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.d.ts +1 -0
  42. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.js +1 -0
  43. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.d.ts +1 -0
  44. package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.js +1 -0
  45. package/dist/runtime/server/api/chat/conversations/groups/index.get.d.ts +1 -0
  46. package/dist/runtime/server/api/chat/conversations/groups/index.get.js +1 -0
  47. package/dist/runtime/server/api/chat/conversations/groups/index.post.d.ts +1 -0
  48. package/dist/runtime/server/api/chat/conversations/groups/index.post.js +1 -0
  49. package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +1 -0
  50. package/dist/runtime/server/api/conversations/[id]/group.patch.d.ts +2 -0
  51. package/dist/runtime/server/api/conversations/[id]/group.patch.js +46 -0
  52. package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +2 -0
  53. package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +26 -4
  54. package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.d.ts +4 -0
  55. package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.js +25 -0
  56. package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.d.ts +2 -0
  57. package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.js +30 -0
  58. package/dist/runtime/server/api/conversations/groups/index.get.d.ts +2 -0
  59. package/dist/runtime/server/api/conversations/groups/index.get.js +13 -0
  60. package/dist/runtime/server/api/conversations/groups/index.post.d.ts +2 -0
  61. package/dist/runtime/server/api/conversations/groups/index.post.js +26 -0
  62. package/dist/runtime/server/api/conversations/index.post.js +1 -0
  63. package/dist/runtime/server/db/schema/conversation-groups.d.ts +143 -0
  64. package/dist/runtime/server/db/schema/conversation-groups.js +20 -0
  65. package/dist/runtime/server/db/schema/conversations.d.ts +17 -0
  66. package/dist/runtime/server/db/schema/conversations.js +3 -0
  67. package/dist/runtime/server/db/schema/index.d.ts +1 -0
  68. package/dist/runtime/server/db/schema/index.js +1 -0
  69. package/dist/runtime/server/db/schema/messages.d.ts +34 -0
  70. package/dist/runtime/server/db/schema/messages.js +3 -1
  71. package/dist/runtime/server/utils/conversation-group.d.ts +4 -0
  72. package/dist/runtime/server/utils/conversation-group.js +48 -0
  73. package/dist/runtime/server/utils/conversation-message-sync.d.ts +1 -1
  74. package/dist/runtime/server/utils/conversation-message-sync.js +2 -2
  75. package/dist/runtime/server/utils/tela-agent-api.d.ts +3 -1
  76. package/dist/runtime/server/utils/tela-agent-api.js +285 -26
  77. package/dist/runtime/server/utils/tela-agent-session.d.ts +14 -4
  78. package/dist/runtime/server/utils/tela-agent-session.js +57 -22
  79. package/dist/runtime/types/chat.d.ts +2 -1
  80. package/dist/runtime/types/tela-agent.d.ts +155 -43
  81. package/dist/runtime/types/tela-agent.js +49 -11
  82. package/dist/runtime/utils/conversation-groups.d.ts +18 -0
  83. package/dist/runtime/utils/conversation-groups.js +30 -0
  84. package/drizzle/0016_dry_aaron_stack.sql +16 -0
  85. package/drizzle/0017_typical_dexter_bennett.sql +2 -0
  86. package/drizzle/meta/0016_snapshot.json +887 -0
  87. package/drizzle/meta/0017_snapshot.json +899 -0
  88. package/drizzle/meta/_journal.json +14 -0
  89. package/package.json +1 -1
@@ -186,6 +186,12 @@ const gerundToPast = new Map(
186
186
  const infinitiveToPast = new Map(
187
187
  ptValues.map((v) => [v.infinitive, v.past])
188
188
  );
189
+ const readFileStepLabels = {
190
+ "instructions.md": "instru\xE7\xF5es",
191
+ "output-format.json": "estrutura de sa\xEDda esperada",
192
+ "input-list.json": "lista de arquivos de entrada",
193
+ "result.json": "resultado"
194
+ };
189
195
  export function useVerbConjugation() {
190
196
  function isEnglish(text) {
191
197
  const firstWord = text.trim().split(/\s+/)[0]?.toLowerCase() || "";
@@ -247,10 +253,17 @@ export function useVerbConjugation() {
247
253
  }
248
254
  return conjugatePortugueseVerb(text);
249
255
  }
256
+ function resolveReadFileStepTitle(fileName, wasExecuted) {
257
+ const label = readFileStepLabels[fileName];
258
+ if (!label)
259
+ return null;
260
+ return conjugateText(`Lendo ${label}`, wasExecuted);
261
+ }
250
262
  return {
251
263
  conjugateText,
252
264
  isEnglish,
253
265
  translateEnglishText,
254
- conjugatePortugueseVerb
266
+ conjugatePortugueseVerb,
267
+ resolveReadFileStepTitle
255
268
  };
256
269
  }
@@ -18,8 +18,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
18
18
  onSave?: ((payload: ChatConfigurationSavePayload) => any) | undefined;
19
19
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
20
20
  }>, {
21
- initialTab: ChatConfigurationTab;
22
21
  saving: boolean;
22
+ initialTab: ChatConfigurationTab;
23
23
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
24
24
  declare const _default: typeof __VLS_export;
25
25
  export default _default;
@@ -18,8 +18,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
18
18
  onSave?: ((payload: ChatConfigurationSavePayload) => any) | undefined;
19
19
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
20
20
  }>, {
21
- initialTab: ChatConfigurationTab;
22
21
  saving: boolean;
22
+ initialTab: ChatConfigurationTab;
23
23
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
24
24
  declare const _default: typeof __VLS_export;
25
25
  export default _default;
@@ -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
+ });
@@ -64,6 +64,8 @@ export default defineEventHandler(async (event) => {
64
64
  reasoningData: null,
65
65
  files: null,
66
66
  ttft: null,
67
+ agentSessionCursor: null,
68
+ agentSessionId: null,
67
69
  updatedAt: null
68
70
  }).where(eq(schema.messages.id, messageId));
69
71
  logger.info({ conversationId, messageId }, "Message retry initiated");
@@ -11,6 +11,7 @@ import {
11
11
  hasPendingMessageExceededGraceWindow,
12
12
  isTerminalMessageUpdate,
13
13
  mapAgentApiSessionToMessageUpdate,
14
+ PENDING_RESPONSE_GRACE_MS,
14
15
  SESSION_STATUS_CHECK_ERROR,
15
16
  THREAD_SESSION_START_ERROR,
16
17
  trackAgentApiConversationUsage,
@@ -18,8 +19,9 @@ import {
18
19
  } from "#chat-runtime/server/utils/conversation-message-sync";
19
20
  import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
20
21
  import { getTelaAgentSession } from "#chat-runtime/server/utils/tela-agent-api";
21
- import { mapTelaAgentSessionToMessageUpdate, mapTelaAgentUsage } from "#chat-runtime/server/utils/tela-agent-session";
22
+ import { mapTelaAgentSessionToMessageUpdate, mapTelaAgentUsage, resolveTelaAgentRequestCursor } from "#chat-runtime/server/utils/tela-agent-session";
22
23
  import { logger } from "#chat-runtime/server/utils/logger";
24
+ const TELA_AGENT_PROGRESS_RECOVERY_MS = 5 * 6e4;
23
25
  export default defineEventHandler(async (event) => {
24
26
  const context = resolveChatRuntimeContext(event);
25
27
  const token = getAgentDataToken(event);
@@ -70,18 +72,38 @@ export default defineEventHandler(async (event) => {
70
72
  }
71
73
  logger.debug({ conversationId, messageId: pendingMessage.id, sessionId: conversation.threadSessionId }, "Polling pending message");
72
74
  if (conversation.telaAgentId) {
73
- const sessionResult2 = await getTelaAgentSession(event, conversation.threadSessionId);
75
+ const agentSessionCursor = resolveTelaAgentRequestCursor({
76
+ pendingMessage,
77
+ lastTerminalMessage: lastTerminalAssistantMessage,
78
+ threadSessionId: conversation.threadSessionId
79
+ });
80
+ const sessionResult2 = await getTelaAgentSession(event, conversation.threadSessionId, {
81
+ cursor: agentSessionCursor
82
+ });
74
83
  if (!sessionResult2.success) {
75
- return { messages };
84
+ const lastProgressAt = pendingMessage.updatedAt ?? pendingMessage.createdAt;
85
+ const hasTelaAgentProgress = typeof pendingMessage.agentSessionCursor === "number" || !!pendingMessage.externalUuid || Array.isArray(pendingMessage.reasoningData) && pendingMessage.reasoningData.length > 0;
86
+ const recoveryMs = hasTelaAgentProgress ? TELA_AGENT_PROGRESS_RECOVERY_MS : PENDING_RESPONSE_GRACE_MS;
87
+ if (!hasPendingMessageExceededGraceWindow({ createdAt: lastProgressAt }, /* @__PURE__ */ new Date(), recoveryMs)) {
88
+ logger.debug({ conversationId, messageId: pendingMessage.id, sessionId: conversation.threadSessionId, error: sessionResult2.error }, "Tela agent session status check failed within grace window");
89
+ return { messages };
90
+ }
91
+ const updateData3 = buildFailedPendingMessageUpdate(SESSION_STATUS_CHECK_ERROR);
92
+ logger.warn({ conversationId, messageId: pendingMessage.id, sessionId: conversation.threadSessionId, error: sessionResult2.error }, "Pending message failed because Tela agent session status check did not recover");
93
+ await db.update(schema.messages).set(updateData3).where(eq(schema.messages.id, pendingMessage.id));
94
+ return buildMessageUpdateResponse(messages, pendingMessage.id, updateData3);
76
95
  }
77
96
  const agentSession2 = sessionResult2.data;
78
97
  const userMessage2 = messages.findLast((message) => message.role === "user");
79
98
  const updateData2 = mapTelaAgentSessionToMessageUpdate(agentSession2, {
80
- lastSyncedStepId: pendingMessage.externalUuid ?? lastTerminalAssistantMessage?.externalUuid,
81
99
  existingReasoningData: Array.isArray(pendingMessage.reasoningData) ? pendingMessage.reasoningData : null,
82
100
  pendingMessageTtft: pendingMessage.ttft,
83
101
  userMessageCreatedAt: userMessage2?.createdAt ?? null
84
102
  });
103
+ if (typeof agentSessionCursor === "number" && pendingMessage.agentSessionCursor !== agentSessionCursor && updateData2.agentSessionCursor === void 0) {
104
+ updateData2.agentSessionCursor = agentSessionCursor;
105
+ updateData2.agentSessionId = conversation.threadSessionId;
106
+ }
85
107
  await attachNewAssistantFilesToMessageUpdate(db, conversationId, updateData2, token);
86
108
  if (!hasMessageUpdate(updateData2)) {
87
109
  return { messages };
@@ -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)