@meistrari/chat-nuxt 4.2.0-rc.1 → 4.2.0-rc.3

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 (42) hide show
  1. package/README.md +2 -0
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/citations/components/chat-citation.vue +63 -47
  4. package/dist/runtime/citations/label.d.ts +7 -0
  5. package/dist/runtime/citations/label.js +13 -0
  6. package/dist/runtime/conversations/components/chat-conversation-item.vue +2 -2
  7. package/dist/runtime/conversations/components/chat-new-conversation-modal.d.vue.ts +1 -1
  8. package/dist/runtime/conversations/components/chat-new-conversation-modal.vue.d.ts +1 -1
  9. package/dist/runtime/conversations/composables/conversation-item.d.ts +0 -1
  10. package/dist/runtime/conversations/composables/conversation-item.js +14 -25
  11. package/dist/runtime/embed/components/chat-configuration-modal.d.vue.ts +2 -2
  12. package/dist/runtime/embed/components/chat-configuration-modal.vue.d.ts +2 -2
  13. package/dist/runtime/embed/components/chat-embed-inner.d.vue.ts +1 -1
  14. package/dist/runtime/embed/components/chat-embed-inner.vue +4 -3
  15. package/dist/runtime/embed/components/chat-embed-inner.vue.d.ts +1 -1
  16. package/dist/runtime/embed/components/chat-embed.vue +4 -0
  17. package/dist/runtime/embed/components/chat-topbar.d.vue.ts +2 -2
  18. package/dist/runtime/embed/components/chat-topbar.vue.d.ts +2 -2
  19. package/dist/runtime/embed/components/meistrari-chat-embed.vue +4 -0
  20. package/dist/runtime/embed/components/mobile/chat-mobile-header.d.vue.ts +6 -6
  21. package/dist/runtime/embed/components/mobile/chat-mobile-header.vue.d.ts +6 -6
  22. package/dist/runtime/embed/components/mobile/chat-mobile-shell.d.vue.ts +9 -9
  23. package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue.d.ts +9 -9
  24. package/dist/runtime/embed/composables/embed-config.d.ts +5 -0
  25. package/dist/runtime/embed/composables/embed-config.js +8 -0
  26. package/dist/runtime/embed/types.d.ts +11 -0
  27. package/dist/runtime/messages/components/chat-message-bubble.vue +1 -1
  28. package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.d.vue.ts +1 -1
  29. package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue +6 -16
  30. package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue.d.ts +1 -1
  31. package/dist/runtime/messages/composables/chat.d.ts +1 -1
  32. package/dist/runtime/messages/composables/chat.js +13 -4
  33. package/dist/runtime/messages/composables/reasoning-timer.d.ts +2 -1
  34. package/dist/runtime/messages/composables/reasoning-timer.js +20 -2
  35. package/dist/runtime/messages/debug/chat-debug-scenarios.js +1 -0
  36. package/dist/runtime/messages/message-state.js +1 -0
  37. package/dist/runtime/types/schemas/chat/conversation-runtime.d.ts +4 -0
  38. package/dist/runtime/types/schemas/chat/conversation-runtime.js +2 -0
  39. package/dist/runtime/types/schemas/chat/conversations.d.ts +2 -0
  40. package/dist/runtime/types/schemas/chat/conversations.js +1 -0
  41. package/dist/runtime/types/schemas/chat/messages.d.ts +3 -0
  42. package/package.json +1 -1
package/README.md CHANGED
@@ -73,6 +73,8 @@ Declared in `src/runtime/embed/types.ts`; prop, feature-flag, and event payload
73
73
  | `conversationScope` | `string \| null` | `null` | Isolate conversation history by technical scope within the current workspace and runtime. Scoped embeds only see same-scope conversations; values are trimmed, blank becomes `null`, and the server accepts 1-200 chars from `A-Z`, `a-z`, `0-9`, `.`, `_`, `:`, `/`, `-` |
74
74
  | `defaultConversationCreatorFilter` | `'all' \| 'mine'` | `'all'` | Initial sidebar creator filter: every conversation in scope, or only the current user's |
75
75
  | `availableModels` | `AvailableChatModels` | Full catalog | Non-empty ordered list of models this embed may display and execute. A missing or invalid restored selection falls back to the first entry; an empty list fails fast |
76
+ | `defaultModel` | `ChatModel` | Package default | Initial model for untouched drafts. Explicit selections and existing conversations take precedence; `availableModels` still limits the selection |
77
+ | `defaultReasoningEffort` | `ChatReasoningEffort` | Automatic (`null`) | Initial reasoning effort for untouched drafts. Explicit user choices (including automatic) take precedence |
76
78
  | `conversationCreatorFilter` | `'all' \| 'mine'` | — | Controlled sidebar creator filter; use with `v-model:conversation-creator-filter` when the host changes it at runtime |
77
79
  | `hideConversationCreatorFilter` | `boolean` | `false` | Hide the creator selector without changing the active filter |
78
80
  | `telaAgentId` | `string` | — | Use Tela agent mode for this embed |
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "4.2.0-rc.1",
4
+ "version": "4.2.0-rc.3",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -3,6 +3,7 @@ import { onClickOutside, useElementBounding, useWindowSize } from "@vueuse/core"
3
3
  import { useEmbedConfig } from "../../embed/composables/embed-config";
4
4
  import { useCitationPanel } from "../composables/citation-panel";
5
5
  import { useCitations } from "../composables/citations";
6
+ import { stripMarkdownMarkers } from "../label";
6
7
  const props = defineProps({
7
8
  citation: { type: Object, required: true },
8
9
  label: { type: String, required: false }
@@ -18,7 +19,10 @@ watchEffect(() => {
18
19
  });
19
20
  const status = computed(() => entry.value?.status.value ?? "loading");
20
21
  const meta = computed(() => entry.value?.meta.value ?? null);
21
- const displayLabel = computed(() => props.label?.trim() || meta.value?.identifier || meta.value?.title || "Documento");
22
+ const displayLabel = computed(() => {
23
+ const label = props.label ? stripMarkdownMarkers(props.label) : "";
24
+ return label || meta.value?.identifier?.trim() || meta.value?.title?.trim() || "Documento";
25
+ });
22
26
  const rootEl = ref(null);
23
27
  const popoverEl = ref(null);
24
28
  const popoverOpen = ref(false);
@@ -30,52 +34,64 @@ onClickOutside(rootEl, () => {
30
34
  const POPOVER_WIDTH = 300;
31
35
  const POPOVER_GAP = 6;
32
36
  const VIEWPORT_PADDING = 8;
33
- const triggerBounds = useElementBounding(rootEl);
34
- const popoverBounds = useElementBounding(popoverEl);
35
- const viewport = useWindowSize();
36
- const popoverStyle = computed(() => {
37
- const maxHeight = Math.max(
38
- 0,
39
- viewport.height.value - VIEWPORT_PADDING * 2
40
- );
41
- const maxLeft = Math.max(
42
- VIEWPORT_PADDING,
43
- viewport.width.value - POPOVER_WIDTH - VIEWPORT_PADDING
44
- );
45
- const left = Math.min(
46
- Math.max(triggerBounds.left.value, VIEWPORT_PADDING),
47
- maxLeft
48
- );
49
- const fitsAbove = triggerBounds.top.value >= popoverBounds.height.value + POPOVER_GAP + VIEWPORT_PADDING;
50
- const fitsBelow = viewport.height.value - triggerBounds.bottom.value >= popoverBounds.height.value + POPOVER_GAP + VIEWPORT_PADDING;
51
- const constrainedStyle = {
52
- left: `${left}px`,
53
- maxHeight: `${maxHeight}px`,
54
- overflowY: "auto"
55
- };
56
- if (popoverBounds.height.value === 0) {
57
- return {
58
- ...constrainedStyle,
59
- top: `${VIEWPORT_PADDING}px`,
60
- visibility: "hidden"
61
- };
62
- }
63
- if (fitsAbove) {
64
- return {
65
- ...constrainedStyle,
66
- bottom: `${viewport.height.value - triggerBounds.top.value + POPOVER_GAP}px`
67
- };
68
- }
69
- if (fitsBelow) {
70
- return {
71
- ...constrainedStyle,
72
- top: `${triggerBounds.bottom.value + POPOVER_GAP}px`
73
- };
74
- }
75
- return {
76
- ...constrainedStyle,
77
- top: `${VIEWPORT_PADDING}px`
78
- };
37
+ const popoverStyle = shallowRef({});
38
+ watch(popoverOpen, (open, _, onCleanup) => {
39
+ if (!open)
40
+ return;
41
+ const scope = effectScope();
42
+ scope.run(() => {
43
+ const triggerBounds = useElementBounding(rootEl);
44
+ const popoverBounds = useElementBounding(popoverEl);
45
+ const viewport = useWindowSize();
46
+ const style = computed(() => {
47
+ const maxHeight = Math.max(
48
+ 0,
49
+ viewport.height.value - VIEWPORT_PADDING * 2
50
+ );
51
+ const maxLeft = Math.max(
52
+ VIEWPORT_PADDING,
53
+ viewport.width.value - POPOVER_WIDTH - VIEWPORT_PADDING
54
+ );
55
+ const left = Math.min(
56
+ Math.max(triggerBounds.left.value, VIEWPORT_PADDING),
57
+ maxLeft
58
+ );
59
+ const fitsAbove = triggerBounds.top.value >= popoverBounds.height.value + POPOVER_GAP + VIEWPORT_PADDING;
60
+ const fitsBelow = viewport.height.value - triggerBounds.bottom.value >= popoverBounds.height.value + POPOVER_GAP + VIEWPORT_PADDING;
61
+ const constrainedStyle = {
62
+ left: `${left}px`,
63
+ maxHeight: `${maxHeight}px`,
64
+ overflowY: "auto"
65
+ };
66
+ if (popoverBounds.height.value === 0) {
67
+ return {
68
+ ...constrainedStyle,
69
+ top: `${VIEWPORT_PADDING}px`,
70
+ visibility: "hidden"
71
+ };
72
+ }
73
+ if (fitsAbove) {
74
+ return {
75
+ ...constrainedStyle,
76
+ bottom: `${viewport.height.value - triggerBounds.top.value + POPOVER_GAP}px`
77
+ };
78
+ }
79
+ if (fitsBelow) {
80
+ return {
81
+ ...constrainedStyle,
82
+ top: `${triggerBounds.bottom.value + POPOVER_GAP}px`
83
+ };
84
+ }
85
+ return {
86
+ ...constrainedStyle,
87
+ top: `${VIEWPORT_PADDING}px`
88
+ };
89
+ });
90
+ watchEffect(() => {
91
+ popoverStyle.value = style.value;
92
+ });
93
+ });
94
+ onCleanup(() => scope.stop());
79
95
  });
80
96
  function togglePopover() {
81
97
  if (status.value === "loading")
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A citation label is the visible text of `[label](cite://...)`, written by the
3
+ * model, which sometimes wraps it in inline markdown. The chip renders plain
4
+ * text, so the markers that wrap the whole label are removed; anything inside
5
+ * is kept, including markers that only appear in the middle.
6
+ */
7
+ export declare function stripMarkdownMarkers(label: string): string;
@@ -0,0 +1,13 @@
1
+ const WRAPPING_MARKER = /^(\*\*|__|\*|_|`+)([\s\S]+)\1$/;
2
+ export function stripMarkdownMarkers(label) {
3
+ let stripped = label.trim();
4
+ let match = stripped.match(WRAPPING_MARKER);
5
+ while (match && !separatesText(match[2], match[1])) {
6
+ stripped = match[2].trim();
7
+ match = stripped.match(WRAPPING_MARKER);
8
+ }
9
+ return stripped;
10
+ }
11
+ function separatesText(inner, marker) {
12
+ return inner.includes(marker) && !(inner.startsWith(marker) && inner.endsWith(marker));
13
+ }
@@ -20,7 +20,7 @@ const {
20
20
  confirmDelete,
21
21
  cancelDelete,
22
22
  handleConversationClick,
23
- handleConversationDoubleClick,
23
+ startRename,
24
24
  handleConversationDragStart,
25
25
  handleConversationDragEnd,
26
26
  cancelRename,
@@ -81,7 +81,7 @@ function setRenameInputRef(element) {
81
81
  class="group data-[active=true]:bg-muted data-[active=false]:hover:bg-subtle"
82
82
  :data-active="active"
83
83
  @click="handleConversationClick"
84
- @dblclick.prevent.stop="handleConversationDoubleClick"
84
+ @dblclick.prevent.stop="startRename"
85
85
  @dragstart="handleConversationDragStart"
86
86
  @dragend="handleConversationDragEnd"
87
87
  >
@@ -6,8 +6,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
6
6
  } & {
7
7
  create: (canvasTools: CanvasTool[]) => any;
8
8
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
9
- "onUpdate:modelValue"?: ((value: boolean | undefined) => any) | undefined;
10
9
  onCreate?: ((canvasTools: CanvasTool[]) => any) | undefined;
10
+ "onUpdate:modelValue"?: ((value: boolean | undefined) => any) | undefined;
11
11
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
12
  declare const _default: typeof __VLS_export;
13
13
  export default _default;
@@ -6,8 +6,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
6
6
  } & {
7
7
  create: (canvasTools: CanvasTool[]) => any;
8
8
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
9
- "onUpdate:modelValue"?: ((value: boolean | undefined) => any) | undefined;
10
9
  onCreate?: ((canvasTools: CanvasTool[]) => any) | undefined;
10
+ "onUpdate:modelValue"?: ((value: boolean | undefined) => any) | undefined;
11
11
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
12
  declare const _default: typeof __VLS_export;
13
13
  export default _default;
@@ -37,7 +37,6 @@ export declare function useConversationItem(options: ConversationItemOptions, em
37
37
  cancelDelete: () => void;
38
38
  handleConversationClick: (event: MouseEvent) => void;
39
39
  startRename: () => void;
40
- handleConversationDoubleClick: () => void;
41
40
  handleConversationDragStart: (event: DragEvent) => void;
42
41
  handleConversationDragEnd: () => void;
43
42
  cancelRename: () => void;
@@ -1,15 +1,21 @@
1
1
  import { resolveConversationRenameTitle } from "../title.js";
2
2
  import { capitalizeFirst } from "../../shared/string.js";
3
- const conversationNavigationDelayMs = 250;
4
3
  export function useConversationItem(options, emit) {
4
+ const route = useRoute();
5
+ const router = useRouter();
5
6
  const isRenamingInline = ref(false);
6
7
  const renameTitle = ref("");
7
8
  const renameInputRef = ref(null);
8
9
  const isIgnoringInitialRenameBlur = ref(false);
9
10
  const showDeleteModal = ref(false);
10
- const conversationPath = computed(() => `/${options.conversation.id}`);
11
+ const conversationPath = computed(() => {
12
+ if (!options.selectConversation)
13
+ return `/${options.conversation.id}`;
14
+ const query = { ...route.query, conversationId: options.conversation.id };
15
+ delete query.conversation;
16
+ return router.resolve({ path: route.path, query, hash: route.hash }).href;
17
+ });
11
18
  const displayTitle = computed(() => capitalizeFirst(options.conversation.title));
12
- let conversationNavigationTimer = null;
13
19
  let renameBlurTimer = null;
14
20
  const escapeCancellable = computed(() => isRenamingInline.value || showDeleteModal.value);
15
21
  function handleEscapeKeydown(event) {
@@ -28,12 +34,6 @@ export function useConversationItem(options, emit) {
28
34
  else
29
35
  window.removeEventListener("keydown", handleEscapeKeydown);
30
36
  });
31
- function clearPendingConversationNavigation() {
32
- if (conversationNavigationTimer === null)
33
- return;
34
- clearTimeout(conversationNavigationTimer);
35
- conversationNavigationTimer = null;
36
- }
37
37
  function clearRenameBlurTimer() {
38
38
  if (renameBlurTimer === null)
39
39
  return;
@@ -58,17 +58,13 @@ export function useConversationItem(options, emit) {
58
58
  if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
59
59
  return;
60
60
  event.preventDefault();
61
- clearPendingConversationNavigation();
62
61
  if (event.detail > 1)
63
62
  return;
64
- conversationNavigationTimer = setTimeout(() => {
65
- if (options.selectConversation) {
66
- void options.selectConversation(options.conversation.id);
67
- } else {
68
- navigateTo(conversationPath.value);
69
- }
70
- conversationNavigationTimer = null;
71
- }, conversationNavigationDelayMs);
63
+ if (options.selectConversation) {
64
+ void options.selectConversation(options.conversation.id);
65
+ } else {
66
+ navigateTo(conversationPath.value);
67
+ }
72
68
  }
73
69
  async function focusRenameInput() {
74
70
  await nextTick();
@@ -77,17 +73,12 @@ export function useConversationItem(options, emit) {
77
73
  allowRenameBlurSoon();
78
74
  }
79
75
  function startRename() {
80
- clearPendingConversationNavigation();
81
76
  clearRenameBlurTimer();
82
77
  renameTitle.value = options.conversation.title;
83
78
  isIgnoringInitialRenameBlur.value = true;
84
79
  isRenamingInline.value = true;
85
80
  void focusRenameInput();
86
81
  }
87
- function handleConversationDoubleClick() {
88
- clearPendingConversationNavigation();
89
- startRename();
90
- }
91
82
  function handleConversationDragStart(event) {
92
83
  if (!options.draggable)
93
84
  return;
@@ -146,7 +137,6 @@ export function useConversationItem(options, emit) {
146
137
  }
147
138
  ];
148
139
  onBeforeUnmount(() => {
149
- clearPendingConversationNavigation();
150
140
  clearRenameBlurTimer();
151
141
  if (typeof window !== "undefined")
152
142
  window.removeEventListener("keydown", handleEscapeKeydown);
@@ -163,7 +153,6 @@ export function useConversationItem(options, emit) {
163
153
  cancelDelete,
164
154
  handleConversationClick,
165
155
  startRename,
166
- handleConversationDoubleClick,
167
156
  handleConversationDragStart,
168
157
  handleConversationDragEnd,
169
158
  cancelRename,
@@ -9,11 +9,11 @@ type __VLS_Props = {
9
9
  showCredentialsTab?: boolean;
10
10
  };
11
11
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
12
- "update:open": (value: boolean) => any;
13
12
  save: (payload: ChatConfigurationSavePayload) => any;
13
+ "update:open": (value: boolean) => any;
14
14
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
- "onUpdate:open"?: ((value: boolean) => any) | undefined;
16
15
  onSave?: ((payload: ChatConfigurationSavePayload) => any) | undefined;
16
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
17
17
  }>, {
18
18
  initialTab: ChatConfigurationTab;
19
19
  saving: boolean;
@@ -9,11 +9,11 @@ type __VLS_Props = {
9
9
  showCredentialsTab?: boolean;
10
10
  };
11
11
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
12
- "update:open": (value: boolean) => any;
13
12
  save: (payload: ChatConfigurationSavePayload) => any;
13
+ "update:open": (value: boolean) => any;
14
14
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
- "onUpdate:open"?: ((value: boolean) => any) | undefined;
16
15
  onSave?: ((payload: ChatConfigurationSavePayload) => any) | undefined;
16
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
17
17
  }>, {
18
18
  initialTab: ChatConfigurationTab;
19
19
  saving: boolean;
@@ -32,10 +32,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
32
32
  "onUpdate:conversationTitle"?: ((value: string | null) => any) | undefined;
33
33
  "onUpdate:conversationCreatorFilter"?: ((value: ConversationCreatorFilter) => any) | undefined;
34
34
  }>, {
35
+ compact: boolean;
35
36
  hideSettings: boolean;
36
37
  hideSidebar: boolean;
37
38
  initialConversationId: string | null;
38
- compact: boolean;
39
39
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
40
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
41
41
  declare const _default: typeof __VLS_export;
@@ -217,8 +217,7 @@ watch(chatError, (message) => {
217
217
  if (message)
218
218
  statusToast.update({ text: message, icon: "i-ph-warning" });
219
219
  });
220
- const conversationIdRef = computed(() => currentConversation.value?.id ?? null);
221
- const { total, files: conversationFiles } = useConversationFiles(conversationIdRef);
220
+ const { total, files: conversationFiles } = useConversationFiles(activeConversationId);
222
221
  function persistConversationModel(conversation, value, wakeAgent) {
223
222
  const previousModel = conversation.model;
224
223
  setConversationModel(value);
@@ -347,7 +346,9 @@ async function ensureConversationLoaded(conversationId) {
347
346
  }
348
347
  loadingConversationId.value = conversationId;
349
348
  try {
350
- await loadConversation(conversationId);
349
+ const ownsChatState = await loadConversation(conversationId);
350
+ if (!ownsChatState)
351
+ return;
351
352
  if (currentConversation.value?.id !== conversationId) {
352
353
  agentWake.skip();
353
354
  statusToast.update({
@@ -32,10 +32,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
32
32
  "onUpdate:conversationTitle"?: ((value: string | null) => any) | undefined;
33
33
  "onUpdate:conversationCreatorFilter"?: ((value: ConversationCreatorFilter) => any) | undefined;
34
34
  }>, {
35
+ compact: boolean;
35
36
  hideSettings: boolean;
36
37
  hideSidebar: boolean;
37
38
  initialConversationId: string | null;
38
- compact: boolean;
39
39
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
40
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
41
41
  declare const _default: typeof __VLS_export;
@@ -17,6 +17,8 @@ const props = defineProps({
17
17
  conversationScope: { type: [String, null], required: false },
18
18
  defaultConversationCreatorFilter: { type: null, required: false },
19
19
  availableModels: { type: Array, required: false },
20
+ defaultModel: { type: null, required: false },
21
+ defaultReasoningEffort: { type: null, required: false },
20
22
  conversationCreatorFilter: { type: null, required: false },
21
23
  hideConversationCreatorFilter: { type: Boolean, required: false },
22
24
  hideSidebar: { type: Boolean, required: false },
@@ -60,6 +62,8 @@ provideEmbedConfig({
60
62
  conversationScope: normalizedConversationScope,
61
63
  defaultConversationCreatorFilter: computed(() => props.defaultConversationCreatorFilter),
62
64
  availableModels: computed(() => props.availableModels),
65
+ defaultModel: computed(() => props.defaultModel),
66
+ defaultReasoningEffort: computed(() => props.defaultReasoningEffort),
63
67
  conversationCreatorFilter: computed(() => props.conversationCreatorFilter),
64
68
  telaAgentInputs: computed(() => props.telaAgentInputs),
65
69
  workspaceSettings: computed(() => props.workspaceSettings),
@@ -16,10 +16,10 @@ type __VLS_Props = {
16
16
  };
17
17
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
18
18
  delete: () => any;
19
+ copyLink: () => any;
19
20
  rename: () => any;
20
21
  duplicate: () => any;
21
22
  export: () => any;
22
- copyLink: () => any;
23
23
  debug: () => any;
24
24
  tabChange: (tab: TopbarTab) => any;
25
25
  cancelEditing: () => any;
@@ -28,10 +28,10 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
28
28
  "update:isEditing": (value: boolean) => any;
29
29
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
30
30
  onDelete?: (() => any) | undefined;
31
+ onCopyLink?: (() => any) | undefined;
31
32
  onRename?: (() => any) | undefined;
32
33
  onDuplicate?: (() => any) | undefined;
33
34
  onExport?: (() => any) | undefined;
34
- onCopyLink?: (() => any) | undefined;
35
35
  onDebug?: (() => any) | undefined;
36
36
  onTabChange?: ((tab: TopbarTab) => any) | undefined;
37
37
  onCancelEditing?: (() => any) | undefined;
@@ -16,10 +16,10 @@ type __VLS_Props = {
16
16
  };
17
17
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
18
18
  delete: () => any;
19
+ copyLink: () => any;
19
20
  rename: () => any;
20
21
  duplicate: () => any;
21
22
  export: () => any;
22
- copyLink: () => any;
23
23
  debug: () => any;
24
24
  tabChange: (tab: TopbarTab) => any;
25
25
  cancelEditing: () => any;
@@ -28,10 +28,10 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
28
28
  "update:isEditing": (value: boolean) => any;
29
29
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
30
30
  onDelete?: (() => any) | undefined;
31
+ onCopyLink?: (() => any) | undefined;
31
32
  onRename?: (() => any) | undefined;
32
33
  onDuplicate?: (() => any) | undefined;
33
34
  onExport?: (() => any) | undefined;
34
- onCopyLink?: (() => any) | undefined;
35
35
  onDebug?: (() => any) | undefined;
36
36
  onTabChange?: ((tab: TopbarTab) => any) | undefined;
37
37
  onCancelEditing?: (() => any) | undefined;
@@ -10,6 +10,8 @@ const { hideSidebar = void 0, ...props } = defineProps({
10
10
  conversationScope: { type: [String, null], required: false },
11
11
  defaultConversationCreatorFilter: { type: null, required: false },
12
12
  availableModels: { type: Array, required: false },
13
+ defaultModel: { type: null, required: false },
14
+ defaultReasoningEffort: { type: null, required: false },
13
15
  conversationCreatorFilter: { type: null, required: false },
14
16
  hideConversationCreatorFilter: { type: Boolean, required: false },
15
17
  hideSidebar: { type: Boolean, required: false },
@@ -71,6 +73,8 @@ const sharedEmbedProps = computed(() => ({
71
73
  conversationScope: props.conversationScope,
72
74
  defaultConversationCreatorFilter: props.defaultConversationCreatorFilter,
73
75
  availableModels: props.availableModels,
76
+ defaultModel: props.defaultModel,
77
+ defaultReasoningEffort: props.defaultReasoningEffort,
74
78
  conversationCreatorFilter: props.conversationCreatorFilter,
75
79
  hideConversationCreatorFilter: props.hideConversationCreatorFilter,
76
80
  hideSettings: hideSettings.value,
@@ -15,22 +15,22 @@ type __VLS_Props = {
15
15
  };
16
16
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
17
17
  delete: () => any;
18
+ openDrawer: () => any;
19
+ copyLink: () => any;
20
+ openOutdatedSettings: () => any;
18
21
  rename: () => any;
19
22
  duplicate: () => any;
20
23
  export: () => any;
21
- copyLink: () => any;
22
- openDrawer: () => any;
23
- openOutdatedSettings: () => any;
24
24
  debug: () => any;
25
25
  tabChange: (tab: TopbarTab) => any;
26
26
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
27
27
  onDelete?: (() => any) | undefined;
28
+ onOpenDrawer?: (() => any) | undefined;
29
+ onCopyLink?: (() => any) | undefined;
30
+ onOpenOutdatedSettings?: (() => any) | undefined;
28
31
  onRename?: (() => any) | undefined;
29
32
  onDuplicate?: (() => any) | undefined;
30
33
  onExport?: (() => any) | undefined;
31
- onCopyLink?: (() => any) | undefined;
32
- onOpenDrawer?: (() => any) | undefined;
33
- onOpenOutdatedSettings?: (() => any) | undefined;
34
34
  onDebug?: (() => any) | undefined;
35
35
  onTabChange?: ((tab: TopbarTab) => any) | undefined;
36
36
  }>, {
@@ -15,22 +15,22 @@ type __VLS_Props = {
15
15
  };
16
16
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
17
17
  delete: () => any;
18
+ openDrawer: () => any;
19
+ copyLink: () => any;
20
+ openOutdatedSettings: () => any;
18
21
  rename: () => any;
19
22
  duplicate: () => any;
20
23
  export: () => any;
21
- copyLink: () => any;
22
- openDrawer: () => any;
23
- openOutdatedSettings: () => any;
24
24
  debug: () => any;
25
25
  tabChange: (tab: TopbarTab) => any;
26
26
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
27
27
  onDelete?: (() => any) | undefined;
28
+ onOpenDrawer?: (() => any) | undefined;
29
+ onCopyLink?: (() => any) | undefined;
30
+ onOpenOutdatedSettings?: (() => any) | undefined;
28
31
  onRename?: (() => any) | undefined;
29
32
  onDuplicate?: (() => any) | undefined;
30
33
  onExport?: (() => any) | undefined;
31
- onCopyLink?: (() => any) | undefined;
32
- onOpenDrawer?: (() => any) | undefined;
33
- onOpenOutdatedSettings?: (() => any) | undefined;
34
34
  onDebug?: (() => any) | undefined;
35
35
  onTabChange?: ((tab: TopbarTab) => any) | undefined;
36
36
  }>, {
@@ -61,13 +61,12 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
61
61
  } & {
62
62
  delete: () => any;
63
63
  cancel: () => any;
64
+ settings: () => any;
65
+ send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
66
+ copyLink: () => any;
64
67
  rename: () => any;
65
68
  duplicate: () => any;
66
69
  export: () => any;
67
- copyLink: () => any;
68
- selectConversation: (id: string) => any;
69
- settings: () => any;
70
- send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
71
70
  debug: () => any;
72
71
  tabChange: (tab: TopbarTab) => any;
73
72
  newConversation: (groupId?: string | null | undefined) => any;
@@ -76,21 +75,21 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
76
75
  cancelEditing: () => any;
77
76
  dismissOutdatedBanner: () => any;
78
77
  saveTitle: () => any;
78
+ selectConversation: (id: string) => any;
79
79
  "update:drawerOpen": (value: boolean) => any;
80
80
  "update:editTitle": (value: string) => any;
81
81
  "update:workspaceSheetOpen": (value: boolean) => any;
82
82
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
83
83
  onDelete?: (() => any) | undefined;
84
84
  onCancel?: (() => any) | undefined;
85
- onRename?: (() => any) | undefined;
86
- onDuplicate?: (() => any) | undefined;
87
- onExport?: (() => any) | undefined;
88
- onCopyLink?: (() => any) | undefined;
89
- onSelectConversation?: ((id: string) => any) | undefined;
90
85
  onSettings?: (() => any) | undefined;
91
86
  onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
92
87
  "onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
93
88
  "onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
89
+ onCopyLink?: (() => any) | undefined;
90
+ onRename?: (() => any) | undefined;
91
+ onDuplicate?: (() => any) | undefined;
92
+ onExport?: (() => any) | undefined;
94
93
  onDebug?: (() => any) | undefined;
95
94
  onTabChange?: ((tab: TopbarTab) => any) | undefined;
96
95
  onNewConversation?: ((groupId?: string | null | undefined) => any) | undefined;
@@ -99,6 +98,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
99
98
  onCancelEditing?: (() => any) | undefined;
100
99
  onDismissOutdatedBanner?: (() => any) | undefined;
101
100
  onSaveTitle?: (() => any) | undefined;
101
+ onSelectConversation?: ((id: string) => any) | undefined;
102
102
  "onUpdate:drawerOpen"?: ((value: boolean) => any) | undefined;
103
103
  "onUpdate:editTitle"?: ((value: string) => any) | undefined;
104
104
  "onUpdate:workspaceSheetOpen"?: ((value: boolean) => any) | undefined;
@@ -61,13 +61,12 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
61
61
  } & {
62
62
  delete: () => any;
63
63
  cancel: () => any;
64
+ settings: () => any;
65
+ send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
66
+ copyLink: () => any;
64
67
  rename: () => any;
65
68
  duplicate: () => any;
66
69
  export: () => any;
67
- copyLink: () => any;
68
- selectConversation: (id: string) => any;
69
- settings: () => any;
70
- send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
71
70
  debug: () => any;
72
71
  tabChange: (tab: TopbarTab) => any;
73
72
  newConversation: (groupId?: string | null | undefined) => any;
@@ -76,21 +75,21 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
76
75
  cancelEditing: () => any;
77
76
  dismissOutdatedBanner: () => any;
78
77
  saveTitle: () => any;
78
+ selectConversation: (id: string) => any;
79
79
  "update:drawerOpen": (value: boolean) => any;
80
80
  "update:editTitle": (value: string) => any;
81
81
  "update:workspaceSheetOpen": (value: boolean) => any;
82
82
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
83
83
  onDelete?: (() => any) | undefined;
84
84
  onCancel?: (() => any) | undefined;
85
- onRename?: (() => any) | undefined;
86
- onDuplicate?: (() => any) | undefined;
87
- onExport?: (() => any) | undefined;
88
- onCopyLink?: (() => any) | undefined;
89
- onSelectConversation?: ((id: string) => any) | undefined;
90
85
  onSettings?: (() => any) | undefined;
91
86
  onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
92
87
  "onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
93
88
  "onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
89
+ onCopyLink?: (() => any) | undefined;
90
+ onRename?: (() => any) | undefined;
91
+ onDuplicate?: (() => any) | undefined;
92
+ onExport?: (() => any) | undefined;
94
93
  onDebug?: (() => any) | undefined;
95
94
  onTabChange?: ((tab: TopbarTab) => any) | undefined;
96
95
  onNewConversation?: ((groupId?: string | null | undefined) => any) | undefined;
@@ -99,6 +98,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
99
98
  onCancelEditing?: (() => any) | undefined;
100
99
  onDismissOutdatedBanner?: (() => any) | undefined;
101
100
  onSaveTitle?: (() => any) | undefined;
101
+ onSelectConversation?: ((id: string) => any) | undefined;
102
102
  "onUpdate:drawerOpen"?: ((value: boolean) => any) | undefined;
103
103
  "onUpdate:editTitle"?: ((value: string) => any) | undefined;
104
104
  "onUpdate:workspaceSheetOpen"?: ((value: boolean) => any) | undefined;
@@ -1,3 +1,4 @@
1
+ import type { ChatModel, ChatReasoningEffort } from '../../types/schemas/chat/models.js';
1
2
  import type { Component, DeepReadonly, MaybeRefOrGetter, Ref } from 'vue';
2
3
  import type { ConversationCreatorFilter } from '../../types/schemas/chat/primitives.js';
3
4
  import type { WorkspaceSettingsResponse as WorkspaceSettings } from '../../types/schemas/chat/workspace-settings.js';
@@ -15,6 +16,8 @@ export type EmbedConfig = {
15
16
  conversationScope: Ref<string | null>;
16
17
  defaultConversationCreatorFilter: Ref<ConversationCreatorFilter>;
17
18
  availableModels: Ref<AvailableChatModels | undefined>;
19
+ defaultModel: Ref<ChatModel | undefined>;
20
+ defaultReasoningEffort: Ref<ChatReasoningEffort | undefined>;
18
21
  conversationCreatorFilter: Ref<ConversationCreatorFilter | undefined>;
19
22
  telaAgentInputs: Ref<ConfiguredTelaAgentInputs>;
20
23
  workspaceSettings: Ref<DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null | undefined>;
@@ -36,6 +39,8 @@ type EmbedConfigInput = {
36
39
  conversationScope?: MaybeRefOrGetter<string | null | undefined>;
37
40
  defaultConversationCreatorFilter?: MaybeRefOrGetter<ConversationCreatorFilter | null | undefined>;
38
41
  availableModels?: MaybeRefOrGetter<AvailableChatModels | undefined>;
42
+ defaultModel?: MaybeRefOrGetter<ChatModel | undefined>;
43
+ defaultReasoningEffort?: MaybeRefOrGetter<ChatReasoningEffort | undefined>;
39
44
  conversationCreatorFilter?: MaybeRefOrGetter<ConversationCreatorFilter | undefined>;
40
45
  telaAgentInputs?: MaybeRefOrGetter<ConfiguredTelaAgentInputs>;
41
46
  workspaceSettings?: MaybeRefOrGetter<DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null | undefined>;
@@ -23,6 +23,8 @@ function useGlobalEmbedConfigState() {
23
23
  conversationScope: useState("chat-embed-config-conversation-scope", () => null),
24
24
  defaultConversationCreatorFilter: useState("chat-embed-config-default-conversation-creator-filter", () => void 0),
25
25
  availableModels: useState("chat-embed-config-available-models", () => void 0),
26
+ defaultModel: useState("chat-embed-config-default-model", () => void 0),
27
+ defaultReasoningEffort: useState("chat-embed-config-default-reasoning-effort", () => void 0),
26
28
  conversationCreatorFilter: useState("chat-embed-config-conversation-creator-filter", () => void 0),
27
29
  telaAgentInputs: useState("chat-embed-config-tela-agent-inputs", () => void 0),
28
30
  workspaceSettings: useState("chat-embed-config-workspace-settings", () => void 0),
@@ -46,6 +48,8 @@ export function provideEmbedConfig(input, options = {}) {
46
48
  conversationScope: computed(() => normalizeConversationScope(toValue(input.conversationScope))),
47
49
  defaultConversationCreatorFilter: computed(() => toValue(input.defaultConversationCreatorFilter) ?? "all"),
48
50
  availableModels: computed(() => toValue(input.availableModels)),
51
+ defaultModel: computed(() => toValue(input.defaultModel)),
52
+ defaultReasoningEffort: computed(() => toValue(input.defaultReasoningEffort)),
49
53
  conversationCreatorFilter: computed(() => toValue(input.conversationCreatorFilter)),
50
54
  telaAgentInputs: computed(() => toValue(input.telaAgentInputs)),
51
55
  workspaceSettings: computed(() => toValue(input.workspaceSettings)),
@@ -70,6 +74,8 @@ export function provideEmbedConfig(input, options = {}) {
70
74
  globalConfig.conversationScope.value = config.conversationScope.value;
71
75
  globalConfig.defaultConversationCreatorFilter.value = config.defaultConversationCreatorFilter.value;
72
76
  globalConfig.availableModels.value = config.availableModels.value;
77
+ globalConfig.defaultModel.value = config.defaultModel.value;
78
+ globalConfig.defaultReasoningEffort.value = config.defaultReasoningEffort.value;
73
79
  globalConfig.conversationCreatorFilter.value = config.conversationCreatorFilter.value;
74
80
  globalConfig.telaAgentInputs.value = config.telaAgentInputs.value;
75
81
  globalConfig.workspaceSettings.value = config.workspaceSettings.value;
@@ -101,6 +107,8 @@ export function useEmbedConfig() {
101
107
  conversationScope: computed(() => globalConfig.conversationScope.value),
102
108
  defaultConversationCreatorFilter: computed(() => globalConfig.defaultConversationCreatorFilter.value ?? "all"),
103
109
  availableModels: computed(() => globalConfig.availableModels.value),
110
+ defaultModel: computed(() => globalConfig.defaultModel.value),
111
+ defaultReasoningEffort: computed(() => globalConfig.defaultReasoningEffort.value),
104
112
  conversationCreatorFilter: computed(() => globalConfig.conversationCreatorFilter.value),
105
113
  telaAgentInputs: computed(() => globalConfig.telaAgentInputs.value),
106
114
  workspaceSettings: computed(() => globalConfig.workspaceSettings.value),
@@ -1,3 +1,4 @@
1
+ import type { ChatModel, ChatReasoningEffort } from '../types/schemas/chat/models.js';
1
2
  import type { Component, DeepReadonly } from 'vue';
2
3
  import type { ConversationEvent } from '../types/schemas/chat/conversations.js';
3
4
  import type { WorkspaceSettingsResponse as WorkspaceSettings } from '../types/schemas/chat/workspace-settings.js';
@@ -76,6 +77,16 @@ export type ChatEmbedSharedProps = {
76
77
  * used when a new or restored selection is not allowed by this list.
77
78
  */
78
79
  availableModels?: AvailableChatModels;
80
+ /**
81
+ * Initial model for untouched drafts. Existing conversations and explicit selections take precedence.
82
+ * When excluded by availableModels, the first allowed model is used.
83
+ */
84
+ defaultModel?: ChatModel;
85
+ /**
86
+ * Initial effort for untouched drafts. Omission preserves automatic reasoning;
87
+ * an explicit user selection (including null) takes precedence.
88
+ */
89
+ defaultReasoningEffort?: ChatReasoningEffort;
79
90
  /** Controlled creator filter. Use with `v-model:conversation-creator-filter` when the host changes it at runtime. */
80
91
  conversationCreatorFilter?: ConversationCreatorFilter;
81
92
  /** Hide the conversation creator filter while preserving its active value. */
@@ -196,7 +196,7 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
196
196
  :completed="!isPending"
197
197
  :preparing-steps="preparingSteps"
198
198
  :started-at="message.generationStartedAt ?? message.createdAt"
199
- :updated-at="message.updatedAt"
199
+ :ended-at="message.generationEndedAt"
200
200
  />
201
201
 
202
202
  <div
@@ -3,7 +3,7 @@ type __VLS_Props = {
3
3
  reasoningData: ReasoningStep[];
4
4
  completed: boolean;
5
5
  startedAt: Date | string;
6
- updatedAt?: Date | string | null;
6
+ endedAt?: Date | string | null;
7
7
  preparingSteps?: readonly string[] | null;
8
8
  };
9
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>;
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import { resolveDisplayedReasoning } from "../../reasoning";
3
- import { useReasoningTimer } from "../../composables/reasoning-timer";
3
+ import { formatThinkingDuration, useReasoningTimer } from "../../composables/reasoning-timer";
4
4
  import { resolveToolTitle } from "../../tool-registry";
5
5
  import ChatReasoningCollapsibleContent from "./chat-reasoning-collapsible-content.vue";
6
6
  import ChatReasoningSteps from "./chat-reasoning-steps.vue";
@@ -8,14 +8,14 @@ const props = defineProps({
8
8
  reasoningData: { type: Array, required: true },
9
9
  completed: { type: Boolean, required: true },
10
10
  startedAt: { type: [Date, String], required: true },
11
- updatedAt: { type: [Date, String, null], required: false },
11
+ endedAt: { type: [Date, String, null], required: false },
12
12
  preparingSteps: { type: [Array, null], required: false }
13
13
  });
14
14
  const TEXT_TRUNCATE_LENGTH = 40;
15
15
  const isExpanded = ref(false);
16
16
  const { elapsedTime, thinkingDuration } = useReasoningTimer({
17
17
  startedAt: computed(() => props.startedAt),
18
- updatedAt: computed(() => props.updatedAt),
18
+ endedAt: computed(() => props.endedAt),
19
19
  completed: computed(() => props.completed)
20
20
  });
21
21
  const displayedReasoning = computed(
@@ -40,18 +40,7 @@ const currentStepName = computed(() => {
40
40
  }
41
41
  return "Trabalhando";
42
42
  });
43
- const formattedDuration = computed(() => {
44
- if (!thinkingDuration.value)
45
- return null;
46
- const seconds = Math.round(thinkingDuration.value / 1e3);
47
- if (seconds < 60)
48
- return `${seconds}s`;
49
- const minutes = Math.floor(seconds / 60);
50
- const remainingSeconds = seconds % 60;
51
- if (remainingSeconds === 0)
52
- return `${minutes}m`;
53
- return `${minutes}m ${remainingSeconds}s`;
54
- });
43
+ const formattedDuration = computed(() => formatThinkingDuration(thinkingDuration.value));
55
44
  const liveFormattedDuration = computed(() => {
56
45
  const seconds = Math.round(elapsedTime.value / 1e3);
57
46
  return seconds > 0 ? `${seconds}s` : "";
@@ -101,7 +90,8 @@ const liveFormattedDuration = computed(() => {
101
90
  group-hover:text-primary
102
91
  transition-colors
103
92
  >
104
- Trabalhou por {{ formattedDuration || "alguns momentos" }}
93
+ <template v-if="formattedDuration">Trabalhou por {{ formattedDuration }}</template>
94
+ <template v-else>Trabalhou</template>
105
95
  </span>
106
96
  <div
107
97
  v-else
@@ -3,7 +3,7 @@ type __VLS_Props = {
3
3
  reasoningData: ReasoningStep[];
4
4
  completed: boolean;
5
5
  startedAt: Date | string;
6
- updatedAt?: Date | string | null;
6
+ endedAt?: Date | string | null;
7
7
  preparingSteps?: readonly string[] | null;
8
8
  };
9
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>;
@@ -30,7 +30,7 @@ export declare function useChat(options?: {
30
30
  cancelling: any;
31
31
  generatingTitle: any;
32
32
  animatedTitle: any;
33
- loadConversation: (id: string) => Promise<void>;
33
+ loadConversation: (id: string) => Promise<boolean>;
34
34
  setConversation: (conv: Conversation | ConversationWithMessages) => void;
35
35
  adoptConversation: (conv: Conversation) => void;
36
36
  setConversationModel: (model: string | null) => void;
@@ -377,7 +377,7 @@ export function useChat(options = {}) {
377
377
  const pendingModelSelection = computed({
378
378
  get: () => {
379
379
  const buckets = pendingModelBuckets.value;
380
- return chatScope.value in buckets ? buckets[chatScope.value] ?? null : defaultChatModel;
380
+ return chatScope.value in buckets ? buckets[chatScope.value] ?? null : embedConfig?.defaultModel?.value ?? defaultChatModel;
381
381
  },
382
382
  set: (value) => {
383
383
  pendingModelBuckets.value = {
@@ -394,7 +394,7 @@ export function useChat(options = {}) {
394
394
  };
395
395
  const pendingReasoningEffortBuckets = useState("chat-pending-reasoning-effort-selection", () => ({}));
396
396
  const pendingReasoningEffortSelection = computed({
397
- get: () => pendingReasoningEffortBuckets.value[chatScope.value] ?? null,
397
+ get: () => chatScope.value in pendingReasoningEffortBuckets.value ? pendingReasoningEffortBuckets.value[chatScope.value] ?? null : embedConfig?.defaultReasoningEffort?.value ?? null,
398
398
  set: (value) => {
399
399
  pendingReasoningEffortBuckets.value = {
400
400
  ...pendingReasoningEffortBuckets.value,
@@ -427,11 +427,13 @@ export function useChat(options = {}) {
427
427
  agentSessionRewakeable.value = false;
428
428
  error.value = null;
429
429
  };
430
+ let loadGeneration = 0;
430
431
  const loadConversation = async (id) => {
431
432
  if (chatApi.conversationV2Enabled) {
432
433
  stopPolling();
433
434
  directStream.disconnect();
434
435
  }
436
+ const generation = ++loadGeneration;
435
437
  runtimeBlocked.value = false;
436
438
  loading.value = true;
437
439
  error.value = null;
@@ -440,6 +442,8 @@ export function useChat(options = {}) {
440
442
  chatApi.path(`/conversations/${id}`),
441
443
  chatApi.withChatHeaders()
442
444
  );
445
+ if (generation !== loadGeneration)
446
+ return false;
443
447
  currentConversation.value = data;
444
448
  messages.value = data.messages;
445
449
  pendingStreamReconciliationIds.value = /* @__PURE__ */ new Set();
@@ -457,14 +461,19 @@ export function useChat(options = {}) {
457
461
  streamingMessageId.value = null;
458
462
  directStream.disconnect();
459
463
  }
464
+ return true;
460
465
  } catch (err) {
466
+ Sentry.captureException(err);
467
+ if (generation !== loadGeneration)
468
+ return false;
461
469
  currentConversation.value = null;
462
470
  messages.value = [];
463
471
  const message = err instanceof Error ? err.message : "Erro ao carregar conversa";
464
472
  error.value = message;
465
- Sentry.captureException(err);
473
+ return true;
466
474
  } finally {
467
- loading.value = false;
475
+ if (generation === loadGeneration)
476
+ loading.value = false;
468
477
  }
469
478
  };
470
479
  const sendMessage = async (content, files, agentInputs, prepare, queuedSend) => {
@@ -1,7 +1,8 @@
1
1
  import { type Ref } from 'vue';
2
+ export declare function formatThinkingDuration(durationMs: number | null): string | null;
2
3
  export declare function useReasoningTimer(options: {
3
4
  startedAt: Readonly<Ref<Date | string>>;
4
- updatedAt: Readonly<Ref<Date | string | null | undefined>>;
5
+ endedAt: Readonly<Ref<Date | string | null | undefined>>;
5
6
  completed: Readonly<Ref<boolean>>;
6
7
  }): {
7
8
  elapsedTime: Ref<number, number>;
@@ -1,11 +1,29 @@
1
1
  import { computed, ref, watch } from "vue";
2
+ const MINUTE_SECONDS = 60;
3
+ const HOUR_SECONDS = 60 * MINUTE_SECONDS;
4
+ const DAY_SECONDS = 24 * HOUR_SECONDS;
5
+ function withRemainder(whole, wholeUnit, remainder, remainderUnit) {
6
+ return remainder === 0 ? `${whole}${wholeUnit}` : `${whole}${wholeUnit} ${remainder}${remainderUnit}`;
7
+ }
8
+ export function formatThinkingDuration(durationMs) {
9
+ if (!durationMs)
10
+ return null;
11
+ const seconds = Math.round(durationMs / 1e3);
12
+ if (seconds < MINUTE_SECONDS)
13
+ return `${seconds}s`;
14
+ if (seconds < HOUR_SECONDS)
15
+ return withRemainder(Math.floor(seconds / MINUTE_SECONDS), "m", seconds % MINUTE_SECONDS, "s");
16
+ if (seconds < DAY_SECONDS)
17
+ return withRemainder(Math.floor(seconds / HOUR_SECONDS), "h", Math.floor(seconds % HOUR_SECONDS / MINUTE_SECONDS), "m");
18
+ return withRemainder(Math.floor(seconds / DAY_SECONDS), "d", Math.floor(seconds % DAY_SECONDS / HOUR_SECONDS), "h");
19
+ }
2
20
  export function useReasoningTimer(options) {
3
21
  const elapsedTime = ref(0);
4
22
  const startTime = computed(() => new Date(options.startedAt.value).getTime());
5
23
  const thinkingDuration = computed(() => {
6
- if (!options.updatedAt.value)
24
+ if (!options.endedAt.value)
7
25
  return null;
8
- const duration = new Date(options.updatedAt.value).getTime() - startTime.value;
26
+ const duration = new Date(options.endedAt.value).getTime() - startTime.value;
9
27
  return Number.isFinite(duration) && duration >= 0 ? duration : null;
10
28
  });
11
29
  watch([startTime, options.completed], ([start, completed], _previous, onCleanup) => {
@@ -89,6 +89,7 @@ function message(baseTime, offsetMs, input) {
89
89
  externalUuid: null,
90
90
  ttft: null,
91
91
  generationStartedAt: null,
92
+ generationEndedAt: input.updatedAt ?? null,
92
93
  createdBy: input.role === "user" ? "debugger@tela.com" : null,
93
94
  createdAt,
94
95
  updatedAt: input.updatedAt ?? createdAt,
@@ -39,6 +39,7 @@ export function beginAssistantMessageRetry(messages, messageId) {
39
39
  failReason: null,
40
40
  reasoningData: null,
41
41
  generationStartedAt: /* @__PURE__ */ new Date(),
42
+ generationEndedAt: null,
42
43
  updatedAt: null
43
44
  };
44
45
  })
@@ -115,6 +115,7 @@ export declare const sendMessageV2ResponseSchema: z.ZodObject<{
115
115
  createdAt: z.ZodDate;
116
116
  updatedAt: z.ZodNullable<z.ZodDate>;
117
117
  generationStartedAt: z.ZodNullable<z.ZodDate>;
118
+ generationEndedAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
118
119
  }, z.core.$strip>;
119
120
  assistantMessage: z.ZodObject<{
120
121
  id: z.ZodString;
@@ -148,6 +149,7 @@ export declare const sendMessageV2ResponseSchema: z.ZodObject<{
148
149
  createdAt: z.ZodDate;
149
150
  updatedAt: z.ZodNullable<z.ZodDate>;
150
151
  generationStartedAt: z.ZodNullable<z.ZodDate>;
152
+ generationEndedAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
151
153
  }, z.core.$strip>;
152
154
  turn: z.ZodNullable<z.ZodObject<{
153
155
  id: z.ZodString;
@@ -228,6 +230,8 @@ export declare const conversationRuntimeWebhookPayloadSchema: z.ZodObject<{
228
230
  "session.waiting_messages": "session.waiting_messages";
229
231
  }>;
230
232
  sessionId: z.ZodString;
233
+ runId: z.ZodOptional<z.ZodString>;
234
+ timestamp: z.ZodOptional<z.ZodString>;
231
235
  status: z.ZodOptional<z.ZodString>;
232
236
  error: z.ZodOptional<z.ZodNullable<z.ZodString>>;
233
237
  failReason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -58,6 +58,8 @@ export const conversationRuntimeWebhookPayloadSchema = z.object({
58
58
  'session.waiting_messages',
59
59
  ]),
60
60
  sessionId: z.string().min(1),
61
+ runId: z.string().optional(),
62
+ timestamp: z.string().optional(),
61
63
  status: z.string().optional(),
62
64
  error: z.string().nullable().optional(),
63
65
  failReason: z.string().nullable().optional(),
@@ -111,6 +111,7 @@ export declare const messageSchema: z.ZodObject<{
111
111
  createdAt: z.ZodDate;
112
112
  updatedAt: z.ZodNullable<z.ZodDate>;
113
113
  generationStartedAt: z.ZodNullable<z.ZodDate>;
114
+ generationEndedAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
114
115
  }, z.core.$strip>;
115
116
  export type Message = z.infer<typeof messageSchema>;
116
117
  export declare const conversationCreatorFilterCountsSchema: z.ZodRecord<z.ZodEnum<{
@@ -224,6 +225,7 @@ export declare const conversationWithMessagesSchema: z.ZodObject<{
224
225
  createdAt: z.ZodDate;
225
226
  updatedAt: z.ZodNullable<z.ZodDate>;
226
227
  generationStartedAt: z.ZodNullable<z.ZodDate>;
228
+ generationEndedAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
227
229
  }, z.core.$strip>>;
228
230
  }, z.core.$strip>;
229
231
  export type ConversationWithMessages = z.infer<typeof conversationWithMessagesSchema>;
@@ -50,6 +50,7 @@ export const messageSchema = z.object({
50
50
  createdAt: z.date(),
51
51
  updatedAt: z.date().nullable(),
52
52
  generationStartedAt: z.date().nullable(),
53
+ generationEndedAt: z.date().nullable().optional(),
53
54
  });
54
55
  export const conversationCreatorFilterCountsSchema = z.record(conversationCreatorFilterSchema, z.number());
55
56
  export const conversationListMetadataSchema = z.object({
@@ -120,6 +120,7 @@ export declare const sendMessageResponseSchema: z.ZodObject<{
120
120
  createdAt: z.ZodDate;
121
121
  updatedAt: z.ZodNullable<z.ZodDate>;
122
122
  generationStartedAt: z.ZodNullable<z.ZodDate>;
123
+ generationEndedAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
123
124
  }, z.core.$strip>;
124
125
  assistantMessage: z.ZodObject<{
125
126
  id: z.ZodString;
@@ -153,6 +154,7 @@ export declare const sendMessageResponseSchema: z.ZodObject<{
153
154
  createdAt: z.ZodDate;
154
155
  updatedAt: z.ZodNullable<z.ZodDate>;
155
156
  generationStartedAt: z.ZodNullable<z.ZodDate>;
157
+ generationEndedAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
156
158
  }, z.core.$strip>;
157
159
  }, z.core.$strip>;
158
160
  export type SendMessageResponse = z.infer<typeof sendMessageResponseSchema>;
@@ -189,6 +191,7 @@ export declare const messagePollResponseSchema: z.ZodObject<{
189
191
  createdAt: z.ZodDate;
190
192
  updatedAt: z.ZodNullable<z.ZodDate>;
191
193
  generationStartedAt: z.ZodNullable<z.ZodDate>;
194
+ generationEndedAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
192
195
  }, z.core.$strip>>;
193
196
  newFiles: z.ZodOptional<z.ZodArray<z.ZodObject<{
194
197
  url: z.ZodString;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "4.2.0-rc.1",
3
+ "version": "4.2.0-rc.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {