@meistrari/chat-nuxt 1.7.0 → 1.8.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 (92) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +2 -0
  3. package/dist/runtime/components/MeistrariChatEmbed.vue +5 -1
  4. package/dist/runtime/components/chat/bash-widget.vue +3 -3
  5. package/dist/runtime/components/chat/doc-sdk-widget.vue +3 -3
  6. package/dist/runtime/components/chat/files-view.vue +3 -64
  7. package/dist/runtime/components/chat/generic-tool-widget.vue +3 -3
  8. package/dist/runtime/components/chat/grep-widget.vue +3 -3
  9. package/dist/runtime/components/chat/message-bubble.vue +1 -1
  10. package/dist/runtime/components/chat/message-input.d.vue.ts +2 -0
  11. package/dist/runtime/components/chat/message-input.vue +21 -5
  12. package/dist/runtime/components/chat/message-input.vue.d.ts +2 -0
  13. package/dist/runtime/components/chat/message-list.vue +1 -1
  14. package/dist/runtime/components/chat/mobile/files/file-preview.d.vue.ts +11 -0
  15. package/dist/runtime/components/chat/mobile/files/file-preview.vue +112 -0
  16. package/dist/runtime/components/chat/mobile/files/file-preview.vue.d.ts +11 -0
  17. package/dist/runtime/components/chat/mobile/files/files-view.d.vue.ts +10 -0
  18. package/dist/runtime/components/chat/mobile/files/files-view.vue +384 -0
  19. package/dist/runtime/components/chat/mobile/files/files-view.vue.d.ts +10 -0
  20. package/dist/runtime/components/chat/mobile/home/header.d.vue.ts +19 -0
  21. package/dist/runtime/components/chat/mobile/home/header.vue +55 -0
  22. package/dist/runtime/components/chat/mobile/home/header.vue.d.ts +19 -0
  23. package/dist/runtime/components/chat/mobile/home/recents.d.vue.ts +14 -0
  24. package/dist/runtime/components/chat/mobile/home/recents.vue +48 -0
  25. package/dist/runtime/components/chat/mobile/home/recents.vue.d.ts +14 -0
  26. package/dist/runtime/components/chat/mobile/home/suggestions.d.vue.ts +7 -0
  27. package/dist/runtime/components/chat/mobile/home/suggestions.vue +31 -0
  28. package/dist/runtime/components/chat/mobile/home/suggestions.vue.d.ts +7 -0
  29. package/dist/runtime/components/chat/mobile/primitives/bottom-sheet.d.vue.ts +21 -0
  30. package/dist/runtime/components/chat/mobile/primitives/bottom-sheet.vue +48 -0
  31. package/dist/runtime/components/chat/mobile/primitives/bottom-sheet.vue.d.ts +21 -0
  32. package/dist/runtime/components/chat/mobile/primitives/info-bubble.d.vue.ts +6 -0
  33. package/dist/runtime/components/chat/mobile/primitives/info-bubble.vue +50 -0
  34. package/dist/runtime/components/chat/mobile/primitives/info-bubble.vue.d.ts +6 -0
  35. package/dist/runtime/components/chat/mobile/shell/drawer.d.vue.ts +22 -0
  36. package/dist/runtime/components/chat/mobile/shell/drawer.vue +143 -0
  37. package/dist/runtime/components/chat/mobile/shell/drawer.vue.d.ts +22 -0
  38. package/dist/runtime/components/chat/mobile/shell/header.d.vue.ts +40 -0
  39. package/dist/runtime/components/chat/mobile/shell/header.vue +139 -0
  40. package/dist/runtime/components/chat/mobile/shell/header.vue.d.ts +40 -0
  41. package/dist/runtime/components/chat/mobile/shell/outdated-settings-sheet.d.vue.ts +14 -0
  42. package/dist/runtime/components/chat/mobile/shell/outdated-settings-sheet.vue +47 -0
  43. package/dist/runtime/components/chat/mobile/shell/outdated-settings-sheet.vue.d.ts +14 -0
  44. package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +122 -0
  45. package/dist/runtime/components/chat/mobile/shell/shell.vue +283 -0
  46. package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +122 -0
  47. package/dist/runtime/components/chat/mobile/shell/workspace-sheet.d.vue.ts +19 -0
  48. package/dist/runtime/components/chat/mobile/shell/workspace-sheet.vue +51 -0
  49. package/dist/runtime/components/chat/mobile/shell/workspace-sheet.vue.d.ts +19 -0
  50. package/dist/runtime/components/chat/mobile/usage/usage-view.d.vue.ts +8 -0
  51. package/dist/runtime/components/chat/mobile/usage/usage-view.vue +243 -0
  52. package/dist/runtime/components/chat/mobile/usage/usage-view.vue.d.ts +8 -0
  53. package/dist/runtime/components/chat/model-selector.vue +6 -0
  54. package/dist/runtime/components/chat/read-widget.vue +3 -3
  55. package/dist/runtime/components/chat/reasoning-step-item.vue +1 -1
  56. package/dist/runtime/components/chat/reasoning-steps.vue +1 -1
  57. package/dist/runtime/components/chat/reasoning-thought.vue +2 -3
  58. package/dist/runtime/components/chat/tela-skill-widget.vue +3 -3
  59. package/dist/runtime/components/chat/thinking-widget.vue +3 -3
  60. package/dist/runtime/components/chat/usage-view.vue +20 -101
  61. package/dist/runtime/components/chat/web-search-widget.vue +3 -3
  62. package/dist/runtime/components/chat/workstation-query-widget.vue +6 -6
  63. package/dist/runtime/components/chat/write-widget.vue +3 -3
  64. package/dist/runtime/composables/useConversationFileDownloads.d.ts +6 -0
  65. package/dist/runtime/composables/useConversationFileDownloads.js +50 -0
  66. package/dist/runtime/composables/useConversationUsage.d.ts +13 -0
  67. package/dist/runtime/composables/useConversationUsage.js +121 -0
  68. package/dist/runtime/composables/useEmbedConfig.d.ts +2 -0
  69. package/dist/runtime/composables/useEmbedConfig.js +4 -0
  70. package/dist/runtime/composables/useMentionAutocomplete.d.ts +6 -0
  71. package/dist/runtime/composables/useMentionAutocomplete.js +71 -23
  72. package/dist/runtime/composables/usePdf.js +2 -2
  73. package/dist/runtime/composables/useWorkspaceSwitchModal.d.ts +5 -0
  74. package/dist/runtime/composables/useWorkspaceSwitchModal.js +14 -0
  75. package/dist/runtime/embed/components/ChatConfigurationModal.vue +119 -13
  76. package/dist/runtime/embed/components/ChatEmbed.vue +4 -0
  77. package/dist/runtime/embed/components/ChatEmbedInner.d.vue.ts +2 -0
  78. package/dist/runtime/embed/components/ChatEmbedInner.vue +201 -11
  79. package/dist/runtime/embed/components/ChatEmbedInner.vue.d.ts +2 -0
  80. package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.d.vue.ts +34 -0
  81. package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue +95 -0
  82. package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue.d.ts +34 -0
  83. package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +3 -1
  84. package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +29 -5
  85. package/dist/runtime/server/utils/conversation-agent-turn.js +4 -0
  86. package/dist/runtime/server/utils/conversation-message-sync.d.ts +8 -2
  87. package/dist/runtime/server/utils/conversation-message-sync.js +52 -6
  88. package/dist/runtime/types/chat-auth.d.ts +1 -0
  89. package/dist/runtime/types/embed.d.ts +2 -0
  90. package/dist/runtime/utils/breakpoints.d.ts +1 -0
  91. package/dist/runtime/utils/breakpoints.js +1 -0
  92. package/package.json +1 -1
