@meistrari/chat-nuxt 1.4.0 → 1.5.1

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 (90) hide show
  1. package/README.md +51 -16
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +24 -9
  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/composables/useWorkspaceSettings.js +24 -5
  21. package/dist/runtime/embed/components/ChatConfigurationModal.d.vue.ts +1 -1
  22. package/dist/runtime/embed/components/ChatConfigurationModal.vue.d.ts +1 -1
  23. package/dist/runtime/embed/components/ChatEmbed.d.vue.ts +3 -23
  24. package/dist/runtime/embed/components/ChatEmbed.vue +34 -9
  25. package/dist/runtime/embed/components/ChatEmbed.vue.d.ts +3 -23
  26. package/dist/runtime/embed/components/ChatEmbedInner.vue +50 -12
  27. package/dist/runtime/plugins/markstream.d.ts +1 -1
  28. package/dist/runtime/plugins/markstream.js +1 -1
  29. package/dist/runtime/server/api/chat/workspace/settings.patch.d.ts +2 -0
  30. package/dist/runtime/server/api/chat/workspace/settings.patch.js +8 -0
  31. package/dist/runtime/server/api/conversations/[id]/cancel.post.d.ts +1 -1
  32. package/dist/runtime/server/api/conversations/[id]/cancel.post.js +37 -17
  33. package/dist/runtime/server/api/conversations/[id]/clear.post.js +28 -13
  34. package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +11 -13
  35. package/dist/runtime/server/api/conversations/[id]/files.get.js +5 -7
  36. package/dist/runtime/server/api/conversations/[id]/index.delete.js +13 -12
  37. package/dist/runtime/server/api/conversations/[id]/index.get.js +6 -13
  38. package/dist/runtime/server/api/conversations/[id]/index.patch.js +11 -10
  39. package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +46 -80
  40. package/dist/runtime/server/api/conversations/[id]/messages/index.get.d.ts +1 -14
  41. package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +81 -187
  42. package/dist/runtime/server/api/conversations/[id]/messages/index.post.js +68 -86
  43. package/dist/runtime/server/api/conversations/[id]/usage.get.js +4 -8
  44. package/dist/runtime/server/api/conversations/generate-title.post.js +4 -3
  45. package/dist/runtime/server/api/conversations/index.get.js +5 -4
  46. package/dist/runtime/server/api/conversations/index.post.js +5 -4
  47. package/dist/runtime/server/api/tela/agents/[id].get.d.ts +6 -0
  48. package/dist/runtime/server/api/tela/agents/[id].get.js +25 -0
  49. package/dist/runtime/server/api/workspace/settings.patch.js +3 -82
  50. package/dist/runtime/server/db/schema/conversations.d.ts +17 -0
  51. package/dist/runtime/server/db/schema/conversations.js +3 -1
  52. package/dist/runtime/server/db/schema/workspace-settings.d.ts +1 -1
  53. package/dist/runtime/server/utils/chat-context.d.ts +5 -0
  54. package/dist/runtime/server/utils/chat-context.js +20 -0
  55. package/dist/runtime/server/utils/conversation-agent-turn.d.ts +39 -0
  56. package/dist/runtime/server/utils/conversation-agent-turn.js +170 -0
  57. package/dist/runtime/server/utils/conversation-message-files.d.ts +10 -0
  58. package/dist/runtime/server/utils/conversation-message-files.js +77 -0
  59. package/dist/runtime/server/utils/conversation-message-sync.d.ts +31 -0
  60. package/dist/runtime/server/utils/conversation-message-sync.js +132 -0
  61. package/dist/runtime/server/utils/conversation-scope.d.ts +2 -0
  62. package/dist/runtime/server/utils/conversation-scope.js +12 -0
  63. package/dist/runtime/server/utils/tela-agent-api.d.ts +29 -0
  64. package/dist/runtime/server/utils/tela-agent-api.js +107 -0
  65. package/dist/runtime/server/utils/tela-agent-session.d.ts +48 -0
  66. package/dist/runtime/server/utils/tela-agent-session.js +350 -0
  67. package/dist/runtime/server/utils/workspace-settings-update.d.ts +16 -0
  68. package/dist/runtime/server/utils/workspace-settings-update.js +84 -0
  69. package/dist/runtime/types/embed.d.ts +29 -0
  70. package/dist/runtime/types/embed.js +0 -0
  71. package/dist/runtime/types/tela-agent.d.ts +243 -0
  72. package/dist/runtime/types/tela-agent.js +98 -0
  73. package/dist/runtime/types/workspace-settings-data.d.ts +19 -0
  74. package/dist/runtime/types/workspace-settings-data.js +0 -0
  75. package/dist/runtime/types/workspace-settings.d.ts +1 -1
  76. package/dist/runtime/utils/agent-input-readiness.d.ts +14 -0
  77. package/dist/runtime/utils/agent-input-readiness.js +15 -0
  78. package/dist/runtime/utils/features.d.ts +9 -0
  79. package/dist/runtime/utils/features.js +5 -4
  80. package/dist/runtime/utils/file.js +46 -7
  81. package/dist/runtime/utils/markdown-nodes.d.ts +1 -0
  82. package/dist/runtime/utils/markdown-nodes.js +6 -0
  83. package/dist/runtime/utils/tela-chat.d.ts +6 -0
  84. package/dist/runtime/utils/tela-chat.js +12 -1
  85. package/drizzle/0014_faulty_lake.sql +2 -0
  86. package/drizzle/meta/0014_snapshot.json +732 -0
  87. package/drizzle/meta/_journal.json +7 -0
  88. package/package.json +1 -1
  89. package/dist/runtime/types/feature-flags.d.ts +0 -7
  90. package/dist/runtime/types/feature-flags.js +0 -6
@@ -1,87 +1,8 @@
1
- import { createError, defineEventHandler, readBody } from "h3";
2
- import { eq, and, inArray } from "drizzle-orm";
3
- import { useDb, schema } from "#chat-runtime/server/db";
1
+ import { defineEventHandler, readBody } from "h3";
4
2
  import { requireUser } from "#chat-runtime/server/utils/auth";
5
- import { rowToExternalSkill } from "#chat-runtime/server/utils/external-skills";
6
- import { logger } from "#chat-runtime/server/utils/logger";
3
+ import { persistWorkspaceSettingsUpdate } from "#chat-runtime/server/utils/workspace-settings-update";
7
4
  export default defineEventHandler(async (event) => {
8
5
  const session = requireUser(event);
9
- const db = useDb();
10
6
  const body = await readBody(event);
11
- const hasSystemMessage = body.systemMessage !== void 0;
12
- const hasContextFiles = body.contextFiles !== void 0;
13
- const hasCanvasTools = body.canvasTools !== void 0;
14
- const hasKnowledgeSources = body.knowledgeSources !== void 0;
15
- const hasExternalSkills = body.externalSkills !== void 0;
16
- if (!hasSystemMessage && !hasContextFiles && !hasCanvasTools && !hasKnowledgeSources && !hasExternalSkills) {
17
- throw createError({
18
- statusCode: 400,
19
- statusMessage: "At least one field is required to update"
20
- });
21
- }
22
- const updateData = {
23
- updatedAt: /* @__PURE__ */ new Date()
24
- };
25
- if (hasSystemMessage) {
26
- updateData.systemMessage = body.systemMessage?.trim() || null;
27
- }
28
- if (hasContextFiles) {
29
- updateData.contextFiles = body.contextFiles && body.contextFiles.length > 0 ? body.contextFiles : null;
30
- }
31
- if (hasCanvasTools) {
32
- updateData.canvasTools = body.canvasTools && body.canvasTools.length > 0 ? body.canvasTools : null;
33
- }
34
- if (hasKnowledgeSources) {
35
- updateData.knowledgeSources = body.knowledgeSources && body.knowledgeSources.length > 0 ? body.knowledgeSources : null;
36
- }
37
- const [existing] = await db.select().from(schema.workspaceSettings).where(eq(schema.workspaceSettings.workspaceId, session.workspace.id));
38
- if (hasExternalSkills) {
39
- const existingSkillRows = await db.select().from(schema.externalSkills).where(eq(schema.externalSkills.workspaceId, session.workspace.id));
40
- const existingRefs = new Set(existingSkillRows.map((s) => s.ref));
41
- const newSkills = body.externalSkills ?? [];
42
- const newRefs = new Set(newSkills.map((s) => s.ref));
43
- const refsToDelete = existingSkillRows.filter((s) => !newRefs.has(s.ref)).map((s) => s.ref);
44
- if (refsToDelete.length > 0) {
45
- await db.delete(schema.externalSkills).where(
46
- and(
47
- eq(schema.externalSkills.workspaceId, session.workspace.id),
48
- inArray(schema.externalSkills.ref, refsToDelete)
49
- )
50
- );
51
- }
52
- const skillsToInsert = newSkills.filter((s) => !existingRefs.has(s.ref)).map((skill) => ({
53
- workspaceId: session.workspace.id,
54
- ref: skill.ref,
55
- name: skill.name,
56
- description: skill.description,
57
- runtime: skill.runtime,
58
- allowedTools: skill.allowedTools,
59
- isPublic: skill.isPublic,
60
- addedByUserId: skill.addedBy?.userId || session.user.id,
61
- addedByUsername: skill.addedBy?.username || session.user.name || "",
62
- addedAt: skill.addedAt ? new Date(skill.addedAt) : /* @__PURE__ */ new Date()
63
- }));
64
- if (skillsToInsert.length > 0) {
65
- await db.insert(schema.externalSkills).values(skillsToInsert);
66
- }
67
- }
68
- logger.info({ workspaceId: session.workspace.id, fields: Object.keys(updateData) }, "Workspace settings updating");
69
- let settings;
70
- if (existing) {
71
- const [updated] = await db.update(schema.workspaceSettings).set(updateData).where(eq(schema.workspaceSettings.workspaceId, session.workspace.id)).returning();
72
- settings = updated;
73
- } else {
74
- const [created] = await db.insert(schema.workspaceSettings).values({
75
- workspaceId: session.workspace.id,
76
- ...updateData
77
- }).returning();
78
- settings = created;
79
- }
80
- const skillRows = await db.select().from(schema.externalSkills).where(eq(schema.externalSkills.workspaceId, session.workspace.id));
81
- const externalSkills = skillRows.map(rowToExternalSkill);
82
- logger.info({ workspaceId: session.workspace.id, externalSkillsCount: externalSkills.length }, "Workspace settings updated");
83
- return {
84
- ...settings,
85
- externalSkills: externalSkills.length > 0 ? externalSkills : null
86
- };
7
+ return persistWorkspaceSettingsUpdate(session.workspace.id, session.user, body);
87
8
  });
