@meistrari/chat-nuxt 3.17.0-rc.8 → 4.0.0-rc.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 (34) hide show
  1. package/README.md +6 -2
  2. package/dist/module.d.mts +1 -1
  3. package/dist/module.json +1 -1
  4. package/dist/runtime/assets/css/markstream.css +1 -1
  5. package/dist/runtime/conversations/components/chat-conversation-search.d.vue.ts +2 -3
  6. package/dist/runtime/conversations/components/chat-conversation-search.vue +9 -2
  7. package/dist/runtime/conversations/components/chat-conversation-search.vue.d.ts +2 -3
  8. package/dist/runtime/embed/components/chat-embed-inner.vue +9 -2
  9. package/dist/runtime/embed/components/chat-embed.vue +2 -0
  10. package/dist/runtime/embed/components/meistrari-chat-embed.vue +4 -2
  11. package/dist/runtime/embed/composables/embed-config.d.ts +3 -0
  12. package/dist/runtime/embed/composables/embed-config.js +4 -0
  13. package/dist/runtime/embed/types.d.ts +8 -0
  14. package/dist/runtime/files/content-detection.d.ts +2 -0
  15. package/dist/runtime/files/content-detection.js +27 -20
  16. package/dist/runtime/files/mime.d.ts +0 -1
  17. package/dist/runtime/files/mime.js +0 -4
  18. package/dist/runtime/files/vault-links.js +1 -1
  19. package/dist/runtime/messages/components/chat-message-bubble.vue +1 -1
  20. package/dist/runtime/messages/components/chat-message-input.vue +16 -2
  21. package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue +2 -1
  22. package/dist/runtime/messages/composables/chat.js +34 -3
  23. package/dist/runtime/types/composer.d.ts +22 -0
  24. package/dist/runtime/types/composer.js +1 -0
  25. package/dist/runtime/types/embed.d.ts +1 -1
  26. package/dist/runtime/types/schemas/chat/conversations.d.ts +6 -0
  27. package/dist/runtime/types/schemas/chat/models.d.ts +4 -1
  28. package/dist/runtime/types/schemas/chat/models.js +3 -0
  29. package/dist/runtime/types/schemas/chat/ui.d.ts +30 -0
  30. package/dist/runtime/types/schemas/chat/ui.js +4 -0
  31. package/dist/runtime/types/schemas/integrations/tela-agent-api.d.ts +3 -0
  32. package/dist/runtime/usage/formatting.js +4 -0
  33. package/dist/types.d.mts +1 -1
  34. package/package.json +2 -3
package/README.md CHANGED
@@ -28,7 +28,9 @@ Runtime internals under `src/runtime/**`, generated Nuxt aliases, and workspace-
28
28
  | [Citations](docs/guides/citations.md) | `cite://` chips, metadata popover, document panel |
29
29
  | [Feature flags](docs/guides/feature-flags.md) | Usage tab, debug entry, cancel button, PostHog wiring |
30
30
  | [Message feedback](docs/guides/message-feedback.md) | 👍/👎 controls and host-owned persistence |
31
+ | [Composer extension](docs/guides/composer-extension.md) | Host actions, shared attachment strip, outbound content and retry lifecycle |
31
32
  | [Workspace settings](docs/guides/workspace-settings.md) | Settings overrides, agent runtime ownership, `<ChatConfigurationModal>` |
33
+ | [Upgrading to 4.0](docs/guides/upgrading-to-4.md) | Required Chat API version for v2 sends, upgrade order, vue-i18n removal |
32
34
 
33
35
  ## API Reference
34
36
 
@@ -66,6 +68,7 @@ Declared in `src/runtime/embed/types.ts`; prop, feature-flag, and event payload
66
68
  | `features` | `Partial<ChatFeatureConfig>` | `{}` | Toggle `showUsageTab`, `showDebugOption`, `showCancelButton`, `showTtft`, and `chatOutboundQueue`; all released features default to `true` |
67
69
  | `loadingMessages` | `readonly string[] \| null` | Default chat messages | Override pending-response messages |
68
70
  | `loadingMessagesMode` | `'ordered' \| 'random' \| null` | `'ordered'` | Rotate custom messages in order or at random; only applies when `loadingMessages` has a non-empty message |
71
+ | `labels` | `Partial<ChatLabels>` | Portuguese defaults | Override conversation-search text; see [UI labels](docs/guides/embedding.md#ui-labels) for supported keys and surfaces |
69
72
  | `customComponents` | `Record<string, Component>` | `undefined` | Custom markdown renderers keyed by markdown node or tag name. Components receive `node` plus the parser lifecycle props `loading` and `autoClosed` |
70
73
  | `customHtmlTags` | `readonly string[]` | `undefined` | Extra HTML tags allowed by the markdown renderer |
71
74
  | `citations` | `ChatCitationsConfig \| null` | `undefined` | Enables `cite://` citations in assistant messages (see [Citations](docs/guides/citations.md)) |
@@ -107,7 +110,7 @@ Installing `@meistrari/chat-nuxt` installs the runtime packages used by the laye
107
110
  |---------|---------|---------|
108
111
  | `@meistrari/auth-nuxt` | `3.31.0` | Authentication (required) |
109
112
  | `@meistrari/logger` | `^2.1.3` | Structured logging for the server proxy routes |
110
- | `@meistrari/tela-build` | `^1.30.0` | UI component library (Nuxt layer) |
113
+ | `@meistrari/tela-build` | `^1.70.2` | UI component library (Nuxt layer), with no global i18n requirement |
111
114
  | `@sentry/nuxt` | `^10.0.0` | Client-side error capture used by chat composables |
112
115
  | `@iconify/vue` + `@iconify-json/ph` | `^5.0.0` / `^1.2.2` | Icon rendering support (Phosphor set) |
113
116
  | `@vueuse/core` | `^12.8.0` | Runtime composables used by chat UI |
@@ -121,10 +124,11 @@ Installing `@meistrari/chat-nuxt` installs the runtime packages used by the laye
121
124
  | `posthog-js` | `^1.364.2` | Optional PostHog feature-flag helper |
122
125
  | `stream-diffs` | `0.0.2` | Native markstream rich code and diff rendering |
123
126
  | `virtua` | `^0.42.0` | Virtualized list runtime expected by shared UI |
124
- | `vue-i18n` | `^11.0.0` | Internationalization runtime expected by shared UI |
125
127
  | `xlsx` | `^0.18.5` | Spreadsheet file previews |
126
128
  | `zod` | `^4.1.13` | Schema validation for API payloads |
127
129
 
130
+ The chat package does not declare or expect the host application to configure an i18n plugin. Hosts can override the supported conversation-search text through `labels`, using plain strings or their own translator. This is not a full-chat localization API; other UI text retains its existing defaults.
131
+
128
132
  Peer dependencies:
129
133
 
130
134
  | Package | Version | Purpose |
package/dist/module.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { ChatMessageStatus } from '../dist/runtime/messages/composables/chat-message-terminal.js';
2
- export { AvailableChatModels, ChatActionPayload, ChatActor, ChatEmbedSharedProps, ChatEmbedVariant, ChatEmbedVariantProps, ChatFeatureConfig, ChatMessageTerminalPayload, ChatSidebarConfig, ChatSuggestion, DefaultChatEmbedProps, MeistrariChatEmbedProps, MessageFeedbackConfig, MessageFeedbackPayload, MessageFeedbackRating, MessageFeedbackSubmitHandler, MessageFeedbackSubmitResult, MessageOverviewResponse, TelaAgentChatEmbedProps, TelaAgentExecutionInput } from '../dist/runtime/types/embed.js';
2
+ export { AvailableChatModels, ChatActionPayload, ChatActor, ChatEmbedSharedProps, ChatEmbedVariant, ChatEmbedVariantProps, ChatFeatureConfig, ChatLabels, ChatMessageTerminalPayload, ChatSidebarConfig, ChatSuggestion, DefaultChatEmbedProps, MeistrariChatEmbedProps, MessageFeedbackConfig, MessageFeedbackPayload, MessageFeedbackRating, MessageFeedbackSubmitHandler, MessageFeedbackSubmitResult, MessageOverviewResponse, TelaAgentChatEmbedProps, TelaAgentExecutionInput } from '../dist/runtime/types/embed.js';
3
3
 
4
4
  type ChatNuxtConfigValidationMode = false | 'warn' | 'error';
5
5
 
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "3.17.0-rc.8",
4
+ "version": "4.0.0-rc.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -1 +1 @@
1
- .markstream-vue{font-family:inherit;--at-apply:"body-16-regular leading-24px text-primary";--list-item-marker:#a3a3a3;--list-item-counter-marker:#262626}.markstream-vue code,.markstream-vue kbd,.markstream-vue pre,.markstream-vue samp{font-family:Geist Mono Variable,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:13px;font-variation-settings:normal;letter-spacing:0;line-height:1.5}.markstream-vue ol,.markstream-vue ul{margin:8px 0;padding-left:1.5em}.markstream-vue .hr-node{border-color:#e5e5e5;border-top-width:.5px;margin-bottom:24px;margin-top:24px}.markstream-vue .strong-node{--at-apply:"font-580"}.markstream-vue .list-node.list-node{margin-bottom:8px;margin-left:4px;margin-top:8px;padding-left:1em}.markstream-vue ul{list-style:disc;list-style-position:outside}.markstream-vue ol{list-style:decimal;list-style-position:outside}.markstream-vue li{margin:4px 0}.markstream-vue .paragraph-node{--at-apply:"body-16-regular leading-24px";margin:0}.markstream-vue>.node-slot[data-node-type=paragraph]:not(:first-child){margin-top:12px}@keyframes chat-stream-chunk-in{0%{opacity:0}to{opacity:1}}.markstream-vue .chat-stream-chunk{animation:chat-stream-chunk-in var(--chat-stream-fade-duration,.32s) cubic-bezier(.2,0,0,1) both}@media (prefers-reduced-motion:reduce){.markstream-vue .chat-stream-chunk{animation:none}}.markstream-vue .typewriter-cursor,.markstream-vue .typewriter-simple-cursor .typewriter-simple-cursor-target:after{display:none}.markstream-vue .my-8{margin-bottom:8px;margin-top:8px}.markstream-vue .heading-node.heading-1,.markstream-vue h1{--at-apply:"heading-h2-semibold text-primary";margin:24px 0 8px}.markstream-vue .heading-node.heading-2,.markstream-vue h2{--at-apply:"heading-h3-semibold text-primary";margin:24px 0 8px}.markstream-vue .heading-node.heading-3,.markstream-vue h3{--at-apply:"heading-h4-semibold text-primary";margin:20px 0 8px}.markstream-vue .heading-node.heading-4,.markstream-vue .heading-node.heading-5,.markstream-vue .heading-node.heading-6,.markstream-vue h4,.markstream-vue h5,.markstream-vue h6{--at-apply:"heading-h5-semibold text-primary";margin:16px 0 8px}.markstream-vue>.node-slot:first-child .heading-node{margin-top:0}.markstream-vue [class*=tooltip],[class*=tooltip-element]{background-color:var(--neutral-900,#1a1a1a)!important;border-radius:4px;color:#fff!important;font-size:12px;padding:4px 8px}.mention-tag{align-items:center;border-radius:9999px;display:inline-flex;font-size:.875em;font-weight:500;gap:4px;padding:2px 8px;vertical-align:middle;white-space:nowrap}.mention-tag-file{background-color:rgba(127,189,247,.12);color:#7fbdf7}.mention-tag-skill{background-color:rgba(155,111,222,.12);color:#9b6fde}.mention-tag-canvas{background-color:rgba(234,161,65,.12);color:#d4882e}.mention-tag>span[class*=i-ph]{flex-shrink:0}
1
+ .markstream-vue{font-family:inherit;--at-apply:"body-16-regular leading-24px text-primary";--list-item-marker:#a3a3a3;--list-item-counter-marker:#262626}.markstream-vue code,.markstream-vue kbd,.markstream-vue pre,.markstream-vue samp{font-family:Geist Mono Variable,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:13px;font-variation-settings:normal;letter-spacing:0;line-height:1.5}.markstream-vue ol,.markstream-vue ul{margin:8px 0;padding-left:1.5em}.markstream-vue .hr-node{border-color:#e5e5e5;border-top-width:.5px;margin-bottom:24px;margin-top:24px}.markstream-vue .strong-node{--at-apply:"font-580"}.markstream-vue .list-node.list-node{margin-bottom:8px;margin-left:4px;margin-top:8px;padding-left:1em}.markstream-vue ol.list-node.list-node{padding-left:3em}.markstream-vue ul{list-style:disc;list-style-position:outside}.markstream-vue ol{list-style:decimal;list-style-position:outside}.markstream-vue li{margin:4px 0}.markstream-vue .paragraph-node{--at-apply:"body-16-regular leading-24px";margin:0}.markstream-vue>.node-slot[data-node-type=paragraph]:not(:first-child){margin-top:12px}@keyframes chat-stream-chunk-in{0%{opacity:0}to{opacity:1}}.markstream-vue .chat-stream-chunk{animation:chat-stream-chunk-in var(--chat-stream-fade-duration,.32s) cubic-bezier(.2,0,0,1) both}@media (prefers-reduced-motion:reduce){.markstream-vue .chat-stream-chunk{animation:none}}.markstream-vue .typewriter-cursor,.markstream-vue .typewriter-simple-cursor .typewriter-simple-cursor-target:after{display:none}.markstream-vue .my-8{margin-bottom:8px;margin-top:8px}.markstream-vue .heading-node.heading-1,.markstream-vue h1{--at-apply:"heading-h2-semibold text-primary";margin:24px 0 8px}.markstream-vue .heading-node.heading-2,.markstream-vue h2{--at-apply:"heading-h3-semibold text-primary";margin:24px 0 8px}.markstream-vue .heading-node.heading-3,.markstream-vue h3{--at-apply:"heading-h4-semibold text-primary";margin:20px 0 8px}.markstream-vue .heading-node.heading-4,.markstream-vue .heading-node.heading-5,.markstream-vue .heading-node.heading-6,.markstream-vue h4,.markstream-vue h5,.markstream-vue h6{--at-apply:"heading-h5-semibold text-primary";margin:16px 0 8px}.markstream-vue>.node-slot:first-child .heading-node{margin-top:0}.markstream-vue [class*=tooltip],[class*=tooltip-element]{background-color:var(--neutral-900,#1a1a1a)!important;border-radius:4px;color:#fff!important;font-size:12px;padding:4px 8px}.mention-tag{align-items:center;border-radius:9999px;display:inline-flex;font-size:.875em;font-weight:500;gap:4px;padding:2px 8px;vertical-align:middle;white-space:nowrap}.mention-tag-file{background-color:rgba(127,189,247,.12);color:#7fbdf7}.mention-tag-skill{background-color:rgba(155,111,222,.12);color:#9b6fde}.mention-tag-canvas{background-color:rgba(234,161,65,.12);color:#d4882e}.mention-tag>span[class*=i-ph]{flex-shrink:0}
@@ -1,5 +1,6 @@
1
1
  type __VLS_Props = {
2
2
  placeholder?: string;
3
+ clearLabel?: string;
3
4
  };
4
5
  type __VLS_ModelProps = {
5
6
  modelValue: string;
@@ -9,8 +10,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
9
10
  "update:modelValue": (value: string) => any;
10
11
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
11
12
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
12
- }>, {
13
- placeholder: string;
14
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
14
  declare const _default: typeof __VLS_export;
16
15
  export default _default;
@@ -1,7 +1,12 @@
1
1
  <script setup>
2
- defineProps({
3
- placeholder: { type: String, required: false, default: "Buscar conversas" }
2
+ import { useEmbedConfig } from "../../embed/composables/embed-config";
3
+ const props = defineProps({
4
+ placeholder: { type: String, required: false },
5
+ clearLabel: { type: String, required: false }
4
6
  });
7
+ const embedConfig = useEmbedConfig();
8
+ const placeholder = computed(() => props.placeholder ?? embedConfig?.labels.value?.conversationSearchPlaceholder ?? "Buscar conversas");
9
+ const clearLabel = computed(() => props.clearLabel ?? embedConfig?.labels.value?.conversationSearchClear ?? "Limpar");
5
10
  const searchQuery = defineModel({ type: String, ...{ required: true } });
6
11
  </script>
7
12
 
@@ -11,5 +16,7 @@ const searchQuery = defineModel({ type: String, ...{ required: true } });
11
16
  icon="i-ph-magnifying-glass-bold"
12
17
  :placeholder="placeholder"
13
18
  autocomplete="off"
19
+ show-clear-button
20
+ :clear-label="clearLabel"
14
21
  />
15
22
  </template>
@@ -1,5 +1,6 @@
1
1
  type __VLS_Props = {
2
2
  placeholder?: string;
3
+ clearLabel?: string;
3
4
  };
4
5
  type __VLS_ModelProps = {
5
6
  modelValue: string;
@@ -9,8 +10,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
9
10
  "update:modelValue": (value: string) => any;
10
11
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
11
12
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
12
- }>, {
13
- placeholder: string;
14
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
14
  declare const _default: typeof __VLS_export;
16
15
  export default _default;
@@ -1,4 +1,6 @@
1
1
  <script setup>
2
+ import { inject } from "vue";
3
+ import { CHAT_COMPOSER_EXTENSION_KEY } from "../../types/composer";
2
4
  import { useChatAuth } from "#chat-auth";
3
5
  import { useBreakpoints } from "@vueuse/core";
4
6
  import { useCitationPanel } from "../../citations/composables/citation-panel";
@@ -68,6 +70,7 @@ function toggleSidebarCollapsed() {
68
70
  isSidebarCollapsed.value = !isSidebarCollapsed.value;
69
71
  }
70
72
  const chatApi = useChatApi();
73
+ const composerExtension = inject(CHAT_COMPOSER_EXTENSION_KEY, null);
71
74
  const route = useRoute();
72
75
  const embedConfig = useEmbedConfig();
73
76
  const chatAction = useChatAction();
@@ -531,9 +534,11 @@ function claimableWakeSessionId() {
531
534
  return agentWake.state.value === "ready" && !currentConversation.value?.threadSessionId ? agentWake.sessionId.value : void 0;
532
535
  }
533
536
  async function handleSend(content, files, agentInputs) {
537
+ const prepared = composerExtension?.prepareSend?.(content);
538
+ const outbound = prepared?.content ?? content;
534
539
  const resolvedAgentInputs = resolveTelaAgentInputs(agentInputs, embedConfig?.telaAgentInputs?.value);
535
540
  let claimedWakeSessionId;
536
- const sent = await sendMessage(content, files.length > 0 ? files : void 0, resolvedAgentInputs, async (signal) => {
541
+ const sent = await sendMessage(outbound, files.length > 0 ? files : void 0, resolvedAgentInputs, async (signal) => {
537
542
  if (!activeConversationId.value || !currentConversation.value) {
538
543
  const conversation2 = await createConversation({
539
544
  model: selectedModel.value,
@@ -581,8 +586,10 @@ async function handleSend(content, files, agentInputs) {
581
586
  });
582
587
  if (sent && claimedWakeSessionId)
583
588
  agentWake.consume();
589
+ if (sent)
590
+ prepared?.commit?.();
584
591
  if (!sent)
585
- restoreQueuedSend({ content, files, agentInputs: resolvedAgentInputs });
592
+ restoreQueuedSend({ content: prepared?.retryContent?.() ?? content, files, agentInputs: resolvedAgentInputs });
586
593
  }
587
594
  watch(
588
595
  [outboundOccupied, agentWake.state],
@@ -26,6 +26,7 @@ const props = defineProps({
26
26
  suggestions: { type: [Array, null], required: false },
27
27
  loadingMessages: { type: [Array, null], required: false },
28
28
  loadingMessagesMode: { type: [String, null], required: false },
29
+ labels: { type: Object, required: false },
29
30
  customComponents: { type: Object, required: false },
30
31
  customHtmlTags: { type: Array, required: false },
31
32
  citations: { type: [Object, null], required: false },
@@ -67,6 +68,7 @@ provideEmbedConfig({
67
68
  hideSettings,
68
69
  loadingMessages: computed(() => props.loadingMessages),
69
70
  loadingMessagesMode: computed(() => props.loadingMessagesMode),
71
+ labels: computed(() => props.labels),
70
72
  customComponents: computed(() => props.customComponents),
71
73
  customHtmlTags: computed(() => props.customHtmlTags),
72
74
  citations: computed(() => props.citations),
@@ -19,6 +19,7 @@ const { hideSidebar = void 0, ...props } = defineProps({
19
19
  suggestions: { type: [Array, null], required: false },
20
20
  loadingMessages: { type: [Array, null], required: false },
21
21
  loadingMessagesMode: { type: [String, null], required: false },
22
+ labels: { type: Object, required: false },
22
23
  customComponents: { type: Object, required: false },
23
24
  customHtmlTags: { type: Array, required: false },
24
25
  citations: { type: [Object, null], required: false },
@@ -77,6 +78,7 @@ const sharedEmbedProps = computed(() => ({
77
78
  features: props.features,
78
79
  loadingMessages: props.loadingMessages,
79
80
  loadingMessagesMode: props.loadingMessagesMode,
81
+ labels: props.labels,
80
82
  customComponents: props.customComponents,
81
83
  customHtmlTags: props.customHtmlTags,
82
84
  citations: props.citations,
@@ -128,7 +130,7 @@ const widgetEmbedProps = computed(() => ({
128
130
  @close="setOpen(false)"
129
131
  >
130
132
  <template #history>
131
- <ChatWidgetHistoryList />
133
+ <ChatWidgetHistoryList :search-placeholder="props.labels?.conversationSearchPlaceholder" />
132
134
  </template>
133
135
  </TelaChatWidgetHeader>
134
136
  <div flex-1 min-h-0>
@@ -157,7 +159,7 @@ const widgetEmbedProps = computed(() => ({
157
159
  @select-conversation="handleWidgetConversationIdChange"
158
160
  >
159
161
  <template #history>
160
- <ChatWidgetHistoryList />
162
+ <ChatWidgetHistoryList :search-placeholder="props.labels?.conversationSearchPlaceholder" />
161
163
  </template>
162
164
  </TelaChatWidgetHeader>
163
165
  <div flex-1 min-h-0>
@@ -3,6 +3,7 @@ import type { ConversationCreatorFilter } from '../../types/schemas/chat/primiti
3
3
  import type { WorkspaceSettingsResponse as WorkspaceSettings } from '../../types/schemas/chat/workspace-settings.js';
4
4
  import type { TelaAgentExecutionInput } from '../../types/schemas/integrations/tela-agent-api.js';
5
5
  import type { ChatLoadingMessageMode } from '../loading-messages.js';
6
+ import type { ChatLabels } from '../types.js';
6
7
  import type { ChatCitationsConfig } from '../../citations/types.js';
7
8
  import type { MessageFeedbackConfig, MessageFeedbackRating } from '../../messages/composables/chat-feedback.js';
8
9
  import type { AvailableChatModels } from '../../messages/model-availability.js';
@@ -22,6 +23,7 @@ export type EmbedConfig = {
22
23
  hideSettings: Ref<boolean>;
23
24
  loadingMessages: Ref<readonly string[] | null | undefined>;
24
25
  loadingMessagesMode: Ref<ChatLoadingMessageMode | null | undefined>;
26
+ labels: Ref<Partial<ChatLabels> | undefined>;
25
27
  customComponents: Ref<Record<string, Component> | undefined>;
26
28
  customHtmlTags: Ref<readonly string[] | undefined>;
27
29
  citations: Ref<ChatCitationsConfig | null | undefined>;
@@ -42,6 +44,7 @@ type EmbedConfigInput = {
42
44
  hideSettings?: MaybeRefOrGetter<boolean | undefined>;
43
45
  loadingMessages?: MaybeRefOrGetter<readonly string[] | null | undefined>;
44
46
  loadingMessagesMode?: MaybeRefOrGetter<ChatLoadingMessageMode | null | undefined>;
47
+ labels?: MaybeRefOrGetter<Partial<ChatLabels> | undefined>;
45
48
  customComponents?: MaybeRefOrGetter<Record<string, Component> | undefined>;
46
49
  customHtmlTags?: MaybeRefOrGetter<readonly string[] | undefined>;
47
50
  citations?: MaybeRefOrGetter<ChatCitationsConfig | null | undefined>;
@@ -31,6 +31,7 @@ function useGlobalEmbedConfigState() {
31
31
  hideSettings: useState("chat-embed-config-hide-settings", () => void 0),
32
32
  loadingMessages: useState("chat-embed-config-loading-messages", () => void 0),
33
33
  loadingMessagesMode: useState("chat-embed-config-loading-messages-mode", () => void 0),
34
+ labels: useState("chat-embed-config-labels", () => void 0),
34
35
  customComponents: useGlobalCustomComponentsState(),
35
36
  customHtmlTags: useState("chat-embed-config-custom-html-tags", () => void 0),
36
37
  citations: useGlobalCitationsState(),
@@ -53,6 +54,7 @@ export function provideEmbedConfig(input, options = {}) {
53
54
  hideSettings: computed(() => toValue(input.hideSettings) ?? false),
54
55
  loadingMessages: computed(() => toValue(input.loadingMessages)),
55
56
  loadingMessagesMode: computed(() => toValue(input.loadingMessagesMode)),
57
+ labels: computed(() => toValue(input.labels)),
56
58
  customComponents: computed(() => toValue(input.customComponents)),
57
59
  customHtmlTags: computed(() => toValue(input.customHtmlTags)),
58
60
  citations: computed(() => toValue(input.citations)),
@@ -76,6 +78,7 @@ export function provideEmbedConfig(input, options = {}) {
76
78
  globalConfig.hideSettings.value = config.hideSettings.value;
77
79
  globalConfig.loadingMessages.value = config.loadingMessages.value;
78
80
  globalConfig.loadingMessagesMode.value = config.loadingMessagesMode.value;
81
+ globalConfig.labels.value = config.labels.value;
79
82
  globalConfig.customComponents.value = config.customComponents.value;
80
83
  globalConfig.customHtmlTags.value = config.customHtmlTags.value;
81
84
  globalConfig.citations.value = config.citations.value;
@@ -106,6 +109,7 @@ export function useEmbedConfig() {
106
109
  hideSettings: computed(() => globalConfig.hideSettings.value ?? false),
107
110
  loadingMessages: computed(() => globalConfig.loadingMessages.value),
108
111
  loadingMessagesMode: computed(() => globalConfig.loadingMessagesMode.value),
112
+ labels: computed(() => globalConfig.labels.value),
109
113
  customComponents: computed(() => globalConfig.customComponents.value),
110
114
  customHtmlTags: computed(() => globalConfig.customHtmlTags.value),
111
115
  citations: computed(() => globalConfig.citations.value),
@@ -10,6 +10,12 @@ import type { MessageFeedbackConfig, MessageFeedbackRating } from '../messages/c
10
10
  import type { AvailableChatModels } from '../messages/model-availability.js';
11
11
  export type { AvailableChatModels } from '../messages/model-availability.js';
12
12
  export type ChatEmbedVariant = 'page' | 'floating' | 'sidepanel';
13
+ export type ChatLabels = {
14
+ /** Search placeholder in the sidebar, mobile drawer and widget history. Defaults to `Buscar conversas`. */
15
+ conversationSearchPlaceholder: string;
16
+ /** Clear-button tooltip in the sidebar and mobile drawer search. Defaults to `Limpar`. */
17
+ conversationSearchClear: string;
18
+ };
13
19
  export type ChatSuggestion = {
14
20
  /** Short label rendered on the suggestion pill. */
15
21
  title: string;
@@ -100,6 +106,8 @@ export type ChatEmbedSharedProps = {
100
106
  * non-empty message.
101
107
  */
102
108
  loadingMessagesMode?: ChatLoadingMessageMode | null;
109
+ /** Override supported UI text. Omitted labels keep their Portuguese defaults; no i18n plugin is required. */
110
+ labels?: Partial<ChatLabels>;
103
111
  /** Custom markdown component renderers keyed by markdown node or tag name. */
104
112
  customComponents?: Record<string, Component>;
105
113
  /** Extra HTML tags allowed by the markdown renderer. */
@@ -20,6 +20,8 @@ export type ContentSegment = {
20
20
  export type ParseContentOptions = {
21
21
  /** Detect bare `cite://` tokens as citation segments. Off by default: only hosts that configured citations opt in. */
22
22
  detectCitations?: boolean;
23
+ /** Keep Markdown links in text so the Markdown renderer can preserve enclosing structures such as tables and lists. */
24
+ preserveMarkdownLinks?: boolean;
23
25
  };
24
26
  export declare function detectFilesInText(text: string): DetectedFile[];
25
27
  export declare function parseContentWithFiles(text: string, options?: ParseContentOptions): ContentSegment[];
@@ -1,5 +1,5 @@
1
1
  import { CITE_REGEX, parseCitationUrl } from "../citations/citation-links.js";
2
- import { getExtensionFromUrl, getMimeTypeFromExtension, isFileUrl } from "./mime.js";
2
+ import { getExtensionFromUrl, getMimeTypeFromExtension } from "./mime.js";
3
3
  import {
4
4
  getPermalinkId,
5
5
  isVaultPermalink,
@@ -18,6 +18,20 @@ function getNameFromUrl(url) {
18
18
  const parts = cleanUrl.split("/");
19
19
  return parts[parts.length - 1] ?? url;
20
20
  }
21
+ function detectExternalFileUrl(url) {
22
+ const ext = getExtensionFromUrl(url);
23
+ if (!ext)
24
+ return null;
25
+ const mimeType = getMimeTypeFromExtension(ext);
26
+ if (mimeType === "unknown" || mimeType === "text/html")
27
+ return null;
28
+ return {
29
+ type: "url",
30
+ reference: url,
31
+ name: getNameFromUrl(url),
32
+ mimeType
33
+ };
34
+ }
21
35
  function getNameFromVaultReference(reference) {
22
36
  const parts = reference.replace("vault://", "").split("/");
23
37
  return parts[parts.length - 1] || "Arquivo";
@@ -116,27 +130,24 @@ export function detectFilesInText(text) {
116
130
  });
117
131
  continue;
118
132
  }
119
- if (!isFileUrl(match))
133
+ const file = detectExternalFileUrl(match);
134
+ if (!file)
120
135
  continue;
121
136
  seen.add(match);
122
- const ext = getExtensionFromUrl(match);
123
- files.push({
124
- type: "url",
125
- reference: match,
126
- name: getNameFromUrl(match),
127
- mimeType: ext ? getMimeTypeFromExtension(ext) : "unknown"
128
- });
137
+ files.push(file);
129
138
  }
130
139
  return files;
131
140
  }
132
141
  export function parseContentWithFiles(text, options) {
133
142
  const segments = [];
134
- const fileMatches = collectMarkdownFileLinkMatches(text);
143
+ const markdownFileLinkMatches = collectMarkdownFileLinkMatches(text);
144
+ const fileMatches = options?.preserveMarkdownLinks ? [] : [...markdownFileLinkMatches];
135
145
  for (const match of text.matchAll(VAULT_REGEX)) {
136
146
  const rawUrl = match[0];
137
147
  const url = stripTrailingPunctuation(rawUrl);
138
- if (overlapsFileMatch(match.index, match.index + url.length, fileMatches))
148
+ if (overlapsFileMatch(match.index, match.index + url.length, markdownFileLinkMatches) || overlapsFileMatch(match.index, match.index + url.length, fileMatches)) {
139
149
  continue;
150
+ }
140
151
  const hashParams = parseVaultHashParams(url);
141
152
  const filenameExt = hashParams?.filename?.split(".").pop()?.toLowerCase();
142
153
  const urlExt = getExtensionFromUrl(url);
@@ -156,8 +167,9 @@ export function parseContentWithFiles(text, options) {
156
167
  for (const match of text.matchAll(URL_REGEX)) {
157
168
  const rawUrl = match[0];
158
169
  const url = stripTrailingPunctuation(rawUrl);
159
- if (overlapsFileMatch(match.index, match.index + url.length, fileMatches))
170
+ if (overlapsFileMatch(match.index, match.index + url.length, markdownFileLinkMatches) || overlapsFileMatch(match.index, match.index + url.length, fileMatches)) {
160
171
  continue;
172
+ }
161
173
  if (isVaultS3Url(url)) {
162
174
  const { name, mimeType } = getFileInfoFromVaultS3Url(url);
163
175
  fileMatches.push({
@@ -185,17 +197,12 @@ export function parseContentWithFiles(text, options) {
185
197
  });
186
198
  continue;
187
199
  }
188
- if (isFileUrl(url)) {
189
- const ext = getExtensionFromUrl(url);
200
+ const file = detectExternalFileUrl(url);
201
+ if (file) {
190
202
  fileMatches.push({
191
203
  start: match.index,
192
204
  end: match.index + url.length,
193
- file: {
194
- type: "url",
195
- reference: url,
196
- name: getNameFromUrl(url),
197
- mimeType: ext ? getMimeTypeFromExtension(ext) : "unknown"
198
- }
205
+ file
199
206
  });
200
207
  }
201
208
  }
@@ -3,4 +3,3 @@ export declare const MIME_TO_EXTENSION: Record<string, string>;
3
3
  export declare function normalizeMimeType(mimeType: string): string;
4
4
  export declare function getExtensionFromUrl(url: string): string | undefined;
5
5
  export declare function getMimeTypeFromExtension(ext: string): string;
6
- export declare function isFileUrl(url: string): boolean;
@@ -72,7 +72,3 @@ export function getExtensionFromUrl(url) {
72
72
  export function getMimeTypeFromExtension(ext) {
73
73
  return FILE_EXTENSIONS[ext] ?? "unknown";
74
74
  }
75
- export function isFileUrl(url) {
76
- const ext = getExtensionFromUrl(url);
77
- return ext !== void 0 && ext in FILE_EXTENSIONS;
78
- }
@@ -1,6 +1,6 @@
1
1
  const URL_CHAR_CLASS = "[^\\s\"'<>`\\[\\]()*_~]+";
2
2
  export const VAULT_REGEX = /(?<!\]\()vault:\/\/[^\s"'<>`\[\]()*_~]+/gi;
3
- export const URL_REGEX = /https?:\/\/[^\s"'<>`\[\]()*_~]+/gi;
3
+ export const URL_REGEX = /(?<!\]\(|<)https?:\/\/[^\s"'<>`\[\]()*_~]+/gi;
4
4
  export const VAULT_PATTERN = new RegExp(`vault://${URL_CHAR_CLASS}`, "gi");
5
5
  export const VAULT_PERMALINK_PATTERN = new RegExp(`https://(?:[a-z0-9-]+\\.)*(?:vault|api)\\.tela(?:staging)?\\.com/(?:[a-z0-9_-]+/)*permalinks/${URL_CHAR_CLASS}`, "gi");
6
6
  export function stripTrailingPunctuation(url) {
@@ -100,7 +100,7 @@ const contentSegments = computed(() => {
100
100
  const content = displayContent.value;
101
101
  if (!content)
102
102
  return [];
103
- return parseContentWithFiles(content);
103
+ return parseContentWithFiles(content, { preserveMarkdownLinks: true });
104
104
  });
105
105
  const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type !== "text"));
106
106
  </script>
@@ -1,4 +1,6 @@
1
1
  <script setup>
2
+ import { inject, unref } from "vue";
3
+ import { CHAT_COMPOSER_EXTENSION_KEY } from "../../types/composer";
2
4
  import { useFileUpload } from "../../files/composables/file-upload";
3
5
  import { MAX_FILE_SIZE, MAX_FILES } from "../../files/file-limits";
4
6
  import { useInputHistory } from "../composables/input-history";
@@ -26,6 +28,7 @@ const props = defineProps({
26
28
  });
27
29
  const emit = defineEmits(["send", "cancel"]);
28
30
  const selectedModel = defineModel("selectedModel", { type: [String, null], ...{ default: null } });
31
+ const composerExtension = inject(CHAT_COMPOSER_EXTENSION_KEY, null);
29
32
  const selectedReasoningEffort = defineModel("selectedReasoningEffort", { type: null, ...{ default: null } });
30
33
  const { uploadFiles, uploading, uploadError } = useFileUpload();
31
34
  const isCancelEnabled = computed(() => props.showCancelButton === true);
@@ -34,6 +37,7 @@ const promptTextareaRef = ref(null);
34
37
  const editorRef = computed(() => promptTextareaRef.value?.el ?? null);
35
38
  const fileInputRef = ref(null);
36
39
  const pendingFiles = ref([]);
40
+ const hasAttachments = computed(() => pendingFiles.value.length > 0 || !!unref(composerExtension?.hasAttachments));
37
41
  const selectedUploadInputName = ref(void 0);
38
42
  const agentInputTexts = ref({});
39
43
  const isAgentInputsOpen = ref(false);
@@ -661,7 +665,7 @@ defineExpose({
661
665
  />
662
666
 
663
667
  <TelaChatPromptInput :locked="promptShellLocked" :class="centered && 'bg-white'">
664
- <TelaChatPromptInputAttachments v-if="pendingFiles.length > 0">
668
+ <TelaChatPromptInputAttachments v-if="hasAttachments">
665
669
  <TelaChatPromptInputAttachment
666
670
  v-for="pendingFile in pendingFiles"
667
671
  :key="pendingFile.id"
@@ -691,13 +695,18 @@ defineExpose({
691
695
  </option>
692
696
  </select>
693
697
  </TelaChatPromptInputAttachment>
698
+ <component
699
+ :is="composerExtension.attachmentsComponent"
700
+ v-if="composerExtension?.attachmentsComponent"
701
+ :disabled="isLocked"
702
+ />
694
703
  </TelaChatPromptInputAttachments>
695
704
 
696
705
  <TelaChatPromptInputTextarea
697
706
  ref="promptTextareaRef"
698
707
  placeholder="Escreva sua mensagem..."
699
708
  :empty="!content.trim()"
700
- :flush="pendingFiles.length > 0"
709
+ :flush="hasAttachments"
701
710
  :disabled="isLocked"
702
711
  @keydown="handleKeydown"
703
712
  @input="handleInput"
@@ -755,6 +764,11 @@ defineExpose({
755
764
  </TelaChatPromptInputActionMenuSub>
756
765
  </TelaChatPromptInputActionMenuContent>
757
766
  </TelaChatPromptInputActionMenu>
767
+ <component
768
+ :is="composerExtension.component"
769
+ v-if="composerExtension?.component"
770
+ :disabled="isLocked"
771
+ />
758
772
  </TelaChatPromptInputTools>
759
773
 
760
774
  <TelaChatPromptInputActions>
@@ -42,7 +42,8 @@ const thinkingDuration = computed(() => {
42
42
  return null;
43
43
  const start = new Date(props.createdAt).getTime();
44
44
  const end = new Date(props.updatedAt).getTime();
45
- return end - start;
45
+ const duration = end - start;
46
+ return Number.isFinite(duration) && duration >= 0 ? duration : null;
46
47
  });
47
48
  const formattedDuration = computed(() => {
48
49
  if (!thinkingDuration.value)
@@ -259,6 +259,17 @@ export function useChat(options = {}) {
259
259
  agentSessionExecutable.value = response.agentSession.executable;
260
260
  agentSessionRewakeable.value = response.agentSession.rewakeable ?? false;
261
261
  }
262
+ const queuedV2Send = chatApi.conversationV2Enabled ? outbound.slot.value : null;
263
+ if (queuedV2Send) {
264
+ const admitted = response.messages.some((message) => message.id === queuedV2Send.optimisticIds.user);
265
+ if (admitted) {
266
+ outbound.clear();
267
+ gateHolding.value = false;
268
+ } else {
269
+ void flushOutboundQueue();
270
+ return;
271
+ }
272
+ }
262
273
  if (gateHolding.value)
263
274
  return;
264
275
  const currentMessagesById = new Map(messages.value.map((message) => [message.id, message]));
@@ -629,9 +640,29 @@ export function useChat(options = {}) {
629
640
  const code = getChatApiErrorCode(err);
630
641
  const status = getChatApiErrorStatus(err);
631
642
  if (v2AdmissionRequested && !controller.signal.aborted && (status === null || status === 408 || status === 429 || status >= 500)) {
632
- error.value = "Confirma\xE7\xE3o do envio pendente. Reconectando \xE0 conversa\u2026";
633
- startPolling();
634
- return optimisticUserMessage;
643
+ const attempts = (queuedSend?.attempts ?? 0) + 1;
644
+ const queuedAt = queuedSend?.queuedAt ?? Date.now();
645
+ if (outboundQueueEnabled.value) {
646
+ if (attempts < 3 && Date.now() - queuedAt <= 9e4) {
647
+ outbound.replace({
648
+ content,
649
+ files,
650
+ agentInputs,
651
+ wakeSessionId: void 0,
652
+ optimisticIds: { user: optimisticUserMessage.id, assistant: optimisticAssistantMessage.id },
653
+ queuedAt,
654
+ attempts
655
+ });
656
+ gateHolding.value = true;
657
+ error.value = "Confirma\xE7\xE3o do envio pendente. Reconectando \xE0 conversa\u2026";
658
+ startPolling();
659
+ return optimisticUserMessage;
660
+ }
661
+ } else {
662
+ error.value = "Confirma\xE7\xE3o do envio pendente. Reconectando \xE0 conversa\u2026";
663
+ startPolling();
664
+ return optimisticUserMessage;
665
+ }
635
666
  }
636
667
  const queuedAttempts = (queuedSend?.attempts ?? 0) + 1;
637
668
  const queuedAgeMs = Date.now() - (queuedSend?.queuedAt ?? Date.now());
@@ -0,0 +1,22 @@
1
+ import type { Component, MaybeRef } from 'vue';
2
+ /** Stable string key, also compatible with hosts providing the literal key. */
3
+ export declare const CHAT_COMPOSER_EXTENSION_KEY = "chat-composer-extension";
4
+ /** A synchronous, per-send snapshot owned by the host application. */
5
+ export interface ChatPreparedSend {
6
+ content: string;
7
+ /** Called only after acceptance, including acceptance into the outbound queue. */
8
+ commit?: () => void;
9
+ /** Read after rejection. Defaults to the original composer text when omitted. */
10
+ retryContent?: () => string;
11
+ }
12
+ /** Provide above MeistrariChatEmbed to extend its composer without replacing it. */
13
+ export interface ChatComposerExtension {
14
+ /** Action next to the file menu. Receives the composer's `disabled` state. */
15
+ component?: Component;
16
+ /** Items inside the shared attachment strip. Receives `disabled` as well. */
17
+ attachmentsComponent?: Component;
18
+ /** Controls attachment-strip visibility and the textarea's flush layout. */
19
+ hasAttachments?: MaybeRef<boolean>;
20
+ /** Prepare without consuming host state; commit after the send is accepted. */
21
+ prepareSend?: (content: string) => ChatPreparedSend | null | undefined;
22
+ }
@@ -0,0 +1 @@
1
+ export const CHAT_COMPOSER_EXTENSION_KEY = "chat-composer-extension";
@@ -1,6 +1,6 @@
1
1
  export type { ChatCitationsConfig, CitationMeta, CitationRef, } from '../citations/types.js';
2
2
  export type { ChatFeatureConfig } from '../embed/feature-config.js';
3
- export type { AvailableChatModels, ChatActor, ChatEmbedSharedProps, ChatEmbedVariant, ChatEmbedVariantProps, ChatSidebarConfig, ChatSuggestion, DefaultChatEmbedProps, MeistrariChatEmbedProps, TelaAgentChatEmbedProps, } from '../embed/types.js';
3
+ export type { AvailableChatModels, ChatActor, ChatEmbedSharedProps, ChatEmbedVariant, ChatEmbedVariantProps, ChatLabels, ChatSidebarConfig, ChatSuggestion, DefaultChatEmbedProps, MeistrariChatEmbedProps, TelaAgentChatEmbedProps, } from '../embed/types.js';
4
4
  export type { ChatActionPayload } from '../messages/composables/chat-action.js';
5
5
  export type { MessageFeedbackConfig, MessageFeedbackPayload, MessageFeedbackRating, MessageFeedbackSubmitHandler, MessageFeedbackSubmitResult, } from '../messages/composables/chat-feedback.js';
6
6
  export { ChatMessageStatus } from '../messages/composables/chat-message-terminal.js';
@@ -232,6 +232,9 @@ export declare const createConversationRequestSchema: z.ZodObject<{
232
232
  "claude-sonnet-5": "claude-sonnet-5";
233
233
  "claude-sonnet-4-6": "claude-sonnet-4-6";
234
234
  "claude-haiku-4-5": "claude-haiku-4-5";
235
+ "openai/gpt-5.6-sol": "openai/gpt-5.6-sol";
236
+ "openai/gpt-5.6-terra": "openai/gpt-5.6-terra";
237
+ "openai/gpt-5.6-luna": "openai/gpt-5.6-luna";
235
238
  }>>>;
236
239
  reasoningEffort: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
237
240
  low: "low";
@@ -407,6 +410,9 @@ export declare const updateConversationRequestSchema: z.ZodObject<{
407
410
  "claude-sonnet-5": "claude-sonnet-5";
408
411
  "claude-sonnet-4-6": "claude-sonnet-4-6";
409
412
  "claude-haiku-4-5": "claude-haiku-4-5";
413
+ "openai/gpt-5.6-sol": "openai/gpt-5.6-sol";
414
+ "openai/gpt-5.6-terra": "openai/gpt-5.6-terra";
415
+ "openai/gpt-5.6-luna": "openai/gpt-5.6-luna";
410
416
  }>>>;
411
417
  reasoningEffort: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
412
418
  low: "low";
@@ -1,11 +1,14 @@
1
1
  import { z } from 'zod';
2
- export declare const chatModelIds: readonly ["claude-opus-5", "claude-opus-4-8", "claude-sonnet-5", "claude-sonnet-4-6", "claude-haiku-4-5"];
2
+ export declare const chatModelIds: readonly ["claude-opus-5", "claude-opus-4-8", "claude-sonnet-5", "claude-sonnet-4-6", "claude-haiku-4-5", "openai/gpt-5.6-sol", "openai/gpt-5.6-terra", "openai/gpt-5.6-luna"];
3
3
  export declare const chatModelSchema: z.ZodEnum<{
4
4
  "claude-opus-5": "claude-opus-5";
5
5
  "claude-opus-4-8": "claude-opus-4-8";
6
6
  "claude-sonnet-5": "claude-sonnet-5";
7
7
  "claude-sonnet-4-6": "claude-sonnet-4-6";
8
8
  "claude-haiku-4-5": "claude-haiku-4-5";
9
+ "openai/gpt-5.6-sol": "openai/gpt-5.6-sol";
10
+ "openai/gpt-5.6-terra": "openai/gpt-5.6-terra";
11
+ "openai/gpt-5.6-luna": "openai/gpt-5.6-luna";
9
12
  }>;
10
13
  export type ChatModel = z.infer<typeof chatModelSchema>;
11
14
  export declare const defaultChatModel = "claude-opus-5";
@@ -5,6 +5,9 @@ export const chatModelIds = [
5
5
  'claude-sonnet-5',
6
6
  'claude-sonnet-4-6',
7
7
  'claude-haiku-4-5',
8
+ 'openai/gpt-5.6-sol',
9
+ 'openai/gpt-5.6-terra',
10
+ 'openai/gpt-5.6-luna',
8
11
  ];
9
12
  export const chatModelSchema = z.enum(chatModelIds);
10
13
  export const defaultChatModel = 'claude-opus-5';
@@ -69,6 +69,36 @@ export declare const chatModelOptions: readonly [{
69
69
  readonly maxOutputTokens: 64000;
70
70
  readonly isMultiModal: true;
71
71
  readonly tabs: readonly ["all", "cheap", "multimodal"];
72
+ }, {
73
+ readonly value: "openai/gpt-5.6-sol";
74
+ readonly label: "GPT-5.6 Sol";
75
+ readonly description: "Modelo OpenAI para tarefas complexas";
76
+ readonly provider: "OpenAI";
77
+ readonly cost: 5;
78
+ readonly maxInputTokens: 1050000;
79
+ readonly maxOutputTokens: 128000;
80
+ readonly isMultiModal: true;
81
+ readonly tabs: readonly ["all", "advanced", "multimodal"];
82
+ }, {
83
+ readonly value: "openai/gpt-5.6-terra";
84
+ readonly label: "GPT-5.6 Terra";
85
+ readonly description: "Equilíbrio entre capacidade e custo";
86
+ readonly provider: "OpenAI";
87
+ readonly cost: 3;
88
+ readonly maxInputTokens: 1050000;
89
+ readonly maxOutputTokens: 128000;
90
+ readonly isMultiModal: true;
91
+ readonly tabs: readonly ["all", "advanced", "multimodal"];
92
+ }, {
93
+ readonly value: "openai/gpt-5.6-luna";
94
+ readonly label: "GPT-5.6 Luna";
95
+ readonly description: "Modelo OpenAI rápido e econômico";
96
+ readonly provider: "OpenAI";
97
+ readonly cost: 1;
98
+ readonly maxInputTokens: 1050000;
99
+ readonly maxOutputTokens: 128000;
100
+ readonly isMultiModal: true;
101
+ readonly tabs: readonly ["all", "cheap", "multimodal"];
72
102
  }];
73
103
  export type ChatReasoningEffortOption = {
74
104
  value: ChatReasoningEffort;
@@ -11,6 +11,10 @@ export const chatModelOptions = [
11
11
  { value: 'claude-sonnet-5', label: 'Claude Sonnet 5', description: 'Agente rápido para tarefas avançadas', provider: 'Anthropic', cost: 4, maxInputTokens: 1_000_000, maxOutputTokens: 64_000, isMultiModal: true, tabs: ['all', 'advanced', 'multimodal'] },
12
12
  { value: 'claude-sonnet-4-6', label: 'Claude 4.6 Sonnet', description: 'Agente rápido para tarefas avançadas', provider: 'Anthropic', cost: 4, maxInputTokens: 1_000_000, maxOutputTokens: 64_000, isMultiModal: true, tabs: ['all', 'advanced', 'multimodal'] },
13
13
  { value: 'claude-haiku-4-5', label: 'Claude 4.5 Haiku', description: 'Agente rápido para tarefas simples', provider: 'Anthropic', cost: 3, maxInputTokens: 200_000, maxOutputTokens: 64_000, isMultiModal: true, tabs: ['all', 'cheap', 'multimodal'] },
14
+ // Agent API catalog: openai/ IDs select the existing gateway route; cost is its 1–5 tier.
15
+ { value: 'openai/gpt-5.6-sol', label: 'GPT-5.6 Sol', description: 'Modelo OpenAI para tarefas complexas', provider: 'OpenAI', cost: 5, maxInputTokens: 1_050_000, maxOutputTokens: 128_000, isMultiModal: true, tabs: ['all', 'advanced', 'multimodal'] },
16
+ { value: 'openai/gpt-5.6-terra', label: 'GPT-5.6 Terra', description: 'Equilíbrio entre capacidade e custo', provider: 'OpenAI', cost: 3, maxInputTokens: 1_050_000, maxOutputTokens: 128_000, isMultiModal: true, tabs: ['all', 'advanced', 'multimodal'] },
17
+ { value: 'openai/gpt-5.6-luna', label: 'GPT-5.6 Luna', description: 'Modelo OpenAI rápido e econômico', provider: 'OpenAI', cost: 1, maxInputTokens: 1_050_000, maxOutputTokens: 128_000, isMultiModal: true, tabs: ['all', 'cheap', 'multimodal'] },
14
18
  ];
15
19
  // Ordered from least to most effort so the composer can lay them out as slider stops.
16
20
  export const chatReasoningEffortOptions = [
@@ -280,6 +280,9 @@ export declare const telaAgentWakeRequestSchema: z.ZodObject<{
280
280
  "claude-sonnet-5": "claude-sonnet-5";
281
281
  "claude-sonnet-4-6": "claude-sonnet-4-6";
282
282
  "claude-haiku-4-5": "claude-haiku-4-5";
283
+ "openai/gpt-5.6-sol": "openai/gpt-5.6-sol";
284
+ "openai/gpt-5.6-terra": "openai/gpt-5.6-terra";
285
+ "openai/gpt-5.6-luna": "openai/gpt-5.6-luna";
283
286
  }>>;
284
287
  }, z.core.$strip>;
285
288
  export type TelaAgentWakeRequest = z.infer<typeof telaAgentWakeRequestSchema>;
@@ -1,3 +1,4 @@
1
+ import { chatModelOptions } from "../types/schemas/chat/ui.js";
1
2
  export function formatTokenCount(count) {
2
3
  if (count >= 1e6) {
3
4
  const val = count / 1e6;
@@ -19,6 +20,9 @@ export function formatCredits(credits) {
19
20
  return `${Math.round(credits)} cr\xE9ditos`;
20
21
  }
21
22
  export function formatModelName(model) {
23
+ const openAiModel = chatModelOptions.find((option) => option.provider === "OpenAI" && (option.value === model || option.value === `openai/${model}`));
24
+ if (openAiModel)
25
+ return openAiModel.label;
22
26
  const id = model.replace(/^claude-/, "");
23
27
  const newFormat = id.match(/^(opus|sonnet|haiku)-(\d+)-(\d+)/);
24
28
  if (newFormat) {
package/dist/types.d.mts CHANGED
@@ -6,7 +6,7 @@ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<
6
6
 
7
7
  export { ChatMessageStatus } from '../dist/runtime/messages/composables/chat-message-terminal.js'
8
8
 
9
- export { type AvailableChatModels, type ChatActionPayload, type ChatActor, type ChatEmbedSharedProps, type ChatEmbedVariant, type ChatEmbedVariantProps, type ChatFeatureConfig, type ChatMessageTerminalPayload, type ChatSidebarConfig, type ChatSuggestion, type DefaultChatEmbedProps, type MeistrariChatEmbedProps, type MessageFeedbackConfig, type MessageFeedbackPayload, type MessageFeedbackRating, type MessageFeedbackSubmitHandler, type MessageFeedbackSubmitResult, type MessageOverviewResponse, type TelaAgentChatEmbedProps, type TelaAgentExecutionInput } from '../dist/runtime/types/embed.js'
9
+ export { type AvailableChatModels, type ChatActionPayload, type ChatActor, type ChatEmbedSharedProps, type ChatEmbedVariant, type ChatEmbedVariantProps, type ChatFeatureConfig, type ChatLabels, type ChatMessageTerminalPayload, type ChatSidebarConfig, type ChatSuggestion, type DefaultChatEmbedProps, type MeistrariChatEmbedProps, type MessageFeedbackConfig, type MessageFeedbackPayload, type MessageFeedbackRating, type MessageFeedbackSubmitHandler, type MessageFeedbackSubmitResult, type MessageOverviewResponse, type TelaAgentChatEmbedProps, type TelaAgentExecutionInput } from '../dist/runtime/types/embed.js'
10
10
 
11
11
  export { default } from './module.mjs'
12
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "3.17.0-rc.8",
3
+ "version": "4.0.0-rc.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -48,7 +48,7 @@
48
48
  "@iconify/vue": "^5.0.0",
49
49
  "@meistrari/auth-nuxt": "3.31.0",
50
50
  "@meistrari/logger": "^2.1.3",
51
- "@meistrari/tela-build": "^1.66.0",
51
+ "@meistrari/tela-build": "^1.70.2",
52
52
  "@sentry/nuxt": "^10.0.0",
53
53
  "@vueuse/components": "^12.8.0",
54
54
  "@vueuse/core": "^12.8.0",
@@ -64,7 +64,6 @@
64
64
  "stream-diffs": "0.0.2",
65
65
  "tldts": "^7.4.10",
66
66
  "virtua": "^0.42.0",
67
- "vue-i18n": "^11.0.0",
68
67
  "xlsx": "^0.18.5",
69
68
  "zod": "^4.1.13"
70
69
  },