@meistrari/chat-nuxt 1.1.2 → 1.2.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 (151) hide show
  1. package/README.md +95 -11
  2. package/dist/module.d.mts +5 -0
  3. package/dist/module.json +2 -2
  4. package/dist/module.mjs +96 -23
  5. package/dist/runtime/components/chat/conversation-list.d.vue.ts +1 -0
  6. package/dist/runtime/components/chat/conversation-list.vue +22 -2
  7. package/dist/runtime/components/chat/conversation-list.vue.d.ts +1 -0
  8. package/dist/runtime/components/chat/message-list.d.vue.ts +1 -0
  9. package/dist/runtime/components/chat/message-list.vue +23 -2
  10. package/dist/runtime/components/chat/message-list.vue.d.ts +1 -0
  11. package/dist/runtime/composables/useChat.d.ts +2 -1
  12. package/dist/runtime/composables/useChat.js +32 -3
  13. package/dist/runtime/composables/useChatApi.js +2 -1
  14. package/dist/runtime/composables/useConversations.d.ts +3 -2
  15. package/dist/runtime/composables/useConversations.js +99 -11
  16. package/dist/runtime/composables/useEmbedConfig.d.ts +11 -6
  17. package/dist/runtime/composables/useEmbedConfig.js +35 -7
  18. package/dist/runtime/composables/useIDB.js +4 -0
  19. package/dist/runtime/composables/usePdf.d.ts +840 -1
  20. package/dist/runtime/composables/usePdf.js +21 -3
  21. package/dist/runtime/composables/useWorkspaceContextFiles.d.ts +3 -3
  22. package/dist/runtime/composables/useWorkspaceContextFiles.js +19 -10
  23. package/dist/runtime/composables/useWorkspaceCredentials.d.ts +3 -3
  24. package/dist/runtime/composables/useWorkspaceExternalSkills.d.ts +1 -1
  25. package/dist/runtime/composables/useWorkspaceExternalSkills.js +5 -6
  26. package/dist/runtime/composables/useWorkspacePersonalization.d.ts +1 -1
  27. package/dist/runtime/composables/useWorkspacePersonalization.js +4 -5
  28. package/dist/runtime/composables/useWorkspaceSettings.js +12 -9
  29. package/dist/runtime/embed/components/ChatConfigurationModal.d.vue.ts +25 -0
  30. package/dist/runtime/embed/components/ChatConfigurationModal.vue +373 -0
  31. package/dist/runtime/embed/components/ChatConfigurationModal.vue.d.ts +25 -0
  32. package/dist/runtime/embed/components/ChatEmbed.vue +12 -1
  33. package/dist/runtime/embed/components/ChatEmbedInner.vue +31 -16
  34. package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.d.vue.ts +22 -0
  35. package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.vue +133 -0
  36. package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.vue.d.ts +22 -0
  37. package/dist/runtime/embed/components/configuration/ChatConfigurationCredentialsTab.d.vue.ts +10 -0
  38. package/dist/runtime/embed/components/configuration/ChatConfigurationCredentialsTab.vue +220 -0
  39. package/dist/runtime/embed/components/configuration/ChatConfigurationCredentialsTab.vue.d.ts +10 -0
  40. package/dist/runtime/embed/components/configuration/ChatConfigurationExternalSkillsTab.d.vue.ts +32 -0
  41. package/dist/runtime/embed/components/configuration/ChatConfigurationExternalSkillsTab.vue +222 -0
  42. package/dist/runtime/embed/components/configuration/ChatConfigurationExternalSkillsTab.vue.d.ts +32 -0
  43. package/dist/runtime/embed/components/configuration/ChatConfigurationKnowledgeSourcesTab.d.vue.ts +18 -0
  44. package/dist/runtime/embed/components/configuration/ChatConfigurationKnowledgeSourcesTab.vue +142 -0
  45. package/dist/runtime/embed/components/configuration/ChatConfigurationKnowledgeSourcesTab.vue.d.ts +18 -0
  46. package/dist/runtime/embed/components/configuration/ChatConfigurationPersonalizationTab.d.vue.ts +10 -0
  47. package/dist/runtime/embed/components/configuration/ChatConfigurationPersonalizationTab.vue +37 -0
  48. package/dist/runtime/embed/components/configuration/ChatConfigurationPersonalizationTab.vue.d.ts +10 -0
  49. package/dist/runtime/embed/components/configuration/ChatConfigurationTelaToolsTab.d.vue.ts +20 -0
  50. package/dist/runtime/embed/components/configuration/ChatConfigurationTelaToolsTab.vue +146 -0
  51. package/dist/runtime/embed/components/configuration/ChatConfigurationTelaToolsTab.vue.d.ts +20 -0
  52. package/dist/runtime/server/api/admin/trigger-usage-report.post.d.ts +4 -1
  53. package/dist/runtime/server/api/admin/trigger-usage-report.post.js +2 -0
  54. package/dist/runtime/server/api/chat/workspace/settings.get.d.ts +1 -1
  55. package/dist/runtime/server/api/chat/workspace/settings.get.js +1 -0
  56. package/dist/runtime/server/api/conversations/[id]/cancel.post.d.ts +4 -1
  57. package/dist/runtime/server/api/conversations/[id]/cancel.post.js +1 -0
  58. package/dist/runtime/server/api/conversations/[id]/clear.post.d.ts +1 -1
  59. package/dist/runtime/server/api/conversations/[id]/clear.post.js +1 -0
  60. package/dist/runtime/server/api/conversations/[id]/duplicate.post.d.ts +1 -1
  61. package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +46 -12
  62. package/dist/runtime/server/api/conversations/[id]/files.get.d.ts +1 -1
  63. package/dist/runtime/server/api/conversations/[id]/files.get.js +2 -0
  64. package/dist/runtime/server/api/conversations/[id]/index.delete.d.ts +3 -1
  65. package/dist/runtime/server/api/conversations/[id]/index.delete.js +1 -0
  66. package/dist/runtime/server/api/conversations/[id]/index.get.d.ts +1 -1
  67. package/dist/runtime/server/api/conversations/[id]/index.get.js +1 -0
  68. package/dist/runtime/server/api/conversations/[id]/index.patch.d.ts +1 -1
  69. package/dist/runtime/server/api/conversations/[id]/index.patch.js +1 -0
  70. package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.d.ts +1 -1
  71. package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +2 -0
  72. package/dist/runtime/server/api/conversations/[id]/messages/index.get.d.ts +14 -1
  73. package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +2 -0
  74. package/dist/runtime/server/api/conversations/[id]/messages/index.post.d.ts +4 -1
  75. package/dist/runtime/server/api/conversations/[id]/messages/index.post.js +2 -0
  76. package/dist/runtime/server/api/conversations/[id]/usage.get.d.ts +1 -1
  77. package/dist/runtime/server/api/conversations/[id]/usage.get.js +1 -0
  78. package/dist/runtime/server/api/conversations/generate-title.post.d.ts +3 -1
  79. package/dist/runtime/server/api/conversations/generate-title.post.js +2 -0
  80. package/dist/runtime/server/api/conversations/index.get.d.ts +1 -1
  81. package/dist/runtime/server/api/conversations/index.get.js +1 -0
  82. package/dist/runtime/server/api/conversations/index.post.d.ts +1 -1
  83. package/dist/runtime/server/api/conversations/index.post.js +1 -0
  84. package/dist/runtime/server/api/external-skills/discover.post.d.ts +1 -1
  85. package/dist/runtime/server/api/external-skills/discover.post.js +2 -0
  86. package/dist/runtime/server/api/github/disconnect.post.d.ts +3 -1
  87. package/dist/runtime/server/api/github/disconnect.post.js +2 -0
  88. package/dist/runtime/server/api/github/status.get.d.ts +1 -1
  89. package/dist/runtime/server/api/github/status.get.js +2 -0
  90. package/dist/runtime/server/api/tela/canvas.get.d.ts +1 -1
  91. package/dist/runtime/server/api/tela/canvas.get.js +1 -0
  92. package/dist/runtime/server/api/tela/projects.get.d.ts +1 -1
  93. package/dist/runtime/server/api/tela/projects.get.js +1 -0
  94. package/dist/runtime/server/api/tela/workspaces.get.d.ts +1 -1
  95. package/dist/runtime/server/api/tela/workspaces.get.js +1 -0
  96. package/dist/runtime/server/api/tela/workstations.get.d.ts +1 -1
  97. package/dist/runtime/server/api/tela/workstations.get.js +1 -0
  98. package/dist/runtime/server/api/vault/permalink-metadata.post.d.ts +5 -1
  99. package/dist/runtime/server/api/vault/permalink-metadata.post.js +2 -0
  100. package/dist/runtime/server/api/vault/resolve.post.d.ts +9 -1
  101. package/dist/runtime/server/api/vault/resolve.post.js +2 -0
  102. package/dist/runtime/server/api/vault/upload.post.d.ts +6 -1
  103. package/dist/runtime/server/api/vault/upload.post.js +2 -0
  104. package/dist/runtime/server/api/workspace/credentials/[id].delete.d.ts +3 -1
  105. package/dist/runtime/server/api/workspace/credentials/[id].delete.js +1 -0
  106. package/dist/runtime/server/api/workspace/credentials/[id].put.d.ts +1 -1
  107. package/dist/runtime/server/api/workspace/credentials/[id].put.js +1 -0
  108. package/dist/runtime/server/api/workspace/credentials/index.get.d.ts +1 -1
  109. package/dist/runtime/server/api/workspace/credentials/index.get.js +1 -0
  110. package/dist/runtime/server/api/workspace/credentials/index.post.d.ts +1 -1
  111. package/dist/runtime/server/api/workspace/credentials/index.post.js +1 -0
  112. package/dist/runtime/server/api/workspace/settings.get.d.ts +1 -1
  113. package/dist/runtime/server/api/workspace/settings.get.js +1 -0
  114. package/dist/runtime/server/api/workspace/settings.patch.d.ts +1 -1
  115. package/dist/runtime/server/api/workspace/settings.patch.js +1 -0
  116. package/dist/runtime/server/api/workspace/usage.get.d.ts +1 -1
  117. package/dist/runtime/server/api/workspace/usage.get.js +1 -0
  118. package/dist/runtime/server/db/index.js +1 -0
  119. package/dist/runtime/server/middleware/domain-validation.d.ts +1 -1
  120. package/dist/runtime/server/middleware/domain-validation.js +2 -0
  121. package/dist/runtime/server/middleware/request-logger.d.ts +1 -1
  122. package/dist/runtime/server/middleware/request-logger.js +1 -0
  123. package/dist/runtime/server/tasks/send-daily-usage-report.d.ts +1 -1
  124. package/dist/runtime/server/tasks/send-daily-usage-report.js +1 -0
  125. package/dist/runtime/server/utils/agent-api.d.ts +1 -1
  126. package/dist/runtime/server/utils/agent-api.js +2 -0
  127. package/dist/runtime/server/utils/auth-api.js +2 -0
  128. package/dist/runtime/server/utils/auth.d.ts +1 -1
  129. package/dist/runtime/server/utils/auth.js +1 -0
  130. package/dist/runtime/server/utils/tela-api.d.ts +1 -1
  131. package/dist/runtime/server/utils/tela-api.js +2 -0
  132. package/dist/runtime/server/utils/workspace.js +1 -0
  133. package/dist/runtime/types/chat.d.ts +2 -1
  134. package/dist/runtime/types/workspace-settings.d.ts +3 -0
  135. package/dist/runtime/types/workspace-settings.js +0 -0
  136. package/dist/runtime/utils/conversation-list-skeleton.d.ts +31 -0
  137. package/dist/runtime/utils/conversation-list-skeleton.js +12 -0
  138. package/dist/runtime/utils/file.js +42 -42
  139. package/dist/types.d.mts +2 -0
  140. package/package.json +24 -12
  141. package/dist/runtime/composables/PageNavigation.d.ts +0 -13
  142. package/dist/runtime/composables/PageNavigation.js +0 -18
  143. /package/dist/runtime/components/{TelaChat.d.vue.ts → MeistrariChatEmbed.d.vue.ts} +0 -0
  144. /package/dist/runtime/components/{TelaChat.vue → MeistrariChatEmbed.vue} +0 -0
  145. /package/dist/runtime/components/{TelaChat.vue.d.ts → MeistrariChatEmbed.vue.d.ts} +0 -0
  146. /package/dist/runtime/components/{pdf-preview.d.vue.ts → pdf-preview.client.d.vue.ts} +0 -0
  147. /package/dist/runtime/components/{pdf-preview.vue → pdf-preview.client.vue} +0 -0
  148. /package/dist/runtime/components/{pdf-preview.vue.d.ts → pdf-preview.client.vue.d.ts} +0 -0
  149. /package/dist/runtime/components/{pdf-viewer.d.vue.ts → pdf-viewer.client.d.vue.ts} +0 -0
  150. /package/dist/runtime/components/{pdf-viewer.vue → pdf-viewer.client.vue} +0 -0
  151. /package/dist/runtime/components/{pdf-viewer.vue.d.ts → pdf-viewer.client.vue.d.ts} +0 -0
package/README.md CHANGED
@@ -5,9 +5,27 @@ Nuxt module that adds a complete AI chat interface to your app. Powered by Tela'
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- pnpm add @meistrari/chat-nuxt @meistrari/auth-nuxt @meistrari/tela-build markstream-vue
8
+ pnpm add @meistrari/chat-nuxt
9
9
  ```
10
10
 
11
+ ### Required: Vue dedupe via pnpm overrides
12
+
13
+ `@meistrari/tela-build` pins `vue@3.5.13` as a direct dependency, which collides with the Vue that Nuxt installs. Without an override, pnpm installs two Vue copies and you'll hit runtime errors such as `root.ce._hasShadowRoot is not a function` when custom elements register against one Vue and mount against the other.
14
+
15
+ Until tela-build is published with Vue as a peer dependency, pin a single Vue version in your consumer `package.json`:
16
+
17
+ ```json
18
+ {
19
+ "pnpm": {
20
+ "overrides": {
21
+ "vue": "3.5.33"
22
+ }
23
+ }
24
+ }
25
+ ```
26
+
27
+ Keep this entry until `@meistrari/tela-build` moves Vue to `peerDependencies` — at that point the override becomes unnecessary and should be removed.
28
+
11
29
  ## Setup
12
30
 
13
31
  ### 1. Register the modules
@@ -15,8 +33,6 @@ pnpm add @meistrari/chat-nuxt @meistrari/auth-nuxt @meistrari/tela-build markstr
15
33
  ```typescript