@@ -88,6 +88,23 @@ export declare const conversations: import("drizzle-orm/pg-core").PgTableWithCol
88
88
  identity: undefined;
89
89
  generated: undefined;
90
90
  }, {}, {}>;
91
+ telaAgentId: import("drizzle-orm/pg-core").PgColumn<{
92
+ name: "tela_agent_id";
93
+ tableName: "conversations";
94
+ dataType: "string";
95
+ columnType: "PgText";
96
+ data: string;
97
+ driverParam: string;
98
+ notNull: false;
99
+ hasDefault: false;
100
+ isPrimaryKey: false;
101
+ isAutoincrement: false;
102
+ hasRuntimeDefault: false;
103
+ enumValues: [string, ...string[]];
104
+ baseColumn: never;
105
+ identity: undefined;
106
+ generated: undefined;
107
+ }, {}, {}>;
91
108
  model: import("drizzle-orm/pg-core").PgColumn<{
92
109
  name: "model";
93
110
  tableName: "conversations";
@@ -6,6 +6,7 @@ export const conversations = chatSchema.table("conversations", {
6
6
  userId: text("user_id").notNull(),
7
7
  title: text("title").notNull().default("Nova conversa"),
8
8
  threadSessionId: text("thread_session_id"),
9
+ telaAgentId: text("tela_agent_id"),
9
10
  model: text("model").default("claude-sonnet-4-5"),
10
11
  appliedSettingsAt: timestamp("applied_settings_at", { withTimezone: true }),
11
12
  appliedSettingsSnapshot: jsonb("applied_settings_snapshot").$type(),
@@ -15,5 +16,6 @@ export const conversations = chatSchema.table("conversations", {
15
16
  }, (table) => [
16
17
  index("idx_conversations_workspace").on(table.workspaceId),
17
18
  index("idx_conversations_user").on(table.userId),
18
- index("idx_conversations_updated").on(table.updatedAt)
19
+ index("idx_conversations_updated").on(table.updatedAt),
20
+ index("idx_conversations_workspace_tela_agent_updated").on(table.workspaceId, table.telaAgentId, table.updatedAt)
19
21
  ]);
@@ -11,7 +11,7 @@ export type ExternalSkill = {
11
11
  name: string;
12
12
  description: string;
13
13
  runtime: string;
14
- allowedTools: string[];
14
+ allowedTools: readonly string[];
15
15
  isPublic: boolean;
16
16
  addedBy: {
17
17
  userId: string;
@@ -4,6 +4,11 @@ export type ChatContext = {
4
4
  user: AppSession['user'];
5
5
  workspaceId: string;
6
6
  };
7
+ export type ChatRuntimeContext = ChatContext & {
8
+ telaAgentId: string | null;
9
+ };
7
10
  export declare function resolveRequestedWorkspaceId(requestedWorkspaceId: string | null | undefined): string | null;
11
+ export declare function resolveRequestedTelaAgentId(requestedTelaAgentId: string | null | undefined): string | null;
8
12
  export declare function resolveChatContextFromSession(session: AppSession, requestedWorkspaceId?: string | null): ChatContext;
9
13
  export declare function resolveChatContext(event: H3Event): ChatContext;
14
+ export declare function resolveChatRuntimeContext(event: H3Event): ChatRuntimeContext;
@@ -1,9 +1,22 @@
1
1
  import { createError, getHeader } from "h3";
2
2
  import { requireUser } from "#chat-runtime/server/utils/auth";
3
+ const TELA_AGENT_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
3
4
  export function resolveRequestedWorkspaceId(requestedWorkspaceId) {
4
5
  const normalized = requestedWorkspaceId?.trim();
5
6
  return normalized || null;
6
7
  }
8
+ export function resolveRequestedTelaAgentId(requestedTelaAgentId) {
9
+ const normalized = requestedTelaAgentId?.trim();
10
+ if (!normalized)
11
+ return null;
12
+ if (!TELA_AGENT_ID_PATTERN.test(normalized)) {
13
+ throw createError({
14
+ statusCode: 400,
15
+ statusMessage: "Invalid Tela agent ID"
16
+ });
17
+ }
18
+ return normalized;
19
+ }
7
20
  export function resolveChatContextFromSession(session, requestedWorkspaceId) {
8
21
  const normalizedRequestedWorkspaceId = resolveRequestedWorkspaceId(requestedWorkspaceId);
9
22
  if (normalizedRequestedWorkspaceId && normalizedRequestedWorkspaceId !== session.workspace.id) {
@@ -22,3 +35,10 @@ export function resolveChatContext(event) {
22
35
  const requestedWorkspaceId = getHeader(event, "x-chat-workspace-id");
23
36
  return resolveChatContextFromSession(session, requestedWorkspaceId);
24
37
  }
38
+ export function resolveChatRuntimeContext(event) {
39
+ const context = resolveChatContext(event);
40
+ return {
41
+ ...context,
42
+ telaAgentId: resolveRequestedTelaAgentId(getHeader(event, "x-chat-tela-agent-id"))
43
+ };
44
+ }
@@ -0,0 +1,39 @@
1
+ import type { H3Event } from 'h3';
2
+ import { schema } from '#chat-runtime/server/db';
3
+ import type { useDb } from '#chat-runtime/server/db';
4
+ import type { ChatRuntimeContext } from '#chat-runtime/server/utils/chat-context';
5
+ import type { FileUploadInput, MessageFile } from '#chat-runtime/types/chat';
6
+ import type { AgentApiChatPayload, ContinueSessionPayload } from '#chat-runtime/types/agent';
7
+ import type { TelaAgentExecutionInput, TelaAgentRunPayload } from '#chat-runtime/types/tela-agent';
8
+ type DbInstance = ReturnType<typeof useDb>;
9
+ type ConversationRecord = typeof schema.conversations.$inferSelect;
10
+ type AgentTurnOperation = 'send' | 'retry';
11
+ type AgentTurnFiles = FileUploadInput[] | MessageFile[] | null | undefined;
12
+ export type PreparedDefaultAgentTurn = {
13
+ needsNewThread: boolean;
14
+ effectiveModel?: string;
15
+ settingsTimestamp: Date;
16
+ messageTitle: string;
17
+ createPayload: AgentApiChatPayload;
18
+ continuePayload: ContinueSessionPayload;
19
+ };
20
+ export declare function buildTelaAgentTurnPayload(event: H3Event, conversation: Pick<ConversationRecord, 'threadSessionId' | 'telaAgentId'>, content: string, files?: FileUploadInput[] | MessageFile[] | null, agentInputs?: TelaAgentExecutionInput[] | null): Promise<TelaAgentRunPayload>;
21
+ export declare function queueTelaAgentTurn(event: H3Event, db: DbInstance, options: {
22
+ conversation: ConversationRecord;
23
+ conversationId: string;
24
+ assistantMessageId: string;
25
+ payload: TelaAgentRunPayload;
26
+ titleContent: string;
27
+ operation: AgentTurnOperation;
28
+ }): void;
29
+ export declare function prepareDefaultAgentTurn(event: H3Event, db: DbInstance, context: ChatRuntimeContext, options: {
30
+ conversation: ConversationRecord;
31
+ conversationId: string;
32
+ content: string;
33
+ files?: AgentTurnFiles;
34
+ requestedModel?: string;
35
+ workspaceSettingsSnapshot?: unknown;
36
+ }): Promise<PreparedDefaultAgentTurn>;
37
+ export declare function resolveDefaultAgentEnvironmentVariables(event: H3Event, context: ChatRuntimeContext, logContext: Record<string, unknown>, failureMessage: string): Promise<Record<string, string>>;
38
+ export declare function withEnvironmentVariables(payload: AgentApiChatPayload, environmentVariables: Record<string, string>): AgentApiChatPayload;
39
+ export {};
@@ -0,0 +1,170 @@
1
+ import { and, eq, isNull } from "drizzle-orm";
2
+ import { useRuntimeConfig } from "nitropack/runtime";
3
+ import { schema } from "#chat-runtime/server/db";
4
+ import { buildMessageWithContext, renderSystemPrompt, truncateTitle } from "#chat-runtime/server/utils/chat-message-builder";
5
+ import {
6
+ getConversationAppliedSettings,
7
+ getWorkspaceSettingsUpdatedAt,
8
+ parseWorkspaceSettingsSnapshot,
9
+ resolveWorkspaceSettings,
10
+ serializeResolvedWorkspaceSettings
11
+ } from "#chat-runtime/server/utils/chat-workspace-settings";
12
+ import { logger } from "#chat-runtime/server/utils/logger";
13
+ import { getTelaAgent, runTelaAgent } from "#chat-runtime/server/utils/tela-agent-api";
14
+ import { toTelaAgentAttachments, toTelaAgentInputSchema } from "#chat-runtime/server/utils/tela-agent-session";
15
+ import { resolveWorkspaceCredentials } from "#chat-runtime/server/utils/workspace";
16
+ import { resolveChatNuxtFeatures } from "#chat-runtime/utils/features";
17
+ import { mergeFileUrls } from "#chat-runtime/utils/file";
18
+ import { isValidModel } from "#chat-runtime/types/chat";
19
+ function getFileVaultRef(file) {
20
+ return file.vaultReference?.trim() || (file.url.startsWith("vault://") ? file.url : null);
21
+ }
22
+ function filterMappedFiles(files, inputSchema) {
23
+ const mappedVaultRefs = new Set(
24
+ inputSchema.filter((input) => input.type === "file").map((input) => input.vaultRef)
25
+ );
26
+ if (mappedVaultRefs.size === 0)
27
+ return files ?? [];
28
+ return (files ?? []).filter((file) => {
29
+ const vaultRef = getFileVaultRef(file);
30
+ return !vaultRef || !mappedVaultRefs.has(vaultRef);
31
+ });
32
+ }
33
+ export async function buildTelaAgentTurnPayload(event, conversation, content, files, agentInputs) {
34
+ let inputSchema = agentInputs ?? [];
35
+ let attachmentFiles = filterMappedFiles(files, inputSchema);
36
+ if (inputSchema.length === 0 && conversation.telaAgentId && files?.length) {
37
+ const agentResult = await getTelaAgent(event, conversation.telaAgentId);
38
+ if (agentResult.success) {
39
+ const fileInputCount = agentResult.data.inputSchema.filter((input) => input.type === "file" && input.name.trim()).length;
40
+ inputSchema = toTelaAgentInputSchema(files, agentResult.data.inputSchema);
41
+ if (inputSchema.length > 0) {
42
+ attachmentFiles = [];
43
+ } else if (fileInputCount > 1) {
44
+ logger.warn({
45
+ agentId: conversation.telaAgentId,
46
+ fileInputCount
47
+ }, "Tela agent file input auto-mapping skipped");
48
+ }
49
+ } else {
50
+ logger.warn({ agentId: conversation.telaAgentId, error: agentResult.error }, "Tela agent metadata fetch failed");
51
+ }
52
+ }
53
+ const attachments = toTelaAgentAttachments(attachmentFiles);
54
+ return {
55
+ message: content.trim(),
56
+ ...conversation.threadSessionId ? { sessionId: conversation.threadSessionId } : {},
57
+ ...inputSchema.length > 0 ? { inputSchema } : {},
58
+ ...attachments.length > 0 ? { attachments } : {}
59
+ };
60
+ }
61
+ export function queueTelaAgentTurn(event, db, options) {
62
+ const needsNewThread = options.conversation.threadSessionId === null;
63
+ const logContext = {
64
+ conversationId: options.conversationId,
65
+ messageId: options.assistantMessageId
66
+ };
67
+ const telaAgentId = options.conversation.telaAgentId;
68
+ if (!telaAgentId) {
69
+ logger.error(logContext, "Tela agent turn queued without agent id");
70
+ void db.update(schema.messages).set({
71
+ content: "Erro: Agente Tela n\xE3o configurado",
72
+ status: "failed"
73
+ }).where(eq(schema.messages.id, options.assistantMessageId));
74
+ return;
75
+ }
76
+ runTelaAgent(event, telaAgentId, options.payload).then(async (result) => {
77
+ if (result.success) {
78
+ await db.update(schema.conversations).set({
79
+ ...needsNewThread ? { title: truncateTitle(options.titleContent) } : {},
80
+ threadSessionId: result.sessionId,
81
+ model: null,
82
+ appliedSettingsAt: null,
83
+ appliedSettingsSnapshot: null,
84
+ updatedAt: /* @__PURE__ */ new Date()
85
+ }).where(eq(schema.conversations.id, options.conversationId));
86
+ } else {
87
+ logger.warn({ ...logContext, error: result.error }, options.operation === "retry" ? "Tela agent retry failed" : "Tela agent run failed");
88
+ await db.update(schema.messages).set({
89
+ content: `Erro: ${result.error}`,
90
+ status: "failed"
91
+ }).where(eq(schema.messages.id, options.assistantMessageId));
92
+ }
93
+ }).catch(async (error) => {
94
+ logger.error({ ...logContext, error: error.message }, options.operation === "retry" ? "Tela agent retry threw exception" : "Tela agent run threw exception");
95
+ await db.update(schema.messages).set({
96
+ content: "Erro: Falha ao conectar ao agente",
97
+ status: "failed"
98
+ }).where(eq(schema.messages.id, options.assistantMessageId));
99
+ });
100
+ }
101
+ export async function prepareDefaultAgentTurn(event, db, context, options) {
102
+ const explicitSettings = !options.conversation.appliedSettingsSnapshot && options.workspaceSettingsSnapshot !== void 0 ? parseWorkspaceSettingsSnapshot(options.workspaceSettingsSnapshot) : void 0;
103
+ const trustedSettings = options.conversation.appliedSettingsSnapshot || explicitSettings ? null : await resolveWorkspaceSettings(event, context, db);
104
+ const appliedSettings = getConversationAppliedSettings(options.conversation, trustedSettings, explicitSettings);
105
+ const settingsTimestamp = getWorkspaceSettingsUpdatedAt(appliedSettings);
106
+ const snapshotSettings = explicitSettings ?? trustedSettings;
107
+ if (!options.conversation.appliedSettingsSnapshot && snapshotSettings) {
108
+ await db.update(schema.conversations).set({
109
+ appliedSettingsAt: getWorkspaceSettingsUpdatedAt(snapshotSettings),
110
+ appliedSettingsSnapshot: serializeResolvedWorkspaceSettings(snapshotSettings),
111
+ updatedAt: /* @__PURE__ */ new Date()
112
+ }).where(and(
113
+ eq(schema.conversations.id, options.conversationId),
114
+ isNull(schema.conversations.appliedSettingsSnapshot)
115
+ ));
116
+ }
117
+ const needsNewThread = options.conversation.threadSessionId === null;
118
+ const effectiveModel = options.requestedModel ?? (isValidModel(options.conversation.model) ? options.conversation.model : void 0);
119
+ const content = options.content.trim();
120
+ const knowledgeSources = appliedSettings.knowledgeSources ?? null;
121
+ const hasKnowledgeSources = knowledgeSources !== null && knowledgeSources.length > 0;
122
+ const externalSkillRefs = appliedSettings.externalSkills?.map((skill) => skill.ref) ?? [];
123
+ const skills = externalSkillRefs.length > 0 ? externalSkillRefs : void 0;
124
+ const fileUrls = mergeFileUrls(appliedSettings.contextFiles, options.files);
125
+ const { vaultUrl } = useRuntimeConfig();
126
+ const messageWithContext = buildMessageWithContext(
127
+ content,
128
+ appliedSettings.systemMessage ?? null,
129
+ knowledgeSources,
130
+ needsNewThread,
131
+ needsNewThread ? renderSystemPrompt(vaultUrl) : ""
132
+ );
133
+ return {
134
+ needsNewThread,
135
+ effectiveModel,
136
+ settingsTimestamp,
137
+ messageTitle: truncateTitle(options.content),
138
+ createPayload: {
139
+ message: messageWithContext,
140
+ fileUrls,
141
+ model: effectiveModel,
142
+ customTools: appliedSettings.canvasTools?.length ? { canvas: appliedSettings.canvasTools } : void 0,
143
+ tools: hasKnowledgeSources ? { workstationQueryTool: true } : void 0,
144
+ skills
145
+ },
146
+ continuePayload: {
147
+ content,
148
+ fileUrls,
149
+ model: effectiveModel
150
+ }
151
+ };
152
+ }
153
+ export async function resolveDefaultAgentEnvironmentVariables(event, context, logContext, failureMessage) {
154
+ if (!resolveChatNuxtFeatures(useRuntimeConfig()).credentials)
155
+ return {};
156
+ try {
157
+ return await resolveWorkspaceCredentials(event, context.workspaceId);
158
+ } catch (error) {
159
+ logger.warn({ ...logContext, error }, failureMessage);
160
+ return {};
161
+ }
162
+ }
163
+ export function withEnvironmentVariables(payload, environmentVariables) {
164
+ if (Object.keys(environmentVariables).length === 0)
165
+ return payload;
166
+ return {
167
+ ...payload,
168
+ environmentVariables
169
+ };
170
+ }
@@ -0,0 +1,10 @@
1
+ import type { useDb } from '#chat-runtime/server/db';
2
+ import type { MessageFile } from '#chat-runtime/types/chat';
3
+ type DbInstance = ReturnType<typeof useDb>;
4
+ type MessageUpdateWithFiles = {
5
+ content?: string;
6
+ status?: string;
7
+ files?: MessageFile[];
8
+ };
9
+ export declare function attachNewAssistantFilesToMessageUpdate(db: DbInstance, conversationId: string, updateData: MessageUpdateWithFiles, authToken: string): Promise<void>;
10
+ export {};
@@ -0,0 +1,77 @@
1
+ import { and, eq, isNotNull } from "drizzle-orm";
2
+ import { useRuntimeConfig } from "nitropack/runtime";
3
+ import { APIKeyAuthStrategy, VaultFile } from "@meistrari/vault-sdk";
4
+ import { schema } from "#chat-runtime/server/db";
5
+ import { logger } from "#chat-runtime/server/utils/logger";
6
+ import { detectFilesInText } from "#chat-runtime/utils/file";
7
+ async function getExistingMessageFileUrls(db, conversationId) {
8
+ const messagesWithFiles = await db.select({ files: schema.messages.files }).from(schema.messages).where(and(
9
+ eq(schema.messages.conversationId, conversationId),
10
+ isNotNull(schema.messages.files)
11
+ ));
12
+ const urls = /* @__PURE__ */ new Set();
13
+ for (const message of messagesWithFiles) {
14
+ if (Array.isArray(message.files)) {
15
+ for (const file of message.files) {
16
+ if (file?.url) {
17
+ urls.add(file.url);
18
+ }
19
+ }
20
+ }
21
+ }
22
+ return urls;
23
+ }
24
+ async function extractAssistantFiles(content, authToken) {
25
+ const detected = detectFilesInText(content);
26
+ const vaultFiles = detected.filter((file) => file.type === "vault");
27
+ if (vaultFiles.length === 0)
28
+ return [];
29
+ const { vaultUrl } = useRuntimeConfig();
30
+ return Promise.all(
31
+ vaultFiles.map(async (file) => {
32
+ let name = file.name;
33
+ let mimeType = file.mimeType !== "unknown" ? file.mimeType : void 0;
34
+ let size;
35
+ if (vaultUrl) {
36
+ try {
37
+ const authStrategy = new APIKeyAuthStrategy(authToken);
38
+ const vaultFile = await VaultFile.fromVaultReference({
39
+ reference: file.reference,
40
+ config: { authStrategy, vaultUrl }
41
+ });
42
+ await vaultFile.populateMetadata();
43
+ if (vaultFile.metadata?.originalFileName) {
44
+ name = vaultFile.metadata.originalFileName;
45
+ }
46
+ if (vaultFile.metadata?.mimeType) {
47
+ mimeType = vaultFile.metadata.mimeType;
48
+ }
49
+ if (vaultFile.metadata?.size) {
50
+ size = vaultFile.metadata.size;
51
+ }
52
+ } catch {
53
+ logger.debug({ reference: file.reference }, "Vault metadata fetch failed for assistant file");
54
+ }
55
+ }
56
+ return {
57
+ url: file.reference,
58
+ name,
59
+ mimeType,
60
+ source: "assistant",
61
+ size
62
+ };
63
+ })
64
+ );
65
+ }
66
+ export async function attachNewAssistantFilesToMessageUpdate(db, conversationId, updateData, authToken) {
67
+ if (!updateData.content || updateData.status !== "completed")
68
+ return;
69
+ const assistantFiles = await extractAssistantFiles(updateData.content, authToken);
70
+ if (assistantFiles.length === 0)
71
+ return;
72
+ const existingUrls = await getExistingMessageFileUrls(db, conversationId);
73
+ const newFiles = assistantFiles.filter((file) => !existingUrls.has(file.url));
74
+ if (newFiles.length > 0) {
75
+ updateData.files = newFiles;
76
+ }
77
+ }
@@ -0,0 +1,31 @@
1
+ import type { useDb } from '#chat-runtime/server/db';
2
+ import type { AgentApiSession, MessageStatus, ReasoningItem } from '#chat-runtime/types/agent';
3
+ import type { ChatRuntimeContext } from '#chat-runtime/server/utils/chat-context';
4
+ import type { Message, MessageFile } from '#chat-runtime/types/chat';
5
+ import type { TelaAgentUsageValues } from '#chat-runtime/server/utils/tela-agent-session';
6
+ type DbInstance = ReturnType<typeof useDb>;
7
+ export type MessageUpdate = {
8
+ reasoningData?: ReasoningItem[];
9
+ externalUuid?: string;
10
+ content?: string;
11
+ status?: MessageStatus;
12
+ ttft?: string;
13
+ updatedAt?: Date;
14
+ files?: MessageFile[];
15
+ };
16
+ type MessageUpdateResponse = {
17
+ messages: Message[];
18
+ newFiles?: MessageFile[];
19
+ };
20
+ export declare function mapAgentApiSessionToMessageUpdate(agentSession: AgentApiSession, options: {
21
+ pendingMessage: Pick<Message, 'ttft' | 'createdAt'>;
22
+ lastAssistantMessage?: Pick<Message, 'externalUuid'> | null;
23
+ userMessage?: Pick<Message, 'createdAt'> | null;
24
+ now?: Date;
25
+ }): MessageUpdate;
26
+ export declare function hasMessageUpdate(updateData: MessageUpdate): boolean;
27
+ export declare function isTerminalMessageUpdate(updateData: MessageUpdate): boolean;
28
+ export declare function buildMessageUpdateResponse(messages: Message[], messageId: string, updateData: MessageUpdate): MessageUpdateResponse;
29
+ export declare function trackTelaAgentConversationUsage(db: DbInstance, context: ChatRuntimeContext, conversationId: string, sessionId: string, usage: TelaAgentUsageValues | null): void;
30
+ export declare function trackAgentApiConversationUsage(db: DbInstance, context: ChatRuntimeContext, conversationId: string, sessionId: string, agentSession: AgentApiSession): void;
31
+ export {};
@@ -0,0 +1,132 @@
1
+ import { schema } from "#chat-runtime/server/db";
2
+ import { extractContentFromSession } from "#chat-runtime/server/utils/agent-api";
3
+ import { logger } from "#chat-runtime/server/utils/logger";
4
+ function toMilliseconds(timestamp) {
5
+ if (typeof timestamp !== "number" || !Number.isFinite(timestamp))
6
+ return null;
7
+ return timestamp < 1e10 ? timestamp * 1e3 : timestamp;
8
+ }
9
+ export function mapAgentApiSessionToMessageUpdate(agentSession, options) {
10
+ const updateData = {};
11
+ const lastSyncedUuid = options.lastAssistantMessage?.externalUuid;
12
+ const lastSyncedIndex = lastSyncedUuid ? agentSession.reasoning?.findIndex((item) => item.uuid === lastSyncedUuid) ?? -1 : -1;
13
+ const newReasoning = agentSession.reasoning?.slice(lastSyncedIndex + 1);
14
+ const hasNewReasoning = newReasoning && newReasoning.length > 0;
15
+ const sessionUpdatedAtMs = toMilliseconds(agentSession.updatedAt);
16
+ const terminalSessionBelongsToPendingMessage = hasNewReasoning || !options.lastAssistantMessage || sessionUpdatedAtMs !== null && sessionUpdatedAtMs >= options.pendingMessage.createdAt.getTime();
17
+ if (hasNewReasoning) {
18
+ updateData.reasoningData = newReasoning;
19
+ const lastReasoningItem = newReasoning.at(-1);
20
+ if (lastReasoningItem?.uuid) {
21
+ updateData.externalUuid = lastReasoningItem.uuid;
22
+ }
23
+ if (!options.pendingMessage.ttft && options.userMessage) {
24
+ const now = options.now ?? /* @__PURE__ */ new Date();
25
+ updateData.ttft = String(now.getTime() - options.userMessage.createdAt.getTime());
26
+ }
27
+ }
28
+ if (agentSession.status === "completed" || agentSession.status === "waiting_messages") {
29
+ if (terminalSessionBelongsToPendingMessage) {
30
+ updateData.content = extractContentFromSession(agentSession);
31
+ updateData.status = "completed";
32
+ updateData.updatedAt = options.now ?? /* @__PURE__ */ new Date();
33
+ }
34
+ } else if (agentSession.status === "failed") {
35
+ const errorContent = agentSession.error || "Agent execution failed";
36
+ updateData.content = `Error: ${errorContent}`;
37
+ updateData.status = "failed";
38
+ updateData.updatedAt = options.now ?? /* @__PURE__ */ new Date();
39
+ }
40
+ return updateData;
41
+ }
42
+ export function hasMessageUpdate(updateData) {
43
+ return Object.keys(updateData).length > 0;
44
+ }
45
+ export function isTerminalMessageUpdate(updateData) {
46
+ return updateData.status === "completed" || updateData.status === "failed";
47
+ }
48
+ export function buildMessageUpdateResponse(messages, messageId, updateData) {
49
+ const updatedMessages = messages.map(
50
+ (message) => message.id === messageId ? { ...message, ...updateData } : message
51
+ );
52
+ if (updateData.files && updateData.files.length > 0) {
53
+ return {
54
+ messages: updatedMessages,
55
+ newFiles: updateData.files
56
+ };
57
+ }
58
+ return { messages: updatedMessages };
59
+ }
60
+ export function trackTelaAgentConversationUsage(db, context, conversationId, sessionId, usage) {
61
+ if (!usage)
62
+ return;
63
+ void db.insert(schema.conversationUsage).values({
64
+ workspaceId: context.workspaceId,
65
+ conversationId,
66
+ ...usage,
67
+ userId: context.user.id,
68
+ userEmail: context.user.email ?? null,
69
+ sessionId
70
+ }).onConflictDoUpdate({
71
+ target: schema.conversationUsage.conversationId,
72
+ set: {
73
+ ...usage,
74
+ updatedAt: /* @__PURE__ */ new Date()
75
+ }
76
+ }).catch((error) => {
77
+ logger.warn({ conversationId, error }, "Failed to track conversation usage");
78
+ });
79
+ }
80
+ export function trackAgentApiConversationUsage(db, context, conversationId, sessionId, agentSession) {
81
+ const usage = agentSession.result?.usage;
82
+ if (!usage)
83
+ return;
84
+ const breakdown = agentSession.result?.usageBreakdown;
85
+ const serverToolUse = breakdown?.serverToolUse;
86
+ const webSearchRequests = typeof serverToolUse?.web_search_requests === "number" ? serverToolUse.web_search_requests : null;
87
+ const webFetchRequests = typeof serverToolUse?.web_fetch_requests === "number" ? serverToolUse.web_fetch_requests : null;
88
+ void db.insert(schema.conversationUsage).values({
89
+ workspaceId: context.workspaceId,
90
+ conversationId,
91
+ promptTokens: usage.promptTokens ?? 0,
92
+ completionTokens: usage.completionTokens ?? 0,
93
+ totalTokens: usage.totalTokens ?? 0,
94
+ promptCost: String(usage.promptCost ?? 0),
95
+ completionCost: String(usage.completionCost ?? 0),
96
+ totalCost: String(usage.totalCost ?? 0),
97
+ durationMs: usage.duration ?? null,
98
+ turns: usage.turns ?? null,
99
+ userId: context.user.id,
100
+ userEmail: context.user.email ?? null,
101
+ model: agentSession.model ?? null,
102
+ sessionId,
103
+ cacheReadInputTokens: breakdown?.cacheReadInputTokens ?? null,
104
+ cacheCreationInputTokens: breakdown?.cacheCreationInputTokens ?? null,
105
+ modelUsage: breakdown?.modelUsage ?? null,
106
+ durationApiMs: breakdown?.durationApiMs ?? null,
107
+ webSearchRequests,
108
+ webFetchRequests
109
+ }).onConflictDoUpdate({
110
+ target: schema.conversationUsage.conversationId,
111
+ set: {
112
+ promptTokens: usage.promptTokens ?? 0,
113
+ completionTokens: usage.completionTokens ?? 0,
114
+ totalTokens: usage.totalTokens ?? 0,
115
+ promptCost: String(usage.promptCost ?? 0),
116
+ completionCost: String(usage.completionCost ?? 0),
117
+ totalCost: String(usage.totalCost ?? 0),
118
+ durationMs: usage.duration ?? null,
119
+ turns: usage.turns ?? null,
120
+ model: agentSession.model ?? null,
121
+ cacheReadInputTokens: breakdown?.cacheReadInputTokens ?? null,
122
+ cacheCreationInputTokens: breakdown?.cacheCreationInputTokens ?? null,
123
+ modelUsage: breakdown?.modelUsage ?? null,
124
+ durationApiMs: breakdown?.durationApiMs ?? null,
125
+ webSearchRequests,
126
+ webFetchRequests,
127
+ updatedAt: /* @__PURE__ */ new Date()
128
+ }
129
+ }).catch((error) => {
130
+ logger.warn({ conversationId, error }, "Failed to track conversation usage");
131
+ });
132
+ }
@@ -0,0 +1,2 @@
1
+ import type { ChatRuntimeContext } from '#chat-runtime/server/utils/chat-context';
2
+ export declare function conversationScopeConditions(context: ChatRuntimeContext, conversationId?: string): import("drizzle-orm").SQL<unknown> | undefined;
@@ -0,0 +1,12 @@
1
+ import { and, eq, isNull } from "drizzle-orm";
2
+ import { schema } from "#chat-runtime/server/db";
3
+ export function conversationScopeConditions(context, conversationId) {
4
+ const conditions = [
5
+ eq(schema.conversations.workspaceId, context.workspaceId),
6
+ context.telaAgentId ? eq(schema.conversations.telaAgentId, context.telaAgentId) : isNull(schema.conversations.telaAgentId)
7
+ ];
8
+ if (conversationId) {
9
+ conditions.unshift(eq(schema.conversations.id, conversationId));
10
+ }
11
+ return and(...conditions);
12
+ }