@meistrari/chat-nuxt 1.2.3 → 1.4.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 (93) hide show
  1. package/README.md +70 -38
  2. package/dist/module.d.mts +24 -1
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +38 -29
  5. package/dist/runtime/components/MeistrariChatEmbed.d.vue.ts +9 -2
  6. package/dist/runtime/components/MeistrariChatEmbed.vue +11 -4
  7. package/dist/runtime/components/MeistrariChatEmbed.vue.d.ts +9 -2
  8. package/dist/runtime/components/app-markdown-render.d.vue.ts +3 -1
  9. package/dist/runtime/components/app-markdown-render.vue +15 -4
  10. package/dist/runtime/components/app-markdown-render.vue.d.ts +3 -1
  11. package/dist/runtime/components/chat/message-bubble.vue +10 -2
  12. package/dist/runtime/components/chat/thinking-indicator.vue +28 -17
  13. package/dist/runtime/composables/useChat.js +8 -1
  14. package/dist/runtime/composables/useChatAction.d.ts +10 -0
  15. package/dist/runtime/composables/useChatAction.js +12 -0
  16. package/dist/runtime/composables/useEmbedConfig.d.ts +10 -1
  17. package/dist/runtime/composables/useEmbedConfig.js +24 -3
  18. package/dist/runtime/embed/components/ChatConfigurationModal.vue +8 -6
  19. package/dist/runtime/embed/components/ChatEmbed.d.vue.ts +9 -2
  20. package/dist/runtime/embed/components/ChatEmbed.vue +22 -13
  21. package/dist/runtime/embed/components/ChatEmbed.vue.d.ts +9 -2
  22. package/dist/runtime/embed/components/ChatEmbedInner.vue +5 -1
  23. package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +7 -4
  24. package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +1 -1
  25. package/dist/runtime/server/api/conversations/[id]/messages/index.post.js +15 -9
  26. package/dist/runtime/server/api/conversations/[id]/usage.get.js +1 -1
  27. package/dist/runtime/server/api/conversations/generate-title.post.js +18 -5
  28. package/dist/runtime/server/api/workspace/usage.get.js +1 -1
  29. package/dist/runtime/server/db/index.d.ts +20 -3
  30. package/dist/runtime/server/db/index.js +149 -10
  31. package/dist/runtime/server/db/schema/_schema.d.ts +2 -0
  32. package/dist/runtime/server/db/schema/_schema.js +3 -0
  33. package/dist/runtime/server/db/schema/conversation-usage.d.ts +1 -1
  34. package/dist/runtime/server/db/schema/conversation-usage.js +3 -2
  35. package/dist/runtime/server/db/schema/conversations.d.ts +1 -1
  36. package/dist/runtime/server/db/schema/conversations.js +3 -2
  37. package/dist/runtime/server/db/schema/external-skills.d.ts +1 -1
  38. package/dist/runtime/server/db/schema/external-skills.js +3 -2
  39. package/dist/runtime/server/db/schema/index.d.ts +1 -0
  40. package/dist/runtime/server/db/schema/index.js +1 -0
  41. package/dist/runtime/server/db/schema/messages.d.ts +1 -1
  42. package/dist/runtime/server/db/schema/messages.js +3 -2
  43. package/dist/runtime/server/db/schema/workspace-settings.d.ts +1 -1
  44. package/dist/runtime/server/db/schema/workspace-settings.js +3 -2
  45. package/dist/runtime/server/plugins/init-db.d.ts +2 -0
  46. package/dist/runtime/server/plugins/init-db.js +11 -0
  47. package/dist/runtime/server/utils/agent-api.d.ts +1 -1
  48. package/dist/runtime/server/utils/agent-api.js +2 -5
  49. package/dist/runtime/server/utils/billing.d.ts +26 -4
  50. package/dist/runtime/server/utils/billing.js +52 -13
  51. package/dist/runtime/server/utils/chat-workspace-settings.d.ts +2 -1
  52. package/dist/runtime/server/utils/chat-workspace-settings.js +11 -2
  53. package/dist/runtime/utils/chat-loading-messages.d.ts +12 -0
  54. package/dist/runtime/utils/chat-loading-messages.js +40 -0
  55. package/dist/runtime/utils/custom-markdown-components.d.ts +9 -0
  56. package/dist/runtime/utils/custom-markdown-components.js +54 -0
  57. package/dist/runtime/utils/features.d.ts +6 -0
  58. package/dist/runtime/utils/features.js +15 -0
  59. package/dist/types.d.mts +1 -1
  60. package/drizzle/0000_material_hiroim.sql +28 -0
  61. package/drizzle/0001_famous_scalphunter.sql +1 -0
  62. package/drizzle/0002_gorgeous_big_bertha.sql +1 -0
  63. package/drizzle/0003_massive_forge.sql +2 -0
  64. package/drizzle/0004_absent_black_tom.sql +2 -0
  65. package/drizzle/0005_first_kid_colt.sql +1 -0
  66. package/drizzle/0006_sloppy_mauler.sql +13 -0
  67. package/drizzle/0007_grey_rhino.sql +1 -0
  68. package/drizzle/0008_chilly_blue_blade.sql +27 -0
  69. package/drizzle/0009_cultured_maddog.sql +16 -0
  70. package/drizzle/0010_small_barracuda.sql +1 -0
  71. package/drizzle/0011_fat_lady_mastermind.sql +6 -0
  72. package/drizzle/0012_puzzling_rick_jones.sql +1 -0
  73. package/drizzle/0013_blue_gwen_stacy.sql +11 -0
  74. package/drizzle/meta/0000_snapshot.json +239 -0
  75. package/drizzle/meta/0001_snapshot.json +245 -0
  76. package/drizzle/meta/0002_snapshot.json +251 -0
  77. package/drizzle/meta/0003_snapshot.json +263 -0
  78. package/drizzle/meta/0004_snapshot.json +275 -0
  79. package/drizzle/meta/0005_snapshot.json +281 -0
  80. package/drizzle/meta/0006_snapshot.json +320 -0
  81. package/drizzle/meta/0007_snapshot.json +326 -0
  82. package/drizzle/meta/0008_snapshot.json +539 -0
  83. package/drizzle/meta/0009_snapshot.json +646 -0
  84. package/drizzle/meta/0010_snapshot.json +653 -0
  85. package/drizzle/meta/0011_snapshot.json +689 -0
  86. package/drizzle/meta/0012_snapshot.json +695 -0
  87. package/drizzle/meta/0013_snapshot.json +699 -0
  88. package/drizzle/meta/_journal.json +104 -0
  89. package/package.json +5 -3
  90. package/dist/runtime/server/tasks/send-daily-usage-report.d.ts +0 -2
  91. package/dist/runtime/server/tasks/send-daily-usage-report.js +0 -74
  92. package/dist/runtime/server/utils/auth-api.d.ts +0 -1
  93. package/dist/runtime/server/utils/auth-api.js +0 -27
@@ -19,7 +19,7 @@ export type ModelUsageEntry = {
19
19
  };
20
20
  export declare const conversationUsage: import("drizzle-orm/pg-core").PgTableWithColumns<{
21
21
  name: "conversation_usage";
22
- schema: undefined;
22
+ schema: "chat";
23
23
  columns: {
24
24
  id: import("drizzle-orm/pg-core").PgColumn<{
25
25
  name: "id";
@@ -1,6 +1,7 @@
1
- import { pgTable, uuid, text, timestamp, index, jsonb, integer, numeric, unique } from "drizzle-orm/pg-core";
1
+ import { uuid, text, timestamp, index, jsonb, integer, numeric, unique } from "drizzle-orm/pg-core";
2
+ import { chatSchema } from "./_schema.js";
2
3
  import { conversations } from "./conversations.js";
3
- export const conversationUsage = pgTable("conversation_usage", {
4
+ export const conversationUsage = chatSchema.table("conversation_usage", {
4
5
  id: uuid("id").primaryKey().defaultRandom(),
5
6
  workspaceId: text("workspace_id").notNull(),
6
7
  conversationId: uuid("conversation_id").notNull().references(() => conversations.id, { onDelete: "cascade" }),
@@ -1,7 +1,7 @@
1
1
  import type { ResolvedWorkspaceSettings } from './workspace-settings.js';
2
2
  export declare const conversations: import("drizzle-orm/pg-core").PgTableWithColumns<{
3
3
  name: "conversations";
4
- schema: undefined;
4
+ schema: "chat";
5
5
  columns: {
6
6
  id: import("drizzle-orm/pg-core").PgColumn<{
7
7
  name: "id";
@@ -1,5 +1,6 @@
1
- import { pgTable, uuid, text, timestamp, index, jsonb } from "drizzle-orm/pg-core";
2
- export const conversations = pgTable("conversations", {
1
+ import { uuid, text, timestamp, index, jsonb } from "drizzle-orm/pg-core";
2
+ import { chatSchema } from "./_schema.js";
3
+ export const conversations = chatSchema.table("conversations", {
3
4
  id: uuid("id").primaryKey().defaultRandom(),
4
5
  workspaceId: text("workspace_id").notNull(),
5
6
  userId: text("user_id").notNull(),
@@ -1,6 +1,6 @@
1
1
  export declare const externalSkills: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
2
  name: "external_skills";
3
- schema: undefined;
3
+ schema: "chat";
4
4
  columns: {
5
5
  id: import("drizzle-orm/pg-core").PgColumn<{
6
6
  name: "id";
@@ -1,7 +1,8 @@
1
- import { pgTable, text, timestamp, jsonb, boolean, serial, unique } from "drizzle-orm/pg-core";
1
+ import { text, timestamp, jsonb, boolean, serial, unique } from "drizzle-orm/pg-core";
2
2
  import { relations } from "drizzle-orm";
3
+ import { chatSchema } from "./_schema.js";
3
4
  import { workspaceSettings } from "./workspace-settings.js";
4
- export const externalSkills = pgTable("external_skills", {
5
+ export const externalSkills = chatSchema.table("external_skills", {
5
6
  id: serial("id").primaryKey(),
6
7
  workspaceId: text("workspace_id").notNull(),
7
8
  ref: text("ref").notNull(),
@@ -1,3 +1,4 @@
1
+ export { CHAT_SCHEMA_NAME, chatSchema } from './_schema.js';
1
2
  export { conversationUsage, type ConversationUsageMetadata, type ModelUsageEntry } from './conversation-usage.js';
2
3
  export { conversations } from './conversations.js';
3
4
  export { type ExternalSkillInsert, type ExternalSkillRow, externalSkills, externalSkillsRelations } from './external-skills.js';
@@ -1,3 +1,4 @@
1
+ export { CHAT_SCHEMA_NAME, chatSchema } from "./_schema.js";
1
2
  export { conversationUsage } from "./conversation-usage.js";
2
3
  export { conversations } from "./conversations.js";
3
4
  export { externalSkills, externalSkillsRelations } from "./external-skills.js";
@@ -8,7 +8,7 @@ export type MessageFile = {
8
8
  };
9
9
  export declare const messages: import("drizzle-orm/pg-core").PgTableWithColumns<{
10
10
  name: "messages";
11
- schema: undefined;
11
+ schema: "chat";
12
12
  columns: {
13
13
  id: import("drizzle-orm/pg-core").PgColumn<{
14
14
  name: "id";
@@ -1,6 +1,7 @@
1
- import { pgTable, uuid, text, timestamp, index, jsonb } from "drizzle-orm/pg-core";
1
+ import { uuid, text, timestamp, index, jsonb } from "drizzle-orm/pg-core";
2
+ import { chatSchema } from "./_schema.js";
2
3
  import { conversations } from "./conversations.js";
3
- export const messages = pgTable("messages", {
4
+ export const messages = chatSchema.table("messages", {
4
5
  id: uuid("id").primaryKey().defaultRandom(),
5
6
  conversationId: uuid("conversation_id").notNull().references(() => conversations.id, { onDelete: "cascade" }),
6
7
  role: text("role", { enum: ["user", "assistant"] }).notNull(),
@@ -29,7 +29,7 @@ export type ResolvedWorkspaceSettings = {
29
29
  };
30
30
  export declare const workspaceSettings: import("drizzle-orm/pg-core").PgTableWithColumns<{
31
31
  name: "workspace_settings";
32
- schema: undefined;
32
+ schema: "chat";
33
33
  columns: {
34
34
  workspaceId: import("drizzle-orm/pg-core").PgColumn<{
35
35
  name: "workspace_id";
@@ -1,7 +1,8 @@
1
- import { pgTable, text, timestamp, jsonb } from "drizzle-orm/pg-core";
1
+ import { text, timestamp, jsonb } from "drizzle-orm/pg-core";
2
2
  import { relations } from "drizzle-orm";
3
+ import { chatSchema } from "./_schema.js";
3
4
  import { externalSkills } from "./external-skills.js";
4
- export const workspaceSettings = pgTable("workspace_settings", {
5
+ export const workspaceSettings = chatSchema.table("workspace_settings", {
5
6
  workspaceId: text("workspace_id").primaryKey(),
6
7
  systemMessage: text("system_message"),
7
8
  contextFiles: jsonb("context_files").$type(),
@@ -0,0 +1,2 @@
1
+ declare const _default: import("nitropack").NitroAppPlugin;
2
+ export default _default;
@@ -0,0 +1,11 @@
1
+ import { defineNitroPlugin } from "nitropack/runtime";
2
+ import { initDb } from "../db/index.js";
3
+ import { logger } from "../utils/logger.js";
4
+ export default defineNitroPlugin(async () => {
5
+ try {
6
+ await initDb();
7
+ } catch (error) {
8
+ logger.error({ error }, "Failed to initialize chat-nuxt database during Nitro boot");
9
+ throw error;
10
+ }
11
+ });
@@ -36,4 +36,4 @@ export declare function cancelSession(token: string, sessionId: string): Promise
36
36
  error: string;
37
37
  }>;
38
38
  export declare function extractContentFromReasoning(reasoning: ReasoningItem[]): string;
39
- export declare function extractContentFromSession(session: AgentApiSession, turnReasoning?: ReasoningItem[]): string;
39
+ export declare function extractContentFromSession(session: AgentApiSession): string;
@@ -150,11 +150,8 @@ export function extractContentFromReasoning(reasoning) {
150
150
  }
151
151
  return "";
152
152
  }
153
- export function extractContentFromSession(session, turnReasoning) {
154
- if (turnReasoning !== void 0) {
155
- return extractContentFromReasoning(turnReasoning);
156
- }
157
- if (session.result?.content) {
153
+ export function extractContentFromSession(session) {
154
+ if (typeof session.result?.content === "string") {
158
155
  return session.result.content;
159
156
  }
160
157
  if (session.result?.structuredContent) {
@@ -1,3 +1,4 @@
1
+ import type { H3Event } from 'h3';
1
2
  export type BillingConfig = {
2
3
  mode: 'plan';
3
4
  cvp: number;
@@ -9,13 +10,17 @@ export type WorkspaceInfo = {
9
10
  name: string;
10
11
  };
11
12
  /**
12
- * Fetch workspace info including billing settings from the auth API
13
+ * Fetch workspace info via the authenticated user's session.
14
+ *
15
+ * Calls auth-api's `whoAmI` with the user's `tela-access-token`, which returns
16
+ * the active organization (including the `settings` JSONB blob and `name`).
17
+ * No internal API key is required.
13
18
  */
14
- export declare function getWorkspaceInfo(workspaceId: string): Promise<WorkspaceInfo>;
19
+ export declare function getWorkspaceInfo(event: H3Event): Promise<WorkspaceInfo>;
15
20
  /**
16
- * Fetch workspace billing settings from the auth API
21
+ * Fetch workspace billing settings via the authenticated user's session.
17
22
  */
18
- export declare function getWorkspaceBilling(workspaceId: string): Promise<BillingConfig>;
23
+ export declare function getWorkspaceBilling(event: H3Event): Promise<BillingConfig>;
19
24
  export type UsageData = {
20
25
  promptCost: number;
21
26
  completionCost: number;
@@ -35,3 +40,20 @@ export type TransformedCosts = {
35
40
  * For 'credits' mode: calculates credits based on costs and token amounts
36
41
  */
37
42
  export declare function transformCostForBilling(usage: UsageData, billing: BillingConfig): TransformedCosts;
43
+ export type DailyUsageRow = {
44
+ workspaceId: string;
45
+ totalTokens: number;
46
+ totalCost: number;
47
+ promptTokens: number;
48
+ completionTokens: number;
49
+ promptCost: number;
50
+ completionCost: number;
51
+ conversationCount: number;
52
+ userCount: number;
53
+ };
54
+ /**
55
+ * Aggregate per-workspace conversation usage between the given timestamps.
56
+ * Returns plain numeric rows so consumers can avoid pulling drizzle types
57
+ * across package boundaries.
58
+ */
59
+ export declare function loadDailyUsageStats(start: Date, end: Date): Promise<DailyUsageRow[]>;
@@ -1,4 +1,8 @@
1
- import { authApiFetch } from "#chat-runtime/server/utils/auth-api";
1
+ import { getCookie } from "h3";
2
+ import { and, gte, lte, sql } from "drizzle-orm";
3
+ import { useRuntimeConfig } from "nitropack/runtime";
4
+ import { AuthClient } from "@meistrari/auth-nuxt/core";
5
+ import { useDb, schema } from "#chat-runtime/server/db";
2
6
  import { logger } from "#chat-runtime/server/utils/logger";
3
7
  const DEFAULT_CVP = 1.5;
4
8
  function calculateCreditsUsed(inputCost, outputCost, inputAmount, outputAmount) {
@@ -13,27 +17,42 @@ function calculateCreditsUsed(inputCost, outputCost, inputAmount, outputAmount)
13
17
  );
14
18
  return credits;
15
19
  }
16
- export async function getWorkspaceInfo(workspaceId) {
20
+ function defaultWorkspaceInfo(name) {
21
+ return {
22
+ billing: { mode: "plan", cvp: DEFAULT_CVP },
23
+ name: name ?? "workspace"
24
+ };
25
+ }
26
+ export async function getWorkspaceInfo(event) {
27
+ const fallbackName = event.context.auth?.workspace?.id;
28
+ const accessToken = event.context.auth?.token ?? getCookie(event, "tela-access-token");
29
+ if (!accessToken) {
30
+ return defaultWorkspaceInfo(fallbackName);
31
+ }
17
32
  try {
18
- const response = await authApiFetch(`/organizations/${workspaceId}`);
19
- const workspace = response.data;
20
- const settings = workspace?.settings;
33
+ const config = useRuntimeConfig();
34
+ const authConfig = config.public.telaAuth;
35
+ const apiUrl = authConfig?.apiUrl;
36
+ if (!apiUrl) {
37
+ logger.warn("telaAuth.apiUrl not configured; falling back to default workspace info");
38
+ return defaultWorkspaceInfo(fallbackName);
39
+ }
40
+ const authClient = new AuthClient(apiUrl);
41
+ const { organization } = await authClient.application.whoAmI(accessToken, {});
42
+ const settings = organization?.settings;
21
43
  const billingSettings = settings?.billing;
22
44
  const billing = billingSettings?.mode === "credits" ? { mode: "credits" } : { mode: "plan", cvp: billingSettings?.cvp ?? DEFAULT_CVP };
23
45
  return {
24
46
  billing,
25
- name: workspace?.title ?? workspaceId
47
+ name: organization?.name ?? fallbackName ?? "workspace"
26
48
  };
27
49
  } catch (error) {
28
- logger.error(error, "Failed to fetch workspace info");
29
- return {
30
- billing: { mode: "plan", cvp: DEFAULT_CVP },
31
- name: workspaceId
32
- };
50
+ logger.error(error, "Failed to fetch workspace info via whoAmI");
51
+ return defaultWorkspaceInfo(fallbackName);
33
52
  }
34
53
  }
35
- export async function getWorkspaceBilling(workspaceId) {
36
- const info = await getWorkspaceInfo(workspaceId);
54
+ export async function getWorkspaceBilling(event) {
55
+ const info = await getWorkspaceInfo(event);
37
56
  return info.billing;
38
57
  }
39
58
  export function transformCostForBilling(usage, billing) {
@@ -66,3 +85,23 @@ export function transformCostForBilling(usage, billing) {
66
85
  totalCost: totalCredits
67
86
  };
68
87
  }
88
+ export async function loadDailyUsageStats(start, end) {
89
+ const db = useDb();
90
+ const rows = await db.select({
91
+ workspaceId: schema.conversationUsage.workspaceId,
92
+ totalTokens: sql`SUM(${schema.conversationUsage.totalTokens})::int`,
93
+ totalCost: sql`SUM(${schema.conversationUsage.totalCost}::numeric)::float`,
94
+ promptTokens: sql`SUM(${schema.conversationUsage.promptTokens})::int`,
95
+ completionTokens: sql`SUM(${schema.conversationUsage.completionTokens})::int`,
96
+ promptCost: sql`SUM(${schema.conversationUsage.promptCost}::numeric)::float`,
97
+ completionCost: sql`SUM(${schema.conversationUsage.completionCost}::numeric)::float`,
98
+ conversationCount: sql`COUNT(DISTINCT ${schema.conversationUsage.conversationId})::int`,
99
+ userCount: sql`COUNT(DISTINCT ${schema.conversationUsage.userId})::int`
100
+ }).from(schema.conversationUsage).where(
101
+ and(
102
+ gte(schema.conversationUsage.createdAt, start),
103
+ lte(schema.conversationUsage.createdAt, end)
104
+ )
105
+ ).groupBy(schema.conversationUsage.workspaceId).orderBy(sql`SUM(${schema.conversationUsage.totalCost}::numeric) DESC`);
106
+ return rows;
107
+ }
@@ -8,8 +8,9 @@ export declare function normalizeResolvedWorkspaceSettings(settings: ResolvedWor
8
8
  export declare function serializeResolvedWorkspaceSettings(settings: ResolvedWorkspaceSettings): ResolvedWorkspaceSettings;
9
9
  export declare function getConversationAppliedSettings(conversation: {
10
10
  appliedSettingsSnapshot?: ResolvedWorkspaceSettingsInput | null;
11
- }, trustedSettings?: ResolvedWorkspaceSettingsInput | null): ResolvedWorkspaceSettings;
11
+ }, trustedSettings?: ResolvedWorkspaceSettingsInput | null, explicitSettings?: ResolvedWorkspaceSettingsInput | null): ResolvedWorkspaceSettings;
12
12
  export declare function hasMaterialWorkspaceSettingsMismatch(currentSettings: ResolvedWorkspaceSettingsInput | null | undefined, trustedSettings: ResolvedWorkspaceSettingsInput | null | undefined): boolean;
13
+ export declare function parseWorkspaceSettingsSnapshot(settings: unknown): ResolvedWorkspaceSettings;
13
14
  export declare function resolveDefaultWorkspaceSettings(db: DbInstance, workspaceId: string): Promise<ResolvedWorkspaceSettings>;
14
15
  export declare function resolveWorkspaceSettings(event: H3Event, context: ChatContext, db: DbInstance): Promise<ResolvedWorkspaceSettings>;
15
16
  export declare function getWorkspaceSettingsUpdatedAt(settings: ResolvedWorkspaceSettings): Date;
@@ -15,7 +15,7 @@ const contextFileSchema = z.object({
15
15
  const resolvedWorkspaceSettingsSchema = z.object({
16
16
  systemMessage: z.string().nullable().optional(),
17
17
  contextFiles: z.array(contextFileSchema).nullable().optional(),
18
- canvasTools: z.array(z.object({ id: z.string() }).passthrough()).nullable().optional(),
18
+ canvasTools: z.array(z.object({ canvasId: z.string() }).passthrough()).nullable().optional(),
19
19
  knowledgeSources: z.array(z.object({ id: z.string() }).passthrough()).nullable().optional(),
20
20
  externalSkills: z.array(z.object({ ref: z.string(), name: z.string() }).passthrough()).nullable().optional(),
21
21
  updatedAt: z.union([z.string(), z.date()]).optional()
@@ -40,10 +40,13 @@ export function serializeResolvedWorkspaceSettings(settings) {
40
40
  updatedAt: settings.updatedAt instanceof Date ? settings.updatedAt.toISOString() : settings.updatedAt
41
41
  };
42
42
  }
43
- export function getConversationAppliedSettings(conversation, trustedSettings) {
43
+ export function getConversationAppliedSettings(conversation, trustedSettings, explicitSettings) {
44
44
  if (conversation.appliedSettingsSnapshot) {
45
45
  return normalizeResolvedWorkspaceSettings(conversation.appliedSettingsSnapshot);
46
46
  }
47
+ if (explicitSettings !== void 0) {
48
+ return normalizeResolvedWorkspaceSettings(explicitSettings);
49
+ }
47
50
  return normalizeResolvedWorkspaceSettings(trustedSettings);
48
51
  }
49
52
  export function hasMaterialWorkspaceSettingsMismatch(currentSettings, trustedSettings) {
@@ -69,6 +72,12 @@ function parseResolverResult(settings) {
69
72
  }
70
73
  return normalizeResolvedWorkspaceSettings(parsedSettings.data);
71
74
  }
75
+ export function parseWorkspaceSettingsSnapshot(settings) {
76
+ if (settings === null) {
77
+ return normalizeResolvedWorkspaceSettings(null);
78
+ }
79
+ return parseResolverResult(settings);
80
+ }
72
81
  export async function resolveDefaultWorkspaceSettings(db, workspaceId) {
73
82
  const [settings, skillRows] = await Promise.all([
74
83
  db.select().from(schema.workspaceSettings).where(eq(schema.workspaceSettings.workspaceId, workspaceId)),
@@ -0,0 +1,12 @@
1
+ export declare const DEFAULT_CHAT_LOADING_MESSAGES: readonly ["Analisando sua pergunta", "Processando informações", "Consultando base de conhecimento", "Organizando ideias", "Elaborando resposta", "Refinando detalhes", "Verificando contexto", "Conectando conceitos", "Preparando resultado", "Quase lá", "Finalizando análise", "Estruturando resposta"];
2
+ export type ChatLoadingMessageMode = 'ordered' | 'random';
3
+ export type ChatLoadingMessagesInput = {
4
+ loadingMessages?: readonly string[] | null;
5
+ loadingMessagesMode?: ChatLoadingMessageMode | null;
6
+ };
7
+ export type ResolvedChatLoadingMessages = {
8
+ mode: ChatLoadingMessageMode;
9
+ messages: readonly string[];
10
+ };
11
+ export declare function resolveChatLoadingMessages(input?: ChatLoadingMessagesInput | null): ResolvedChatLoadingMessages;
12
+ export declare function getNextChatLoadingMessageIndex(mode: ChatLoadingMessageMode, messageCount: number, currentIndex: number, random?: () => number): number;
@@ -0,0 +1,40 @@
1
+ export const DEFAULT_CHAT_LOADING_MESSAGES = [
2
+ "Analisando sua pergunta",
3
+ "Processando informa\xE7\xF5es",
4
+ "Consultando base de conhecimento",
5
+ "Organizando ideias",
6
+ "Elaborando resposta",
7
+ "Refinando detalhes",
8
+ "Verificando contexto",
9
+ "Conectando conceitos",
10
+ "Preparando resultado",
11
+ "Quase l\xE1",
12
+ "Finalizando an\xE1lise",
13
+ "Estruturando resposta"
14
+ ];
15
+ function normalizeMessages(messages) {
16
+ if (!messages?.length)
17
+ return [];
18
+ return messages.map((message) => message.trim()).filter((message) => message.length > 0);
19
+ }
20
+ export function resolveChatLoadingMessages(input) {
21
+ const loadingMessages = normalizeMessages(input?.loadingMessages);
22
+ if (loadingMessages.length > 0) {
23
+ return {
24
+ mode: input?.loadingMessagesMode === "random" ? "random" : "ordered",
25
+ messages: loadingMessages
26
+ };
27
+ }
28
+ return {
29
+ mode: "ordered",
30
+ messages: DEFAULT_CHAT_LOADING_MESSAGES
31
+ };
32
+ }
33
+ export function getNextChatLoadingMessageIndex(mode, messageCount, currentIndex, random = Math.random) {
34
+ if (messageCount <= 1)
35
+ return 0;
36
+ if (mode === "ordered")
37
+ return (currentIndex + 1) % messageCount;
38
+ const nextIndex = Math.floor(random() * (messageCount - 1));
39
+ return nextIndex >= currentIndex ? nextIndex + 1 : nextIndex;
40
+ }
@@ -0,0 +1,9 @@
1
+ import { type Component } from 'vue';
2
+ import type { BaseNode } from 'markstream-vue';
3
+ export type CustomMarkdownNode = BaseNode & {
4
+ attrs?: [string, unknown][];
5
+ content?: string;
6
+ };
7
+ export declare function customMarkdownNodeProps(node: CustomMarkdownNode): Record<string, unknown>;
8
+ export declare function wrapCustomMarkdownComponent(component: Component, tagName?: string): Component;
9
+ export declare function wrapCustomMarkdownComponents(components: Record<string, Component>): Record<string, Component>;
@@ -0,0 +1,54 @@
1
+ import { defineComponent, h } from "vue";
2
+ function camelizeAttributeName(name) {
3
+ return name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
4
+ }
5
+ export function customMarkdownNodeProps(node) {
6
+ const attrProps = {};
7
+ for (const attr of node.attrs ?? []) {
8
+ const [name, value] = attr;
9
+ if (!name)
10
+ continue;
11
+ const propName = camelizeAttributeName(name);
12
+ if (propName === "node")
13
+ continue;
14
+ attrProps[propName] = value;
15
+ }
16
+ return { node, ...attrProps };
17
+ }
18
+ function customMarkdownComponentAdapterName(tagName, component) {
19
+ if (typeof component === "function" && component.name)
20
+ return component.name;
21
+ if (typeof component === "object" && component) {
22
+ const namedComponent = component;
23
+ return namedComponent.displayName ?? namedComponent.name ?? tagName;
24
+ }
25
+ return tagName;
26
+ }
27
+ export function wrapCustomMarkdownComponent(component, tagName = "unknown") {
28
+ return defineComponent({
29
+ name: `CustomMarkdownComponentAdapter(${customMarkdownComponentAdapterName(tagName, component)})`,
30
+ props: {
31
+ node: {
32
+ type: Object,
33
+ required: true
34
+ }
35
+ },
36
+ setup(props, { attrs }) {
37
+ return () => h(
38
+ component,
39
+ {
40
+ ...attrs,
41
+ ...customMarkdownNodeProps(props.node)
42
+ },
43
+ {
44
+ default: () => props.node.content ?? ""
45
+ }
46
+ );
47
+ }
48
+ });
49
+ }
50
+ export function wrapCustomMarkdownComponents(components) {
51
+ return Object.fromEntries(
52
+ Object.entries(components).map(([name, component]) => [name, wrapCustomMarkdownComponent(component, name)])
53
+ );
54
+ }
@@ -0,0 +1,6 @@
1
+ export interface ResolvedChatNuxtFeatures {
2
+ usage: boolean;
3
+ generateTitle: boolean;
4
+ credentials: boolean;
5
+ }
6
+ export declare function useChatNuxtFeatures(): ResolvedChatNuxtFeatures;
@@ -0,0 +1,15 @@
1
+ import { useRuntimeConfig } from "#imports";
2
+ const DEFAULTS = {
3
+ usage: true,
4
+ generateTitle: false,
5
+ credentials: false
6
+ };
7
+ export function useChatNuxtFeatures() {
8
+ const config = useRuntimeConfig();
9
+ const features = config.public.chatNuxtFeatures;
10
+ return {
11
+ usage: features?.usage ?? DEFAULTS.usage,
12
+ generateTitle: features?.generateTitle ?? DEFAULTS.generateTitle,
13
+ credentials: features?.credentials ?? DEFAULTS.credentials
14
+ };
15
+ }
package/dist/types.d.mts CHANGED
@@ -6,4 +6,4 @@ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<
6
6
 
7
7
  export { default } from './module.mjs'
8
8
 
9
- export { type ChatNuxtModuleOptions } from './module.mjs'
9
+ export { type ChatNuxtFeatures, type ChatNuxtModuleOptions } from './module.mjs'
@@ -0,0 +1,28 @@
1
+ CREATE TABLE "conversations" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "workspace_id" text NOT NULL,
4
+ "user_id" text NOT NULL,
5
+ "title" text DEFAULT 'Nova conversa' NOT NULL,
6
+ "thread_session_id" text,
7
+ "canvas_tools" jsonb,
8
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
9
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL
10
+ );
11
+ --> statement-breakpoint
12
+ CREATE TABLE "messages" (
13
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
14
+ "conversation_id" uuid NOT NULL,
15
+ "role" text NOT NULL,
16
+ "content" text NOT NULL,
17
+ "status" text DEFAULT 'completed',
18
+ "reasoning_data" jsonb,
19
+ "files" jsonb,
20
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
21
+ );
22
+ --> statement-breakpoint
23
+ ALTER TABLE "messages" ADD CONSTRAINT "messages_conversation_id_conversations_id_fk" FOREIGN KEY ("conversation_id") REFERENCES "public"."conversations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
24
+ CREATE INDEX "idx_conversations_workspace" ON "conversations" USING btree ("workspace_id");--> statement-breakpoint
25
+ CREATE INDEX "idx_conversations_user" ON "conversations" USING btree ("user_id");--> statement-breakpoint
26
+ CREATE INDEX "idx_conversations_updated" ON "conversations" USING btree ("updated_at");--> statement-breakpoint
27
+ CREATE INDEX "idx_messages_conversation" ON "messages" USING btree ("conversation_id");--> statement-breakpoint
28
+ CREATE INDEX "idx_messages_created" ON "messages" USING btree ("created_at");
@@ -0,0 +1 @@
1
+ ALTER TABLE "messages" ADD COLUMN "external_uuid" text;
@@ -0,0 +1 @@
1
+ ALTER TABLE "messages" ADD COLUMN "ttft" text;
@@ -0,0 +1,2 @@
1
+ ALTER TABLE "conversations" ADD COLUMN "created_by" text;--> statement-breakpoint
2
+ ALTER TABLE "messages" ADD COLUMN "created_by" text;
@@ -0,0 +1,2 @@
1
+ ALTER TABLE "conversations" ADD COLUMN "system_message" text;--> statement-breakpoint
2
+ ALTER TABLE "conversations" ADD COLUMN "context_files" jsonb;
@@ -0,0 +1 @@
1
+ ALTER TABLE "messages" ADD COLUMN "updated_at" timestamp with time zone;
@@ -0,0 +1,13 @@
1
+ CREATE TABLE IF NOT EXISTS "workspace_settings" (
2
+ "workspace_id" text PRIMARY KEY NOT NULL,
3
+ "system_message" text,
4
+ "context_files" jsonb,
5
+ "canvas_tools" jsonb,
6
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
7
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL
8
+ );
9
+ --> statement-breakpoint
10
+ ALTER TABLE "conversations" ADD COLUMN IF NOT EXISTS "applied_settings_at" timestamp with time zone;--> statement-breakpoint
11
+ ALTER TABLE "conversations" DROP COLUMN IF EXISTS "canvas_tools";--> statement-breakpoint
12
+ ALTER TABLE "conversations" DROP COLUMN IF EXISTS "system_message";--> statement-breakpoint
13
+ ALTER TABLE "conversations" DROP COLUMN IF EXISTS "context_files";
@@ -0,0 +1 @@
1
+ ALTER TABLE "workspace_settings" ADD COLUMN IF NOT EXISTS "knowledge_sources" jsonb;
@@ -0,0 +1,27 @@
1
+ CREATE TABLE "conversation_usage" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "workspace_id" text NOT NULL,
4
+ "conversation_id" uuid NOT NULL,
5
+ "prompt_tokens" integer NOT NULL,
6
+ "completion_tokens" integer NOT NULL,
7
+ "total_tokens" integer NOT NULL,
8
+ "prompt_cost" numeric(12, 8) NOT NULL,
9
+ "completion_cost" numeric(12, 8) NOT NULL,
10
+ "total_cost" numeric(12, 8) NOT NULL,
11
+ "duration_ms" integer,
12
+ "turns" integer,
13
+ "user_id" text NOT NULL,
14
+ "user_email" text,
15
+ "model" text,
16
+ "session_id" text,
17
+ "metadata" jsonb,
18
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
19
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
20
+ CONSTRAINT "conversation_usage_conversation_id_unique" UNIQUE("conversation_id")
21
+ );
22
+ --> statement-breakpoint
23
+ ALTER TABLE "conversation_usage" ADD CONSTRAINT "conversation_usage_conversation_id_conversations_id_fk" FOREIGN KEY ("conversation_id") REFERENCES "public"."conversations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
24
+ CREATE INDEX "idx_usage_workspace" ON "conversation_usage" USING btree ("workspace_id");--> statement-breakpoint
25
+ CREATE INDEX "idx_usage_workspace_created" ON "conversation_usage" USING btree ("workspace_id","created_at");--> statement-breakpoint
26
+ CREATE INDEX "idx_usage_conversation" ON "conversation_usage" USING btree ("conversation_id");--> statement-breakpoint
27
+ CREATE INDEX "idx_usage_user" ON "conversation_usage" USING btree ("user_id");
@@ -0,0 +1,16 @@
1
+ CREATE TABLE "external_skills" (
2
+ "id" serial PRIMARY KEY NOT NULL,
3
+ "workspace_id" text NOT NULL,
4
+ "ref" text NOT NULL,
5
+ "name" text NOT NULL,
6
+ "description" text DEFAULT '' NOT NULL,
7
+ "runtime" text DEFAULT 'unknown' NOT NULL,
8
+ "allowed_tools" jsonb DEFAULT '[]'::jsonb NOT NULL,
9
+ "is_public" boolean DEFAULT true NOT NULL,
10
+ "added_by_user_id" text NOT NULL,
11
+ "added_by_username" text NOT NULL,
12
+ "added_at" timestamp with time zone DEFAULT now() NOT NULL,
13
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
14
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
15
+ CONSTRAINT "external_skills_workspace_ref_unique" UNIQUE("workspace_id","ref")
16
+ );
@@ -0,0 +1 @@
1
+ ALTER TABLE "conversations" ADD COLUMN "model" text DEFAULT 'claude-sonnet-4-5';
@@ -0,0 +1,6 @@
1
+ ALTER TABLE "conversation_usage" ADD COLUMN "cache_read_input_tokens" integer;--> statement-breakpoint
2
+ ALTER TABLE "conversation_usage" ADD COLUMN "cache_creation_input_tokens" integer;--> statement-breakpoint
3
+ ALTER TABLE "conversation_usage" ADD COLUMN "model_usage" jsonb;--> statement-breakpoint
4
+ ALTER TABLE "conversation_usage" ADD COLUMN "duration_api_ms" integer;--> statement-breakpoint
5
+ ALTER TABLE "conversation_usage" ADD COLUMN "web_search_requests" integer;--> statement-breakpoint
6
+ ALTER TABLE "conversation_usage" ADD COLUMN "web_fetch_requests" integer;
@@ -0,0 +1 @@
1
+ ALTER TABLE "conversations" ADD COLUMN "applied_settings_snapshot" jsonb;