@meistrari/chat-nuxt 1.4.0 → 1.5.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 (85) hide show
  1. package/README.md +51 -16
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +12 -8
  4. package/dist/runtime/components/MeistrariChatEmbed.d.vue.ts +2 -18
  5. package/dist/runtime/components/MeistrariChatEmbed.vue +22 -4
  6. package/dist/runtime/components/MeistrariChatEmbed.vue.d.ts +2 -18
  7. package/dist/runtime/components/chat/message-input.d.vue.ts +30 -4
  8. package/dist/runtime/components/chat/message-input.vue +268 -24
  9. package/dist/runtime/components/chat/message-input.vue.d.ts +30 -4
  10. package/dist/runtime/composables/useChat.d.ts +3 -5
  11. package/dist/runtime/composables/useChat.js +18 -4
  12. package/dist/runtime/composables/useChatApi.js +4 -0
  13. package/dist/runtime/composables/useConversations.js +8 -2
  14. package/dist/runtime/composables/useEmbedConfig.d.ts +7 -1
  15. package/dist/runtime/composables/useEmbedConfig.js +8 -0
  16. package/dist/runtime/composables/useFeatureFlags.d.ts +1 -2
  17. package/dist/runtime/composables/useTelaAgentMetadata.d.ts +8 -0
  18. package/dist/runtime/composables/useTelaAgentMetadata.js +65 -0
  19. package/dist/runtime/composables/useWorkspaceSettings.d.ts +1 -19
  20. package/dist/runtime/embed/components/ChatConfigurationModal.d.vue.ts +1 -1
  21. package/dist/runtime/embed/components/ChatConfigurationModal.vue.d.ts +1 -1
  22. package/dist/runtime/embed/components/ChatEmbed.d.vue.ts +3 -23
  23. package/dist/runtime/embed/components/ChatEmbed.vue +34 -9
  24. package/dist/runtime/embed/components/ChatEmbed.vue.d.ts +3 -23
  25. package/dist/runtime/embed/components/ChatEmbedInner.vue +36 -12
  26. package/dist/runtime/plugins/markstream.d.ts +1 -1
  27. package/dist/runtime/plugins/markstream.js +1 -1
  28. package/dist/runtime/server/api/conversations/[id]/cancel.post.d.ts +1 -1
  29. package/dist/runtime/server/api/conversations/[id]/cancel.post.js +37 -17
  30. package/dist/runtime/server/api/conversations/[id]/clear.post.js +28 -13
  31. package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +11 -13
  32. package/dist/runtime/server/api/conversations/[id]/files.get.js +5 -7
  33. package/dist/runtime/server/api/conversations/[id]/index.delete.js +13 -12
  34. package/dist/runtime/server/api/conversations/[id]/index.get.js +6 -13
  35. package/dist/runtime/server/api/conversations/[id]/index.patch.js +11 -10
  36. package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +46 -80
  37. package/dist/runtime/server/api/conversations/[id]/messages/index.get.d.ts +1 -14
  38. package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +81 -187
  39. package/dist/runtime/server/api/conversations/[id]/messages/index.post.js +68 -86
  40. package/dist/runtime/server/api/conversations/[id]/usage.get.js +4 -8
  41. package/dist/runtime/server/api/conversations/generate-title.post.js +4 -3
  42. package/dist/runtime/server/api/conversations/index.get.js +5 -4
  43. package/dist/runtime/server/api/conversations/index.post.js +5 -4
  44. package/dist/runtime/server/api/tela/agents/[id].get.d.ts +6 -0
  45. package/dist/runtime/server/api/tela/agents/[id].get.js +25 -0
  46. package/dist/runtime/server/api/workspace/settings.patch.js +1 -1
  47. package/dist/runtime/server/db/schema/conversations.d.ts +17 -0
  48. package/dist/runtime/server/db/schema/conversations.js +3 -1
  49. package/dist/runtime/server/db/schema/workspace-settings.d.ts +1 -1
  50. package/dist/runtime/server/utils/chat-context.d.ts +5 -0
  51. package/dist/runtime/server/utils/chat-context.js +20 -0
  52. package/dist/runtime/server/utils/conversation-agent-turn.d.ts +39 -0
  53. package/dist/runtime/server/utils/conversation-agent-turn.js +170 -0
  54. package/dist/runtime/server/utils/conversation-message-files.d.ts +10 -0
  55. package/dist/runtime/server/utils/conversation-message-files.js +77 -0
  56. package/dist/runtime/server/utils/conversation-message-sync.d.ts +31 -0
  57. package/dist/runtime/server/utils/conversation-message-sync.js +132 -0
  58. package/dist/runtime/server/utils/conversation-scope.d.ts +2 -0
  59. package/dist/runtime/server/utils/conversation-scope.js +12 -0
  60. package/dist/runtime/server/utils/tela-agent-api.d.ts +29 -0
  61. package/dist/runtime/server/utils/tela-agent-api.js +107 -0
  62. package/dist/runtime/server/utils/tela-agent-session.d.ts +48 -0
  63. package/dist/runtime/server/utils/tela-agent-session.js +350 -0
  64. package/dist/runtime/types/embed.d.ts +29 -0
  65. package/dist/runtime/types/embed.js +0 -0
  66. package/dist/runtime/types/tela-agent.d.ts +243 -0
  67. package/dist/runtime/types/tela-agent.js +98 -0
  68. package/dist/runtime/types/workspace-settings-data.d.ts +19 -0
  69. package/dist/runtime/types/workspace-settings-data.js +0 -0
  70. package/dist/runtime/types/workspace-settings.d.ts +1 -1
  71. package/dist/runtime/utils/agent-input-readiness.d.ts +14 -0
  72. package/dist/runtime/utils/agent-input-readiness.js +15 -0
  73. package/dist/runtime/utils/features.d.ts +9 -0
  74. package/dist/runtime/utils/features.js +5 -4
  75. package/dist/runtime/utils/file.js +46 -7
  76. package/dist/runtime/utils/markdown-nodes.d.ts +1 -0
  77. package/dist/runtime/utils/markdown-nodes.js +6 -0
  78. package/dist/runtime/utils/tela-chat.d.ts +6 -0
  79. package/dist/runtime/utils/tela-chat.js +12 -1
  80. package/drizzle/0014_faulty_lake.sql +2 -0
  81. package/drizzle/meta/0014_snapshot.json +732 -0
  82. package/drizzle/meta/_journal.json +7 -0
  83. package/package.json +1 -1
  84. package/dist/runtime/types/feature-flags.d.ts +0 -7
  85. package/dist/runtime/types/feature-flags.js +0 -6