@@ -1,6 +1,10 @@
1
1
  <script setup>
2
2
  import { useChatAuth } from "#chat-auth";
3
+ import { useBreakpoints } from "@vueuse/core";
3
4
  import { exportConversationToMarkdown } from "../../utils/export-conversation";
5
+ import { CHAT_MOBILE_BREAKPOINT } from "../../utils/breakpoints";
6
+ import ChatMobileFilePreview from "../../components/chat/mobile/files/file-preview.vue";
7
+ import ChatMobileShell from "../../components/chat/mobile/shell/shell.vue";
4
8
  import { conversationListSkeletonRows } from "../../utils/conversation-list-skeleton";
5
9
  import {
6
10
  normalizeConversationId,
@@ -11,13 +15,15 @@ const props = defineProps({
11
15
  conversationId: { type: [String, null], required: false },
12
16
  initialConversationId: { type: [String, null], required: false, default: null },
13
17
  hideSidebar: { type: Boolean, required: false, default: true },
18
+ hideSettings: { type: Boolean, required: false, default: false },
14
19
  user: { type: [Object, null], required: false },
15
20
  features: { type: Object, required: false }
16
21
  });
17
22
  const emit = defineEmits(["update:conversationId"]);
18
23
  const chatApi = useChatApi();
19
24
  const embedConfig = useEmbedConfig();
20
- const { user: authUser } = useChatAuth();
25
+ const chatAction = useChatAction();
26
+ const { user: authUser, activeOrganization, logout } = useChatAuth();
21
27
  const { getMember } = useWorkspaceMembers();
22
28
  const statusToast = useStatusToast();
23
29
  const { conversations, loading: conversationsLoading, fetchConversations, createConversation, deleteConversation, renameConversation, duplicateConversation, updateConversationInList } = useConversations();
@@ -35,7 +41,7 @@ const effectiveWorkspaceSettings = computed(() => {
35
41
  return embedConfig?.workspaceSettings.value !== void 0 ? embedConfig.workspaceSettings.value : workspaceSettingsState.value;
36
42
  });
37
43
  const { closePanel: closeFilesPanel } = useConversationFilesPanel();
38
- const { isOpen: isPreviewPanelOpen, panelWidth: previewPanelWidth, closePreview } = useFilePreviewPanel();
44
+ const { isOpen: isPreviewPanelOpen, panelWidth: previewPanelWidth, currentFile: currentPreviewFile, closePreview } = useFilePreviewPanel();
39
45
  const activeConversationId = ref(
40
46
  normalizeConversationId(props.conversationId) ?? normalizeConversationId(props.initialConversationId)
41
47
  );
@@ -45,6 +51,9 @@ const resolvedFeatures = computed(() => resolveChatFeatures(props.features));
45
51
  const dropZoneRef = ref(null);
46
52
  const activeTab = ref("chat");
47
53
  const messageInputRef = ref(null);
54
+ const breakpoints = useBreakpoints({ mobile: CHAT_MOBILE_BREAKPOINT });
55
+ const isMobile = breakpoints.smaller("mobile");
56
+ const drawerOpen = ref(false);
48
57
  const { isDragging, setupDropZone } = useFileDrop({
49
58
  onDrop: (files) => {
50
59
  messageInputRef.value?.addFiles(files);
@@ -79,6 +88,19 @@ const conversationIdRef = computed(() => currentConversation.value?.id ?? null);
79
88
  const { total, files: conversationFiles } = useConversationFiles(conversationIdRef);
80
89
  const filesCount = computed(() => total.value);
81
90
  const hasMessages = computed(() => messages.value.length > 0);
91
+ const mobilePreviewFile = computed(() => {
92
+ if (!isPreviewPanelOpen.value || !currentPreviewFile.value)
93
+ return null;
94
+ const file = currentPreviewFile.value;
95
+ return {
96
+ id: file.file.reference,
97
+ url: file.resolvedUrl ?? file.file.reference,
98
+ name: file.file.hashParams?.filename ?? file.originalFileName ?? file.file.name,
99
+ mimeType: file.resolvedMimeType ?? file.file.mimeType,
100
+ source: "message",
101
+ size: file.fileSize
102
+ };
103
+ });
82
104
  const visibleTelaAgentInputSchema = computed(() => !hasMessages.value ? telaAgentInputSchema.value : null);
83
105
  const visibleTelaAgentInputSchemaLoading = computed(() => !hasMessages.value && shouldLoadTelaAgentMetadata.value && telaAgentMetadataLoading.value);
84
106
  const visibleConversations = computed(
@@ -268,6 +290,33 @@ async function handleRetry(messageId) {
268
290
  async function handleNewConversation() {
269
291
  resetConversation();
270
292
  }
293
+ const workspaceSheetOpen = ref(false);
294
+ const {
295
+ settings: workspaceSettingsForModal,
296
+ fetchSettings: fetchWorkspaceSettingsForModal,
297
+ openModal: openWorkspaceSettingsModal
298
+ } = useWorkspaceSettings();
299
+ const { openModal: openWorkspaceSwitchModal } = useWorkspaceSwitchModal();
300
+ async function handleOpenWorkspaceSettings() {
301
+ if (!workspaceSettingsForModal.value) {
302
+ try {
303
+ await fetchWorkspaceSettingsForModal();
304
+ } catch (err) {
305
+ console.error("Failed to load workspace settings:", err);
306
+ return;
307
+ }
308
+ }
309
+ openWorkspaceSettingsModal();
310
+ }
311
+ function handleSwitchWorkspace() {
312
+ openWorkspaceSwitchModal();
313
+ }
314
+ async function handleSignout() {
315
+ chatAction.emit("signout");
316
+ await logout();
317
+ }
318
+ const workspaceName = computed(() => activeOrganization.value?.name ?? "Workspace");
319
+ const workspaceLogo = computed(() => activeOrganization.value?.logo ?? void 0);
271
320
  function handleTabChange(tab) {
272
321
  activeTab.value = tab;
273
322
  if (tab === "chat")
@@ -355,7 +404,7 @@ async function handleDelete() {
355
404
  <div ref="dropZoneRef" h-full flex bg-white relative>
356
405
  <!-- Sidebar -->
357
406
  <div
358
- v-if="!hideSidebar"
407
+ v-if="!hideSidebar && !isMobile"
359
408
  w-240px h-full flex="~ col" flex-shrink-0
360
409
  bg-neutral-50 b-r=".5px neutral-200"
361
410
  >
@@ -408,8 +457,143 @@ async function handleDelete() {
408
457
  <div flex-1 h-full flex="~ col" overflow-hidden relative style="--chat-panel-offset: 0px">
409
458
  <div v-if="loading && currentConversation" class="loading-bar" />
410
459
 
460
+ <ChatMobileShell
461
+ v-if="isMobile"
462
+ v-model:drawer-open="drawerOpen"
463
+ v-model:edit-title="editTitle"
464
+ v-model:model="selectedModel"
465
+ v-model:workspace-sheet-open="workspaceSheetOpen"
466
+ :current-conversation="currentConversation"
467
+ :display-title="displayTitle"
468
+ :conversation-creator="conversationCreator"
469
+ :current-user="currentUser"
470
+ :active-tab="activeTab"
471
+ :active-conversation-id="activeConversationId"
472
+ :conversations="visibleConversations"
473
+ :conversations-loading="conversationsLoading"
474
+ :conversation-files="conversationFiles"
475
+ :files-count="filesCount"
476
+ :generating-title="generatingTitle"
477
+ :has-messages="hasMessages"
478
+ :has-pending-message="hasPendingMessage"
479
+ :hide-settings="hideSettings"
480
+ :hide-sidebar="hideSidebar"
481
+ :is-editing="isEditing"
482
+ :loading="loading"
483
+ :messages="messages"
484
+ :allow-model-selection="!isTelaAgentMode"
485
+ :agent-input-schema="visibleTelaAgentInputSchema"
486
+ :agent-input-schema-loading="visibleTelaAgentInputSchemaLoading"
487
+ :show-model-selector="resolvedFeatures.showModelSelector"
488
+ :show-cancel-button="resolvedFeatures.showCancelButton"
489
+ :show-debug-option="resolvedFeatures.showDebugOption"
490
+ :show-outdated-badge="showOutdatedBadge"
491
+ :show-outdated-banner="showOutdatedBanner"
492
+ :show-usage-tab="resolvedFeatures.showUsageTab"
493
+ :workspace-logo="workspaceLogo"
494
+ :workspace-name="workspaceName"
495
+ :workspace-settings="effectiveWorkspaceSettings"
496
+ @cancel="cancelConversation"
497
+ @cancel-editing="cancelEditing"
498
+ @copy-link="handleShare"
499
+ @debug="showDebugModal = true"
500
+ @delete="showDeleteModal = true"
501
+ @dismiss-outdated-banner="dismissOutdatedBanner"
502
+ @duplicate="handleDuplicate"
503
+ @export="handleExport"
504
+ @new-conversation="handleNewConversation"
505
+ @rename="startEditing"
506
+ @save-title="saveTitle"
507
+ @select-conversation="openConversation"
508
+ @send="handleSend"
509
+ @settings="handleOpenWorkspaceSettings"
510
+ @signout="handleSignout"
511
+ @switch-workspace="handleSwitchWorkspace"
512
+ @tab-change="handleTabChange"
513
+ >
514
+ <template #loading>
515
+ <ChatMessageList
516
+ :messages="[]"
517
+ loading
518
+ />
519
+ <ChatMessageInput
520
+ v-model:model="selectedModel"
521
+ :disabled="true"
522
+ :allow-model-selection="!isTelaAgentMode"
523
+ :agent-input-schema="visibleTelaAgentInputSchema"
524
+ :agent-input-schema-loading="visibleTelaAgentInputSchemaLoading"
525
+ :show-model-selector="resolvedFeatures.showModelSelector"
526
+ :show-cancel-button="resolvedFeatures.showCancelButton"
527
+ />
528
+ </template>
529
+
530
+ <template #empty>
531
+ <div
532
+ flex-1 flex="~ col" items-center justify-center gap-40px px-24px
533
+ :class="currentConversation ? 'pt-64px' : ''"
534
+ >
535
+ <div flex="~ col" items-center gap-8px text-center>
536
+ <h1 text-24px font-semibold text-gray-800 leading-28px tracking="-.8px">
537
+ Olá, {{ currentUser?.name?.split(" ")[0] ?? "usu\xE1rio" }}
538
+ </h1>
539
+ <p text-16px text-gray-500 leading-20px tracking="-.2px">
540
+ Como posso te ajudar hoje?
541
+ </p>
542
+ </div>
543
+ <ChatMessageInput
544
+ ref="messageInputRef"
545
+ v-model:model="selectedModel"
546
+ :disabled="hasPendingMessage"
547
+ :loading="hasPendingMessage"
548
+ :creator-name="currentUser?.name ?? void 0"
549
+ :creator-email="currentConversation?.createdBy ?? currentUser?.email ?? void 0"
550
+ :creator-image="currentUser?.image ?? void 0"
551
+ :messages="messages"
552
+ :conversation-files="conversationFiles"
553
+ :workspace-settings="effectiveWorkspaceSettings"
554
+ :allow-model-selection="!isTelaAgentMode"
555
+ :agent-input-schema="visibleTelaAgentInputSchema"
556
+ :agent-input-schema-loading="visibleTelaAgentInputSchemaLoading"
557
+ :show-model-selector="resolvedFeatures.showModelSelector"
558
+ :show-cancel-button="resolvedFeatures.showCancelButton"
559
+ centered
560
+ @send="handleSend"
561
+ @cancel="cancelConversation"
562
+ />
563
+ </div>
564
+ </template>
565
+
566
+ <ChatMessageList
567
+ :messages="messages"
568
+ @retry="handleRetry"
569
+ />
570
+ <ChatMessageInput
571
+ ref="messageInputRef"
572
+ v-model:model="selectedModel"
573
+ :disabled="hasPendingMessage"
574
+ :loading="hasPendingMessage"
575
+ :creator-name="currentUser?.name ?? void 0"
576
+ :creator-email="currentConversation?.createdBy ?? currentUser?.email ?? void 0"
577
+ :creator-image="currentUser?.image ?? void 0"
578
+ :messages="messages"
579
+ :conversation-files="conversationFiles"
580
+ :workspace-settings="effectiveWorkspaceSettings"
581
+ :allow-model-selection="!isTelaAgentMode"
582
+ :agent-input-schema="visibleTelaAgentInputSchema"
583
+ :agent-input-schema-loading="visibleTelaAgentInputSchemaLoading"
584
+ :show-model-selector="resolvedFeatures.showModelSelector"
585
+ :show-cancel-button="resolvedFeatures.showCancelButton"
586
+ has-disclaimer
587
+ @send="handleSend"
588
+ @cancel="cancelConversation"
589
+ />
590
+ <p text-center text-12px font-400 leading-16px text-tertiary my-8px>
591
+ As respostas podem conter erros. Verifique documentos originais.
592
+ </p>
593
+ </ChatMobileShell>
594
+
411
595
  <ChatTopbar
412
- v-if="currentConversation"
596
+ v-if="currentConversation && !isMobile"
413
597
  :title="displayTitle"
414
598
  :creator-name="conversationCreator?.name ?? void 0"
415
599
  :creator-email="currentConversation.createdBy ?? void 0"
@@ -437,7 +621,7 @@ async function handleDelete() {
437
621
  />
438
622
 
439
623
  <div
440
- v-if="showOutdatedBanner"
624
+ v-if="!isMobile && showOutdatedBanner"
441
625
  mt-64px flex items-center h-36px px-12px bg="#FFF1D8"
442
626
  >
443
627
  <div flex items-center gap-8px flex-1>
@@ -469,11 +653,11 @@ async function handleDelete() {
469
653
  </div>
470
654
  </div>
471
655
 
472
- <template v-if="activeTab === 'files' && currentConversation">
656
+ <template v-if="!isMobile && activeTab === 'files' && currentConversation">
473
657
  <ChatFilesView :conversation-id="currentConversation.id" />
474
658
  </template>
475
659
 
476
- <template v-else-if="activeTab === 'usage' && resolvedFeatures.showUsageTab && currentConversation">
660
+ <template v-else-if="!isMobile && activeTab === 'usage' && resolvedFeatures.showUsageTab && currentConversation">
477
661
  <ChatUsageView
478
662
  :conversation-id="currentConversation.id"
479
663
  :created-at="currentConversation.createdAt"
@@ -481,7 +665,7 @@ async function handleDelete() {
481
665
  />
482
666
  </template>
483
667
 
484
- <template v-else-if="loading">
668
+ <template v-else-if="!isMobile && loading">
485
669
  <ChatMessageList
486
670
  :messages="[]"
487
671
  loading
@@ -497,7 +681,7 @@ async function handleDelete() {
497
681
  />
498
682
  </template>
499
683
 
500
- <template v-else-if="!hasMessages">
684
+ <template v-else-if="!isMobile && !hasMessages">
501
685
  <div
502
686
  flex-1 flex="~ col" items-center justify-center gap-40px px-24px
503
687
  :class="currentConversation ? 'pt-64px' : ''"
@@ -533,7 +717,7 @@ async function handleDelete() {
533
717
  </div>
534
718
  </template>
535
719
 
536
- <template v-else>
720
+ <template v-else-if="!isMobile">
537
721
  <ChatMessageList
538
722
  :messages="messages"
539
723
  @retry="handleRetry"
@@ -648,8 +832,14 @@ async function handleDelete() {
648
832
  </TelaModal>
649
833
  </div>
650
834
 
835
+ <ChatMobileFilePreview
836
+ v-if="isPreviewPanelOpen && isMobile"
837
+ :file="mobilePreviewFile"
838
+ @close="closePreview"
839
+ />
840
+
651
841
  <div
652
- v-if="isPreviewPanelOpen"
842
+ v-if="isPreviewPanelOpen && !isMobile"
653
843
  h-full flex-shrink-0 pt-64px
654
844
  :style="{ width: `${previewPanelWidth}px` }"
655
845
  >
@@ -4,6 +4,7 @@ type __VLS_Props = {
4
4
  conversationId?: string | null;
5
5
  initialConversationId?: string | null;
6
6
  hideSidebar?: boolean;
7
+ hideSettings?: boolean;
7
8
  user?: EmbedUser | null;
8
9
  features?: Partial<ChatFeatureConfig>;
9
10
  };
@@ -14,6 +15,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
14
15
  }>, {
15
16
  initialConversationId: string | null;
16
17
  hideSidebar: boolean;
18
+ hideSettings: boolean;
17
19
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
20
  declare const _default: typeof __VLS_export;
19
21
  export default _default;
@@ -0,0 +1,34 @@
1
+ type SettingsTab = {
2
+ value: string;
3
+ label: string;
4
+ icon: string;
5
+ };
6
+ type __VLS_Props = {
7
+ tabs: SettingsTab[];
8
+ activeTab: string | null;
9
+ hasChanges?: boolean;
10
+ isBusy?: boolean;
11
+ };
12
+ declare var __VLS_19: {};
13
+ type __VLS_Slots = {} & {
14
+ default?: (props: typeof __VLS_19) => any;
15
+ };
16
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
17
+ close: () => any;
18
+ save: () => any;
19
+ back: () => any;
20
+ selectTab: (value: string) => any;
21
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
22
+ onClose?: (() => any) | undefined;
23
+ onSave?: (() => any) | undefined;
24
+ onBack?: (() => any) | undefined;
25
+ onSelectTab?: ((value: string) => any) | undefined;
26
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
27
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
28
+ declare const _default: typeof __VLS_export;
29
+ export default _default;
30
+ type __VLS_WithSlots<T, S> = T & {
31
+ new (): {
32
+ $slots: S;
33
+ };
34
+ };
@@ -0,0 +1,95 @@
1
+ <script setup>
2
+ const props = defineProps({
3
+ tabs: { type: Array, required: true },
4
+ activeTab: { type: [String, null], required: true },
5
+ hasChanges: { type: Boolean, required: false },
6
+ isBusy: { type: Boolean, required: false }
7
+ });
8
+ const emit = defineEmits(["back", "close", "save", "selectTab"]);
9
+ const transitionName = ref("mobile-config-slide-forward");
10
+ const activeTabLabel = computed(() => props.tabs.find((tab) => tab.value === props.activeTab)?.label ?? "Configura\xE7\xF5es");
11
+ watch(() => props.activeTab, (activeTab, previousActiveTab) => {
12
+ if (activeTab && !previousActiveTab)
13
+ transitionName.value = "mobile-config-slide-forward";
14
+ else if (!activeTab && previousActiveTab)
15
+ transitionName.value = "mobile-config-slide-back";
16
+ else
17
+ transitionName.value = "mobile-config-fade";
18
+ });
19
+ </script>
20
+
21
+ <template>
22
+ <div class="mobile-config" role="dialog" aria-modal="true">
23
+ <header class="mobile-config__header">
24
+ <Transition name="mobile-config-fade" mode="out-in">
25
+ <button
26
+ v-if="activeTab"
27
+ key="back"
28
+ type="button"
29
+ class="mobile-config__icon-btn"
30
+ aria-label="Voltar"
31
+ @click="emit('back')"
32
+ >
33
+ <span i-ph-arrow-left text-20px />
34
+ </button>
35
+ <div v-else key="spacer" class="mobile-config__header-spacer" />
36
+ </Transition>
37
+
38
+ <Transition name="mobile-config-title" mode="out-in">
39
+ <h1 :key="activeTab ?? 'settings-list'">
40
+ {{ activeTab ? activeTabLabel : "Configura\xE7\xF5es" }}
41
+ </h1>
42
+ </Transition>
43
+
44
+ <button
45
+ type="button"
46
+ class="mobile-config__icon-btn"
47
+ aria-label="Fechar configurações"
48
+ @click="emit('close')"
49
+ >
50
+ <span i-ph-x text-20px />
51
+ </button>
52
+ </header>
53
+
54
+ <Transition :name="transitionName" mode="out-in">
55
+ <main v-if="!activeTab" key="settings-list" class="mobile-config__list">
56
+ <button
57
+ v-for="tab in tabs"
58
+ :key="tab.value"
59
+ type="button"
60
+ class="mobile-config__list-item"
61
+ @click="emit('selectTab', tab.value)"
62
+ >
63
+ <span :class="tab.icon" class="mobile-config__list-icon" />
64
+ <span>{{ tab.label }}</span>
65
+ <span i-ph-caret-right text-18px text-neutral-400 />
66
+ </button>
67
+ </main>
68
+
69
+ <main v-else :key="activeTab" class="mobile-config__detail">
70
+ <div class="mobile-config__content">
71
+ <slot />
72
+ </div>
73
+
74
+ <footer class="mobile-config__footer">
75
+ <TelaButton
76
+ size="lg"
77
+ class="mobile-config__save"
78
+ :disabled="!hasChanges || isBusy"
79
+ @click="emit('save')"
80
+ >
81
+ <span v-if="isBusy" flex items-center gap-6px>
82
+ <div i-ph-spinner animate-spin />
83
+ Salvando...
84
+ </span>
85
+ <span v-else>Salvar</span>
86
+ </TelaButton>
87
+ </footer>
88
+ </main>
89
+ </Transition>
90
+ </div>
91
+ </template>
92
+
93
+ <style scoped>
94
+ .mobile-config{background:#fff;display:flex;flex-direction:column;inset:0;position:fixed;z-index:550}.mobile-config__header{align-items:center;background:#fff;border-bottom:.5px solid rgba(0,0,0,.08);display:grid;flex-shrink:0;grid-template-columns:44px 1fr 44px;min-height:56px;padding:6px 10px}.mobile-config__header h1{color:#171717;font-size:16px;font-weight:580;line-height:20px;margin:0;overflow:hidden;text-align:center;text-overflow:ellipsis;white-space:nowrap}.mobile-config__header-spacer{height:44px;width:44px}.mobile-config__icon-btn{align-items:center;background:transparent;border-radius:12px;color:#171717;display:inline-flex;height:44px;justify-content:center;transition:background .15s,transform .12s;width:44px}.mobile-config__icon-btn:active{background:#f5f5f5;transform:scale(.96)}.mobile-config__list{background:#f7f7f7;flex:1;overflow-y:auto;padding:12px 12px max(20px,env(safe-area-inset-bottom))}.mobile-config__list-item{align-items:center;background:#fff;border-bottom:.5px solid #f0f0f0;color:#171717;display:grid;gap:12px;grid-template-columns:24px 1fr 20px;min-height:52px;padding:12px 14px;text-align:left;width:100%}.mobile-config__list-item:first-child{border-radius:14px 14px 0 0}.mobile-config__list-item:last-child{border-bottom:0;border-radius:0 0 14px 14px}.mobile-config__list-item:only-child{border-radius:14px}.mobile-config__list-item:active{background:#f5f5f5}.mobile-config__list-icon{color:#6b7280;font-size:20px}.mobile-config__list-item span:nth-child(2){font-size:15px;font-weight:500;line-height:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mobile-config__detail{display:flex;flex:1;flex-direction:column;min-height:0}.mobile-config__content{flex:1;min-height:0;overflow-y:auto;padding:20px 18px 24px}.mobile-config__footer{background:#fff;border-top:.5px solid rgba(0,0,0,.08);flex-shrink:0;padding:12px 18px max(18px,env(safe-area-inset-bottom))}.mobile-config__save{width:100%}.mobile-config-fade-enter-active,.mobile-config-fade-leave-active,.mobile-config-slide-back-enter-active,.mobile-config-slide-back-leave-active,.mobile-config-slide-forward-enter-active,.mobile-config-slide-forward-leave-active,.mobile-config-title-enter-active,.mobile-config-title-leave-active{transition:opacity .18s cubic-bezier(.22,1,.36,1),transform .18s cubic-bezier(.22,1,.36,1)}.mobile-config-slide-back-enter-active,.mobile-config-slide-back-leave-active,.mobile-config-slide-forward-enter-active,.mobile-config-slide-forward-leave-active{will-change:opacity,transform}.mobile-config-slide-forward-enter-from{opacity:0;transform:translateX(18px)}.mobile-config-slide-back-enter-from,.mobile-config-slide-forward-leave-to{opacity:0;transform:translateX(-18px)}.mobile-config-slide-back-leave-to{opacity:0;transform:translateX(18px)}.mobile-config-fade-enter-from,.mobile-config-fade-leave-to,.mobile-config-title-enter-from,.mobile-config-title-leave-to{opacity:0}.mobile-config-title-enter-from{transform:translateY(3px)}.mobile-config-title-leave-to{transform:translateY(-3px)}@media (prefers-reduced-motion:reduce){.mobile-config-fade-enter-active,.mobile-config-fade-leave-active,.mobile-config-slide-back-enter-active,.mobile-config-slide-back-leave-active,.mobile-config-slide-forward-enter-active,.mobile-config-slide-forward-leave-active,.mobile-config-title-enter-active,.mobile-config-title-leave-active{transition-duration:1ms}.mobile-config-slide-back-enter-from,.mobile-config-slide-back-leave-to,.mobile-config-slide-forward-enter-from,.mobile-config-slide-forward-leave-to,.mobile-config-title-enter-from,.mobile-config-title-leave-to{transform:none}}
95
+ </style>
@@ -0,0 +1,34 @@
1
+ type SettingsTab = {
2
+ value: string;
3
+ label: string;
4
+ icon: string;
5
+ };
6
+ type __VLS_Props = {
7
+ tabs: SettingsTab[];
8
+ activeTab: string | null;
9
+ hasChanges?: boolean;
10
+ isBusy?: boolean;
11
+ };
12
+ declare var __VLS_19: {};
13
+ type __VLS_Slots = {} & {
14
+ default?: (props: typeof __VLS_19) => any;
15
+ };
16
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
17
+ close: () => any;
18
+ save: () => any;
19
+ back: () => any;
20
+ selectTab: (value: string) => any;
21
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
22
+ onClose?: (() => any) | undefined;
23
+ onSave?: (() => any) | undefined;
24
+ onBack?: (() => any) | undefined;
25
+ onSelectTab?: ((value: string) => any) | undefined;
26
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
27
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
28
+ declare const _default: typeof __VLS_export;
29
+ export default _default;
30
+ type __VLS_WithSlots<T, S> = T & {
31
+ new (): {
32
+ $slots: S;
33
+ };
34
+ };
@@ -62,7 +62,9 @@ export default defineEventHandler(async (event) => {
62
62
  content: "",
63
63
  status: "pending",
64
64
  reasoningData: null,
65
- files: null
65
+ files: null,
66
+ ttft: null,
67
+ updatedAt: null
66
68
  }).where(eq(schema.messages.id, messageId));
67
69
  logger.info({ conversationId, messageId }, "Message retry initiated");
68
70
  if (conversation.telaAgentId) {
@@ -5,10 +5,14 @@ import { getAgentDataToken, getSessionStatus } from "#chat-runtime/server/utils/
5
5
  import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
6
6
  import { attachNewAssistantFilesToMessageUpdate } from "#chat-runtime/server/utils/conversation-message-files";
7
7
  import {
8
+ buildFailedPendingMessageUpdate,
8
9
  buildMessageUpdateResponse,
9
10
  hasMessageUpdate,
11
+ hasPendingMessageExceededGraceWindow,
10
12
  isTerminalMessageUpdate,
11
13
  mapAgentApiSessionToMessageUpdate,
14
+ SESSION_STATUS_CHECK_ERROR,
15
+ THREAD_SESSION_START_ERROR,
12
16
  trackAgentApiConversationUsage,
13
17
  trackTelaAgentConversationUsage
14
18
  } from "#chat-runtime/server/utils/conversation-message-sync";
@@ -48,10 +52,22 @@ export default defineEventHandler(async (event) => {
48
52
  });
49
53
  }
50
54
  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) {
55
+ const lastCompletedAssistantMessage = messages.findLast((message) => message.role === "assistant" && message.status === "completed");
56
+ const lastTerminalAssistantMessage = messages.findLast(
57
+ (message) => message.role === "assistant" && (message.status === "completed" || message.status === "failed")
58
+ );
59
+ if (!pendingMessage) {
53
60
  return { messages };
54
61
  }
62
+ if (!conversation.threadSessionId) {
63
+ if (!hasPendingMessageExceededGraceWindow(pendingMessage)) {
64
+ return { messages };
65
+ }
66
+ const updateData2 = buildFailedPendingMessageUpdate(THREAD_SESSION_START_ERROR);
67
+ logger.warn({ conversationId, messageId: pendingMessage.id }, "Pending message failed because thread session was not created");
68
+ await db.update(schema.messages).set(updateData2).where(eq(schema.messages.id, pendingMessage.id));
69
+ return buildMessageUpdateResponse(messages, pendingMessage.id, updateData2);
70
+ }
55
71
  logger.debug({ conversationId, messageId: pendingMessage.id, sessionId: conversation.threadSessionId }, "Polling pending message");
56
72
  if (conversation.telaAgentId) {
57
73
  const sessionResult2 = await getTelaAgentSession(event, conversation.threadSessionId);
@@ -61,7 +77,7 @@ export default defineEventHandler(async (event) => {
61
77
  const agentSession2 = sessionResult2.data;
62
78
  const userMessage2 = messages.findLast((message) => message.role === "user");
63
79
  const updateData2 = mapTelaAgentSessionToMessageUpdate(agentSession2, {
64
- lastSyncedStepId: pendingMessage.externalUuid ?? lastAssistantMessage?.externalUuid,
80
+ lastSyncedStepId: pendingMessage.externalUuid ?? lastTerminalAssistantMessage?.externalUuid,
65
81
  existingReasoningData: Array.isArray(pendingMessage.reasoningData) ? pendingMessage.reasoningData : null,
66
82
  pendingMessageTtft: pendingMessage.ttft,
67
83
  userMessageCreatedAt: userMessage2?.createdAt ?? null
@@ -84,13 +100,21 @@ export default defineEventHandler(async (event) => {
84
100
  }
85
101
  const sessionResult = await getSessionStatus(token, conversation.threadSessionId);
86
102
  if (!sessionResult.success) {
87
- return { messages };
103
+ if (!hasPendingMessageExceededGraceWindow(pendingMessage)) {
104
+ logger.debug({ conversationId, messageId: pendingMessage.id, sessionId: conversation.threadSessionId, error: sessionResult.error }, "Agent session status check failed within grace window");
105
+ return { messages };
106
+ }
107
+ const updateData2 = buildFailedPendingMessageUpdate(SESSION_STATUS_CHECK_ERROR);
108
+ logger.warn({ conversationId, messageId: pendingMessage.id, sessionId: conversation.threadSessionId, error: sessionResult.error }, "Pending message failed because session status check did not recover");
109
+ await db.update(schema.messages).set(updateData2).where(eq(schema.messages.id, pendingMessage.id));
110
+ return buildMessageUpdateResponse(messages, pendingMessage.id, updateData2);
88
111
  }
89
112
  const agentSession = sessionResult.data;
90
113
  const userMessage = messages.findLast((message) => message.role === "user");
91
114
  const updateData = mapAgentApiSessionToMessageUpdate(agentSession, {
92
115
  pendingMessage,
93
- lastAssistantMessage,
116
+ lastAssistantMessage: lastCompletedAssistantMessage,
117
+ lastSyncedMessage: pendingMessage.externalUuid ? pendingMessage : lastTerminalAssistantMessage,
94
118
  userMessage
95
119
  });
96
120
  if (updateData.status === "completed") {
@@ -161,6 +161,10 @@ export async function resolveDefaultAgentEnvironmentVariables(event, context, op
161
161
  if (resolveChatNuxtFeatures(useRuntimeConfig()).credentials) {
162
162
  try {
163
163
  credentialVariables = await resolveWorkspaceCredentials(event, context.workspaceId);
164
+ logger.info({
165
+ ...logContext,
166
+ credentialKeys: Object.keys(credentialVariables)
167
+ }, "Workspace credentials loaded for agent API");
164
168
  } catch (error) {
165
169
  logger.warn({ ...logContext, error }, options.credentialsFailureMessage);
166
170
  }
@@ -17,9 +17,15 @@ type MessageUpdateResponse = {
17
17
  messages: Message[];
18
18
  newFiles?: MessageFile[];
19
19
  };
20
+ export declare const PENDING_RESPONSE_GRACE_MS = 30000;
21
+ export declare const THREAD_SESSION_START_ERROR = "Erro: N\u00E3o foi poss\u00EDvel iniciar a sess\u00E3o de resposta. Tente enviar a mensagem novamente.";
22
+ export declare const SESSION_STATUS_CHECK_ERROR = "Erro: N\u00E3o foi poss\u00EDvel verificar o andamento da resposta. Tente enviar a mensagem novamente.";
23
+ export declare function hasPendingMessageExceededGraceWindow(pendingMessage: Pick<Message, 'createdAt'>, now?: Date): boolean;
24
+ export declare function buildFailedPendingMessageUpdate(content: string, now?: Date): MessageUpdate;
20
25
  export declare function mapAgentApiSessionToMessageUpdate(agentSession: AgentApiSession, options: {
21
- pendingMessage: Pick<Message, 'ttft' | 'createdAt'>;
22
- lastAssistantMessage?: Pick<Message, 'externalUuid'> | null;
26
+ pendingMessage: Pick<Message, 'ttft' | 'createdAt' | 'updatedAt'>;
27
+ lastAssistantMessage?: Pick<Message, 'externalUuid' | 'content'> | null;
28
+ lastSyncedMessage?: Pick<Message, 'externalUuid'> | null;
23
29
  userMessage?: Pick<Message, 'createdAt'> | null;
24
30
  now?: Date;
25
31
  }): MessageUpdate;