16
34
  // nuxt.config.ts
17
35
  export default defineNuxtConfig({
18
- extends: ['@meistrari/tela-build'],
19
-
20
36
  modules: [
21
37
  '@meistrari/auth-nuxt', // must come before chat-nuxt
22
38
  '@meistrari/chat-nuxt',
@@ -24,6 +40,10 @@ export default defineNuxtConfig({
24
40
 
25
41
  ssr: false,
26
42
 
43
+ chatNuxt: {
44
+ validateConfig: 'warn',
45
+ },
46
+
27
47
  telaAuth: {
28
48
  apiUrl: process.env.AUTH_API_URL ?? '',
29
49
  application: {
@@ -58,6 +78,9 @@ The module injects these into Nuxt's `runtimeConfig` automatically — just set
58
78
  | `AGENT_API_URL` | Agent API endpoint |
59
79
  | `VAULT_URL` | Vault service URL |
60
80
  | `VAULT_S3_BUCKET` | Vault S3 bucket name |
81
+ | `CHAT_APP_SERVICE_KEY` | Service key used when resolving workspace credentials |
82
+
83
+ Set `chatNuxt.validateConfig` to `'warn'` to report missing server config during Nuxt setup, or `'error'` to fail fast in CI or production builds. The check is disabled by default because many deployments inject runtime config after build.
61
84
 
62
85
  ### 3. Setup `app.vue`
63
86
 
@@ -91,7 +114,7 @@ const workspaceId = computed(() => activeOrganization.value?.id ?? null)
91
114
 
92
115
  <template>
93
116
  <div v-if="workspaceId" h-screen>
94
- <TelaChat :workspace-id="workspaceId" />
117
+ <MeistrariChatEmbed :workspace-id="workspaceId" />
95
118
  </div>
96
119
  </template>
97
120
  ```
@@ -110,7 +133,7 @@ The main use case: drop the chat inside your existing app UI.
110
133
  <main>
111
134
  <!-- Chat embedded in a card -->
112
135
  <div style="height: 600px" class="rounded-2xl overflow-hidden border">
113
- <TelaChat
136
+ <MeistrariChatEmbed
114
137
  v-model:conversation-id="activeConversation"
115
138
  :workspace-id="workspaceId"
116
139
  :hide-sidebar="true"
@@ -127,7 +150,7 @@ Key props for embedding:
127
150
  - **`v-model:conversation-id`** — sync the active conversation with your app's state
128
151
  - **`:initial-conversation-id`** — open a specific conversation on mount
129
152
 
130
- ## `<TelaChat>` Props
153
+ ## `<MeistrariChatEmbed>` Props
131
154
 
132
155
  | Prop | Type | Default | Description |
133
156
  |------|------|---------|-------------|
@@ -179,7 +202,7 @@ onMounted(async () => {
179
202
  </script>
180
203
  ```
181
204
 
182
- 3. Pass flags to `<TelaChat>`:
205
+ 3. Pass flags to `<MeistrariChatEmbed>`:
183
206
 
184
207
  ```vue
185
208
  <script setup lang="ts">
@@ -194,14 +217,14 @@ const features = computed(() => ({
194
217
  </script>
195
218
 
196
219
  <template>
197
- <TelaChat :workspace-id="workspaceId" :features="features" />
220
+ <MeistrariChatEmbed :workspace-id="workspaceId" :features="features" />
198
221
  </template>
199
222
  ```
200
223
 
201
224
  Without PostHog, you can pass features statically:
202
225
 
203
226
  ```vue
204
- <TelaChat :workspace-id="workspaceId" :features="{ showUsageTab: true }" />
227
+ <MeistrariChatEmbed :workspace-id="workspaceId" :features="{ showUsageTab: true }" />
205
228
  ```
206
229
 
207
230
  ## Optional: Custom Workspace Settings
@@ -215,7 +238,7 @@ You can override this in two ways:
215
238
  Pass settings directly from your host app:
216
239
 
217
240
  ```vue
218
- <TelaChat
241
+ <MeistrariChatEmbed
219
242
  :workspace-id="workspaceId"
220
243
  :workspace-settings="{
221
244
  workspaceId: 'ws_123',
@@ -254,6 +277,49 @@ export default async function resolveWorkspaceSettings(
254
277
  }
255
278
  ```
256
279
 
280
+ ## `<ChatConfigurationModal>`
281
+
282
+ A reusable settings modal (system prompt, context files, Tela workstations, Tela skills, external skills, credentials) that any host app can open from any button. By default, the modal uses `useWorkspaceSettings()` to fetch and persist settings. If the host passes the `settings` prop, the host owns workspace settings state and persistence.
283
+
284
+ ```vue
285
+ <script setup lang="ts">
286
+ import type { ChatConfigurationTab } from '@meistrari/chat-nuxt/types/workspace-settings'
287
+
288
+ const open = ref(false)
289
+ const initialTab = ref<ChatConfigurationTab>('personalization')
290
+
291
+ function openSettings(tab: ChatConfigurationTab = 'personalization') {
292
+ initialTab.value = tab
293
+ open.value = true
294
+ }
295
+ </script>
296
+
297
+ <template>
298
+ <button @click="openSettings()">
299
+ Configurações
300
+ </button>
301
+
302
+ <ChatConfigurationModal
303
+ v-model:open="open"
304
+ :initial-tab="initialTab"
305
+ />
306
+ </template>
307
+ ```
308
+
309
+ | Prop | Type | Default | Description |
310
+ |------|------|---------|-------------|
311
+ | `open` | `boolean` | — | Modal visibility (supports `v-model:open`) |
312
+ | `settings` | `WorkspaceSettings \| null` | `undefined` | Optional host-controlled settings. Omit this prop to let the modal fetch and persist via `useWorkspaceSettings()`; pass `null` or a settings object to make the host own state and persistence |
313
+ | `initialTab` | `ChatConfigurationTab` | `'personalization'` | Tab to open on |
314
+ | `saving` | `boolean` | `false` | Host-controlled spinner/disable while persistence is in flight |
315
+
316
+ | Event | Payload | Description |
317
+ |-------|---------|-------------|
318
+ | `update:open` | `boolean` | Modal open state changed |
319
+ | `save` | `ChatConfigurationSavePayload` | Emitted only when the host passes `settings`; diff of the changed fields plus `onSaved`; host persists (e.g. via `useWorkspaceSettings().updateSettings()`), calls `onSaved` after persistence succeeds, and closes the modal |
320
+
321
+ The `credentials` tab manages its own data via the module's `/api/workspace/credentials` endpoints and is never part of the `save` payload.
322
+
257
323
  ## What's Included
258
324
 
259
325
  The module auto-registers everything — no manual imports needed:
@@ -263,10 +329,28 @@ The module auto-registers everything — no manual imports needed:
263
329
  - **Server routes**: conversation CRUD, message streaming, workspace settings, vault file storage, GitHub integration
264
330
  - **Database**: conversations, messages, usage tracking (Drizzle ORM + PostgreSQL)
265
331
 
266
- ## Peer Dependencies
332
+ ## Package Dependencies
333
+
334
+ Installing `@meistrari/chat-nuxt` installs the runtime packages used by the layer. The host Nuxt app still owns the Nuxt and Vue versions.
267
335
 
268
336
  | Package | Version | Purpose |
269
337
  |---------|---------|---------|
270
338
  | `@meistrari/auth-nuxt` | `^3.4.1` | Authentication (required) |
271
339
  | `@meistrari/tela-build` | `^1.30.0` | UI component library (Nuxt layer) |
340
+ | `@sentry/nuxt` | `^10.0.0` | Client-side error capture used by chat composables |
341
+ | `@iconify/vue` | `^5.0.0` | Icon rendering support |
342
+ | `@vueuse/core` | `^12.8.0` | Runtime composables used by chat UI |
343
+ | `@vueuse/components` | `^12.8.0` | VueUse component peer expected by the shared runtime |
344
+ | `h3` | `^1.15.0` | Server route utilities provided by Nuxt/Nitro |
272
345
  | `markstream-vue` | `0.0.3-beta.6` | Markdown streaming renderer |
346
+ | `motion` | `^11.13.0` | Animation component runtime |
347
+ | `nitropack` | `^2.10.0` | Runtime config and task utilities provided by Nuxt/Nitro |
348
+ | `virtua` | `^0.42.0` | Virtualized list runtime expected by shared UI |
349
+ | `vue-i18n` | `^11.0.0` | Internationalization runtime expected by shared UI |
350
+
351
+ Peer dependencies:
352
+
353
+ | Package | Version | Purpose |
354
+ |---------|---------|---------|
355
+ | `nuxt` | `^3.17.0` | Host framework |
356
+ | `vue` | `^3.5.0` | Host Vue runtime |
package/dist/module.d.mts CHANGED
@@ -1,3 +1,8 @@
1
+ type ChatNuxtConfigValidationMode = false | 'warn' | 'error';
2
+ interface ChatNuxtModuleOptions {
3
+ validateConfig?: ChatNuxtConfigValidationMode;
4
+ }
1
5
  declare const _default: any;
2
6
 
3
7
  export { _default as default };
8
+ export type { ChatNuxtModuleOptions };
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "configKey": "telaChat",
4
- "version": "1.1.2",
3
+ "configKey": "chatNuxt",
4
+ "version": "1.2.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { existsSync, mkdirSync, symlinkSync } from 'node:fs';
1
+ import { existsSync, readFileSync, mkdirSync, symlinkSync } from 'node:fs';
2
2
  import { createRequire } from 'node:module';
3
3
  import { join, dirname } from 'node:path';
4
- import { defineNuxtModule, createResolver, addImportsDir, addComponentsDir } from 'nuxt/kit';
4
+ import { defineNuxtModule, createResolver, addImportsDir, addComponent, addComponentsDir } from 'nuxt/kit';
5
5
 
6
6
  const localRequire = createRequire(import.meta.url);
7
7
  const runtimeAliasPackages = [
@@ -37,7 +37,7 @@ function isExpectedResolutionError(error) {
37
37
  }
38
38
  return error.code === "MODULE_NOT_FOUND" || error.code === "ERR_PACKAGE_PATH_NOT_EXPORTED";
39
39
  }
40
- function resolvePackageRoot(resolver, packageName) {
40
+ function resolvePackageRoot$1(resolver, packageName) {
41
41
  try {
42
42
  return dirname(resolver.resolve(`${packageName}/package.json`));
43
43
  } catch (error) {
@@ -81,7 +81,7 @@ function resolveTelaBuildLayer() {
81
81
  return localPath;
82
82
  }
83
83
  }
84
- const telaBuildRoot = resolvePackageRoot(localRequire, "@meistrari/tela-build");
84
+ const telaBuildRoot = resolvePackageRoot$1(localRequire, "@meistrari/tela-build");
85
85
  if (!telaBuildRoot) {
86
86
  throw new Error(
87
87
  "[@meistrari/chat-nuxt] Cannot resolve @meistrari/tela-build. Ensure it is installed."
@@ -95,19 +95,19 @@ function resolveRuntimeAliases(telaBuildPath) {
95
95
  const telaBuildRequire = createRequire(join(telaBuildPath, "package.json"));
96
96
  const vueRuntimeEntry = resolvePackageEntry(nuxtRequire, "vue/dist/vue.runtime.esm-bundler.js");
97
97
  const aliases = [
98
- ["vue", vueRuntimeEntry ?? resolvePackageRoot(nuxtRequire, "vue")],
98
+ ["vue", vueRuntimeEntry ?? resolvePackageRoot$1(nuxtRequire, "vue")],
99
99
  ["vue/dist/vue.runtime.esm-bundler.js", vueRuntimeEntry],
100
- ["@vue/runtime-core", resolvePackageEntry(nuxtRequire, "@vue/runtime-core/dist/runtime-core.esm-bundler.js") ?? resolvePackageRoot(nuxtRequire, "@vue/runtime-core")],
101
- ["@vue/runtime-dom", resolvePackageEntry(nuxtRequire, "@vue/runtime-dom/dist/runtime-dom.esm-bundler.js") ?? resolvePackageRoot(nuxtRequire, "@vue/runtime-dom")],
102
- ["@vue/reactivity", resolvePackageEntry(nuxtRequire, "@vue/reactivity/dist/reactivity.esm-bundler.js") ?? resolvePackageRoot(nuxtRequire, "@vue/reactivity")],
103
- ["@vue/shared", resolvePackageEntry(nuxtRequire, "@vue/shared/dist/shared.esm-bundler.js") ?? resolvePackageRoot(nuxtRequire, "@vue/shared")],
104
- ["vue-router", resolvePackageEntry(nuxtRequire, "vue-router/dist/vue-router.mjs") ?? resolvePackageRoot(nuxtRequire, "vue-router")],
105
- ["@vueuse/core", resolvePackageRoot(localRequire, "@vueuse/core")],
106
- ["@vueuse/components", resolvePackageRoot(localRequire, "@vueuse/components")],
107
- ["@vueuse/shared", resolvePackageRoot(vueUseCoreRequire, "@vueuse/shared")],
108
- ["reka-ui", resolvePackageRoot(telaBuildRequire, "reka-ui")],
109
- ["radix-vue", resolvePackageRoot(telaBuildRequire, "radix-vue")],
110
- ["@floating-ui/vue", resolvePackageRoot(telaBuildRequire, "@floating-ui/vue")]
100
+ ["@vue/runtime-core", resolvePackageEntry(nuxtRequire, "@vue/runtime-core/dist/runtime-core.esm-bundler.js") ?? resolvePackageRoot$1(nuxtRequire, "@vue/runtime-core")],
101
+ ["@vue/runtime-dom", resolvePackageEntry(nuxtRequire, "@vue/runtime-dom/dist/runtime-dom.esm-bundler.js") ?? resolvePackageRoot$1(nuxtRequire, "@vue/runtime-dom")],
102
+ ["@vue/reactivity", resolvePackageEntry(nuxtRequire, "@vue/reactivity/dist/reactivity.esm-bundler.js") ?? resolvePackageRoot$1(nuxtRequire, "@vue/reactivity")],
103
+ ["@vue/shared", resolvePackageEntry(nuxtRequire, "@vue/shared/dist/shared.esm-bundler.js") ?? resolvePackageRoot$1(nuxtRequire, "@vue/shared")],
104
+ ["vue-router", resolvePackageEntry(nuxtRequire, "vue-router/dist/vue-router.mjs") ?? resolvePackageRoot$1(nuxtRequire, "vue-router")],
105
+ ["@vueuse/core", resolvePackageRoot$1(localRequire, "@vueuse/core")],
106
+ ["@vueuse/components", resolvePackageRoot$1(localRequire, "@vueuse/components")],
107
+ ["@vueuse/shared", resolvePackageRoot$1(vueUseCoreRequire, "@vueuse/shared")],
108
+ ["reka-ui", resolvePackageRoot$1(telaBuildRequire, "reka-ui")],
109
+ ["radix-vue", resolvePackageRoot$1(telaBuildRequire, "radix-vue")],
110
+ ["@floating-ui/vue", resolvePackageRoot$1(telaBuildRequire, "@floating-ui/vue")]
111
111
  ];
112
112
  const resolvedAliases = aliases.reduce((acc, [name, resolvedPath]) => {
113
113
  if (resolvedPath) {
@@ -148,6 +148,20 @@ function resolveWorkspaceSettingsResolverEntry(srcDir, fallbackResolverBase) {
148
148
  }
149
149
 
150
150
  const _chatNuxtRequire = createRequire(import.meta.url);
151
+ const requiredRuntimeConfigKeys = [
152
+ "databaseUrl",
153
+ "telaApiUrl",
154
+ "telaApiKey",
155
+ "authApiUrl",
156
+ "authApiSecret",
157
+ "agentApiUrl",
158
+ "authJwksUrl",
159
+ "vaultUrl",
160
+ "chatAppServiceKey"
161
+ ];
162
+ function resolvePackageRoot(packageName) {
163
+ return dirname(_chatNuxtRequire.resolve(`${packageName}/package.json`));
164
+ }
151
165
  function ensureIconifyPackages(consumerRoot) {
152
166
  const packages = ["@iconify-json/ph"];
153
167
  for (const pkg of packages) {
@@ -156,18 +170,34 @@ function ensureIconifyPackages(consumerRoot) {
156
170
  continue;
157
171
  }
158
172
  try {
159
- const realPath = dirname(_chatNuxtRequire.resolve(`${pkg}/package.json`));
160
173
  mkdirSync(dirname(consumerPath), { recursive: true });
161
- symlinkSync(realPath, consumerPath);
174
+ symlinkSync(resolvePackageRoot(pkg), consumerPath);
162
175
  } catch (err) {
163
- console.warn(`[@meistrari/chat-nuxt] Failed to symlink ${pkg} \u2014 icons may not render.`, err);
176
+ console.warn(`[@meistrari/chat-nuxt] Failed to link ${pkg} into the consumer app. Icons may not render.`, err);
164
177
  }
165
178
  }
166
179
  }
180
+ function validateRuntimeConfig(runtimeConfig, mode) {
181
+ if (!mode) {
182
+ return;
183
+ }
184
+ const missingKeys = requiredRuntimeConfigKeys.filter((key) => !runtimeConfig[key]);
185
+ if (missingKeys.length === 0) {
186
+ return;
187
+ }
188
+ const message = `[@meistrari/chat-nuxt] Missing runtime config: ${missingKeys.join(", ")}. Set these values in runtimeConfig or the matching environment variables before using chat server routes.`;
189
+ if (mode === "error") {
190
+ throw new Error(message);
191
+ }
192
+ console.warn(message);
193
+ }
167
194
  function isAuthModuleEntry(entry) {
168
195
  if (typeof entry === "string") {
169
196
  return entry === "@meistrari/auth-nuxt";
170
197
  }
198
+ if (entry && typeof entry === "object" && "src" in entry) {
199
+ return entry.src === "@meistrari/auth-nuxt";
200
+ }
171
201
  if (Array.isArray(entry)) {
172
202
  return entry[0] === "@meistrari/auth-nuxt";
173
203
  }
@@ -176,16 +206,19 @@ function isAuthModuleEntry(entry) {
176
206
  const module$1 = defineNuxtModule({
177
207
  meta: {
178
208
  name: "@meistrari/chat-nuxt",
179
- configKey: "telaChat"
209
+ configKey: "chatNuxt"
180
210
  },
181
- setup(_, nuxt) {
182
- ensureIconifyPackages(nuxt.options.rootDir);
211
+ defaults: {
212
+ validateConfig: false
213
+ },
214
+ setup(options, nuxt) {
183
215
  const hasAuthModule = (nuxt.options.modules ?? []).some(isAuthModuleEntry);
184
216
  if (!hasAuthModule) {
185
217
  throw new Error(
186
218
  '[@meistrari/chat-nuxt] Missing required module "@meistrari/auth-nuxt". Install and enable it so useTelaApplicationAuth() and event.context.auth are available.'
187
219
  );
188
220
  }
221
+ ensureIconifyPackages(nuxt.options.rootDir);
189
222
  const resolver = createResolver(import.meta.url);
190
223
  const runtimeDir = resolver.resolve("./runtime");
191
224
  const workspaceSettingsResolverEntry = resolveWorkspaceSettingsResolverEntry(
@@ -201,8 +234,16 @@ const module$1 = defineNuxtModule({
201
234
  nuxt.options.alias = {
202
235
  ...nuxt.options.alias,
203
236
  "#chat-workspace-settings-resolver": workspaceSettingsResolverEntry,
204
- "#chat-runtime": runtimeDir
237
+ "#chat-runtime": runtimeDir,
238
+ "@iconify-json/ph": resolvePackageRoot("@iconify-json/ph")
205
239
  };
240
+ if (nuxt.options.imports !== false) {
241
+ nuxt.options.imports = nuxt.options.imports || {};
242
+ nuxt.options.imports.transform = nuxt.options.imports.transform || {};
243
+ nuxt.options.imports.transform.include = nuxt.options.imports.transform.include || [];
244
+ const runtimeDirRegExp = new RegExp(runtimeDir.replace(/\\/g, "/").replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
245
+ nuxt.options.imports.transform.include.push(runtimeDirRegExp);
246
+ }
206
247
  const rc = nuxt.options.runtimeConfig;
207
248
  rc.databaseUrl = rc.databaseUrl || process.env.DATABASE_URL || "";
208
249
  rc.telaApiUrl = rc.telaApiUrl || process.env.TELA_API_URL || "";
@@ -216,8 +257,14 @@ const module$1 = defineNuxtModule({
216
257
  rc.chatAppServiceKey = rc.chatAppServiceKey || process.env.CHAT_APP_SERVICE_KEY || "";
217
258
  rc.public = rc.public || {};
218
259
  rc.public.agentApiUrl = rc.public.agentApiUrl || process.env.AGENT_API_URL || "";
260
+ validateRuntimeConfig(rc, options.validateConfig ?? false);
219
261
  addImportsDir(resolver.resolve("./runtime/composables"));
220
262
  addImportsDir(resolver.resolve("./runtime/utils"));
263
+ addComponent({
264
+ name: "MeistrariChatEmbed",
265
+ filePath: resolver.resolve("./runtime/components/MeistrariChatEmbed.vue"),
266
+ priority: 1e3
267
+ });
221
268
  addComponentsDir({
222
269
  path: resolver.resolve("./runtime/components")
223
270
  });
@@ -243,8 +290,28 @@ const module$1 = defineNuxtModule({
243
290
  ...config.resolve.dedupe || [],
244
291
  ...runtimeAliasDedupe
245
292
  ]));
293
+ config.plugins.push({
294
+ name: "@meistrari/chat-nuxt:elkjs-umd-interop",
295
+ enforce: "pre",
296
+ apply: "serve",
297
+ load(id) {
298
+ const cleanId = id.split("?")[0] ?? id;
299
+ if (!cleanId.endsWith("/elkjs/lib/elk.bundled.js")) {
300
+ return null;
301
+ }
302
+ try {
303
+ const source = readFileSync(cleanId, "utf8");
304
+ return `${source}
305
+ export default globalThis.ELK;
306
+ `;
307
+ } catch {
308
+ return null;
309
+ }
310
+ }
311
+ });
246
312
  });
247
313
  const serverDir = resolver.resolve("./runtime/server");
314
+ const publicFilesDir = resolver.resolve("./runtime/public/files");
248
315
  nuxt.hook("nitro:config", (nitroConfig) => {
249
316
  nitroConfig.scanDirs = nitroConfig.scanDirs || [];
250
317
  nitroConfig.scanDirs.push(serverDir);
@@ -253,6 +320,12 @@ const module$1 = defineNuxtModule({
253
320
  "#chat-runtime": runtimeDir,
254
321
  "#chat-workspace-settings-resolver": workspaceSettingsResolverEntry
255
322
  };
323
+ nitroConfig.publicAssets = nitroConfig.publicAssets || [];
324
+ nitroConfig.publicAssets.push({
325
+ dir: publicFilesDir,
326
+ baseURL: "/chat-files",
327
+ maxAge: 60 * 60 * 24 * 7
328
+ });
256
329
  });
257
330
  }
258
331
  });
@@ -4,6 +4,7 @@ type __VLS_Props = {
4
4
  conversations: DeepReadonly<Conversation[]>;
5
5
  currentId?: string;
6
6
  getMemberImage?: (userId: string) => string | undefined;
7
+ loading?: boolean;
7
8
  };
8
9
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
10
  declare const _default: typeof __VLS_export;
@@ -1,13 +1,16 @@
1
1
  <script setup>
2
2
  import { groupConversationsByDate } from "../../utils/format-time";
3
3
  import { exportConversationToMarkdown } from "../../utils/export-conversation";
4
+ import { conversationListSkeletonRows } from "../../utils/conversation-list-skeleton";
4
5
  const props = defineProps({
5
6
  conversations: { type: null, required: true },
6
7
  currentId: { type: String, required: false },
7
- getMemberImage: { type: Function, required: false }
8
+ getMemberImage: { type: Function, required: false },
9
+ loading: { type: Boolean, required: false }
8
10
  });
9
11
  const chatApi = useChatApi();
10
12
  const { deleteConversation, renameConversation, duplicateConversation } = useConversations();
13
+ const { setConversation } = useChat();
11
14
  const router = useRouter();
12
15
  const groupedConversations = computed(() => {
13
16
  return groupConversationsByDate(props.conversations);
@@ -25,6 +28,7 @@ async function handleRename(id, title) {
25
28
  async function handleDuplicate(id) {
26
29
  const duplicated = await duplicateConversation(id);
27
30
  if (duplicated) {
31
+ setConversation(duplicated);
28
32
  router.push(`/${duplicated.id}`);
29
33
  }
30
34
  }
@@ -43,7 +47,23 @@ async function handleExport(id) {
43
47
 
44
48
  <template>
45
49
  <div flex="~ col" gap-16px>
46
- <div v-if="conversations.length === 0" p-16px text-center>
50
+ <div v-if="loading && conversations.length === 0" flex="~ col" gap-4px>
51
+ <div h-20px flex items-center pl-12px pr-4px mb-4px>
52
+ <TelaSkeleton rounded-4px bg-neutral-200 style="width: 68px; height: 12px;" />
53
+ </div>
54
+
55
+ <div flex="~ col" gap-1px>
56
+ <div
57
+ v-for="row in conversationListSkeletonRows"
58
+ :key="row.id"
59
+ flex items-center h-32px px-8px rounded-8px
60
+ >
61
+ <TelaSkeleton rounded-6px bg-neutral-200 :style="{ width: row.width, height: '13px' }" />
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+ <div v-else-if="conversations.length === 0" p-16px text-center>
47
67
  <p text-14px text-neutral-400>
48
68
  Nenhuma conversa ainda
49
69
  </p>
@@ -4,6 +4,7 @@ type __VLS_Props = {
4
4
  conversations: DeepReadonly<Conversation[]>;
5
5
  currentId?: string;
6
6
  getMemberImage?: (userId: string) => string | undefined;
7
+ loading?: boolean;
7
8
  };
8
9
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
10
  declare const _default: typeof __VLS_export;
@@ -1,6 +1,7 @@
1
1
  import type { Message } from '../../types/chat.js';
2
2
  type __VLS_Props = {
3
3
  messages: readonly Message[];
4
+ loading?: boolean;
4
5
  };
5
6
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
7
  retry: (messageId: string) => any;
@@ -1,9 +1,16 @@
1
1
  <script setup>
2
2
  const props = defineProps({
3
- messages: { type: Array, required: true }
3
+ messages: { type: Array, required: true },
4
+ loading: { type: Boolean, required: false }
4
5
  });
5
6
  const emit = defineEmits(["retry"]);
6
7
  const containerRef = ref(null);
8
+ const skeletonMessages = [
9
+ { id: "user-1", align: "end", width: "58%", height: "44px" },
10
+ { id: "assistant-1", align: "start", width: "92%", height: "116px" },
11
+ { id: "user-2", align: "end", width: "46%", height: "44px" },
12
+ { id: "assistant-2", align: "start", width: "84%", height: "92px" }
13
+ ];
7
14
  function scrollToBottom() {
8
15
  nextTick(() => {
9
16
  if (containerRef.value) {
@@ -23,7 +30,21 @@ onMounted(scrollToBottom);
23
30
  pt-88px pb-24px px-24px
24
31
  >
25
32
  <div max-w-800px mx-auto>
26
- <div flex="~ col" gap-16px>
33
+ <div v-if="loading" flex="~ col" gap-16px>
34
+ <div
35
+ v-for="message in skeletonMessages"
36
+ :key="message.id"
37
+ flex
38
+ :class="message.align === 'end' ? 'justify-end' : 'justify-start'"
39
+ >
40
+ <TelaSkeleton
41
+ rounded-16px
42
+ :style="{ width: message.width, height: message.height }"
43
+ />
44
+ </div>
45
+ </div>
46
+
47
+ <div v-else flex="~ col" gap-16px>
27
48
  <ChatMessageBubble
28
49
  v-for="message in messages"
29
50
  :key="message.id"
@@ -1,6 +1,7 @@
1
1
  import type { Message } from '../../types/chat.js';
2
2
  type __VLS_Props = {
3
3
  messages: readonly Message[];
4
+ loading?: boolean;
4
5
  };
5
6
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
7
  retry: (messageId: string) => any;
@@ -1,4 +1,4 @@
1
- import type { Message } from '../types/chat.js';
1
+ import type { Conversation, ConversationWithMessages, Message } from '../types/chat.js';
2
2
  export declare function useChat(): {
3
3
  currentConversation: any;
4
4
  messages: any;
@@ -11,6 +11,7 @@ export declare function useChat(): {
11
11
  generatingTitle: any;
12
12
  animatedTitle: any;
13
13
  loadConversation: (id: string) => Promise<void>;
14
+ setConversation: (conv: Conversation | ConversationWithMessages) => void;
14
15
  sendMessage: (content: string, files?: {
15
16
  url: string;
16
17
  name: string;
@@ -144,8 +144,29 @@ function parseReasoningFromPolling(reasoning) {
144
144
  }
145
145
  export function useChat() {
146
146
  const chatApi = useChatApi();
147
- const currentConversation = ref(null);
148
- const messages = ref([]);
147
+ const embedConfig = useEmbedConfig();
148
+ const { user: authUser, activeOrganization } = useTelaApplicationAuth();
149
+ const chatScope = computed(() => embedConfig?.workspaceId.value.trim() || activeOrganization.value?.id?.trim() || "default");
150
+ const currentConversationBuckets = useState("chat-current-conversation", () => ({}));
151
+ const messageBuckets = useState("chat-messages", () => ({}));
152
+ const currentConversation = computed({
153
+ get: () => currentConversationBuckets.value[chatScope.value] ?? null,
154
+ set: (value) => {
155
+ currentConversationBuckets.value = {
156
+ ...currentConversationBuckets.value,
157
+ [chatScope.value]: value
158
+ };
159
+ }
160
+ });
161
+ const messages = computed({
162
+ get: () => messageBuckets.value[chatScope.value] ?? [],
163
+ set: (value) => {
164
+ messageBuckets.value = {
165
+ ...messageBuckets.value,
166
+ [chatScope.value]: value
167
+ };
168
+ }
169
+ });
149
170
  const loading = ref(false);
150
171
  const sending = ref(false);
151
172
  const error = ref(null);
@@ -213,6 +234,13 @@ export function useChat() {
213
234
  interval: 500,
214
235
  stopCondition
215
236
  });
237
+ const setConversation = (conv) => {
238
+ const conversationMessages = "messages" in conv ? conv.messages : [];
239
+ currentConversation.value = { ...conv, messages: conversationMessages };
240
+ messages.value = conversationMessages;
241
+ titleGenerated.value = false;
242
+ error.value = null;
243
+ };
216
244
  const loadConversation = async (id) => {
217
245
  loading.value = true;
218
246
  error.value = null;
@@ -258,7 +286,7 @@ export function useChat() {
258
286
  files: filesWithSource,
259
287
  externalUuid: null,
260
288
  ttft: null,
261
- createdBy: null,
289
+ createdBy: authUser.value?.email ?? null,
262
290
  createdAt: /* @__PURE__ */ new Date(),
263
291
  updatedAt: null
264
292
  };
@@ -500,6 +528,7 @@ export function useChat() {
500
528
  generatingTitle: readonly(generatingTitle),
501
529
  animatedTitle: readonly(animatedTitle),
502
530
  loadConversation,
531
+ setConversation,
503
532
  sendMessage,
504
533
  retryMessage,
505
534
  cancelConversation,
@@ -1,10 +1,11 @@
1
1
  export function useChatApi() {
2
2
  const embedConfig = useEmbedConfig();
3
+ const { activeOrganization } = useTelaApplicationAuth();
3
4
  function buildHeaders(headers) {
4
5
  const mergedHeaders = {
5
6
  ...headers ?? {}
6
7
  };
7
- const workspaceId = embedConfig?.workspaceId.value.trim();
8
+ const workspaceId = embedConfig?.workspaceId.value.trim() || activeOrganization.value?.id?.trim();
8
9
  if (workspaceId) {
9
10
  mergedHeaders["x-chat-workspace-id"] = workspaceId;
10
11
  }
@@ -1,5 +1,6 @@
1
1
  import type { DeepReadonly } from 'vue';
2
- import type { Conversation, ModelOption } from '../types/chat.js';
2
+ import type { Conversation, ConversationWithMessages, ModelOption } from '../types/chat.js';
3
+ export declare function mergeFetchedConversations(fetchedConversations: Conversation[], localConversations: readonly Conversation[], deletedConversationIds?: readonly string[], locallyMutatedConversationIds?: readonly string[]): Conversation[];
3
4
  export declare function useConversations(): {
4
5
  conversations: any;
5
6
  loading: any;
@@ -10,6 +11,6 @@ export declare function useConversations(): {
10
11
  }) => Promise<Conversation | null>;
11
12
  deleteConversation: (id: string) => Promise<boolean>;
12
13
  renameConversation: (id: string, title: string) => Promise<boolean>;
13
- duplicateConversation: (id: string) => Promise<Conversation | null>;
14
+ duplicateConversation: (id: string) => Promise<ConversationWithMessages | null>;
14
15
  updateConversationInList: (updated: Conversation | DeepReadonly<Conversation>) => void;
15
16
  };