@meistrari/chat-nuxt 4.7.0 → 4.8.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 (30) hide show
  1. package/README.md +58 -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 +43 -1
  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 +4 -0
  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 +21 -1
  26. package/dist/runtime/messages/components/chat-message-input.vue.d.ts +4 -0
  27. package/dist/runtime/messages/composables/chat.d.ts +5 -0
  28. package/dist/runtime/messages/composables/chat.js +27 -1
  29. package/dist/runtime/types/embed.d.ts +1 -0
  30. 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,59 @@ 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 sends model and effort in
209
+ one update; a failed update restores both values. Sending waits for that update.
210
+
211
+ In `simple` mode, new conversations start with the `default` profile, regardless
212
+ of `defaultModel` and `defaultReasoningEffort`. A selection stays within the current
213
+ conversation. Changing control modes preserves explicit draft choices. Starting
214
+ a new conversation discards a simple selection even if the controls have since
215
+ been hidden or switched to advanced mode. If only one field is manually changed in advanced mode, the remaining simple
216
+ selection still causes both fields to reset for a new conversation. Manually
217
+ replacing both fields in advanced mode retains the existing behavior of carrying
218
+ them over to new conversations.
219
+ Restored conversations keep their settings: the control shows
220
+ **Personalizado** when the saved model and effort do not match either profile.
221
+ The menu still offers only **Padrão** and **Aprofundar**. Changes apply to subsequent
222
+ messages and do not regenerate earlier responses. If profiles are identical,
223
+ **Padrão** wins the label match.
224
+
225
+ In `hidden` and `advanced` modes, the existing model and effort defaults and saved
226
+ conversation settings retain their behavior. Hiding controls does not force a
227
+ model or effort. `availableModels` continues to filter the advanced model picker;
228
+ it does not filter or rewrite the simple profiles.
229
+
230
+ | Legacy configuration | Replacement |
231
+ | --- | --- |
232
+ | Both picker flags `false` | `{ mode: 'hidden' }` |
233
+ | Both picker flags `true` | `{ mode: 'advanced' }` |
234
+ | Only effort picker `false` | `{ mode: 'advanced', showReasoningEffortSelector: false }` |
235
+ | Only model picker `false` | `{ mode: 'advanced', showModelSelector: false }` |
236
+
237
+ Advanced picker flags default to `true`. Import `ChatExecutionControls` and
238
+ `ChatExecutionProfile` from `@meistrari/chat-nuxt/types/embed` when declaring host
239
+ configuration separately. `defaultModel` and `defaultReasoningEffort` are not
240
+ 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.0-rc.1",
4
+ "version": "4.8.0-rc.0",
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;
@@ -97,6 +97,7 @@ const {
97
97
  renameConversation,
98
98
  updateConversationModel,
99
99
  updateConversationReasoningEffort,
100
+ updateConversationExecutionProfile,
100
101
  duplicateConversation,
101
102
  forkConversation,
102
103
  moveConversationToGroup,
@@ -173,7 +174,7 @@ const {
173
174
  setPendingConversationGroup,
174
175
  clearPendingConversationGroup
175
176
  } = usePendingConversationGroup();
176
- const resolvedFeatures = computed(() => resolveChatFeatures(props.features));
177
+ const resolvedFeatures = computed(() => resolveChatFeatures(props.features, embedConfig?.executionControls?.value));
177
178
  const dropZoneRef = ref(null);
178
179
  const activeTab = ref("chat");
179
180
  const answerTarget = ref(null);
@@ -229,6 +230,7 @@ const {
229
230
  setConversationModel,
230
231
  pendingModelSelection,
231
232
  setConversationReasoningEffort,
233
+ setConversationExecutionProfile,
232
234
  pendingReasoningEffortSelection,
233
235
  sendMessage,
234
236
  flushOutboundQueue,
@@ -328,6 +330,32 @@ const selectedReasoningEffort = computed({
328
330
  void persistConversationReasoningEffort(conversation, value);
329
331
  }
330
332
  });
333
+ function selectExecutionProfile(profile) {
334
+ if (modelUpdateInFlight.value || reasoningEffortUpdateInFlight.value)
335
+ return;
336
+ const conversation = currentConversation.value;
337
+ if (!conversation) {
338
+ pendingModelSelection.value = profile.model;
339
+ pendingReasoningEffortSelection.value = profile.reasoningEffort;
340
+ return;
341
+ }
342
+ const previous = { model: conversation.model, reasoningEffort: conversation.reasoningEffort ?? null };
343
+ setConversationExecutionProfile(profile);
344
+ const update = updateConversationExecutionProfile(conversation.id, profile).then((updated) => {
345
+ if (!updated) {
346
+ if (currentConversation.value?.id === conversation.id && currentConversation.value.model === profile.model && (currentConversation.value.reasoningEffort ?? null) === profile.reasoningEffort) {
347
+ setConversationExecutionProfile(previous);
348
+ }
349
+ statusToast.update({ text: "N\xE3o foi poss\xEDvel atualizar o modo de resposta da conversa", icon: "i-ph-warning" });
350
+ }
351
+ return updated;
352
+ });
353
+ const trackedUpdate = update.finally(() => {
354
+ if (modelUpdateInFlight.value === trackedUpdate)
355
+ modelUpdateInFlight.value = null;
356
+ });
357
+ modelUpdateInFlight.value = trackedUpdate;
358
+ }
331
359
  const filesCount = computed(() => total.value);
332
360
  const showDetailsPanel = computed(() => !!currentConversation.value && !isMobile.value && !props.compact);
333
361
  const detailsConversationId = computed(() => activeConversationId.value ?? currentConversation.value?.id ?? null);
@@ -1005,6 +1033,7 @@ async function handleDelete() {
1005
1033
  :show-cancel-button="resolvedFeatures.showCancelButton"
1006
1034
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1007
1035
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
1036
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1008
1037
  :show-debug-option="resolvedFeatures.showDebugOption"
1009
1038
  :show-outdated-badge="showOutdatedBadge"
1010
1039
  :show-outdated-banner="showOutdatedBanner"
@@ -1012,6 +1041,7 @@ async function handleDelete() {
1012
1041
  :workspace-logo="workspaceLogo"
1013
1042
  :workspace-name="workspaceName"
1014
1043
  :workspace-settings="effectiveWorkspaceSettings"
1044
+ @select-execution-profile="selectExecutionProfile"
1015
1045
  @cancel="handleCancel"
1016
1046
  @cancel-editing="cancelEditing"
1017
1047
  @copy-link="handleShare()"
@@ -1048,6 +1078,8 @@ async function handleDelete() {
1048
1078
  :show-cancel-button="resolvedFeatures.showCancelButton"
1049
1079
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1050
1080
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
1081
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1082
+ @select-execution-profile="selectExecutionProfile"
1051
1083
  />
1052
1084
  </template>
1053
1085
 
@@ -1093,7 +1125,9 @@ async function handleDelete() {
1093
1125
  :show-cancel-button="resolvedFeatures.showCancelButton"
1094
1126
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1095
1127
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
1128
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1096
1129
  centered
1130
+ @select-execution-profile="selectExecutionProfile"
1097
1131
  @send="handleSend"
1098
1132
  @cancel="handleCancel"
1099
1133
  />
@@ -1147,7 +1181,9 @@ async function handleDelete() {
1147
1181
  :show-cancel-button="resolvedFeatures.showCancelButton"
1148
1182
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1149
1183
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
1184
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1150
1185
  has-disclaimer
1186
+ @select-execution-profile="selectExecutionProfile"
1151
1187
  @send="handleSend"
1152
1188
  @cancel="handleCancel"
1153
1189
  />
@@ -1246,6 +1282,8 @@ async function handleDelete() {
1246
1282
  :show-cancel-button="resolvedFeatures.showCancelButton"
1247
1283
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1248
1284
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
1285
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1286
+ @select-execution-profile="selectExecutionProfile"
1249
1287
  />
1250
1288
  </TelaChatBody>
1251
1289
 
@@ -1297,7 +1335,9 @@ async function handleDelete() {
1297
1335
  :show-cancel-button="resolvedFeatures.showCancelButton"
1298
1336
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1299
1337
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
1338
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1300
1339
  centered
1340
+ @select-execution-profile="selectExecutionProfile"
1301
1341
  @send="handleSend"
1302
1342
  @cancel="handleCancel"
1303
1343
  />
@@ -1354,7 +1394,9 @@ async function handleDelete() {
1354
1394
  :show-cancel-button="resolvedFeatures.showCancelButton"
1355
1395
  :hide-model-selector="!resolvedFeatures.showModelSelector"
1356
1396
  :hide-reasoning-effort-selector="!resolvedFeatures.showReasoningEffortSelector"
1397
+ :execution-controls-disabled="!!modelUpdateInFlight || !!reasoningEffortUpdateInFlight"
1357
1398
  has-disclaimer
1399
+ @select-execution-profile="selectExecutionProfile"
1358
1400
  @send="handleSend"
1359
1401
  @cancel="handleCancel"
1360
1402
  />
@@ -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;
@@ -41,6 +41,7 @@ const props = defineProps({
41
41
  showCancelButton: { type: Boolean, required: false },
42
42
  hideModelSelector: { type: Boolean, required: false },
43
43
  hideReasoningEffortSelector: { type: Boolean, required: false },
44
+ executionControlsDisabled: { type: Boolean, required: false },
44
45
  showDebugOption: { type: Boolean, required: false },
45
46
  showOutdatedBadge: { type: Boolean, required: false },
46
47
  showOutdatedBanner: { type: Boolean, required: false },
@@ -50,7 +51,7 @@ const props = defineProps({
50
51
  workspaceSettings: { type: null, required: false },
51
52
  workspaceSheetOpen: { type: Boolean, required: true }
52
53
  });
53
- const emit = defineEmits(["cancel", "cancelEditing", "copyLink", "debug", "delete", "dismissOutdatedBanner", "duplicate", "export", "newConversation", "rename", "saveTitle", "selectConversation", "send", "settings", "signout", "switchWorkspace", "tabChange", "update:drawerOpen", "update:editTitle", "update:workspaceSheetOpen"]);
54
+ const emit = defineEmits(["cancel", "selectExecutionProfile", "cancelEditing", "copyLink", "debug", "delete", "dismissOutdatedBanner", "duplicate", "export", "newConversation", "rename", "saveTitle", "selectConversation", "send", "settings", "signout", "switchWorkspace", "tabChange", "update:drawerOpen", "update:editTitle", "update:workspaceSheetOpen"]);
54
55
  const embedConfig = useEmbedConfig();
55
56
  const emptyStateLabels = computed(() => resolveEmptyStateLabels(embedConfig?.labels.value, props.currentUser?.name));
56
57
  const selectedModel = defineModel("selectedModel", { type: [String, null], ...{ default: null } });
@@ -247,7 +248,9 @@ function startNewConversationFromOutdatedSettings() {
247
248
  :show-cancel-button="showCancelButton"
248
249
  :hide-model-selector="hideModelSelector"
249
250
  :hide-reasoning-effort-selector="hideReasoningEffortSelector"
251
+ :execution-controls-disabled="executionControlsDisabled"
250
252
  centered
253
+ @select-execution-profile="emit('selectExecutionProfile', $event)"
251
254
  @send="(content, files, agentInputs) => emit('send', content, files, agentInputs)"
252
255
  @cancel="emit('cancel')"
253
256
  />
@@ -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;
@@ -1,3 +1,4 @@
1
+ import type { ChatExecutionControls } from '../execution-controls.js';
1
2
  import type { ChatReasoningEffort } from '../../types/schemas/chat/models.js';
2
3
  import type { Component, DeepReadonly, MaybeRefOrGetter, Ref } from 'vue';
3
4
  import type { ConversationCreatorFilter } from '../../types/schemas/chat/primitives.js';
@@ -15,6 +16,7 @@ export type EmbedConfig = {
15
16
  conversationScope: Ref<string | null>;
16
17
  defaultConversationCreatorFilter: Ref<ConversationCreatorFilter>;
17
18
  availableModels: Ref<AvailableChatModels | undefined>;
19
+ executionControls: Ref<ChatExecutionControls | undefined>;
18
20
  defaultModel: Ref<string | undefined>;
19
21
  defaultReasoningEffort: Ref<ChatReasoningEffort | undefined>;
20
22
  conversationCreatorFilter: Ref<ConversationCreatorFilter | undefined>;
@@ -38,6 +40,7 @@ type EmbedConfigInput = {
38
40
  conversationScope?: MaybeRefOrGetter<string | null | undefined>;
39
41
  defaultConversationCreatorFilter?: MaybeRefOrGetter<ConversationCreatorFilter | null | undefined>;
40
42
  availableModels?: MaybeRefOrGetter<AvailableChatModels | undefined>;
43
+ executionControls?: MaybeRefOrGetter<ChatExecutionControls | undefined>;
41
44
  defaultModel?: MaybeRefOrGetter<string | undefined>;
42
45
  defaultReasoningEffort?: MaybeRefOrGetter<ChatReasoningEffort | undefined>;
43
46
  conversationCreatorFilter?: MaybeRefOrGetter<ConversationCreatorFilter | undefined>;
@@ -23,6 +23,7 @@ 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
+ executionControls: useState("chat-embed-config-execution-controls", () => void 0),
26
27
  defaultModel: useState("chat-embed-config-default-model", () => void 0),
27
28
  defaultReasoningEffort: useState("chat-embed-config-default-reasoning-effort", () => void 0),
28
29
  conversationCreatorFilter: useState("chat-embed-config-conversation-creator-filter", () => void 0),
@@ -48,6 +49,7 @@ export function provideEmbedConfig(input, options = {}) {
48
49
  conversationScope: computed(() => normalizeConversationScope(toValue(input.conversationScope))),
49
50
  defaultConversationCreatorFilter: computed(() => toValue(input.defaultConversationCreatorFilter) ?? "all"),
50
51
  availableModels: computed(() => toValue(input.availableModels)),
52
+ executionControls: computed(() => toValue(input.executionControls)),
51
53
  defaultModel: computed(() => toValue(input.defaultModel)),
52
54
  defaultReasoningEffort: computed(() => toValue(input.defaultReasoningEffort)),
53
55
  conversationCreatorFilter: computed(() => toValue(input.conversationCreatorFilter)),
@@ -74,6 +76,7 @@ export function provideEmbedConfig(input, options = {}) {
74
76
  globalConfig.conversationScope.value = config.conversationScope.value;
75
77
  globalConfig.defaultConversationCreatorFilter.value = config.defaultConversationCreatorFilter.value;
76
78
  globalConfig.availableModels.value = config.availableModels.value;
79
+ globalConfig.executionControls.value = config.executionControls.value;
77
80
  globalConfig.defaultModel.value = config.defaultModel.value;
78
81
  globalConfig.defaultReasoningEffort.value = config.defaultReasoningEffort.value;
79
82
  globalConfig.conversationCreatorFilter.value = config.conversationCreatorFilter.value;
@@ -107,6 +110,7 @@ export function useEmbedConfig() {
107
110
  conversationScope: computed(() => globalConfig.conversationScope.value),
108
111
  defaultConversationCreatorFilter: computed(() => globalConfig.defaultConversationCreatorFilter.value ?? "all"),
109
112
  availableModels: computed(() => globalConfig.availableModels.value),
113
+ executionControls: computed(() => globalConfig.executionControls.value),
110
114
  defaultModel: computed(() => globalConfig.defaultModel.value),
111
115
  defaultReasoningEffort: computed(() => globalConfig.defaultReasoningEffort.value),
112
116
  conversationCreatorFilter: computed(() => globalConfig.conversationCreatorFilter.value),
@@ -0,0 +1,26 @@
1
+ import type { ChatReasoningEffort } from '../types/schemas/chat/models.js';
2
+ import type { ChatFeatureConfig } from './feature-config.js';
3
+ export type ChatExecutionProfile = {
4
+ model: string;
5
+ reasoningEffort: ChatReasoningEffort | null;
6
+ };
7
+ export type ChatExecutionControls = {
8
+ mode: 'hidden';
9
+ } | {
10
+ mode: 'simple';
11
+ profiles: {
12
+ default: ChatExecutionProfile;
13
+ deep: ChatExecutionProfile;
14
+ };
15
+ } | {
16
+ mode: 'advanced';
17
+ showModelSelector?: boolean;
18
+ showReasoningEffortSelector?: boolean;
19
+ };
20
+ export declare function resolveChatExecutionControls(controls?: ChatExecutionControls, features?: ChatFeatureConfig): ChatExecutionControls;
21
+ export declare function resolveChatExecutionMode(profiles: Extract<ChatExecutionControls, {
22
+ mode: 'simple';
23
+ }>['profiles'], selection: {
24
+ model: string | null;
25
+ reasoningEffort?: ChatReasoningEffort | null;
26
+ }): 'default' | 'deep' | 'custom';
@@ -0,0 +1,18 @@
1
+ export function resolveChatExecutionControls(controls, features) {
2
+ if (controls && controls.mode !== "advanced")
3
+ return controls;
4
+ const flags = controls ?? features;
5
+ return {
6
+ mode: "advanced",
7
+ showModelSelector: flags?.showModelSelector ?? true,
8
+ showReasoningEffortSelector: flags?.showReasoningEffortSelector ?? true
9
+ };
10
+ }
11
+ export function resolveChatExecutionMode(profiles, selection) {
12
+ for (const mode of ["default", "deep"]) {
13
+ const profile = profiles[mode];
14
+ if (profile.model === selection.model && profile.reasoningEffort === (selection.reasoningEffort ?? null))
15
+ return mode;
16
+ }
17
+ return "custom";
18
+ }
@@ -1,3 +1,4 @@
1
+ import { type ChatExecutionControls } from './execution-controls.js';
1
2
  /** Optional feature toggles the host passes per embed instance. */
2
3
  export type ChatFeatureConfig = {
3
4
  /** Cost section in the conversation details panel, with per-conversation token and cost stats. Defaults to true. */
@@ -13,11 +14,13 @@ export type ChatFeatureConfig = {
13
14
  /** `@` / `/` shortcut hints below the empty-state composer. Defaults to true. */
14
15
  showPromptInputHints?: boolean;
15
16
  /**
17
+ * @deprecated Use executionControls: { mode: 'advanced', showModelSelector: false }.
16
18
  * Model picker in the composer toolbar. Defaults to true. Hiding it keeps model
17
19
  * resolution unchanged: `defaultModel`, the conversation's saved model, or the agent default.
18
20
  */
19
21
  showModelSelector?: boolean;
20
22
  /**
23
+ * @deprecated Use executionControls: { mode: 'advanced', showReasoningEffortSelector: false }.
21
24
  * Reasoning effort picker in the composer toolbar. Defaults to true. Hiding it keeps
22
25
  * effort resolution unchanged: `defaultReasoningEffort`, the conversation's saved effort,
23
26
  * or the agent default.
@@ -34,4 +37,4 @@ export type ResolvedChatFeatureConfig = {
34
37
  showModelSelector: boolean;
35
38
  showReasoningEffortSelector: boolean;
36
39
  };
37
- export declare function resolveChatFeatures(features?: Partial<ChatFeatureConfig>): ResolvedChatFeatureConfig;
40
+ export declare function resolveChatFeatures(features?: Partial<ChatFeatureConfig>, executionControls?: ChatExecutionControls): ResolvedChatFeatureConfig;
@@ -1,4 +1,6 @@
1
- export function resolveChatFeatures(features) {
1
+ import { resolveChatExecutionControls } from "./execution-controls.js";
2
+ export function resolveChatFeatures(features, executionControls) {
3
+ const controls = resolveChatExecutionControls(executionControls, features);
2
4
  return {
3
5
  showUsageTab: features?.showUsageTab ?? true,
4
6
  showDebugOption: features?.showDebugOption ?? true,
@@ -6,7 +8,7 @@ export function resolveChatFeatures(features) {
6
8
  showTtft: features?.showTtft ?? true,
7
9
  chatOutboundQueue: features?.chatOutboundQueue ?? true,
8
10
  showPromptInputHints: features?.showPromptInputHints ?? true,
9
- showModelSelector: features?.showModelSelector ?? true,
10
- showReasoningEffortSelector: features?.showReasoningEffortSelector ?? true
11
+ showModelSelector: controls.mode === "advanced" && controls.showModelSelector === true,
12
+ showReasoningEffortSelector: controls.mode === "advanced" && controls.showReasoningEffortSelector === true
11
13
  };
12
14
  }
@@ -5,6 +5,7 @@ import type { WorkspaceSettingsResponse as WorkspaceSettings } from '../types/sc
5
5
  import type { TelaAgentExecutionInput } from '../types/schemas/integrations/tela-agent-api.js';
6
6
  import type { ConversationCreatorFilter } from '../types/schemas/chat/primitives.js';
7
7
  import type { ChatLoadingMessageMode } from './loading-messages.js';
8
+ import type { ChatExecutionControls } from './execution-controls.js';
8
9
  import type { ChatFeatureConfig } from './feature-config.js';
9
10
  import type { ChatCitationsConfig } from '../citations/types.js';
10
11
  import type { MessageFeedbackConfig, MessageFeedbackRating } from '../messages/composables/chat-feedback.js';
@@ -95,6 +96,8 @@ export type ChatEmbedSharedProps = {
95
96
  * Initial model for untouched drafts. Existing conversations and explicit selections take precedence.
96
97
  */
97
98
  defaultModel?: string;
99
+ /** Execution controls. Omission preserves legacy feature flags; simple profiles supply draft defaults. */
100
+ executionControls?: ChatExecutionControls;
98
101
  /**
99
102
  * Initial effort for untouched drafts. Omission preserves automatic reasoning;
100
103
  * an explicit user selection (including null) takes precedence.
@@ -25,6 +25,10 @@ export declare function useChat(options?: Parameters<typeof useSharedChat>[0]):
25
25
  setConversationModel: (model: string | null) => void;
26
26
  pendingModelSelection: any;
27
27
  setConversationReasoningEffort: (reasoningEffort: ChatReasoningEffort | null) => void;
28
+ setConversationExecutionProfile: (profile: {
29
+ model: string | null;
30
+ reasoningEffort: import("../../../embed/execution-controls.js").ChatExecutionProfile["reasoningEffort"];
31
+ }) => void;
28
32
  pendingReasoningEffortSelection: any;
29
33
  sendMessage: (content: string, files?: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[], prepare?: (signal: AbortSignal) => Promise<{
30
34
  wakeSessionId?: string;
@@ -0,0 +1,17 @@
1
+ import type { ChatReasoningEffort } from '../../types/schemas/chat/models.js';
2
+ import { type ChatExecutionControls, type ChatExecutionProfile } from '../../embed/execution-controls.js';
3
+ type __VLS_Props = {
4
+ profiles: Extract<ChatExecutionControls, {
5
+ mode: 'simple';
6
+ }>['profiles'];
7
+ model: string | null;
8
+ reasoningEffort: ChatReasoningEffort | null;
9
+ disabled?: boolean;
10
+ };
11
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
12
+ select: (profile: ChatExecutionProfile) => any;
13
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
14
+ onSelect?: ((profile: ChatExecutionProfile) => any) | undefined;
15
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -0,0 +1,40 @@
1
+ <script setup>
2
+ import { resolveChatExecutionMode } from "../../embed/execution-controls";
3
+ const props = defineProps({
4
+ profiles: { type: null, required: true },
5
+ model: { type: [String, null], required: true },
6
+ reasoningEffort: { type: null, required: true },
7
+ disabled: { type: Boolean, required: false }
8
+ });
9
+ const emit = defineEmits(["select"]);
10
+ const options = [
11
+ { value: "default", label: "Padr\xE3o", description: "Para as tarefas do dia a dia." },
12
+ { value: "deep", label: "Aprofundar", description: "Dedica mais racioc\xEDnio a tarefas complexas. Pode demorar mais." }
13
+ ];
14
+ const mode = computed(() => resolveChatExecutionMode(props.profiles, props));
15
+ const label = computed(() => mode.value === "custom" ? "Personalizado" : options.find((option) => option.value === mode.value).label);
16
+ function select(value) {
17
+ if (!props.disabled && (value === "default" || value === "deep"))
18
+ emit("select", { ...props.profiles[value] });
19
+ }
20
+ </script>
21
+
22
+ <template>
23
+ <TelaSelectMenu
24
+ :model-value="mode === 'custom' ? void 0 : mode"
25
+ :options="options"
26
+ :disabled="disabled"
27
+ content-class="w-280px max-w-[calc(100vw-32px)]"
28
+ @update:model-value="select"
29
+ >
30
+ <template #trigger>
31
+ <TelaSelectMenuTrigger
32
+ data-testid="chat-execution-mode-selector"
33
+ :aria-label="`Modo de resposta: ${label}`"
34
+ class="w-auto border-0 rounded-8px bg-transparent shadow-none gap-6px pl-10px body-14-medium"
35
+ >
36
+ <span whitespace-nowrap>{{ label }}</span>
37
+ </TelaSelectMenuTrigger>
38
+ </template>
39
+ </TelaSelectMenu>
40
+ </template>
@@ -0,0 +1,17 @@
1
+ import type { ChatReasoningEffort } from '../../types/schemas/chat/models.js';
2
+ import { type ChatExecutionControls, type ChatExecutionProfile } from '../../embed/execution-controls.js';
3
+ type __VLS_Props = {
4
+ profiles: Extract<ChatExecutionControls, {
5
+ mode: 'simple';
6
+ }>['profiles'];
7
+ model: string | null;
8
+ reasoningEffort: ChatReasoningEffort | null;
9
+ disabled?: boolean;
10
+ };
11
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
12
+ select: (profile: ChatExecutionProfile) => any;
13
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
14
+ onSelect?: ((profile: ChatExecutionProfile) => any) | undefined;
15
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -1,3 +1,4 @@
1
+ import type { ChatExecutionProfile } from '../../embed/execution-controls.js';
1
2
  import type { DeepReadonly } from 'vue';
2
3
  import type { Message } from '../../types/schemas/chat/conversations.js';
3
4
  import type { ChatReasoningEffort } from '../../types/schemas/chat/models.js';
@@ -23,6 +24,7 @@ type __VLS_Props = {
23
24
  /** Absent means shown, so internal callers keep the picker unless they opt out. */
24
25
  hideModelSelector?: boolean;
25
26
  hideReasoningEffortSelector?: boolean;
27
+ executionControlsDisabled?: boolean;
26
28
  sendBlocked?: boolean;
27
29
  };
28
30
  declare function addFiles(files: File[], inputName?: string): void;
@@ -47,9 +49,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
47
49
  } & {
48
50
  cancel: () => any;
49
51
  send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
52
+ selectExecutionProfile: (profile: ChatExecutionProfile) => any;
50
53
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
51
54
  onCancel?: (() => any) | undefined;
52
55
  onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
56
+ onSelectExecutionProfile?: ((profile: ChatExecutionProfile) => any) | undefined;
53
57
  "onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
54
58
  "onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
55
59
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -1,4 +1,6 @@
1
1
  <script setup>
2
+ import ChatExecutionModeSelector from "./chat-execution-mode-selector.vue";
3
+ import { useEmbedConfig } from "../../embed/composables/embed-config";
2
4
  import ChatModelSelector from "./chat-model-selector.vue";
3
5
  import { inject, unref } from "vue";
4
6
  import { useResizeObserver } from "@vueuse/core";
@@ -28,14 +30,16 @@ const props = defineProps({
28
30
  showCancelButton: { type: Boolean, required: false },
29
31
  hideModelSelector: { type: Boolean, required: false },
30
32
  hideReasoningEffortSelector: { type: Boolean, required: false },
33
+ executionControlsDisabled: { type: Boolean, required: false },
31
34
  sendBlocked: { type: Boolean, required: false }
32
35
  });
33
- const emit = defineEmits(["send", "cancel"]);
36
+ const emit = defineEmits(["send", "cancel", "selectExecutionProfile"]);
34
37
  const selectedModel = defineModel("selectedModel", { type: [String, null], ...{ default: null } });
35
38
  const composerExtension = inject(CHAT_COMPOSER_EXTENSION_KEY, null);
36
39
  const selectedReasoningEffort = defineModel("selectedReasoningEffort", { type: null, ...{ default: null } });
37
40
  const { uploadFiles, uploading, uploadError } = useFileUpload();
38
41
  const isCancelEnabled = computed(() => props.showCancelButton === true);
42
+ const executionControls = useEmbedConfig()?.executionControls;
39
43
  const content = ref("");
40
44
  const promptTextareaRef = ref(null);
41
45
  const editorRef = computed(() => promptTextareaRef.value?.el ?? null);
@@ -424,7 +428,14 @@ function handleInput() {
424
428
  }
425
429
  function handlePaste(e) {
426
430
  e.preventDefault();
431
+ if (isLocked.value)
432
+ return;
433
+ const files = Array.from(e.clipboardData?.files ?? []);
434
+ if (files.length > 0)
435
+ addFiles(files);
427
436
  const text = e.clipboardData?.getData("text/plain") ?? "";
437
+ if (!text)
438
+ return;
428
439
  const sel = window.getSelection();
429
440
  if (!sel || sel.rangeCount === 0)
430
441
  return;
@@ -786,6 +797,15 @@ defineExpose({
786
797
  </TelaChatPromptInputTools>
787
798
 
788
799
  <TelaChatPromptInputActions>
800
+ <ChatExecutionModeSelector
801
+ v-if="executionControls?.mode === 'simple'"
802
+ :profiles="executionControls.profiles"
803
+ :model="selectedModel"
804
+ :reasoning-effort="selectedReasoningEffort"
805
+ :disabled="isLocked || executionControlsDisabled"
806
+ @select="emit('selectExecutionProfile', $event)"
807
+ @click.stop
808
+ />
789
809
  <ChatReasoningEffortSelector
790
810
  v-if="!hideReasoningEffortSelector"
791
811
  v-model="selectedReasoningEffort"
@@ -1,3 +1,4 @@
1
+ import type { ChatExecutionProfile } from '../../embed/execution-controls.js';
1
2
  import type { DeepReadonly } from 'vue';
2
3
  import type { Message } from '../../types/schemas/chat/conversations.js';
3
4
  import type { ChatReasoningEffort } from '../../types/schemas/chat/models.js';
@@ -23,6 +24,7 @@ type __VLS_Props = {
23
24
  /** Absent means shown, so internal callers keep the picker unless they opt out. */
24
25
  hideModelSelector?: boolean;
25
26
  hideReasoningEffortSelector?: boolean;
27
+ executionControlsDisabled?: boolean;
26
28
  sendBlocked?: boolean;
27
29
  };
28
30
  declare function addFiles(files: File[], inputName?: string): void;
@@ -47,9 +49,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
47
49
  } & {
48
50
  cancel: () => any;
49
51
  send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
52
+ selectExecutionProfile: (profile: ChatExecutionProfile) => any;
50
53
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
51
54
  onCancel?: (() => any) | undefined;
52
55
  onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
56
+ onSelectExecutionProfile?: ((profile: ChatExecutionProfile) => any) | undefined;
53
57
  "onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
54
58
  "onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
55
59
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -1,3 +1,4 @@
1
+ import type { ChatExecutionProfile } from '../../embed/execution-controls.js';
1
2
  import type { ReasoningItem } from '../../types/schemas/integrations/agent-api.js';
2
3
  import { type DeepReadonly, type MaybeRefOrGetter, type Ref } from 'vue';
3
4
  import type { Conversation, ConversationWithMessages, Message } from '../../types/schemas/chat/conversations.js';
@@ -47,6 +48,10 @@ export declare function useChat(options?: {
47
48
  setConversationModel: (model: string | null) => void;
48
49
  pendingModelSelection: any;
49
50
  setConversationReasoningEffort: (reasoningEffort: ChatReasoningEffort | null) => void;
51
+ setConversationExecutionProfile: (profile: {
52
+ model: string | null;
53
+ reasoningEffort: ChatExecutionProfile["reasoningEffort"];
54
+ }) => void;
50
55
  pendingReasoningEffortSelection: any;
51
56
  sendMessage: (content: string, files?: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[], prepare?: (signal: AbortSignal) => Promise<{
52
57
  wakeSessionId?: string;
@@ -411,15 +411,26 @@ export function useChat(options = {}, adapter = {}) {
411
411
  if (!chatApi.conversationV2Enabled)
412
412
  void pollMessages();
413
413
  };
414
+ const simpleExecutionProfiles = computed(() => {
415
+ const controls = embedConfig?.executionControls?.value;
416
+ return controls?.mode === "simple" ? controls.profiles : null;
417
+ });
418
+ const pendingSimpleSelectionBuckets = useState("chat-pending-simple-selection", () => ({}));
414
419
  const pendingModelBuckets = useState("chat-pending-model-selection", () => ({}));
415
420
  const pendingModelSelection = computed({
416
421
  get: () => {
417
422
  const buckets = pendingModelBuckets.value;
418
423
  if (chatScope.value in buckets)
419
424
  return buckets[chatScope.value] ?? null;
425
+ if (simpleExecutionProfiles.value)
426
+ return simpleExecutionProfiles.value.default.model;
420
427
  return embedConfig?.defaultModel?.value ?? (adapter.defaultModel === void 0 ? defaultChatModel : adapter.defaultModel);
421
428
  },
422
429
  set: (value) => {
430
+ pendingSimpleSelectionBuckets.value[chatScope.value] = {
431
+ ...pendingSimpleSelectionBuckets.value[chatScope.value],
432
+ model: !!simpleExecutionProfiles.value
433
+ };
423
434
  pendingModelBuckets.value = {
424
435
  ...pendingModelBuckets.value,
425
436
  [chatScope.value]: value
@@ -434,14 +445,22 @@ export function useChat(options = {}, adapter = {}) {
434
445
  };
435
446
  const pendingReasoningEffortBuckets = useState("chat-pending-reasoning-effort-selection", () => ({}));
436
447
  const pendingReasoningEffortSelection = computed({
437
- get: () => chatScope.value in pendingReasoningEffortBuckets.value ? pendingReasoningEffortBuckets.value[chatScope.value] ?? null : embedConfig?.defaultReasoningEffort?.value ?? null,
448
+ get: () => chatScope.value in pendingReasoningEffortBuckets.value ? pendingReasoningEffortBuckets.value[chatScope.value] ?? null : simpleExecutionProfiles.value ? simpleExecutionProfiles.value.default.reasoningEffort : embedConfig?.defaultReasoningEffort?.value ?? null,
438
449
  set: (value) => {
450
+ pendingSimpleSelectionBuckets.value[chatScope.value] = {
451
+ ...pendingSimpleSelectionBuckets.value[chatScope.value],
452
+ reasoningEffort: !!simpleExecutionProfiles.value
453
+ };
439
454
  pendingReasoningEffortBuckets.value = {
440
455
  ...pendingReasoningEffortBuckets.value,
441
456
  [chatScope.value]: value
442
457
  };
443
458
  }
444
459
  });
460
+ const setConversationExecutionProfile = (profile) => {
461
+ if (currentConversation.value)
462
+ currentConversation.value = { ...currentConversation.value, ...profile };
463
+ };
445
464
  const setConversationReasoningEffort = (reasoningEffort) => {
446
465
  const conversation = currentConversation.value;
447
466
  if (!conversation || (conversation.reasoningEffort ?? null) === reasoningEffort)
@@ -776,6 +795,12 @@ export function useChat(options = {}, adapter = {}) {
776
795
  void flushOutboundQueue();
777
796
  });
778
797
  const clearConversation = () => {
798
+ const simpleSelection = pendingSimpleSelectionBuckets.value[chatScope.value];
799
+ if (simpleExecutionProfiles.value || simpleSelection?.model || simpleSelection?.reasoningEffort) {
800
+ delete pendingModelBuckets.value[chatScope.value];
801
+ delete pendingReasoningEffortBuckets.value[chatScope.value];
802
+ delete pendingSimpleSelectionBuckets.value[chatScope.value];
803
+ }
779
804
  runtimeBlocked.value = false;
780
805
  stopPolling();
781
806
  gateController?.abort();
@@ -1029,6 +1054,7 @@ export function useChat(options = {}, adapter = {}) {
1029
1054
  setConversationModel,
1030
1055
  pendingModelSelection,
1031
1056
  setConversationReasoningEffort,
1057
+ setConversationExecutionProfile,
1032
1058
  pendingReasoningEffortSelection,
1033
1059
  sendMessage,
1034
1060
  flushOutboundQueue,
@@ -1,4 +1,5 @@
1
1
  export type { ChatCitationsConfig, CitationMeta, CitationRef, } from '../citations/types.js';
2
+ export type { ChatExecutionControls, ChatExecutionProfile } from '../embed/execution-controls.js';
2
3
  export type { ChatFeatureConfig } from '../embed/feature-config.js';
3
4
  export type { AvailableChatModels, ChatActor, ChatEmbedSharedProps, ChatEmbedVariant, ChatEmbedVariantProps, ChatLabels, ChatSidebarConfig, ChatSuggestion, DefaultChatEmbedProps, MeistrariChatEmbedProps, TelaAgentChatEmbedProps, } from '../embed/types.js';
4
5
  export type { ChatActionPayload } from '../messages/composables/chat-action.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "4.7.0",
3
+ "version": "4.8.0-rc.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {