@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,74 +1,23 @@
1
1
  import { createError, defineEventHandler, getQuery, getRouterParam } from "h3";
2
- import { useRuntimeConfig } from "nitropack/runtime";
3
- import { and, asc, eq, gt, isNotNull } from "drizzle-orm";
4
- import { APIKeyAuthStrategy, VaultFile } from "@meistrari/vault-sdk";
2
+ import { and, asc, eq, gt } from "drizzle-orm";
5
3
  import { schema, useDb } from "#chat-runtime/server/db";
6
- import { extractContentFromSession, getAgentDataToken, getSessionStatus } from "#chat-runtime/server/utils/agent-api";
7
- import { resolveChatContext } from "#chat-runtime/server/utils/chat-context";
8
- import { detectFilesInText } from "#chat-runtime/utils/file";
4
+ import { getAgentDataToken, getSessionStatus } from "#chat-runtime/server/utils/agent-api";
5
+ import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
6
+ import { attachNewAssistantFilesToMessageUpdate } from "#chat-runtime/server/utils/conversation-message-files";
7
+ import {
8
+ buildMessageUpdateResponse,
9
+ hasMessageUpdate,
10
+ isTerminalMessageUpdate,
11
+ mapAgentApiSessionToMessageUpdate,
12
+ trackAgentApiConversationUsage,
13
+ trackTelaAgentConversationUsage
14
+ } from "#chat-runtime/server/utils/conversation-message-sync";
15
+ import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
16
+ import { getTelaAgentSession } from "#chat-runtime/server/utils/tela-agent-api";
17
+ import { mapTelaAgentSessionToMessageUpdate, mapTelaAgentUsage } from "#chat-runtime/server/utils/tela-agent-session";
9
18
  import { logger } from "#chat-runtime/server/utils/logger";
