@meistrari/chat-nuxt 4.7.1-rc.0 → 4.8.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +60 -1
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/conversations/composables/conversations.d.ts +2 -0
  4. package/dist/runtime/conversations/composables/conversations.js +2 -0
  5. package/dist/runtime/embed/components/chat-embed-inner.d.vue.ts +10 -10
  6. package/dist/runtime/embed/components/chat-embed-inner.vue +67 -112
  7. package/dist/runtime/embed/components/chat-embed-inner.vue.d.ts +10 -10
  8. package/dist/runtime/embed/components/chat-embed.vue +2 -0
  9. package/dist/runtime/embed/components/meistrari-chat-embed.vue +2 -0
  10. package/dist/runtime/embed/components/mobile/chat-mobile-shell.d.vue.ts +7 -3
  11. package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue +4 -1
  12. package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue.d.ts +7 -3
  13. package/dist/runtime/embed/composables/embed-config.d.ts +3 -0
  14. package/dist/runtime/embed/composables/embed-config.js +4 -0
  15. package/dist/runtime/embed/execution-controls.d.ts +26 -0
  16. package/dist/runtime/embed/execution-controls.js +18 -0
  17. package/dist/runtime/embed/feature-config.d.ts +4 -1
  18. package/dist/runtime/embed/feature-config.js +5 -3
  19. package/dist/runtime/embed/types.d.ts +3 -0
  20. package/dist/runtime/lightweight/messages/composables/chat.d.ts +8 -1
  21. package/dist/runtime/messages/components/chat-execution-mode-selector.d.vue.ts +17 -0
  22. package/dist/runtime/messages/components/chat-execution-mode-selector.vue +40 -0
  23. package/dist/runtime/messages/components/chat-execution-mode-selector.vue.d.ts +17 -0
  24. package/dist/runtime/messages/components/chat-message-input.d.vue.ts +4 -0
  25. package/dist/runtime/messages/components/chat-message-input.vue +14 -1
  26. package/dist/runtime/messages/components/chat-message-input.vue.d.ts +4 -0
  27. package/dist/runtime/messages/composables/chat.d.ts +10 -2
  28. package/dist/runtime/messages/composables/chat.js +37 -3
  29. package/dist/runtime/messages/composables/outbound-queue.d.ts +2 -0
  30. package/dist/runtime/messages/conversation-timeline.js +10 -0
  31. package/dist/runtime/types/embed.d.ts +1 -0
  32. package/dist/runtime/types/schemas/chat/conversation-runtime.d.ts +10 -0
  33. package/dist/runtime/types/schemas/chat/conversation-runtime.js +2 -1
  34. package/dist/runtime/types/schemas/chat/conversations.d.ts +19 -0
  35. package/dist/runtime/types/schemas/chat/conversations.js +7 -0
  36. package/dist/runtime/types/schemas/chat/messages.d.ts +33 -0
  37. package/dist/runtime/types/schemas/chat/messages.js +9 -0
  38. package/package.json +1 -1
package/README.md CHANGED
@@ -101,6 +101,7 @@ Declared in `src/runtime/embed/types.ts`; prop, feature-flag, and event payload
101
101
  | `defaultConversationCreatorFilter` | `'all' \| 'mine'` | `'all'` | Initial sidebar creator filter: every conversation in scope, or only the current user's |
102
102
  | `availableModels` | `AvailableChatModels` | Harness catalog | Visual filter and ordering for catalog options. Unknown ids are omitted; if no ids match, the full catalog is shown. Does not change defaults, restored selections or execution. The Agent validates availability |
103
103
  | `defaultModel` | `ChatModel` | Package default | Initial model for untouched drafts. Explicit selections and existing conversations take precedence; `availableModels` only filters the selector |
104
+ | `executionControls` | `ChatExecutionControls` | Legacy feature flags | Choose `hidden`, `simple` (Padrão / Aprofundar), or `advanced` (model and effort pickers). See below |
104
105
  | `defaultReasoningEffort` | `ChatReasoningEffort` | Automatic (`null`) | Initial reasoning effort for untouched drafts. Explicit user choices (including automatic) take precedence |
105
106
  | `conversationCreatorFilter` | `'all' \| 'mine'` | — | Controlled sidebar creator filter; use with `v-model:conversation-creator-filter` when the host changes it at runtime |
106
107
  | `hideConversationCreatorFilter` | `boolean` | `false` | Hide the creator selector without changing the active filter |
@@ -109,7 +110,7 @@ Declared in `src/runtime/embed/types.ts`; prop, feature-flag, and event payload
109
110
  | `telaAgentInputs` | `TelaAgentExecutionInput[] \| null` | `undefined` | Inputs sent with each Tela agent run; hides the variables panel when non-null |
110
111
  | `workspaceSettings` | `WorkspaceSettings \| null` | `null` | Override settings from host app; not accepted in Tela agent mode |
111
112
  | `user` | `ChatActor \| null` | `null` | Override current user display info; not accepted in Tela agent mode |
112
- | `features` | `Partial<ChatFeatureConfig>` | `{}` | Toggle `showUsageTab`, `showDebugOption`, `showCancelButton`, `showTtft`, `chatOutboundQueue`, `showPromptInputHints`, `showModelSelector`, and `showReasoningEffortSelector`; all released features default to `true` |
113
+ | `features` | `Partial<ChatFeatureConfig>` | `{}` | Toggle `showUsageTab`, `showDebugOption`, `showCancelButton`, `showTtft`, `chatOutboundQueue`, `showPromptInputHints`, `showModelSelector`, and `showReasoningEffortSelector`; all released features default to `true`. The two picker flags are deprecated in favor of `executionControls` |
113
114
  | `loadingMessages` | `readonly string[] \| null` | Default chat messages | Override pending-response messages |
114
115
  | `loadingMessagesMode` | `'ordered' \| 'random' \| null` | `'ordered'` | Rotate custom messages in order or at random; only applies when `loadingMessages` has a non-empty message |
115
116
  | `labels` | `Partial<ChatLabels>` | Portuguese defaults | Override conversation-search text; see [UI labels](docs/guides/embedding.md#ui-labels) for supported keys and surfaces |
@@ -181,3 +182,61 @@ Peer dependencies:
181
182
  |---------|---------|---------|
182
183
  | `nuxt` | `^3.17.0 \|\| ^4.0.0` | Host framework |
183
184
  | `vue` | `^3.5.0` | Host Vue runtime |
185
+
186
+ ### Execution controls
187
+
188
+ `executionControls` selects how users configure a response. Omit it to preserve
189
+ existing behavior, including `features.showModelSelector` and
190
+ `features.showReasoningEffortSelector`. Both legacy fields remain supported and
191
+ are marked `@deprecated` for editor guidance. When `executionControls` is present,
192
+ it takes precedence over both legacy flags as a whole.
193
+
194
+ ```vue
195
+ <MeistrariChatEmbed
196
+ :execution-controls="{
197
+ mode: 'simple',
198
+ profiles: {
199
+ default: { model: standardModelId, reasoningEffort: 'medium' },
200
+ deep: { model: deepModelId, reasoningEffort: 'high' },
201
+ },
202
+ }"
203
+ />
204
+ ```
205
+
206
+ Supply model IDs available to the configured agent and effort levels supported
207
+ by those models. Both profiles may use the same model. An explicit `null` effort
208
+ uses the agent's configured effort. Profile selection keeps both settings local
209
+ until the next send or fresh retry. That request persists the settings and records
210
+ each changed field in the conversation history. Reloading discards unsent choices;
211
+ a rejected admission restores the prior persisted settings.
212
+
213
+ In `simple` mode, new conversations start with the `default` profile, regardless
214
+ of `defaultModel` and `defaultReasoningEffort`. A selection stays within the current
215
+ conversation. Changing control modes preserves explicit draft choices. Starting
216
+ a new conversation discards a simple selection even if the controls have since
217
+ been hidden or switched to advanced mode. If only one field is manually changed in advanced mode, the remaining simple
218
+ selection still causes both fields to reset for a new conversation. Manually
219
+ replacing both fields in advanced mode retains the existing behavior of carrying
220
+ them over to new conversations.
221
+ Restored conversations keep their settings: the control shows
222
+ **Personalizado** when the saved model and effort do not match either profile.
223
+ The menu still offers only **Padrão** and **Aprofundar**. Changes apply to subsequent
224
+ messages and do not regenerate earlier responses. If profiles are identical,
225
+ **Padrão** wins the label match.
226
+
227
+ In `hidden` and `advanced` modes, the existing model and effort defaults and saved
228
+ conversation settings retain their behavior. Hiding controls does not force a
229
+ model or effort. `availableModels` continues to filter the advanced model picker;
230
+ it does not filter or rewrite the simple profiles.
231
+
232
+ | Legacy configuration | Replacement |
233
+ | --- | --- |
234
+ | Both picker flags `false` | `{ mode: 'hidden' }` |
235
+ | Both picker flags `true` | `{ mode: 'advanced' }` |
236
+ | Only effort picker `false` | `{ mode: 'advanced', showReasoningEffortSelector: false }` |
237
+ | Only model picker `false` | `{ mode: 'advanced', showModelSelector: false }` |
238
+
239
+ Advanced picker flags default to `true`. Import `ChatExecutionControls` and
240
+ `ChatExecutionProfile` from `@meistrari/chat-nuxt/types/embed` when declaring host
241
+ configuration separately. `defaultModel` and `defaultReasoningEffort` are not
242
+ deprecated.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "4.7.1-rc.0",
4
+ "version": "4.8.0-rc.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -1,3 +1,4 @@
1
+ import type { ChatExecutionProfile } from '../../embed/execution-controls.js';
1
2
  import type { DeepReadonly } from 'vue';
2
3
  import type { Conversation, ConversationWithMessages, CreateConversationRequest } from '../../types/schemas/chat/conversations.js';
3
4
  import type { ChatReasoningEffort } from '../../types/schemas/chat/models.js';
@@ -14,6 +15,7 @@ export declare function useConversations(): {
14
15
  renameConversation: (id: string, title: string) => Promise<boolean>;
15
16
  updateConversationModel: (id: string, model: string | null) => Promise<boolean>;
16
17
  updateConversationReasoningEffort: (id: string, reasoningEffort: ChatReasoningEffort | null) => Promise<boolean>;
18
+ updateConversationExecutionProfile: (id: string, profile: ChatExecutionProfile) => Promise<boolean>;
17
19
  moveConversationToGroup: (id: string, groupId: string | null) => Promise<boolean>;
18
20
  duplicateConversation: (id: string) => Promise<ConversationWithMessages | null>;
19
21
  forkConversation: (id: string, messageId: string) => Promise<ConversationWithMessages | null>;
@@ -505,6 +505,7 @@ export function useConversations() {
505
505
  };
506
506
  const updateConversationModel = (id, model) => patchConversationSetting(id, { model }, "Erro ao atualizar modelo da conversa");
507
507
  const updateConversationReasoningEffort = (id, reasoningEffort) => patchConversationSetting(id, { reasoningEffort }, "Erro ao atualizar o esfor\xE7o de racioc\xEDnio da conversa");
508
+ const updateConversationExecutionProfile = (id, profile) => patchConversationSetting(id, profile, "Erro ao atualizar o modo de resposta da conversa");
508
509
  const forkConversation = async (id, messageId) => {
509
510
  error.value = null;
510
511
  const statusToast = useStatusToast();
@@ -561,6 +562,7 @@ export function useConversations() {
561
562
  renameConversation,
562
563
  updateConversationModel,
563
564
  updateConversationReasoningEffort,
565
+ updateConversationExecutionProfile,
564
566
  moveConversationToGroup,
565
567
  duplicateConversation,
566
568
  forkConversation,
@@ -19,27 +19,27 @@ type __VLS_Props = {
19
19
  user?: EmbedUser | null;
20
20
  features?: Partial<ChatFeatureConfig>;
21
21
  };
22
- declare var __VLS_14: {}, __VLS_88: {}, __VLS_101: {
22
+ declare var __VLS_14: {}, __VLS_92: {}, __VLS_105: {
23
23
  message: useChatAuth;
24
- }, __VLS_113: {}, __VLS_122: {}, __VLS_183: {}, __VLS_202: {
24
+ }, __VLS_118: {}, __VLS_127: {}, __VLS_191: {}, __VLS_210: {
25
25
  message: useChatAuth;
26
- }, __VLS_214: {};
26
+ }, __VLS_223: {};
27
27
  type __VLS_Slots = {} & {
28
28
  'sidebar-bottom'?: (props: typeof __VLS_14) => any;
29
29
  } & {
30
- 'composer-bottom'?: (props: typeof __VLS_88) => any;
30
+ 'composer-bottom'?: (props: typeof __VLS_92) => any;
31
31
  } & {
32
- 'failed-message-actions'?: (props: typeof __VLS_101) => any;
32
+ 'failed-message-actions'?: (props: typeof __VLS_105) => any;
33
33
  } & {
34
- 'composer-bottom'?: (props: typeof __VLS_113) => any;
34
+ 'composer-bottom'?: (props: typeof __VLS_118) => any;
35
35
  } & {
36
- 'topbar-leading'?: (props: typeof __VLS_122) => any;
36
+ 'topbar-leading'?: (props: typeof __VLS_127) => any;
37
37
  } & {
38
- 'composer-bottom'?: (props: typeof __VLS_183) => any;
38
+ 'composer-bottom'?: (props: typeof __VLS_191) => any;
39
39
  } & {
40
- 'failed-message-actions'?: (props: typeof __VLS_202) => any;
40
+ 'failed-message-actions'?: (props: typeof __VLS_210) => any;
41
41
  } & {
42
- 'composer-bottom'?: (props: typeof __VLS_214) => any;
42
+ 'composer-bottom'?: (props: typeof __VLS_223) => any;
43
43
  };
44
44
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
45
45
  "update:conversationId": (value: string | null) => any;
@@ -23,10 +23,6 @@ import {
23
23
  resolveTelaAgentInputs,
24
24
  useChat
25
25
  } from "../../messages/composables/chat";
26
- import {
27
- ensureConversationUsesSelectedModel,
28
- MODEL_UPDATE_FAILED_MESSAGE
29
- } from "../../messages/model-availability";
30
26
  import { useChatAction } from "../../messages/composables/chat-action";
31
27
  import { useChatApi } from "../../shared/composables/chat-api";
32
28
  import { useConversationFiles } from "../../files/composables/conversation-files";
@@ -95,8 +91,6 @@ const {
95
91
  createConversation,
96
92
  deleteConversation,
97
93
  renameConversation,
98
- updateConversationModel,
99
- updateConversationReasoningEffort,
100
94
  duplicateConversation,
101
95
  forkConversation,
102
96
  moveConversationToGroup,
@@ -173,7 +167,7 @@ const {
173
167
  setPendingConversationGroup,
174
168
  clearPendingConversationGroup
175
169
  } = usePendingConversationGroup();
176
- const resolvedFeatures = computed(() => resolveChatFeatures(props.features));
170
+ const resolvedFeatures = computed(() => resolveChatFeatures(props.features, embedConfig?.executionControls?.value));
177
171
  const dropZoneRef = ref(null);
178
172
  const activeTab = ref("chat");
179
173
  const answerTarget = ref(null);
@@ -226,9 +220,7 @@ const {
226
220
  loadConversation,
227
221
  setConversation,
228
222
  adoptConversation,
229
- setConversationModel,
230
223
  pendingModelSelection,
231
- setConversationReasoningEffort,
232
224
  pendingReasoningEffortSelection,
233
225
  sendMessage,
234
226
  flushOutboundQueue,
@@ -245,89 +237,43 @@ const {
245
237
  onQueuedSendFailed: restoreQueuedSend
246
238
  });
247
239
  const agentWake = harnessAdapter?.requiresSandboxWake === false ? null : useAgentWake();
248
- const modelUpdateInFlight = ref(null);
249
- const reasoningEffortUpdateInFlight = ref(null);
250
- const REASONING_EFFORT_UPDATE_FAILED_MESSAGE = "N\xE3o foi poss\xEDvel atualizar o esfor\xE7o de racioc\xEDnio da conversa";
251
240
  watch(chatError, (message) => {
252
241
  if (message)
253
242
  statusToast.update({ text: message, icon: "i-ph-warning" });
254
243
  });
255
244
  const { total, files: conversationFiles, loading: conversationFilesLoading } = useConversationFiles(activeConversationId);
256
- function persistConversationModel(conversation, value, wakeAgent) {
257
- const previousModel = conversation.model;
258
- setConversationModel(value);
259
- const update = updateConversationModel(conversation.id, value).then(async (updated) => {
260
- if (!updated && currentConversation.value?.id === conversation.id && currentConversation.value.model === value) {
261
- setConversationModel(previousModel);
262
- } else if (agentWake && updated && value && wakeAgent && !chatApi.conversationV2Enabled) {
263
- if (conversation.threadSessionId)
264
- await agentWake.ensureReady(conversation.threadSessionId, value);
265
- else
266
- await agentWake.wake(void 0, value);
267
- }
268
- return updated;
269
- });
270
- const trackedUpdate = update.finally(() => {
271
- if (modelUpdateInFlight.value === trackedUpdate)
272
- modelUpdateInFlight.value = null;
273
- });
274
- modelUpdateInFlight.value = trackedUpdate;
275
- return trackedUpdate;
276
- }
277
- async function waitForPendingModelUpdate() {
278
- while (modelUpdateInFlight.value) {
279
- const update = modelUpdateInFlight.value;
280
- if (!await update)
281
- throw new Error(MODEL_UPDATE_FAILED_MESSAGE);
282
- }
283
- }
284
- function persistConversationReasoningEffort(conversation, value) {
285
- const previousEffort = conversation.reasoningEffort ?? null;
286
- setConversationReasoningEffort(value);
287
- const update = updateConversationReasoningEffort(conversation.id, value).then((updated) => {
288
- if (!updated && currentConversation.value?.id === conversation.id && (currentConversation.value.reasoningEffort ?? null) === value) {
289
- setConversationReasoningEffort(previousEffort);
290
- }
291
- return updated;
292
- });
293
- const trackedUpdate = update.finally(() => {
294
- if (reasoningEffortUpdateInFlight.value === trackedUpdate)
295
- reasoningEffortUpdateInFlight.value = null;
296
- });
297
- reasoningEffortUpdateInFlight.value = trackedUpdate;
298
- return trackedUpdate;
299
- }
300
- async function waitForPendingReasoningEffortUpdate() {
301
- while (reasoningEffortUpdateInFlight.value) {
302
- const update = reasoningEffortUpdateInFlight.value;
303
- if (!await update)
304
- throw new Error(REASONING_EFFORT_UPDATE_FAILED_MESSAGE);
305
- }
306
- }
307
245
  function resolveSelectedModel(value) {
308
246
  return nativeModels ? nativeModels.resolve(value) : value;
309
247
  }
248
+ const draftModel = ref(void 0);
249
+ const draftReasoningEffort = ref(void 0);
250
+ watch(() => currentConversation.value?.id, () => {
251
+ draftModel.value = void 0;
252
+ draftReasoningEffort.value = void 0;
253
+ }, { flush: "sync" });
310
254
  const selectedModel = computed({
311
- get: () => resolveSelectedModel(
312
- currentConversation.value ? currentConversation.value.model : pendingModelSelection.value
313
- ),
255
+ get: () => resolveSelectedModel(draftModel.value !== void 0 ? draftModel.value : currentConversation.value ? currentConversation.value.model : pendingModelSelection.value),
314
256
  set: (value) => {
315
- const resolvedValue = resolveSelectedModel(value);
316
- pendingModelSelection.value = resolvedValue;
317
- const conversation = currentConversation.value;
318
- if (conversation && conversation.model !== resolvedValue)
319
- void persistConversationModel(conversation, resolvedValue, true);
257
+ const resolved = resolveSelectedModel(value);
258
+ pendingModelSelection.value = resolved;
259
+ draftModel.value = resolved;
320
260
  }
321
261
  });
322
262
  const selectedReasoningEffort = computed({
323
- get: () => currentConversation.value ? currentConversation.value.reasoningEffort ?? null : pendingReasoningEffortSelection.value,
263
+ get: () => draftReasoningEffort.value !== void 0 ? draftReasoningEffort.value : currentConversation.value ? currentConversation.value.reasoningEffort ?? null : pendingReasoningEffortSelection.value,
324
264
  set: (value) => {
325
265
  pendingReasoningEffortSelection.value = value;
326
- const conversation = currentConversation.value;
327
- if (conversation && (conversation.reasoningEffort ?? null) !== value)
328
- void persistConversationReasoningEffort(conversation, value);
266
+ draftReasoningEffort.value = value;
329
267
  }
330
268
  });
269
+ function selectExecutionProfile(profile) {
270
+ draftModel.value = resolveSelectedModel(profile.model);
271
+ draftReasoningEffort.value = profile.reasoningEffort;
272
+ if (!currentConversation.value) {
273
+ pendingModelSelection.value = draftModel.value;
274
+ pendingReasoningEffortSelection.value = profile.reasoningEffort;
275
+ }
276
+ }
331
277
  const filesCount = computed(() => total.value);
332
278
  const showDetailsPanel = computed(() => !!currentConversation.value && !isMobile.value && !props.compact);
333
279
  const detailsConversationId = computed(() => activeConversationId.value ?? currentConversation.value?.id ?? null);
@@ -413,6 +359,8 @@ async function ensureConversationLoaded(conversationId) {
413
359
  }
414
360
  }
415
361
  function resetConversation(shouldEmit = true) {
362
+ draftModel.value = void 0;
363
+ draftReasoningEffort.value = void 0;
416
364
  clearConversation();
417
365
  agentWake?.consume();
418
366
  if (agentWake && !chatApi.conversationV2Enabled)
@@ -594,14 +542,14 @@ async function handleSend(content, files, agentInputs) {
594
542
  restoreQueuedSend({ content: prepared?.retryContent?.() ?? content, files, agentInputs: resolvedAgentInputs });
595
543
  }
596
544
  async function dispatchMessage(outbound, files, resolvedAgentInputs) {
545
+ const executionSettings = { model: selectedModel.value, reasoningEffort: selectedReasoningEffort.value };
597
546
  if (props.sendBlocked)
598
547
  return false;
599
548
  let claimedWakeSessionId;
600
549
  const sent = await sendMessage(outbound, files.length > 0 ? files : void 0, resolvedAgentInputs, async (signal) => {
601
550
  if (!activeConversationId.value || !currentConversation.value) {
602
551
  const conversation2 = await createConversation({
603
- model: selectedModel.value,
604
- reasoningEffort: selectedReasoningEffort.value,
552
+ ...executionSettings,
605
553
  ...resolvedAgentInputs !== void 0 ? { agentInputs: resolvedAgentInputs } : {}
606
554
  });
607
555
  if (!conversation2)
@@ -612,36 +560,29 @@ async function dispatchMessage(outbound, files, resolvedAgentInputs) {
612
560
  adoptConversation(grouped ? { ...conversation2, groupId } : conversation2);
613
561
  updateConversationId(conversation2.id);
614
562
  }
615
- await waitForPendingModelUpdate();
616
- await waitForPendingReasoningEffortUpdate();
617
563
  const conversation = currentConversation.value;
618
564
  if (!conversation)
619
565
  throw new Error("Erro ao preparar conversa");
620
- await (nativeModels?.ensureSelectedModel ?? ensureConversationUsesSelectedModel)(
621
- conversation.model,
622
- selectedModel.value,
623
- (value) => persistConversationModel(conversation, value, false)
624
- );
625
566
  if (!agentWake || chatApi.conversationV2Enabled)
626
- return {};
567
+ return { executionSettings };
627
568
  if (conversation.threadSessionId && resolvedFeatures.value.chatOutboundQueue) {
628
- const wake = await agentWake.ensureReady(conversation.threadSessionId, selectedModel.value);
569
+ const wake = await agentWake.ensureReady(conversation.threadSessionId, executionSettings.model);
629
570
  if (wake.status !== "ready") {
630
571
  if (wake.wokenSandbox && !wake.wokenSandbox.rewakeable)
631
- return { defer: true };
572
+ return { defer: true, executionSettings };
632
573
  throw new Error("N\xE3o foi poss\xEDvel acordar o agente. Tente novamente.");
633
574
  }
634
- return {};
575
+ return { executionSettings };
635
576
  }
636
577
  if (!conversation.threadSessionId) {
637
578
  await agentWake.waitUntilSettled(signal);
638
579
  if (agentWake.state.value === "idle")
639
- await agentWake.wake(void 0, selectedModel.value);
580
+ await agentWake.wake(void 0, executionSettings.model);
640
581
  }
641
582
  claimedWakeSessionId = claimableWakeSessionId();
642
583
  if (!conversation.threadSessionId && !claimedWakeSessionId)
643
584
  throw new Error("N\xE3o foi poss\xEDvel acordar o agente. Tente novamente.");
644
- return claimedWakeSessionId ? { wakeSessionId: claimedWakeSessionId } : {};
585
+ return { executionSettings, ...claimedWakeSessionId ? { wakeSessionId: claimedWakeSessionId } : {} };
645
586
  });
646
587
  if (sent && claimedWakeSessionId)
647
588
  agentWake?.consume();
@@ -684,26 +625,21 @@ async function handleCancel() {
684
625
  await cancelConversation();
685
626
  }
686
627
  async function handleRetry(messageId) {
628
+ const executionSettings = { model: selectedModel.value, reasoningEffort: selectedReasoningEffort.value };
687
629
  await retryMessage(messageId, async () => {
688
- await waitForPendingModelUpdate();
689
- await waitForPendingReasoningEffortUpdate();
690
630
  const conversation = currentConversation.value;
691
631
  if (!conversation)
692
632
  throw new Error("Erro ao preparar conversa");
693
- await (nativeModels?.ensureSelectedModel ?? ensureConversationUsesSelectedModel)(
694
- conversation.model,
695
- selectedModel.value,
696
- (value) => persistConversationModel(conversation, value, false)
697
- );
698
633
  if (!agentWake || chatApi.conversationV2Enabled)
699
- return;
634
+ return { executionSettings };
700
635
  if (conversation.threadSessionId) {
701
- const wake = await agentWake.ensureReady(conversation.threadSessionId, selectedModel.value);
636
+ const wake = await agentWake.ensureReady(conversation.threadSessionId, executionSettings.model);
702
637
  if (wake.status !== "ready")
703
638
  throw new Error("N\xE3o foi poss\xEDvel acordar o agente. Tente novamente.");
704
639
  } else {
705
640
  agentWake.consume();
706
641
  }
642
+ return { executionSettings };
707
643
  });
708
644
  }
709
645
  async function handleNewConversation(groupId) {
@@ -790,30 +726,35 @@ function handleShare(conversationId) {
790
726
  if (id)
791
727
  void copyConversationLink(id);
792
728
  }
793
- const forkEvents = shallowRef([]);
729
+ const persistedConversationEvents = shallowRef([]);
794
730
  const displayConversationEvents = computed(() => [...new Map([
795
- ...forkEvents.value,
731
+ ...persistedConversationEvents.value,
796
732
  ...props.conversationEvents ?? []
797
733
  ].filter((event) => event.conversationId === currentConversation.value?.id).map((event) => [event.id, event])).values()]);
798
734
  watch([
799
735
  () => currentConversation.value?.id,
800
- () => JSON.stringify(chatApi.withChatHeaders().headers)
801
- ], async ([conversationId], _previous, onCleanup) => {
802
- forkEvents.value = [];
803
- if (!conversationId || !chatApi.conversationV2Enabled)
736
+ () => JSON.stringify(chatApi.withChatHeaders().headers),
737
+ () => JSON.stringify(messages.value.filter((message) => message.role === "user").map((message) => [message.id, message.createdAt, message.externalUuid])),
738
+ () => isPolling.value
739
+ ], async ([conversationId], previous, onCleanup) => {
740
+ if (conversationId !== previous?.[0] || JSON.stringify(chatApi.withChatHeaders().headers) !== previous?.[1])
741
+ persistedConversationEvents.value = [];
742
+ if (!conversationId)
804
743
  return;
805
- const controller = new AbortController();
806
- onCleanup(() => controller.abort());
744
+ let current = true;
745
+ onCleanup(() => {
746
+ current = false;
747
+ });
807
748
  try {
808
749
  const { events } = await $fetch(
809
750
  chatApi.path(`/conversations/${conversationId}/events`),
810
- chatApi.withChatHeaders({ signal: controller.signal })
751
+ chatApi.withChatHeaders()
811
752
  );
812
- if (!controller.signal.aborted)
813
- forkEvents.value = events.filter((event) => event.type === "conversation_forked");
753
+ if (current)
754
+ persistedConversationEvents.value = events;
814
755
  } catch {
815
- if (!controller.signal.aborted)
816
- statusToast.update({ text: "Erro ao carregar a origem da conversa", icon: "i-ph-warning" });
756
+ if (current)
757
+ statusToast.update({ text: "Erro ao carregar os eventos da conversa", icon: "i-ph-warning" });
817
758
  }
818
759
  }, { immediate: true });
819
760
  const forking = ref(false);
@@ -1005,6 +946,7 @@ async function handleDelete() {
1005
946
  :show-cancel-button="resolvedFeatures.showCancelButton"
1006
947
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1007
948
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
949
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1008
950
  :show-debug-option="resolvedFeatures.showDebugOption"
1009
951
  :show-outdated-badge="showOutdatedBadge"
1010
952
  :show-outdated-banner="showOutdatedBanner"
@@ -1012,6 +954,7 @@ async function handleDelete() {
1012
954
  :workspace-logo="workspaceLogo"
1013
955
  :workspace-name="workspaceName"
1014
956
  :workspace-settings="effectiveWorkspaceSettings"
957
+ @select-execution-profile="selectExecutionProfile"
1015
958
  @cancel="handleCancel"
1016
959
  @cancel-editing="cancelEditing"
1017
960
  @copy-link="handleShare()"
@@ -1048,6 +991,8 @@ async function handleDelete() {
1048
991
  :show-cancel-button="resolvedFeatures.showCancelButton"
1049
992
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1050
993
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
994
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
995
+ @select-execution-profile="selectExecutionProfile"
1051
996
  />
1052
997
  </template>
1053
998
 
@@ -1093,7 +1038,9 @@ async function handleDelete() {
1093
1038
  :show-cancel-button="resolvedFeatures.showCancelButton"
1094
1039
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1095
1040
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
1041
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1096
1042
  centered
1043
+ @select-execution-profile="selectExecutionProfile"
1097
1044
  @send="handleSend"
1098
1045
  @cancel="handleCancel"
1099
1046
  />
@@ -1147,7 +1094,9 @@ async function handleDelete() {
1147
1094
  :show-cancel-button="resolvedFeatures.showCancelButton"
1148
1095
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1149
1096
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
1097
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1150
1098
  has-disclaimer
1099
+ @select-execution-profile="selectExecutionProfile"
1151
1100
  @send="handleSend"
1152
1101
  @cancel="handleCancel"
1153
1102
  />
@@ -1246,6 +1195,8 @@ async function handleDelete() {
1246
1195
  :show-cancel-button="resolvedFeatures.showCancelButton"
1247
1196
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1248
1197
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
1198
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1199
+ @select-execution-profile="selectExecutionProfile"
1249
1200
  />
1250
1201
  </TelaChatBody>
1251
1202
 
@@ -1297,7 +1248,9 @@ async function handleDelete() {
1297
1248
  :show-cancel-button="resolvedFeatures.showCancelButton"
1298
1249
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1299
1250
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
1251
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1300
1252
  centered
1253
+ @select-execution-profile="selectExecutionProfile"
1301
1254
  @send="handleSend"
1302
1255
  @cancel="handleCancel"
1303
1256
  />
@@ -1354,7 +1307,9 @@ async function handleDelete() {
1354
1307
  :show-cancel-button="resolvedFeatures.showCancelButton"
1355
1308
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1356
1309
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
1310
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1357
1311
  has-disclaimer
1312
+ @select-execution-profile="selectExecutionProfile"
1358
1313
  @send="handleSend"
1359
1314
  @cancel="handleCancel"
1360
1315
  />
@@ -19,27 +19,27 @@ type __VLS_Props = {
19
19
  user?: EmbedUser | null;
20
20
  features?: Partial<ChatFeatureConfig>;
21
21
  };
22
- declare var __VLS_14: {}, __VLS_88: {}, __VLS_101: {
22
+ declare var __VLS_14: {}, __VLS_92: {}, __VLS_105: {
23
23
  message: useChatAuth;
24
- }, __VLS_113: {}, __VLS_122: {}, __VLS_183: {}, __VLS_202: {
24
+ }, __VLS_118: {}, __VLS_127: {}, __VLS_191: {}, __VLS_210: {
25
25
  message: useChatAuth;
26
- }, __VLS_214: {};
26
+ }, __VLS_223: {};
27
27
  type __VLS_Slots = {} & {
28
28
  'sidebar-bottom'?: (props: typeof __VLS_14) => any;
29
29
  } & {
30
- 'composer-bottom'?: (props: typeof __VLS_88) => any;
30
+ 'composer-bottom'?: (props: typeof __VLS_92) => any;
31
31
  } & {
32
- 'failed-message-actions'?: (props: typeof __VLS_101) => any;
32
+ 'failed-message-actions'?: (props: typeof __VLS_105) => any;
33
33
  } & {
34
- 'composer-bottom'?: (props: typeof __VLS_113) => any;
34
+ 'composer-bottom'?: (props: typeof __VLS_118) => any;
35
35
  } & {
36
- 'topbar-leading'?: (props: typeof __VLS_122) => any;
36
+ 'topbar-leading'?: (props: typeof __VLS_127) => any;
37
37
  } & {
38
- 'composer-bottom'?: (props: typeof __VLS_183) => any;
38
+ 'composer-bottom'?: (props: typeof __VLS_191) => any;
39
39
  } & {
40
- 'failed-message-actions'?: (props: typeof __VLS_202) => any;
40
+ 'failed-message-actions'?: (props: typeof __VLS_210) => any;
41
41
  } & {
42
- 'composer-bottom'?: (props: typeof __VLS_214) => any;
42
+ 'composer-bottom'?: (props: typeof __VLS_223) => any;
43
43
  };
44
44
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
45
45
  "update:conversationId": (value: string | null) => any;
@@ -20,6 +20,7 @@ const props = defineProps({
20
20
  defaultConversationCreatorFilter: { type: null, required: false },
21
21
  availableModels: { type: Array, required: false },
22
22
  defaultModel: { type: String, required: false },
23
+ executionControls: { type: Object, required: false },
23
24
  defaultReasoningEffort: { type: null, required: false },
24
25
  conversationCreatorFilter: { type: null, required: false },
25
26
  hideConversationCreatorFilter: { type: Boolean, required: false },
@@ -86,6 +87,7 @@ provideEmbedConfig({
86
87
  conversationScope: normalizedConversationScope,
87
88
  defaultConversationCreatorFilter: computed(() => props.defaultConversationCreatorFilter),
88
89
  availableModels: computed(() => props.availableModels),
90
+ executionControls: computed(() => props.executionControls),
89
91
  defaultModel: computed(() => props.defaultModel),
90
92
  defaultReasoningEffort: computed(() => props.defaultReasoningEffort),
91
93
  conversationCreatorFilter: computed(() => props.conversationCreatorFilter),
@@ -12,6 +12,7 @@ const { hideSidebar = void 0, syncBrowserState = true, closeOnOutsideClick = tru
12
12
  defaultConversationCreatorFilter: { type: null, required: false },
13
13
  availableModels: { type: Array, required: false },
14
14
  defaultModel: { type: String, required: false },
15
+ executionControls: { type: Object, required: false },
15
16
  defaultReasoningEffort: { type: null, required: false },
16
17
  conversationCreatorFilter: { type: null, required: false },
17
18
  hideConversationCreatorFilter: { type: Boolean, required: false },
@@ -78,6 +79,7 @@ const sharedEmbedProps = computed(() => ({
78
79
  conversationScope: props.conversationScope,
79
80
  defaultConversationCreatorFilter: props.defaultConversationCreatorFilter,
80
81
  availableModels: props.availableModels,
82
+ executionControls: props.executionControls,
81
83
  defaultModel: props.defaultModel,
82
84
  defaultReasoningEffort: props.defaultReasoningEffort,
83
85
  conversationCreatorFilter: props.conversationCreatorFilter,
@@ -1,3 +1,4 @@
1
+ import type { ChatExecutionProfile } from '../../execution-controls.js';
1
2
  import type { DeepReadonly } from 'vue';
2
3
  import type { Conversation, ConversationWithMessages, Message } from '../../../types/schemas/chat/conversations.js';
3
4
  import type { ConversationFile } from '../../../types/schemas/chat/files.js';
@@ -35,6 +36,7 @@ type __VLS_Props = {
35
36
  showCancelButton?: boolean;
36
37
  hideModelSelector?: boolean;
37
38
  hideReasoningEffortSelector?: boolean;
39
+ executionControlsDisabled?: boolean;
38
40
  showDebugOption?: boolean;
39
41
  showOutdatedBadge?: boolean;
40
42
  showOutdatedBanner?: boolean;
@@ -49,13 +51,13 @@ type __VLS_ModelProps = {
49
51
  'selectedReasoningEffort'?: ChatReasoningEffort | null;
50
52
  };
51
53
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
52
- declare var __VLS_54: {}, __VLS_99: {}, __VLS_101: {};
54
+ declare var __VLS_54: {}, __VLS_100: {}, __VLS_102: {};
53
55
  type __VLS_Slots = {} & {
54
56
  loading?: (props: typeof __VLS_54) => any;
55
57
  } & {
56
- empty?: (props: typeof __VLS_99) => any;
58
+ empty?: (props: typeof __VLS_100) => any;
57
59
  } & {
58
- default?: (props: typeof __VLS_101) => any;
60
+ default?: (props: typeof __VLS_102) => any;
59
61
  };
60
62
  declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
61
63
  "update:selectedModel": (value: string | null) => any;
@@ -71,6 +73,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
71
73
  selectConversation: (id: string) => any;
72
74
  settings: () => any;
73
75
  send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
76
+ selectExecutionProfile: (profile: ChatExecutionProfile) => any;
74
77
  tabChange: (tab: TopbarTab) => any;
75
78
  newConversation: (groupId?: string | null | undefined) => any;
76
79
  switchWorkspace: () => any;
@@ -92,6 +95,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
92
95
  onSelectConversation?: ((id: string) => any) | undefined;
93
96
  onSettings?: (() => any) | undefined;
94
97
  onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
98
+ onSelectExecutionProfile?: ((profile: ChatExecutionProfile) => any) | undefined;
95
99
  "onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
96
100
  "onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
97
101
  onTabChange?: ((tab: TopbarTab) => any) | undefined;