@@ -0,0 +1,98 @@
1
+ import { z } from "zod";
2
+ export const telaAgentAttachmentSchema = z.object({
3
+ vaultRef: z.string().startsWith("vault://"),
4
+ filename: z.string().min(1).max(255)
5
+ });
6
+ export const telaAgentInputVariableSchema = z.object({
7
+ id: z.string().optional(),
8
+ name: z.string().min(1),
9
+ type: z.enum(["file", "text"]),
10
+ required: z.boolean().optional(),
11
+ description: z.string().nullable().optional()
12
+ }).catchall(z.unknown());
13
+ export const telaAgentFileInputSchema = z.object({
14
+ type: z.literal("file"),
15
+ name: z.string().min(1),
16
+ vaultRef: z.string().startsWith("vault://"),
17
+ filename: z.string().min(1).max(255),
18
+ metadata: z.string().optional()
19
+ });
20
+ export const telaAgentTextInputSchema = z.object({
21
+ type: z.literal("text"),
22
+ name: z.string().min(1),
23
+ content: z.string()
24
+ });
25
+ export const telaAgentExecutionInputSchema = z.discriminatedUnion("type", [
26
+ telaAgentFileInputSchema,
27
+ telaAgentTextInputSchema
28
+ ]);
29
+ export const telaAgentRunPayloadSchema = z.object({
30
+ message: z.string().min(1),
31
+ sessionId: z.string().optional(),
32
+ inputSchema: z.array(telaAgentExecutionInputSchema).optional(),
33
+ attachments: z.array(telaAgentAttachmentSchema).optional()
34
+ }).catchall(z.unknown());
35
+ export const telaAgentSchema = z.object({
36
+ id: z.string(),
37
+ inputSchema: z.array(telaAgentInputVariableSchema).nullable().optional().transform((value) => value ?? []),
38
+ isMultiturn: z.boolean().nullable().optional()
39
+ }).catchall(z.unknown());
40
+ export const telaAgentRunResponseSchema = z.object({
41
+ sessionId: z.string()
42
+ }).catchall(z.unknown());
43
+ export const telaAgentSessionStatusSchema = z.enum([
44
+ "pending",
45
+ "running",
46
+ "completed",
47
+ "failed",
48
+ "waiting_messages",
49
+ "cancelled"
50
+ ]);
51
+ export const telaAgentStepSchema = z.object({
52
+ id: z.string(),
53
+ eventId: z.string().optional(),
54
+ inputTokens: z.number().nullable().optional(),
55
+ outputTokens: z.number().nullable().optional(),
56
+ cacheReadInputTokens: z.number().nullable().optional(),
57
+ cacheCreationInputTokens: z.number().nullable().optional(),
58
+ calculatedCostUsd: z.number().nullable().optional()
59
+ }).catchall(z.unknown());
60
+ export const telaAgentUsageSchema = z.object({
61
+ duration: z.number().optional(),
62
+ turns: z.number().optional(),
63
+ completionTokens: z.number().optional(),
64
+ promptTokens: z.number().optional(),
65
+ totalTokens: z.number().optional(),
66
+ completionCost: z.number().optional(),
67
+ promptCost: z.number().optional(),
68
+ totalCost: z.number().optional()
69
+ }).catchall(z.unknown());
70
+ export const telaAgentSessionSchema = z.object({
71
+ sessionId: z.string(),
72
+ status: telaAgentSessionStatusSchema,
73
+ steps: z.array(telaAgentStepSchema),
74
+ nextCursor: z.number().nullable().optional(),
75
+ result: z.object({
76
+ usage: telaAgentUsageSchema.optional(),
77
+ content: z.string().optional(),
78
+ structuredContent: z.record(z.string(), z.unknown()).optional(),
79
+ childEventIds: z.array(z.string()).optional()
80
+ }).catchall(z.unknown()).optional(),
81
+ error: z.string().optional(),
82
+ createdAt: z.number().optional(),
83
+ updatedAt: z.number().optional()
84
+ }).catchall(z.unknown());
85
+ export const telaAgentRunApiResponseSchema = z.object({
86
+ data: telaAgentRunResponseSchema
87
+ }).catchall(z.unknown());
88
+ export const telaAgentApiResponseSchema = z.object({
89
+ data: telaAgentSchema
90
+ }).catchall(z.unknown());
91
+ export const telaAgentSessionApiResponseSchema = z.object({
92
+ data: telaAgentSessionSchema
93
+ }).catchall(z.unknown());
94
+ export const telaAgentEndSessionApiResponseSchema = z.object({
95
+ data: z.object({
96
+ success: z.boolean()
97
+ }).catchall(z.unknown())
98
+ }).catchall(z.unknown());
@@ -0,0 +1,19 @@
1
+ import type { ContextFile, ExternalSkill } from '../server/db/schema/workspace-settings.js';
2
+ import type { CanvasTool } from './agent.js';
3
+ import type { KnowledgeSource } from './chat.js';
4
+ export type WorkspaceSettings = {
5
+ workspaceId: string;
6
+ systemMessage: string | null;
7
+ contextFiles: ContextFile[] | null;
8
+ canvasTools: CanvasTool[] | null;
9
+ knowledgeSources: KnowledgeSource[] | null;
10
+ externalSkills: ExternalSkill[] | null;
11
+ updatedAt: Date;
12
+ };
13
+ export type WorkspaceSettingsUpdate = {
14
+ systemMessage?: string | null;
15
+ contextFiles?: ContextFile[] | null;
16
+ canvasTools?: CanvasTool[] | null;
17
+ knowledgeSources?: KnowledgeSource[] | null;
18
+ externalSkills?: ExternalSkill[] | null;
19
+ };
File without changes
@@ -1,3 +1,3 @@
1
1
  import type { ChatConfigurationSavePayload, ChatConfigurationTab } from '../embed/components/ChatConfigurationModal.vue.js';
2
- import type { WorkspaceSettings, WorkspaceSettingsUpdate } from '../composables/useWorkspaceSettings.js';
2
+ import type { WorkspaceSettings, WorkspaceSettingsUpdate } from './workspace-settings-data.js';
3
3
  export type { ChatConfigurationSavePayload, ChatConfigurationTab, WorkspaceSettings, WorkspaceSettingsUpdate, };
@@ -0,0 +1,14 @@
1
+ import type { TelaAgentInputVariable } from '../types/tela-agent.js';
2
+ export type AgentInputPendingFile = {
3
+ inputName?: string | null;
4
+ };
5
+ export type AgentInputReadiness = {
6
+ ready: boolean;
7
+ missingRequiredInputs: boolean;
8
+ hasUnmappedFiles: boolean;
9
+ };
10
+ export declare function getAgentInputReadiness(options: {
11
+ inputVariables: readonly TelaAgentInputVariable[];
12
+ inputTexts: Record<string, string | undefined>;
13
+ files: readonly AgentInputPendingFile[];
14
+ }): AgentInputReadiness;
@@ -0,0 +1,15 @@
1
+ export function getAgentInputReadiness(options) {
2
+ const inputVariables = options.inputVariables.filter((input) => input.name.trim());
3
+ const fileInputs = inputVariables.filter((input) => input.type === "file");
4
+ const textInputs = inputVariables.filter((input) => input.type === "text");
5
+ const fileInputNames = new Set(fileInputs.map((input) => input.name));
6
+ const missingRequiredFileInput = fileInputs.filter((input) => input.required).some((input) => !options.files.some((file) => file.inputName === input.name));
7
+ const missingRequiredTextInput = textInputs.filter((input) => input.required).some((input) => (options.inputTexts[input.name] ?? "").trim().length === 0);
8
+ const hasUnmappedFiles = fileInputs.length > 0 && options.files.some((file) => !file.inputName || !fileInputNames.has(file.inputName));
9
+ const missingRequiredInputs = missingRequiredFileInput || missingRequiredTextInput;
10
+ return {
11
+ ready: !missingRequiredInputs && !hasUnmappedFiles,
12
+ missingRequiredInputs,
13
+ hasUnmappedFiles
14
+ };
15
+ }
@@ -3,4 +3,13 @@ export interface ResolvedChatNuxtFeatures {
3
3
  generateTitle: boolean;
4
4
  credentials: boolean;
5
5
  }
6
+ export interface ChatNuxtFeatureRuntimeConfig {
7
+ public?: {
8
+ chatNuxtFeatures?: Partial<ResolvedChatNuxtFeatures>;
9
+ };
10
+ chatNuxt?: {
11
+ features?: Partial<ResolvedChatNuxtFeatures>;
12
+ };
13
+ }
14
+ export declare function resolveChatNuxtFeatures(config?: ChatNuxtFeatureRuntimeConfig): ResolvedChatNuxtFeatures;
6
15
  export declare function useChatNuxtFeatures(): ResolvedChatNuxtFeatures;
@@ -1,15 +1,16 @@
1
- import { useRuntimeConfig } from "#imports";
2
1
  const DEFAULTS = {
3
2
  usage: true,
4
3
  generateTitle: false,
5
4
  credentials: false
6
5
  };
7
- export function useChatNuxtFeatures() {
8
- const config = useRuntimeConfig();
9
- const features = config.public.chatNuxtFeatures;
6
+ export function resolveChatNuxtFeatures(config = {}) {
7
+ const features = config.chatNuxt?.features ?? config.public?.chatNuxtFeatures;
10
8
  return {
11
9
  usage: features?.usage ?? DEFAULTS.usage,
12
10
  generateTitle: features?.generateTitle ?? DEFAULTS.generateTitle,
13
11
  credentials: features?.credentials ?? DEFAULTS.credentials
14
12
  };
15
13
  }
14
+ export function useChatNuxtFeatures() {
15
+ return resolveChatNuxtFeatures(useRuntimeConfig());
16
+ }
@@ -3,6 +3,7 @@ const VAULT_REGEX = /(?<!\]\()vault:\/\/[^\s"'<>`\[\]()*_~]+/gi;
3
3
  const URL_REGEX = /https?:\/\/[^\s"'<>`\[\]()*_~]+/gi;
4
4
  const VAULT_PATTERN = new RegExp(`vault://${URL_CHAR_CLASS}`, "gi");
5
5
  const VAULT_PERMALINK_PATTERN = new RegExp(`https://(?:[a-z0-9-]+\\.)*(?:vault|api)\\.tela(?:staging)?\\.com/(?:[a-z0-9_-]+/)*permalinks/${URL_CHAR_CLASS}`, "gi");
6
+ const MARKDOWN_LINK_PATTERN = /\[([^\]\n]*)\]\(([^)\s]+)(?:\s+["'][^)]*["'])?\)/g;
6
7
  const FILE_EXTENSIONS = {
7
8
  pdf: "application/pdf",
8
9
  doc: "application/msword",
@@ -257,12 +258,44 @@ function getPermalinkId(url) {
257
258
  const match = url.match(/permalinks\/([a-f0-9-]+)/);
258
259
  return match?.[1] ?? "Arquivo";
259
260
  }
261
+ function overlapsFileMatch(start, end, fileMatches) {
262
+ return fileMatches.some((fileMatch) => start < fileMatch.end && end > fileMatch.start);
263
+ }
264
+ function collectMarkdownFileLinkMatches(text) {
265
+ const matches = [];
266
+ for (const match of text.matchAll(MARKDOWN_LINK_PATTERN)) {
267
+ const start = match.index;
268
+ if (text[start - 1] === "!")
269
+ continue;
270
+ const href = match[2];
271
+ if (!href)
272
+ continue;
273
+ const file = detectFileFromUrl(href);
274
+ if (!file)
275
+ continue;
276
+ matches.push({
277
+ start,
278
+ end: start + match[0].length,
279
+ file
280
+ });
281
+ }
282
+ return matches;
283
+ }
260
284
  function detectFilesInText(text) {
261
285
  const files = [];
262
286
  const seen = /* @__PURE__ */ new Set();
263
- const vaultMatches = text.match(VAULT_REGEX) ?? [];
264
- for (const rawMatch of vaultMatches) {
265
- const match = stripTrailingPunctuation(rawMatch);
287
+ const markdownFileLinkMatches = collectMarkdownFileLinkMatches(text);
288
+ for (const { file } of markdownFileLinkMatches) {
289
+ if (seen.has(file.reference))
290
+ continue;
291
+ seen.add(file.reference);
292
+ files.push(file);
293
+ }
294
+ for (const rawMatch of text.matchAll(VAULT_REGEX)) {
295
+ const match = stripTrailingPunctuation(rawMatch[0]);
296
+ const start = rawMatch.index;
297
+ if (overlapsFileMatch(start, start + match.length, markdownFileLinkMatches))
298
+ continue;
266
299
  if (seen.has(match))
267
300
  continue;
268
301
  seen.add(match);
@@ -278,9 +311,11 @@ function detectFilesInText(text) {
278
311
  hashParams
279
312
  });
280
313
  }
281
- const urlMatches = text.match(URL_REGEX) ?? [];
282
- for (const rawMatch of urlMatches) {
283
- const match = stripTrailingPunctuation(rawMatch);
314
+ for (const rawMatch of text.matchAll(URL_REGEX)) {
315
+ const match = stripTrailingPunctuation(rawMatch[0]);
316
+ const start = rawMatch.index;
317
+ if (overlapsFileMatch(start, start + match.length, markdownFileLinkMatches))
318
+ continue;
284
319
  if (seen.has(match))
285
320
  continue;
286
321
  if (isVaultS3Url(match)) {
@@ -320,10 +355,12 @@ function detectFilesInText(text) {
320
355
  }
321
356
  function parseContentWithFiles(text) {
322
357
  const segments = [];
323
- const fileMatches = [];
358
+ const fileMatches = collectMarkdownFileLinkMatches(text);
324
359
  for (const match of text.matchAll(VAULT_REGEX)) {
325
360
  const rawUrl = match[0];
326
361
  const url = stripTrailingPunctuation(rawUrl);
362
+ if (overlapsFileMatch(match.index, match.index + url.length, fileMatches))
363
+ continue;
327
364
  const hashParams = parseVaultHashParams(url);
328
365
  const filenameExt = hashParams?.filename?.split(".").pop()?.toLowerCase();
329
366
  const urlExt = getExtensionFromUrl(url);
@@ -343,6 +380,8 @@ function parseContentWithFiles(text) {
343
380
  for (const match of text.matchAll(URL_REGEX)) {
344
381
  const rawUrl = match[0];
345
382
  const url = stripTrailingPunctuation(rawUrl);
383
+ if (overlapsFileMatch(match.index, match.index + url.length, fileMatches))
384
+ continue;
346
385
  if (isVaultS3Url(url)) {
347
386
  const { name, mimeType } = getFileInfoFromVaultS3Url(url);
348
387
  fileMatches.push({
@@ -20,3 +20,4 @@ export interface MarkdownGenericNode {
20
20
  export type MarkdownNode = MarkdownCodeBlockNode | MarkdownMermaidNode | MarkdownGenericNode;
21
21
  export declare function transformMarkdownNodes<T>(value: T): T;
22
22
  export declare function extractMermaidSource(node: MarkdownNode | null | undefined): string | undefined;
23
+ export declare function jsonMarkdown(value: unknown): string;
@@ -38,3 +38,9 @@ export function extractMermaidSource(node) {
38
38
  }
39
39
  return normalizeMermaidSource(node);
40
40
  }
41
+ export function jsonMarkdown(value) {
42
+ const json = JSON.stringify(value, null, 2);
43
+ return json ? `\`\`\`json
44
+ ${json}
45
+ \`\`\`` : "";
46
+ }
@@ -1,10 +1,14 @@
1
1
  export type ChatFeatureConfig = {
2
2
  showUsageTab?: boolean;
3
3
  showDebugOption?: boolean;
4
+ showModelSelector?: boolean;
5
+ showCancelButton?: boolean;
4
6
  };
5
7
  export type ResolvedChatFeatureConfig = {
6
8
  showUsageTab: boolean;
7
9
  showDebugOption: boolean;
10
+ showModelSelector: boolean;
11
+ showCancelButton: boolean;
8
12
  };
9
13
  export type ChatActor = {
10
14
  name?: string | null;
@@ -12,6 +16,8 @@ export type ChatActor = {
12
16
  image?: string | null;
13
17
  };
14
18
  export declare function normalizeConversationId(value: string | null | undefined): string | null;
19
+ export declare function normalizeTelaAgentId(value: string | null | undefined): string | null;
20
+ export declare function resolveChatStateScope(workspaceId: string | null | undefined, telaAgentId: string | null | undefined): string;
15
21
  export declare function resolveChatFeatures(features?: Partial<ChatFeatureConfig>): ResolvedChatFeatureConfig;
16
22
  export declare function resolveConversationCreator(conversationUserId: string | null | undefined, options: {
17
23
  currentUser?: ChatActor | null;
@@ -5,10 +5,21 @@ export function normalizeConversationId(value) {
5
5
  const normalized = value?.trim();
6
6
  return normalized || null;
7
7
  }
8
+ export function normalizeTelaAgentId(value) {
9
+ const normalized = value?.trim();
10
+ return normalized || null;
11
+ }
12
+ export function resolveChatStateScope(workspaceId, telaAgentId) {
13
+ const normalizedWorkspaceId = workspaceId?.trim() || "default";
14
+ const normalizedTelaAgentId = normalizeTelaAgentId(telaAgentId);
15
+ return normalizedTelaAgentId ? `${normalizedWorkspaceId}:tela-agent:${normalizedTelaAgentId}` : `${normalizedWorkspaceId}:default-chat`;
16
+ }
8
17
  export function resolveChatFeatures(features) {
9
18
  return {
10
19
  showUsageTab: features?.showUsageTab ?? false,
11
- showDebugOption: features?.showDebugOption ?? false
20
+ showDebugOption: features?.showDebugOption ?? false,
21
+ showModelSelector: features?.showModelSelector ?? false,
22
+ showCancelButton: features?.showCancelButton ?? false
12
23
  };
13
24
  }
14
25
  export function resolveConversationCreator(conversationUserId, options) {
@@ -0,0 +1,2 @@
1
+ ALTER TABLE "chat"."conversations" ADD COLUMN "tela_agent_id" text;--> statement-breakpoint
2
+ CREATE INDEX "idx_conversations_workspace_tela_agent_updated" ON "chat"."conversations" USING btree ("workspace_id","tela_agent_id","updated_at");