10
- async function getExistingFileUrls(db, conversationId) {
11
- const messagesWithFiles = await db.select({ files: schema.messages.files }).from(schema.messages).where(and(
12
- eq(schema.messages.conversationId, conversationId),
13
- isNotNull(schema.messages.files)
14
- ));
15
- const urls = /* @__PURE__ */ new Set();
16
- for (const message of messagesWithFiles) {
17
- if (Array.isArray(message.files)) {
18
- for (const file of message.files) {
19
- if (file?.url) {
20
- urls.add(file.url);
21
- }
22
- }
23
- }
24
- }
25
- return urls;
26
- }
27
- async function extractAssistantFiles(content, authToken) {
28
- const detected = detectFilesInText(content);
29
- const vaultFiles = detected.filter((f) => f.type === "vault");
30
- if (vaultFiles.length === 0)
31
- return [];
32
- const { vaultUrl } = useRuntimeConfig();
33
- const filesWithMetadata = await Promise.all(
34
- vaultFiles.map(async (file) => {
35
- let name = file.name;
36
- let mimeType = file.mimeType !== "unknown" ? file.mimeType : void 0;
37
- let size;
38
- if (vaultUrl) {
39
- try {
40
- const authStrategy = new APIKeyAuthStrategy(authToken);
41
- const vaultFile = await VaultFile.fromVaultReference({
42
- reference: file.reference,
43
- config: { authStrategy, vaultUrl }
44
- });
45
- await vaultFile.populateMetadata();
46
- if (vaultFile.metadata?.originalFileName) {
47
- name = vaultFile.metadata.originalFileName;
48
- }
49
- if (vaultFile.metadata?.mimeType) {
50
- mimeType = vaultFile.metadata.mimeType;
51
- }
52
- if (vaultFile.metadata?.size) {
53
- size = vaultFile.metadata.size;
54
- }
55
- } catch {
56
- logger.debug({ reference: file.reference }, "Vault metadata fetch failed for assistant file");
57
- }
58
- }
59
- return {
60
- url: file.reference,
61
- name,
62
- mimeType,
63
- source: "assistant",
64
- size
65
- };
66
- })
67
- );
68
- return filesWithMetadata;
69
- }
70
19
  export default defineEventHandler(async (event) => {
71
- const context = resolveChatContext(event);
20
+ const context = resolveChatRuntimeContext(event);
72
21
  const token = getAgentDataToken(event);
73
22
  const db = useDb();
74
23
  const conversationId = getRouterParam(event, "id");
@@ -88,12 +37,7 @@ export default defineEventHandler(async (event) => {
88
37
  }
89
38
  }
90
39
  const [conversationResult, messages] = await Promise.all([
91
- db.select().from(schema.conversations).where(
92
- and(
93
- eq(schema.conversations.id, conversationId),
94
- eq(schema.conversations.workspaceId, context.workspaceId)
95
- )
96
- ),
40
+ db.select().from(schema.conversations).where(conversationScopeConditions(context, conversationId)),
97
41
  db.select().from(schema.messages).where(and(...conditions)).orderBy(asc(schema.messages.createdAt))
98
42
  ]);
99
43
  const [conversation] = conversationResult;
@@ -103,120 +47,70 @@ export default defineEventHandler(async (event) => {
103
47
  statusMessage: "Conversation not found"
104
48
  });
105
49
  }
106
- const pendingMessage = messages.find((m) => m.status === "pending");
107
- const lastAssistantMessage = messages.findLast((m) => m.role === "assistant" && m.status === "completed");
108
- if (pendingMessage && conversation.threadSessionId) {
109
- logger.debug({ conversationId, messageId: pendingMessage.id, sessionId: conversation.threadSessionId }, "Polling pending message");
110
- const sessionResult = await getSessionStatus(token, conversation.threadSessionId);
111
- if (sessionResult.success) {
112
- const agentSession = sessionResult.data;
113
- const updateData = {};
114
- const lastSyncedUuid = lastAssistantMessage?.externalUuid;
115
- const lastSyncedIndex = lastSyncedUuid ? agentSession.reasoning?.findIndex((r) => r.uuid === lastSyncedUuid) ?? -1 : -1;
116
- const newReasoning = agentSession.reasoning?.slice(lastSyncedIndex + 1);
117
- const hasNewReasoning = newReasoning && newReasoning.length > 0;
118
- if (hasNewReasoning) {
119
- updateData.reasoningData = newReasoning;
120
- const lastReasoningItem = newReasoning.at(-1);
121
- if (lastReasoningItem?.uuid) {
122
- updateData.externalUuid = lastReasoningItem.uuid;
123
- }
124
- if (!pendingMessage.ttft) {
125
- const userMessage = messages.findLast((m) => m.role === "user");
126
- if (userMessage) {
127
- const ttft = Date.now() - userMessage.createdAt.getTime();
128
- updateData.ttft = String(ttft);
129
- }
130
- }
131
- }
132
- if (agentSession.status === "completed" || agentSession.status === "waiting_messages") {
133
- if (hasNewReasoning) {
134
- const content = extractContentFromSession(agentSession);
135
- updateData.content = content;
136
- updateData.status = "completed";
137
- updateData.updatedAt = /* @__PURE__ */ new Date();
138
- logger.info({ conversationId, messageId: pendingMessage.id, status: "completed" }, "Message completed");
139
- const assistantFiles = await extractAssistantFiles(content, token);
140
- if (assistantFiles.length > 0) {
141
- const existingUrls = await getExistingFileUrls(db, conversationId);
142
- const newFiles = assistantFiles.filter((f) => !existingUrls.has(f.url));
143
- if (newFiles.length > 0) {
144
- updateData.files = newFiles;
145
- }
146
- }
147
- }
148
- } else if (agentSession.status === "failed") {
149
- const errorContent = agentSession.error || "Agent execution failed";
150
- updateData.content = `Error: ${errorContent}`;
151
- updateData.status = agentSession.status;
152
- updateData.updatedAt = /* @__PURE__ */ new Date();
153
- logger.warn({ conversationId, messageId: pendingMessage.id, status: "failed" }, "Message failed");
154
- }
155
- if (Object.keys(updateData).length > 0) {
156
- await db.update(schema.messages).set(updateData).where(eq(schema.messages.id, pendingMessage.id));
157
- if ((updateData.status === "completed" || updateData.status === "failed") && agentSession.result?.usage) {
158
- const usage = agentSession.result.usage;
159
- const breakdown = agentSession.result?.usageBreakdown;
160
- const serverToolUse = breakdown?.serverToolUse;
161
- const webSearchRequests = typeof serverToolUse?.web_search_requests === "number" ? serverToolUse.web_search_requests : null;
162
- const webFetchRequests = typeof serverToolUse?.web_fetch_requests === "number" ? serverToolUse.web_fetch_requests : null;
163
- void db.insert(schema.conversationUsage).values({
164
- workspaceId: context.workspaceId,
165
- conversationId,
166
- promptTokens: usage.promptTokens ?? 0,
167
- completionTokens: usage.completionTokens ?? 0,
168
- totalTokens: usage.totalTokens ?? 0,
169
- promptCost: String(usage.promptCost ?? 0),
170
- completionCost: String(usage.completionCost ?? 0),
171
- totalCost: String(usage.totalCost ?? 0),
172
- durationMs: usage.duration ?? null,
173
- turns: usage.turns ?? null,
174
- userId: context.user.id,
175
- userEmail: context.user.email ?? null,
176
- model: agentSession.model ?? null,
177
- sessionId: conversation.threadSessionId,
178
- cacheReadInputTokens: breakdown?.cacheReadInputTokens ?? null,
179
- cacheCreationInputTokens: breakdown?.cacheCreationInputTokens ?? null,
180
- modelUsage: breakdown?.modelUsage ?? null,
181
- durationApiMs: breakdown?.durationApiMs ?? null,
182
- webSearchRequests,
183
- webFetchRequests
184
- }).onConflictDoUpdate({
185
- target: schema.conversationUsage.conversationId,
186
- set: {
187
- promptTokens: usage.promptTokens ?? 0,
188
- completionTokens: usage.completionTokens ?? 0,
189
- totalTokens: usage.totalTokens ?? 0,
190
- promptCost: String(usage.promptCost ?? 0),
191
- completionCost: String(usage.completionCost ?? 0),
192
- totalCost: String(usage.totalCost ?? 0),
193
- durationMs: usage.duration ?? null,
194
- turns: usage.turns ?? null,
195
- model: agentSession.model ?? null,
196
- cacheReadInputTokens: breakdown?.cacheReadInputTokens ?? null,
197
- cacheCreationInputTokens: breakdown?.cacheCreationInputTokens ?? null,
198
- modelUsage: breakdown?.modelUsage ?? null,
199
- durationApiMs: breakdown?.durationApiMs ?? null,
200
- webSearchRequests,
201
- webFetchRequests,
202
- updatedAt: /* @__PURE__ */ new Date()
203
- }
204
- }).catch((error) => {
205
- console.warn("Failed to track conversation usage:", error);
206
- });
207
- }
208
- const updatedMessages = messages.map(
209
- (m) => m.id === pendingMessage.id ? { ...m, ...updateData } : m
210
- );
211
- if (updateData.files && updateData.files.length > 0) {
212
- return {
213
- messages: updatedMessages,
214
- newFiles: updateData.files
215
- };
216
- }
217
- return { messages: updatedMessages };
218
- }
50
+ const pendingMessage = messages.find((message) => message.status === "pending");
51
+ const lastAssistantMessage = messages.findLast((message) => message.role === "assistant" && message.status === "completed");
52
+ if (!pendingMessage || !conversation.threadSessionId) {
53
+ return { messages };
54
+ }
55
+ logger.debug({ conversationId, messageId: pendingMessage.id, sessionId: conversation.threadSessionId }, "Polling pending message");
56
+ if (conversation.telaAgentId) {
57
+ const sessionResult2 = await getTelaAgentSession(event, conversation.threadSessionId);
58
+ if (!sessionResult2.success) {
59
+ return { messages };
60
+ }
61
+ const agentSession2 = sessionResult2.data;
62
+ const userMessage2 = messages.findLast((message) => message.role === "user");
63
+ const updateData2 = mapTelaAgentSessionToMessageUpdate(agentSession2, {
64
+ lastSyncedStepId: pendingMessage.externalUuid ?? lastAssistantMessage?.externalUuid,
65
+ existingReasoningData: Array.isArray(pendingMessage.reasoningData) ? pendingMessage.reasoningData : null,
66
+ pendingMessageTtft: pendingMessage.ttft,
67
+ userMessageCreatedAt: userMessage2?.createdAt ?? null
68
+ });
69
+ await attachNewAssistantFilesToMessageUpdate(db, conversationId, updateData2, token);
70
+ if (!hasMessageUpdate(updateData2)) {
71
+ return { messages };
219
72
  }
73
+ await db.update(schema.messages).set(updateData2).where(eq(schema.messages.id, pendingMessage.id));
74
+ if (isTerminalMessageUpdate(updateData2)) {
75
+ trackTelaAgentConversationUsage(
76
+ db,
77
+ context,
78
+ conversationId,
79
+ conversation.threadSessionId,
80
+ mapTelaAgentUsage(agentSession2)
81
+ );
82
+ }
83
+ return buildMessageUpdateResponse(messages, pendingMessage.id, updateData2);
84
+ }
85
+ const sessionResult = await getSessionStatus(token, conversation.threadSessionId);
86
+ if (!sessionResult.success) {
87
+ return { messages };
88
+ }
89
+ const agentSession = sessionResult.data;
90
+ const userMessage = messages.findLast((message) => message.role === "user");
91
+ const updateData = mapAgentApiSessionToMessageUpdate(agentSession, {
92
+ pendingMessage,
93
+ lastAssistantMessage,
94
+ userMessage
95
+ });
96
+ if (updateData.status === "completed") {
97
+ logger.info({ conversationId, messageId: pendingMessage.id, status: "completed" }, "Message completed");
98
+ } else if (updateData.status === "failed") {
99
+ logger.warn({ conversationId, messageId: pendingMessage.id, status: "failed" }, "Message failed");
100
+ }
101
+ await attachNewAssistantFilesToMessageUpdate(db, conversationId, updateData, token);
102
+ if (!hasMessageUpdate(updateData)) {
103
+ return { messages };
104
+ }
105
+ await db.update(schema.messages).set(updateData).where(eq(schema.messages.id, pendingMessage.id));
106
+ if (isTerminalMessageUpdate(updateData)) {
107
+ trackAgentApiConversationUsage(
108
+ db,
109
+ context,
110
+ conversationId,
111
+ conversation.threadSessionId,
112
+ agentSession
113
+ );
220
114
  }
221
- return { messages };
115
+ return buildMessageUpdateResponse(messages, pendingMessage.id, updateData);
222
116
  });
@@ -1,25 +1,21 @@
1
1
  import { createError, defineEventHandler, getRouterParam, readBody } from "h3";
2
- import { useRuntimeConfig } from "nitropack/runtime";
3
- import { eq, and, isNull } from "drizzle-orm";
2
+ import { eq } from "drizzle-orm";
4
3
  import { useDb, schema } from "#chat-runtime/server/db";
5
- import { resolveChatContext } from "#chat-runtime/server/utils/chat-context";
6
- import { buildMessageWithContext, renderSystemPrompt, truncateTitle } from "#chat-runtime/server/utils/chat-message-builder";
4
+ import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
5
+ import {
6
+ buildTelaAgentTurnPayload,
7
+ prepareDefaultAgentTurn,
8
+ queueTelaAgentTurn,
9
+ resolveDefaultAgentEnvironmentVariables,
10
+ withEnvironmentVariables
11
+ } from "#chat-runtime/server/utils/conversation-agent-turn";
12
+ import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
7
13
  import { getAgentDataToken, createChatAgentSession, continueSession } from "#chat-runtime/server/utils/agent-api";
8
14
  import { logger } from "#chat-runtime/server/utils/logger";
9
- import {
10
- getConversationAppliedSettings,
11
- getWorkspaceSettingsUpdatedAt,
12
- parseWorkspaceSettingsSnapshot,
13
- resolveWorkspaceSettings,
14
- serializeResolvedWorkspaceSettings
15
- } from "#chat-runtime/server/utils/chat-workspace-settings";
16
- import { resolveWorkspaceCredentials } from "#chat-runtime/server/utils/workspace";
17
- import { useChatNuxtFeatures } from "#chat-runtime/utils/features";
18
- import { mergeFileUrls } from "#chat-runtime/utils/file";
19
15
  import { isValidModel } from "#chat-runtime/types/chat";
16
+ import { telaAgentExecutionInputSchema } from "#chat-runtime/types/tela-agent";
20
17
  export default defineEventHandler(async (event) => {
21
- const context = resolveChatContext(event);
22
- const token = getAgentDataToken(event);
18
+ const context = resolveChatRuntimeContext(event);
23
19
  const db = useDb();
24
20
  const conversationId = getRouterParam(event, "id");
25
21
  const body = await readBody(event);
@@ -35,41 +31,37 @@ export default defineEventHandler(async (event) => {
35
31
  statusMessage: "Message content is required"
36
32
  });
37
33
  }
38
- if (body.model !== void 0 && !isValidModel(body.model)) {
34
+ const [conversation] = await db.select().from(schema.conversations).where(conversationScopeConditions(context, conversationId));
35
+ if (!conversation) {
36
+ throw createError({
37
+ statusCode: 404,
38
+ statusMessage: "Conversation not found"
39
+ });
40
+ }
41
+ if (!conversation.telaAgentId && body.model !== void 0 && !isValidModel(body.model)) {
39
42
  throw createError({
40
43
  statusCode: 400,
41
44
  statusMessage: "Invalid model"
42
45
  });
43
46
  }
44
- const [conversation] = await db.select().from(schema.conversations).where(
45
- and(
46
- eq(schema.conversations.id, conversationId),
47
- eq(schema.conversations.workspaceId, context.workspaceId)
48
- )
49
- );
50
- if (!conversation) {
47
+ const agentInputs = conversation.telaAgentId && body.agentInputs !== void 0 ? telaAgentExecutionInputSchema.array().safeParse(body.agentInputs) : null;
48
+ if (agentInputs && !agentInputs.success) {
51
49
  throw createError({
52
- statusCode: 404,
53
- statusMessage: "Conversation not found"
50
+ statusCode: 400,
51
+ statusMessage: "Invalid Tela agent inputs"
54
52
  });
55
53
  }
56
- const effectiveModel = body.model ?? (isValidModel(conversation.model) ? conversation.model : void 0);
57
- const explicitSettings = !conversation.appliedSettingsSnapshot && body.workspaceSettingsSnapshot !== void 0 ? parseWorkspaceSettingsSnapshot(body.workspaceSettingsSnapshot) : void 0;
58
- const trustedSettings = conversation.appliedSettingsSnapshot || explicitSettings ? null : await resolveWorkspaceSettings(event, context, db);
59
- const appliedSettings = getConversationAppliedSettings(conversation, trustedSettings, explicitSettings);
54
+ const telaAgentPayload = conversation.telaAgentId ? await buildTelaAgentTurnPayload(event, conversation, body.content, body.files, agentInputs?.data) : null;
55
+ const defaultAgentTurn = conversation.telaAgentId ? null : await prepareDefaultAgentTurn(event, db, context, {
56
+ conversation,
57
+ conversationId,
58
+ content: body.content,
59
+ files: body.files,
60
+ requestedModel: body.model,
61
+ workspaceSettingsSnapshot: body.workspaceSettingsSnapshot
62
+ });
60
63
  const now = /* @__PURE__ */ new Date();
61
64
  const assistantCreatedAt = new Date(now.getTime() + 1);
62
- const snapshotSettings = explicitSettings ?? trustedSettings;
63
- if (!conversation.appliedSettingsSnapshot && snapshotSettings) {
64
- await db.update(schema.conversations).set({
65
- appliedSettingsAt: getWorkspaceSettingsUpdatedAt(snapshotSettings),
66
- appliedSettingsSnapshot: serializeResolvedWorkspaceSettings(snapshotSettings),
67
- updatedAt: now
68
- }).where(and(
69
- eq(schema.conversations.id, conversationId),
70
- isNull(schema.conversations.appliedSettingsSnapshot)
71
- ));
72
- }
73
65
  const [userMessages, assistantMessages] = await Promise.all([
74
66
  db.insert(schema.messages).values({
75
67
  conversationId,
@@ -98,49 +90,42 @@ export default defineEventHandler(async (event) => {
98
90
  }
99
91
  logger.info({ conversationId, userMessageId: userMessage.id, hasFiles: !!body.files?.length }, "Message sent");
100
92
  const assistantMessageId = assistantMessage.id;
101
- const needsNewThread = conversation.threadSessionId === null;
102
- const fileUrls = mergeFileUrls(appliedSettings.contextFiles, body.files);
103
- const messageTitle = truncateTitle(body.content);
104
- const knowledgeSources = appliedSettings.knowledgeSources ?? null;
105
- const { vaultUrl } = useRuntimeConfig();
106
- const messageWithContext = buildMessageWithContext(
107
- body.content.trim(),
108
- appliedSettings.systemMessage ?? null,
109
- knowledgeSources,
110
- needsNewThread,
111
- needsNewThread ? renderSystemPrompt(vaultUrl) : ""
112
- );
113
- const canvasTools = appliedSettings.canvasTools;
114
- const hasKnowledgeSources = knowledgeSources && knowledgeSources.length > 0;
115
- const externalSkillRefs = appliedSettings.externalSkills?.map((skill) => skill.ref) ?? [];
116
- const settingsTimestamp = getWorkspaceSettingsUpdatedAt(appliedSettings);
117
- const skills = externalSkillRefs.length > 0 ? externalSkillRefs : void 0;
118
- if (needsNewThread) {
119
- let environmentVariables = {};
120
- if (useChatNuxtFeatures().credentials) {
121
- try {
122
- environmentVariables = await resolveWorkspaceCredentials(event, context.workspaceId);
123
- } catch (error) {
124
- logger.warn({ conversationId, error }, "Failed to resolve workspace credentials, proceeding without env vars");
125
- }
126
- }
127
- const hasEnvironmentVariables = Object.keys(environmentVariables).length > 0;
128
- createChatAgentSession(token, {
129
- message: messageWithContext,
130
- fileUrls,
131
- model: effectiveModel,
132
- customTools: canvasTools?.length ? { canvas: canvasTools } : void 0,
133
- tools: hasKnowledgeSources ? { workstationQueryTool: true } : void 0,
134
- environmentVariables: hasEnvironmentVariables ? environmentVariables : void 0,
135
- skills
136
- }).then(async (result) => {
93
+ if (conversation.telaAgentId) {
94
+ queueTelaAgentTurn(event, db, {
95
+ conversation,
96
+ conversationId,
97
+ assistantMessageId,
98
+ payload: telaAgentPayload,
99
+ titleContent: body.content,
100
+ operation: "send"
101
+ });
102
+ return {
103
+ userMessage,
104
+ assistantMessage
105
+ };
106
+ }
107
+ if (!defaultAgentTurn) {
108
+ throw createError({
109
+ statusCode: 500,
110
+ statusMessage: "Failed to prepare agent turn"
111
+ });
112
+ }
113
+ const token = getAgentDataToken(event);
114
+ if (defaultAgentTurn.needsNewThread) {
115
+ const environmentVariables = await resolveDefaultAgentEnvironmentVariables(
116
+ event,
117
+ context,
118
+ { conversationId },
119
+ "Failed to resolve workspace credentials, proceeding without env vars"
120
+ );
121
+ createChatAgentSession(token, withEnvironmentVariables(defaultAgentTurn.createPayload, environmentVariables)).then(async (result) => {
137
122
  if (result.success) {
138
123
  logger.info({ conversationId, sessionId: result.sessionId }, "New agent thread created");
139
124
  await db.update(schema.conversations).set({
140
125
  threadSessionId: result.sessionId,
141
- title: messageTitle,
142
- appliedSettingsAt: settingsTimestamp,
143
- model: effectiveModel ?? null,
126
+ title: defaultAgentTurn.messageTitle,
127
+ appliedSettingsAt: defaultAgentTurn.settingsTimestamp,
128
+ model: defaultAgentTurn.effectiveModel ?? null,
144
129
  updatedAt: /* @__PURE__ */ new Date()
145
130
  }).where(eq(schema.conversations.id, conversationId));
146
131
  } else {
@@ -158,20 +143,17 @@ export default defineEventHandler(async (event) => {
158
143
  }).where(eq(schema.messages.id, assistantMessageId));
159
144
  });
160
145
  } else {
161
- continueSession(token, conversation.threadSessionId, {
162
- content: body.content.trim(),
163
- fileUrls,
164
- model: effectiveModel
165
- }).then(async (result) => {
146
+ continueSession(token, conversation.threadSessionId, defaultAgentTurn.continuePayload).then(async (result) => {
166
147
  if (!result.success) {
167
148
  logger.warn({ conversationId, sessionId: conversation.threadSessionId, error: result.error }, "Agent session continue failed");
168
149
  await db.update(schema.messages).set({
169
150
  content: `Erro: ${result.error}`,
170
151
  status: "failed"
171
152
  }).where(eq(schema.messages.id, assistantMessageId));
153
+ return;
172
154
  }
173
155
  await db.update(schema.conversations).set({
174
- ...effectiveModel ? { model: effectiveModel } : {},
156
+ ...defaultAgentTurn.effectiveModel ? { model: defaultAgentTurn.effectiveModel } : {},
175
157
  updatedAt: /* @__PURE__ */ new Date()
176
158
  }).where(eq(schema.conversations.id, conversationId));
177
159
  }).catch(async (e) => {
@@ -1,10 +1,11 @@
1
1
  import { createError, defineEventHandler, getRouterParam } from "h3";
2
2
  import { eq, and } from "drizzle-orm";
3
3
  import { useDb, schema } from "#chat-runtime/server/db";
4
- import { resolveChatContext } from "#chat-runtime/server/utils/chat-context";
4
+ import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
5
+ import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
5
6
  import { getWorkspaceBilling, transformCostForBilling } from "#chat-runtime/server/utils/billing";
6
7
  export default defineEventHandler(async (event) => {
7
- const context = resolveChatContext(event);
8
+ const context = resolveChatRuntimeContext(event);
8
9
  const db = useDb();
9
10
  const id = getRouterParam(event, "id");
10
11
  if (!id) {
@@ -13,12 +14,7 @@ export default defineEventHandler(async (event) => {
13
14
  statusMessage: "Conversation ID is required"
14
15
  });
15
16
  }
16
- const [conversation] = await db.select({ id: schema.conversations.id }).from(schema.conversations).where(
17
- and(
18
- eq(schema.conversations.id, id),
19
- eq(schema.conversations.workspaceId, context.workspaceId)
20
- )
21
- );
17
+ const [conversation] = await db.select({ id: schema.conversations.id }).from(schema.conversations).where(conversationScopeConditions(context, id));
22
18
  if (!conversation) {
23
19
  throw createError({
24
20
  statusCode: 404,
@@ -3,7 +3,7 @@ import { useRuntimeConfig } from "nitropack/runtime";
3
3
  import { createTelaClient } from "@meistrari/tela-sdk-js";
4
4
  import { logger } from "#chat-runtime/server/utils/logger";
5
5
  import { requireUser } from "#chat-runtime/server/utils/auth";
6
- import { useChatNuxtFeatures } from "#chat-runtime/utils/features";
6
+ import { resolveChatNuxtFeatures } from "#chat-runtime/utils/features";
7
7
  const TITLE_CANVAS_ID = "ba06c4a0-ef05-4fb6-b4ef-5af3d63a1ba2";
8
8
  const FALLBACK_TITLE_MAX_LENGTH = 60;
9
9
  const FALLBACK_TITLE_DEFAULT = "New chat";
@@ -26,8 +26,9 @@ export default defineEventHandler(async (event) => {
26
26
  statusMessage: "userMessage and assistantResponse are required"
27
27
  });
28
28
  }
29
- const { telaApiKey } = useRuntimeConfig();
30
- const features = useChatNuxtFeatures();
29
+ const runtimeConfig = useRuntimeConfig();
30
+ const { telaApiKey } = runtimeConfig;
31
+ const features = resolveChatNuxtFeatures(runtimeConfig);
31
32
  if (!features.generateTitle || !telaApiKey) {
32
33
  const fallback = buildFallbackTitle(userMessage);
33
34
  logger.debug({ titleLength: fallback.length, reason: !features.generateTitle ? "feature-disabled" : "no-tela-api-key" }, "Title generated via fallback");
@@ -1,12 +1,13 @@
1
1
  import { defineEventHandler } from "h3";
2
- import { desc, eq } from "drizzle-orm";
2
+ import { desc } from "drizzle-orm";
3
3
  import { useDb, schema } from "#chat-runtime/server/db";
4
- import { resolveChatContext } from "#chat-runtime/server/utils/chat-context";
4
+ import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
5
+ import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
5
6
  import { logger } from "#chat-runtime/server/utils/logger";
6
7
  export default defineEventHandler(async (event) => {
7
- const context = resolveChatContext(event);
8
+ const context = resolveChatRuntimeContext(event);
8
9
  const db = useDb();
9
- const conversations = await db.select().from(schema.conversations).where(eq(schema.conversations.workspaceId, context.workspaceId)).orderBy(desc(schema.conversations.updatedAt));
10
+ const conversations = await db.select().from(schema.conversations).where(conversationScopeConditions(context)).orderBy(desc(schema.conversations.updatedAt));
10
11
  logger.debug({ workspaceId: context.workspaceId, count: conversations.length }, "Conversations listed");
11
12
  return conversations;
12
13
  });
@@ -1,10 +1,10 @@
1
1
  import { createError, defineEventHandler, getRequestHeader, readBody } from "h3";
2
2
  import { useDb, schema } from "#chat-runtime/server/db";
3
- import { resolveChatContext } from "#chat-runtime/server/utils/chat-context";
3
+ import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
4
4
  import { logger } from "#chat-runtime/server/utils/logger";
5
5
  import { isValidModel } from "#chat-runtime/types/chat";
6
6
  export default defineEventHandler(async (event) => {
7
- const context = resolveChatContext(event);
7
+ const context = resolveChatRuntimeContext(event);
8
8
  const db = useDb();
9
9
  let body = {};
10
10
  const contentLength = getRequestHeader(event, "content-length");
@@ -19,7 +19,7 @@ export default defineEventHandler(async (event) => {
19
19
  });
20
20
  }
21
21
  }
22
- if (body.model !== void 0 && body.model !== null && !isValidModel(body.model)) {
22
+ if (!context.telaAgentId && body.model !== void 0 && body.model !== null && !isValidModel(body.model)) {
23
23
  throw createError({
24
24
  statusCode: 400,
25
25
  statusMessage: "Invalid model"
@@ -29,7 +29,8 @@ export default defineEventHandler(async (event) => {
29
29
  workspaceId: context.workspaceId,
30
30
  userId: context.user.id,
31
31
  createdBy: context.user.email,
32
- ...body.model ? { model: body.model } : {}
32
+ telaAgentId: context.telaAgentId,
33
+ ...context.telaAgentId ? { model: null } : body.model ? { model: body.model } : {}
33
34
  }).returning();
34
35
  if (!conversation) {
35
36
  throw createError({
@@ -0,0 +1,6 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
2
+ id: any;
3
+ inputSchema: any;
4
+ isMultiturn: any;
5
+ }>>;
6
+ export default _default;
@@ -0,0 +1,25 @@
1
+ import { createError, defineEventHandler, getRouterParam } from "h3";
2
+ import { requireUser } from "#chat-runtime/server/utils/auth";
3
+ import { getTelaAgent } from "#chat-runtime/server/utils/tela-agent-api";
4
+ export default defineEventHandler(async (event) => {
5
+ requireUser(event);
6
+ const agentId = getRouterParam(event, "id")?.trim();
7
+ if (!agentId) {
8
+ throw createError({
9
+ statusCode: 400,
10
+ statusMessage: "Agent ID is required"
11
+ });
12
+ }
13
+ const result = await getTelaAgent(event, agentId);
14
+ if (!result.success) {
15
+ throw createError({
16
+ statusCode: 502,
17
+ statusMessage: result.error
18
+ });
19
+ }
20
+ return {
21
+ id: result.data.id,
22
+ inputSchema: result.data.inputSchema,
23
+ isMultiturn: result.data.isMultiturn ?? null
24
+ };
25
+ });