@meistrari/chat-nuxt 4.12.0-rc.1 → 4.12.0-rc.2

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 (37) hide show
  1. package/README.md +10 -0
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/embed/components/chat-embed-inner.d.vue.ts +27 -11
  4. package/dist/runtime/embed/components/chat-embed-inner.vue +55 -6
  5. package/dist/runtime/embed/components/chat-embed-inner.vue.d.ts +27 -11
  6. package/dist/runtime/embed/components/chat-embed.d.vue.ts +7 -5
  7. package/dist/runtime/embed/components/chat-embed.vue +17 -1
  8. package/dist/runtime/embed/components/chat-embed.vue.d.ts +7 -5
  9. package/dist/runtime/embed/components/meistrari-chat-embed.d.vue.ts +7 -1
  10. package/dist/runtime/embed/components/meistrari-chat-embed.vue +27 -1
  11. package/dist/runtime/embed/components/meistrari-chat-embed.vue.d.ts +7 -1
  12. package/dist/runtime/embed/components/mobile/chat-mobile-outdated-settings-sheet.d.vue.ts +2 -2
  13. package/dist/runtime/embed/components/mobile/chat-mobile-outdated-settings-sheet.vue.d.ts +2 -2
  14. package/dist/runtime/embed/components/mobile/chat-mobile-shell.d.vue.ts +5 -5
  15. package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue +1 -0
  16. package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue.d.ts +5 -5
  17. package/dist/runtime/files/composables/conversation-files.d.ts +1 -0
  18. package/dist/runtime/files/composables/conversation-files.js +18 -5
  19. package/dist/runtime/lightweight/embed/components/chat-embed-adapter.d.vue.ts +7 -3
  20. package/dist/runtime/lightweight/embed/components/chat-embed-adapter.vue +7 -1
  21. package/dist/runtime/lightweight/embed/components/chat-embed-adapter.vue.d.ts +7 -3
  22. package/dist/runtime/lightweight/messages/composables/chat.d.ts +1 -1
  23. package/dist/runtime/messages/components/chat-message-bubble.d.vue.ts +7 -0
  24. package/dist/runtime/messages/components/chat-message-bubble.vue +6 -4
  25. package/dist/runtime/messages/components/chat-message-bubble.vue.d.ts +7 -0
  26. package/dist/runtime/messages/components/chat-message-input.d.vue.ts +2 -0
  27. package/dist/runtime/messages/components/chat-message-input.vue +20 -3
  28. package/dist/runtime/messages/components/chat-message-input.vue.d.ts +2 -0
  29. package/dist/runtime/messages/components/chat-message-list.d.vue.ts +3 -0
  30. package/dist/runtime/messages/components/chat-message-list.vue +3 -0
  31. package/dist/runtime/messages/components/chat-message-list.vue.d.ts +3 -0
  32. package/dist/runtime/messages/composables/chat.d.ts +1 -1
  33. package/dist/runtime/messages/composables/chat.js +34 -12
  34. package/dist/runtime/messages/message-state.d.ts +1 -0
  35. package/dist/runtime/messages/message-state.js +1 -1
  36. package/dist/runtime/types/embed.d.ts +8 -0
  37. package/package.json +1 -1
package/README.md CHANGED
@@ -240,3 +240,13 @@ Advanced picker flags default to `true`. Import `ChatExecutionControls` and
240
240
  `ChatExecutionProfile` from `@meistrari/chat-nuxt/types/embed` when declaring host
241
241
  configuration separately. `defaultModel` and `defaultReasoningEffort` are not
242
242
  deprecated.
243
+
244
+ ### Host chat controls
245
+
246
+ The public embed exposes `sendMessage`, `cancel` and `clear` for host applications.
247
+
248
+ ### Create a new conversation
249
+
250
+ Every embed offers `/new` (“Criar uma nova conversa”). Typing the command, selecting it from the menu or calling `sendMessage('/new')` opens a fresh chat. Like the sidebar action, it creates the new conversation on the first send. Previous conversations retain their messages and attachments.
251
+
252
+ Hosts can call `ChatEmbedHost.clear(): Promise<boolean>` and listen for `@conversation-cleared="onCleared"` (payload: the unchanged conversation ID) to discard their own session state. The event fires only after a successful reset. On failure, history remains visible and the host can retry the clear. Clear is serialized against turn/wake admission; stale polling and wake completions cannot restore the previous session.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "4.12.0-rc.1",
4
+ "version": "4.12.0-rc.2",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -19,33 +19,49 @@ type __VLS_Props = {
19
19
  user?: EmbedUser | null;
20
20
  features?: Partial<ChatFeatureConfig>;
21
21
  };
22
- declare var __VLS_14: {}, __VLS_99: {}, __VLS_114: {
22
+ declare function clearThread(): Promise<boolean>;
23
+ declare function handleCancel(): Promise<void>;
24
+ declare var __VLS_14: {}, __VLS_100: {}, __VLS_115: {
23
25
  message: useChatAuth;
24
- }, __VLS_127: {}, __VLS_136: {}, __VLS_207: {}, __VLS_228: {
26
+ }, __VLS_118: {
25
27
  message: useChatAuth;
26
- }, __VLS_241: {};
28
+ }, __VLS_132: {}, __VLS_141: {}, __VLS_213: {}, __VLS_234: {
29
+ message: useChatAuth;
30
+ }, __VLS_237: {
31
+ message: useChatAuth;
32
+ }, __VLS_251: {};
27
33
  type __VLS_Slots = {} & {
28
34
  'sidebar-bottom'?: (props: typeof __VLS_14) => any;
29
35
  } & {
30
- 'composer-bottom'?: (props: typeof __VLS_99) => any;
36
+ 'composer-bottom'?: (props: typeof __VLS_100) => any;
37
+ } & {
38
+ 'failed-message-actions'?: (props: typeof __VLS_115) => any;
39
+ } & {
40
+ 'user-message'?: (props: typeof __VLS_118) => any;
31
41
  } & {
32
- 'failed-message-actions'?: (props: typeof __VLS_114) => any;
42
+ 'composer-bottom'?: (props: typeof __VLS_132) => any;
33
43
  } & {
34
- 'composer-bottom'?: (props: typeof __VLS_127) => any;
44
+ 'topbar-leading'?: (props: typeof __VLS_141) => any;
35
45
  } & {
36
- 'topbar-leading'?: (props: typeof __VLS_136) => any;
46
+ 'composer-bottom'?: (props: typeof __VLS_213) => any;
37
47
  } & {
38
- 'composer-bottom'?: (props: typeof __VLS_207) => any;
48
+ 'failed-message-actions'?: (props: typeof __VLS_234) => any;
39
49
  } & {
40
- 'failed-message-actions'?: (props: typeof __VLS_228) => any;
50
+ 'user-message'?: (props: typeof __VLS_237) => any;
41
51
  } & {
42
- 'composer-bottom'?: (props: typeof __VLS_241) => any;
52
+ 'composer-bottom'?: (props: typeof __VLS_251) => any;
43
53
  };
44
- declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
54
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
55
+ clear: typeof clearThread;
56
+ sendMessage: (content: string, messageId?: string) => Promise<boolean>;
57
+ cancel: typeof handleCancel;
58
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
59
+ conversationCleared: (conversationId: string) => any;
45
60
  "update:conversationId": (value: string | null) => any;
46
61
  "update:conversationTitle": (value: string | null) => any;
47
62
  "update:conversationCreatorFilter": (value: ConversationCreatorFilter) => any;
48
63
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
64
+ onConversationCleared?: ((conversationId: string) => any) | undefined;
49
65
  "onUpdate:conversationId"?: ((value: string | null) => any) | undefined;
50
66
  "onUpdate:conversationTitle"?: ((value: string | null) => any) | undefined;
51
67
  "onUpdate:conversationCreatorFilter"?: ((value: ConversationCreatorFilter) => any) | undefined;
@@ -60,7 +60,7 @@ const props = defineProps({
60
60
  user: { type: [Object, null], required: false },
61
61
  features: { type: Object, required: false }
62
62
  });
63
- const emit = defineEmits(["update:conversationId", "update:conversationTitle", "update:conversationCreatorFilter"]);
63
+ const emit = defineEmits(["conversationCleared", "update:conversationId", "update:conversationTitle", "update:conversationCreatorFilter"]);
64
64
  const sidebarPosition = computed(() => props.sidebar?.position ?? "left");
65
65
  const sidebarWidth = computed(() => props.sidebar?.width ?? 240);
66
66
  const sidebarBottomHeight = computed(
@@ -300,6 +300,7 @@ const {
300
300
  startPolling,
301
301
  reconnect,
302
302
  clearConversation,
303
+ clearConversationThread,
303
304
  error: chatError
304
305
  } = createChat({
305
306
  outboundQueueEnabled: computed(() => resolvedFeatures.value.chatOutboundQueue),
@@ -314,7 +315,7 @@ watch(chatError, (message) => {
314
315
  if (message)
315
316
  statusToast.update({ text: message, icon: "i-ph-warning" });
316
317
  });
317
- const { total, files: conversationFiles, loading: conversationFilesLoading } = useConversationFiles(activeConversationId);
318
+ const { total, files: conversationFiles, loading: conversationFilesLoading, clearFiles } = useConversationFiles(activeConversationId);
318
319
  function resolveSelectedModel(value) {
319
320
  return nativeModels ? nativeModels.resolve(value) : value;
320
321
  }
@@ -606,12 +607,38 @@ const pendingFork = computed(() => {
606
607
  return fork && fork.status !== "ready" ? fork : null;
607
608
  });
608
609
  const forkBlocked = computed(() => Boolean(pendingFork.value));
610
+ const clearRevision = ref(0);
609
611
  function claimableWakeSessionId() {
610
612
  return agentWake?.state.value === "ready" && !currentConversation.value?.threadSessionId ? agentWake.sessionId.value : void 0;
611
613
  }
614
+ async function clearThread() {
615
+ const conversationId = currentConversation.value?.id;
616
+ if (!conversationId || forkBlocked.value)
617
+ return false;
618
+ const cleared = await clearConversationThread();
619
+ if (cleared && activeConversationId.value === conversationId && currentConversation.value?.id === conversationId) {
620
+ agentWake?.consume();
621
+ const key = composerKey(conversationId);
622
+ if (key) {
623
+ writeComposerDraft(key, "");
624
+ writeComposerDraft(`${key}:pending-send`, "");
625
+ }
626
+ unconfirmedDraft = null;
627
+ clearFiles();
628
+ closeFilesPanel();
629
+ clearRevision.value++;
630
+ }
631
+ if (cleared)
632
+ emit("conversationCleared", conversationId);
633
+ return cleared;
634
+ }
612
635
  async function handleSend(content, files, agentInputs) {
613
636
  if (forkBlocked.value)
614
637
  return;
638
+ if (content.trim() === "/new") {
639
+ await handleNewConversation();
640
+ return;
641
+ }
615
642
  const prepared = composerExtension?.prepareSend?.(content);
616
643
  const outbound = prepared?.content ?? content;
617
644
  unconfirmedDraft = {
@@ -627,7 +654,7 @@ async function handleSend(content, files, agentInputs) {
627
654
  if (!sent)
628
655
  restoreQueuedSend({ content: prepared?.retryContent?.() ?? content, files, agentInputs: resolvedAgentInputs });
629
656
  }
630
- async function dispatchMessage(outbound, files, resolvedAgentInputs) {
657
+ async function dispatchMessage(outbound, files, resolvedAgentInputs, messageId) {
631
658
  const executionSettings = { model: selectedModel.value, reasoningEffort: selectedReasoningEffort.value };
632
659
  if (props.sendBlocked)
633
660
  return false;
@@ -681,7 +708,7 @@ async function dispatchMessage(outbound, files, resolvedAgentInputs) {
681
708
  if (!conversation.threadSessionId && !claimedWakeSessionId)
682
709
  throw new Error("N\xE3o foi poss\xEDvel acordar o agente. Tente novamente.");
683
710
  return { executionSettings, ...claimedWakeSessionId ? { wakeSessionId: claimedWakeSessionId } : {} };
684
- });
711
+ }, void 0, messageId);
685
712
  if (sent && claimedWakeSessionId)
686
713
  agentWake?.consume();
687
714
  return sent;
@@ -833,11 +860,12 @@ const displayConversationEvents = computed(() => [...new Map([
833
860
  ].filter((event) => event.conversationId === currentConversation.value?.id).map((event) => [event.id, event])).values()]);
834
861
  watch([
835
862
  () => currentConversation.value?.id,
863
+ () => clearRevision.value,
836
864
  () => JSON.stringify(chatApi.withChatHeaders().headers),
837
865
  () => JSON.stringify(messages.value.filter((message) => message.role === "user").map((message) => [message.id, message.createdAt, message.externalUuid])),
838
866
  () => isPolling.value
839
- ], async ([conversationId], previous, onCleanup) => {
840
- if (conversationId !== previous?.[0] || JSON.stringify(chatApi.withChatHeaders().headers) !== previous?.[1])
867
+ ], async ([conversationId, revision, headers], previous, onCleanup) => {
868
+ if (conversationId !== previous?.[0] || revision !== previous?.[1] || headers !== previous?.[2])
841
869
  persistedConversationEvents.value = [];
842
870
  if (!conversationId)
843
871
  return;
@@ -958,6 +986,11 @@ async function handleDelete() {
958
986
  await handleNewConversation();
959
987
  }
960
988
  }
989
+ defineExpose({
990
+ clear: clearThread,
991
+ sendMessage: async (content, messageId) => content.trim() === "/new" ? handleNewConversation().then(() => true) : Boolean(await dispatchMessage(content, [], void 0, messageId)),
992
+ cancel: handleCancel
993
+ });
961
994
  </script>
962
995
 
963
996
  <template>
@@ -1136,6 +1169,7 @@ async function handleDelete() {
1136
1169
  variant="mobile"
1137
1170
  />
1138
1171
  <ChatMessageInput
1172
+ :key="clearRevision"
1139
1173
  v-model:selected-model="selectedModel"
1140
1174
  v-model:selected-reasoning-effort="selectedReasoningEffort"
1141
1175
  :disabled="true"
@@ -1180,6 +1214,7 @@ async function handleDelete() {
1180
1214
 
1181
1215
  <template #input>
1182
1216
  <ChatMessageInput
1217
+ :key="clearRevision"
1183
1218
  ref="messageInputRef"
1184
1219
  v-model:selected-model="selectedModel"
1185
1220
  v-model:selected-reasoning-effort="selectedReasoningEffort"
@@ -1206,6 +1241,7 @@ async function handleDelete() {
1206
1241
  centered
1207
1242
  @select-execution-profile="selectExecutionProfile"
1208
1243
  @send="handleSend"
1244
+ @new-conversation="handleNewConversation"
1209
1245
  @cancel="handleCancel"
1210
1246
  />
1211
1247
  <div v-if="$slots['composer-bottom']" data-chat-composer-bottom w-full>
@@ -1236,10 +1272,14 @@ async function handleDelete() {
1236
1272
  <template v-if="$slots['failed-message-actions']" #failed-message-actions="slotProps">
1237
1273
  <slot name="failed-message-actions" v-bind="slotProps" />
1238
1274
  </template>
1275
+ <template v-if="$slots['user-message']" #user-message="slotProps">
1276
+ <slot name="user-message" v-bind="slotProps" />
1277
+ </template>
1239
1278
  </ChatMessageList>
1240
1279
  <div ref="composerRef" data-chat-composer flex="~ col" gap-12px min-h-0>
1241
1280
  <div :ref="setAnswerTarget" data-chat-answer-dock class="empty:hidden max-h-[50dvh] overflow-y-auto" />
1242
1281
  <ChatMessageInput
1282
+ :key="clearRevision"
1243
1283
  ref="messageInputRef"
1244
1284
  v-model:selected-model="selectedModel"
1245
1285
  v-model:selected-reasoning-effort="selectedReasoningEffort"
@@ -1267,6 +1307,7 @@ async function handleDelete() {
1267
1307
  has-disclaimer
1268
1308
  @select-execution-profile="selectExecutionProfile"
1269
1309
  @send="handleSend"
1310
+ @new-conversation="handleNewConversation"
1270
1311
  @cancel="handleCancel"
1271
1312
  />
1272
1313
  <div v-if="$slots['composer-bottom']" data-chat-composer-bottom w-full max-w-700px mx-auto mt--12px>
@@ -1358,6 +1399,7 @@ async function handleDelete() {
1358
1399
  :variant="compact ? 'widget' : 'page'"
1359
1400
  />
1360
1401
  <ChatMessageInput
1402
+ :key="clearRevision"
1361
1403
  v-model:selected-model="selectedModel"
1362
1404
  v-model:selected-reasoning-effort="selectedReasoningEffort"
1363
1405
  :disabled="true"
@@ -1408,6 +1450,7 @@ async function handleDelete() {
1408
1450
 
1409
1451
  <template #input>
1410
1452
  <ChatMessageInput
1453
+ :key="clearRevision"
1411
1454
  ref="messageInputRef"
1412
1455
  v-model:selected-model="selectedModel"
1413
1456
  v-model:selected-reasoning-effort="selectedReasoningEffort"
@@ -1434,6 +1477,7 @@ async function handleDelete() {
1434
1477
  centered
1435
1478
  @select-execution-profile="selectExecutionProfile"
1436
1479
  @send="handleSend"
1480
+ @new-conversation="handleNewConversation"
1437
1481
  @cancel="handleCancel"
1438
1482
  />
1439
1483
  <div v-if="$slots['composer-bottom']" data-chat-composer-bottom w-full>
@@ -1467,10 +1511,14 @@ async function handleDelete() {
1467
1511
  <template v-if="$slots['failed-message-actions']" #failed-message-actions="slotProps">
1468
1512
  <slot name="failed-message-actions" v-bind="slotProps" />
1469
1513
  </template>
1514
+ <template v-if="$slots['user-message']" #user-message="slotProps">
1515
+ <slot name="user-message" v-bind="slotProps" />
1516
+ </template>
1470
1517
  </ChatMessageList>
1471
1518
  <div ref="composerRef" data-chat-composer flex="~ col" gap-12px min-h-0>
1472
1519
  <div :ref="setAnswerTarget" data-chat-answer-dock class="empty:hidden max-h-[50dvh] overflow-y-auto" />
1473
1520
  <ChatMessageInput
1521
+ :key="clearRevision"
1474
1522
  ref="messageInputRef"
1475
1523
  v-model:selected-model="selectedModel"
1476
1524
  v-model:selected-reasoning-effort="selectedReasoningEffort"
@@ -1498,6 +1546,7 @@ async function handleDelete() {
1498
1546
  has-disclaimer
1499
1547
  @select-execution-profile="selectExecutionProfile"
1500
1548
  @send="handleSend"
1549
+ @new-conversation="handleNewConversation"
1501
1550
  @cancel="handleCancel"
1502
1551
  />
1503
1552
  <div v-if="$slots['composer-bottom']" data-chat-composer-bottom w-full max-w-700px mx-auto mt--12px>
@@ -19,33 +19,49 @@ type __VLS_Props = {
19
19
  user?: EmbedUser | null;
20
20
  features?: Partial<ChatFeatureConfig>;
21
21
  };
22
- declare var __VLS_14: {}, __VLS_99: {}, __VLS_114: {
22
+ declare function clearThread(): Promise<boolean>;
23
+ declare function handleCancel(): Promise<void>;
24
+ declare var __VLS_14: {}, __VLS_100: {}, __VLS_115: {
23
25
  message: useChatAuth;
24
- }, __VLS_127: {}, __VLS_136: {}, __VLS_207: {}, __VLS_228: {
26
+ }, __VLS_118: {
25
27
  message: useChatAuth;
26
- }, __VLS_241: {};
28
+ }, __VLS_132: {}, __VLS_141: {}, __VLS_213: {}, __VLS_234: {
29
+ message: useChatAuth;
30
+ }, __VLS_237: {
31
+ message: useChatAuth;
32
+ }, __VLS_251: {};
27
33
  type __VLS_Slots = {} & {
28
34
  'sidebar-bottom'?: (props: typeof __VLS_14) => any;
29
35
  } & {
30
- 'composer-bottom'?: (props: typeof __VLS_99) => any;
36
+ 'composer-bottom'?: (props: typeof __VLS_100) => any;
37
+ } & {
38
+ 'failed-message-actions'?: (props: typeof __VLS_115) => any;
39
+ } & {
40
+ 'user-message'?: (props: typeof __VLS_118) => any;
31
41
  } & {
32
- 'failed-message-actions'?: (props: typeof __VLS_114) => any;
42
+ 'composer-bottom'?: (props: typeof __VLS_132) => any;
33
43
  } & {
34
- 'composer-bottom'?: (props: typeof __VLS_127) => any;
44
+ 'topbar-leading'?: (props: typeof __VLS_141) => any;
35
45
  } & {
36
- 'topbar-leading'?: (props: typeof __VLS_136) => any;
46
+ 'composer-bottom'?: (props: typeof __VLS_213) => any;
37
47
  } & {
38
- 'composer-bottom'?: (props: typeof __VLS_207) => any;
48
+ 'failed-message-actions'?: (props: typeof __VLS_234) => any;
39
49
  } & {
40
- 'failed-message-actions'?: (props: typeof __VLS_228) => any;
50
+ 'user-message'?: (props: typeof __VLS_237) => any;
41
51
  } & {
42
- 'composer-bottom'?: (props: typeof __VLS_241) => any;
52
+ 'composer-bottom'?: (props: typeof __VLS_251) => any;
43
53
  };
44
- declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
54
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
55
+ clear: typeof clearThread;
56
+ sendMessage: (content: string, messageId?: string) => Promise<boolean>;
57
+ cancel: typeof handleCancel;
58
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
59
+ conversationCleared: (conversationId: string) => any;
45
60
  "update:conversationId": (value: string | null) => any;
46
61
  "update:conversationTitle": (value: string | null) => any;
47
62
  "update:conversationCreatorFilter": (value: ConversationCreatorFilter) => any;
48
63
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
64
+ onConversationCleared?: ((conversationId: string) => any) | undefined;
49
65
  "onUpdate:conversationId"?: ((value: string | null) => any) | undefined;
50
66
  "onUpdate:conversationTitle"?: ((value: string | null) => any) | undefined;
51
67
  "onUpdate:conversationCreatorFilter"?: ((value: ConversationCreatorFilter) => any) | undefined;
@@ -1,12 +1,14 @@
1
- declare var __VLS_26: {}, __VLS_29: {}, __VLS_32: {}, __VLS_35: any;
1
+ declare var __VLS_29: {}, __VLS_32: {}, __VLS_35: {}, __VLS_38: any, __VLS_41: any;
2
2
  type __VLS_Slots = {} & {
3
- 'sidebar-bottom'?: (props: typeof __VLS_26) => any;
3
+ 'sidebar-bottom'?: (props: typeof __VLS_29) => any;
4
4
  } & {
5
- 'topbar-leading'?: (props: typeof __VLS_29) => any;
5
+ 'topbar-leading'?: (props: typeof __VLS_32) => any;
6
6
  } & {
7
- 'composer-bottom'?: (props: typeof __VLS_32) => any;
7
+ 'composer-bottom'?: (props: typeof __VLS_35) => any;
8
8
  } & {
9
- 'failed-message-actions'?: (props: typeof __VLS_35) => any;
9
+ 'failed-message-actions'?: (props: typeof __VLS_38) => any;
10
+ } & {
11
+ 'user-message'?: (props: typeof __VLS_41) => any;
10
12
  };
11
13
  declare const __VLS_base: import("vue").DefineSetupFnComponent<Record<string, any>, {}, {}, Record<string, any> & {}, import("vue").PublicProps>;
12
14
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -50,7 +50,7 @@ const props = defineProps({
50
50
  keepMounted: { type: Boolean, required: false },
51
51
  launcherLabel: { type: String, required: false }
52
52
  });
53
- const emit = defineEmits(["update:conversationId", "update:conversationTitle", "update:conversationCreatorFilter", "action", "messageFeedback", "messageTerminal", "messageSent"]);
53
+ const emit = defineEmits(["conversationCleared", "update:conversationId", "update:conversationTitle", "update:conversationCreatorFilter", "action", "messageFeedback", "messageTerminal", "messageSent"]);
54
54
  const LightweightAdapter = defineAsyncComponent(() => import("../../lightweight/embed/components/chat-embed-adapter.vue"));
55
55
  const parentEmbedConfig = useEmbedConfig();
56
56
  const { activeOrganization } = useChatAuth();
@@ -145,6 +145,17 @@ provideChatMessageTerminal((payload) => {
145
145
  provideChatMessageSent((payload) => {
146
146
  emit("messageSent", payload);
147
147
  });
148
+ const host = ref(null);
149
+ function requireHost() {
150
+ if (!host.value)
151
+ throw new Error("Chat is not ready");
152
+ return host.value;
153
+ }
154
+ defineExpose({
155
+ sendMessage: (content, messageId) => requireHost().sendMessage(content, messageId),
156
+ cancel: () => requireHost().cancel(),
157
+ clear: () => requireHost().clear()
158
+ });
148
159
  </script>
149
160
 
150
161
  <template>
@@ -162,6 +173,7 @@ provideChatMessageSent((payload) => {
162
173
  <component
163
174
  :is="harnessComponent"
164
175
  v-if="workspaceId && harnessComponent"
176
+ ref="host"
165
177
  :key="`${harness}:${workspaceId}:${normalizedTelaAgentId ?? 'default-chat'}:branch:${telaAgentBranch ?? ''}:scope:${resolveConversationScopeKeySegment(normalizedConversationScope)}`"
166
178
  :sync-browser-state="syncBrowserState"
167
179
  :conversation-id="selectedConversationId"
@@ -176,6 +188,7 @@ provideChatMessageSent((payload) => {
176
188
  :suggestions="suggestions"
177
189
  :user="normalizedTelaAgentId ? null : user"
178
190
  :features="features"
191
+ @conversation-cleared="emit('conversationCleared', $event)"
179
192
  @update:conversation-id="selectConversation"
180
193
  @update:conversation-title="$emit('update:conversationTitle', $event)"
181
194
  @update:conversation-creator-filter="$emit('update:conversationCreatorFilter', $event)"
@@ -192,6 +205,9 @@ provideChatMessageSent((payload) => {
192
205
  <template v-if="$slots['failed-message-actions']" #failed-message-actions="slotProps">
193
206
  <slot name="failed-message-actions" v-bind="slotProps" />
194
207
  </template>
208
+ <template v-if="$slots['user-message']" #user-message="slotProps">
209
+ <slot name="user-message" v-bind="slotProps" />
210
+ </template>
195
211
  </component>
196
212
  <TelaChatEmptyState v-else-if="workspaceId && harnessLoading" compact role="status" aria-live="polite">
197
213
  <TelaChatTextShimmer>Carregando agente…</TelaChatTextShimmer>
@@ -1,12 +1,14 @@
1
- declare var __VLS_26: {}, __VLS_29: {}, __VLS_32: {}, __VLS_35: any;
1
+ declare var __VLS_29: {}, __VLS_32: {}, __VLS_35: {}, __VLS_38: any, __VLS_41: any;
2
2
  type __VLS_Slots = {} & {
3
- 'sidebar-bottom'?: (props: typeof __VLS_26) => any;
3
+ 'sidebar-bottom'?: (props: typeof __VLS_29) => any;
4
4
  } & {
5
- 'topbar-leading'?: (props: typeof __VLS_29) => any;
5
+ 'topbar-leading'?: (props: typeof __VLS_32) => any;
6
6
  } & {
7
- 'composer-bottom'?: (props: typeof __VLS_32) => any;
7
+ 'composer-bottom'?: (props: typeof __VLS_35) => any;
8
8
  } & {
9
- 'failed-message-actions'?: (props: typeof __VLS_35) => any;
9
+ 'failed-message-actions'?: (props: typeof __VLS_38) => any;
10
+ } & {
11
+ 'user-message'?: (props: typeof __VLS_41) => any;
10
12
  };
11
13
  declare const __VLS_base: import("vue").DefineSetupFnComponent<Record<string, any>, {}, {}, Record<string, any> & {}, import("vue").PublicProps>;
12
14
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1,4 +1,5 @@
1
1
  import type { DefineSetupFnComponent, SlotsType } from 'vue';
2
+ import type { ChatEmbedHost } from '../../types/embed.js';
2
3
  import type { ChatActionPayload } from '../../messages/composables/chat-action.js';
3
4
  import type { MessageFeedbackPayload } from '../../messages/composables/chat-feedback.js';
4
5
  import type { ChatMessageTerminalPayload } from '../../messages/composables/chat-message-terminal.js';
@@ -22,6 +23,8 @@ type MeistrariChatEmbedEmits = {
22
23
  'messageTerminal': (payload: ChatMessageTerminalPayload) => any;
23
24
  /** Fired once the server accepts a user message, including retries of a failed answer. */
24
25
  'messageSent': (payload: ChatMessageSentPayload) => any;
26
+ /** Fired after an in-place reset; the conversation ID is unchanged. */
27
+ 'conversationCleared': (conversationId: string) => any;
25
28
  };
26
29
 
27
30
  type MeistrariChatEmbedSlots = SlotsType<{
@@ -33,6 +36,8 @@ type MeistrariChatEmbedSlots = SlotsType<{
33
36
  'composer-bottom'?: () => any;
34
37
  /** Rendered inside a failed assistant message's error box, next to "Tentar novamente"; receives the failed message. */
35
38
  'failed-message-actions'?: (props: { message: Message }) => any;
39
+ /** Replaces the text of a user message, keeping its attachments, sender and timestamp; render nothing to keep the default text. */
40
+ 'user-message'?: (props: { message: Message }) => any;
36
41
  }>;
37
42
 
38
43
  /**
@@ -61,7 +66,8 @@ type MeistrariChatEmbedSlots = SlotsType<{
61
66
  * </template>
62
67
  * ```
63
68
  */
64
- declare const MeistrariChatEmbed: DefineSetupFnComponent<MeistrariChatEmbedProps, MeistrariChatEmbedEmits, MeistrariChatEmbedSlots>;
69
+ type EmbedComponent = DefineSetupFnComponent<MeistrariChatEmbedProps, MeistrariChatEmbedEmits, MeistrariChatEmbedSlots>;
70
+ declare const MeistrariChatEmbed: EmbedComponent & { new (): InstanceType<EmbedComponent> & ChatEmbedHost };
65
71
 
66
72
  export default MeistrariChatEmbed;
67
73
  export type { AvailableChatModels, ChatActionPayload, ChatEmbedVariant, ChatEmbedVariantProps, ChatFeatureConfig, ChatMessageSentPayload, ChatMessageTerminalPayload, ChatSidebarConfig, ChatSuggestion, DefaultChatEmbedProps, MeistrariChatEmbedProps, Message, MessageFeedbackPayload, TelaAgentChatEmbedProps };
@@ -41,7 +41,7 @@ const { hideSidebar = void 0, syncBrowserState = true, closeOnOutsideClick = tru
41
41
  keepMounted: { type: Boolean, required: false },
42
42
  launcherLabel: { type: String, required: false }
43
43
  });
44
- const emit = defineEmits(["update:conversationId", "update:conversationCreatorFilter", "update:open", "action", "messageFeedback", "messageTerminal", "messageSent"]);
44
+ const emit = defineEmits(["conversationCleared", "update:conversationId", "update:conversationCreatorFilter", "update:open", "action", "messageFeedback", "messageTerminal", "messageSent"]);
45
45
  defineSlots();
46
46
  provideConversationCreatorFilterState({
47
47
  defaultConversationCreatorFilter: () => props.defaultConversationCreatorFilter,
@@ -107,11 +107,23 @@ const widgetEmbedProps = computed(() => ({
107
107
  conversationId: widgetConversationId.value,
108
108
  hideSidebar: hideSidebar ?? true
109
109
  }));
110
+ const host = ref(null);
111
+ function requireHost() {
112
+ if (!host.value)
113
+ throw new Error("Chat is not ready");
114
+ return host.value;
115
+ }
116
+ defineExpose({
117
+ sendMessage: (content, messageId) => requireHost().sendMessage(content, messageId),
118
+ cancel: () => requireHost().cancel(),
119
+ clear: () => requireHost().clear()
120
+ });
110
121
  </script>
111
122
 
112
123
  <template>
113
124
  <ChatEmbed
114
125
  v-if="variant === 'page'"
126
+ ref="host"
115
127
  v-bind="pageEmbedProps"
116
128
  @update:conversation-id="emit('update:conversationId', $event)"
117
129
  @update:conversation-creator-filter="emit('update:conversationCreatorFilter', $event)"
@@ -119,6 +131,7 @@ const widgetEmbedProps = computed(() => ({
119
131
  @message-feedback="emit('messageFeedback', $event)"
120
132
  @message-terminal="emit('messageTerminal', $event)"
121
133
  @message-sent="emit('messageSent', $event)"
134
+ @conversation-cleared="emit('conversationCleared', $event)"
122
135
  >
123
136
  <template v-if="$slots['sidebar-bottom']" #sidebar-bottom>
124
137
  <slot name="sidebar-bottom" />
@@ -132,6 +145,9 @@ const widgetEmbedProps = computed(() => ({
132
145
  <template v-if="$slots['failed-message-actions']" #failed-message-actions="slotProps">
133
146
  <slot name="failed-message-actions" v-bind="slotProps" />
134
147
  </template>
148
+ <template v-if="$slots['user-message']" #user-message="slotProps">
149
+ <slot name="user-message" v-bind="slotProps" />
150
+ </template>
135
151
  </ChatEmbed>
136
152
 
137
153
  <ClientOnly v-else>
@@ -153,6 +169,7 @@ const widgetEmbedProps = computed(() => ({
153
169
  </TelaChatWidgetHeader>
154
170
  <div flex-1 min-h-0>
155
171
  <ChatEmbed
172
+ ref="host"
156
173
  v-bind="widgetEmbedProps"
157
174
  compact
158
175
  @update:conversation-id="handleWidgetConversationIdChange"
@@ -162,10 +179,14 @@ const widgetEmbedProps = computed(() => ({
162
179
  @message-feedback="emit('messageFeedback', $event)"
163
180
  @message-terminal="emit('messageTerminal', $event)"
164
181
  @message-sent="emit('messageSent', $event)"
182
+ @conversation-cleared="emit('conversationCleared', $event)"
165
183
  >
166
184
  <template v-if="$slots['failed-message-actions']" #failed-message-actions="slotProps">
167
185
  <slot name="failed-message-actions" v-bind="slotProps" />
168
186
  </template>
187
+ <template v-if="$slots['user-message']" #user-message="slotProps">
188
+ <slot name="user-message" v-bind="slotProps" />
189
+ </template>
169
190
  </ChatEmbed>
170
191
  </div>
171
192
  </TelaChatFloatingWindow>
@@ -183,6 +204,7 @@ const widgetEmbedProps = computed(() => ({
183
204
  </TelaChatWidgetHeader>
184
205
  <div flex-1 min-h-0>
185
206
  <ChatEmbed
207
+ ref="host"
186
208
  v-bind="widgetEmbedProps"
187
209
  compact
188
210
  @update:conversation-id="handleWidgetConversationIdChange"
@@ -192,10 +214,14 @@ const widgetEmbedProps = computed(() => ({
192
214
  @message-feedback="emit('messageFeedback', $event)"
193
215
  @message-terminal="emit('messageTerminal', $event)"
194
216
  @message-sent="emit('messageSent', $event)"
217
+ @conversation-cleared="emit('conversationCleared', $event)"
195
218
  >
196
219
  <template v-if="$slots['failed-message-actions']" #failed-message-actions="slotProps">
197
220
  <slot name="failed-message-actions" v-bind="slotProps" />
198
221
  </template>
222
+ <template v-if="$slots['user-message']" #user-message="slotProps">
223
+ <slot name="user-message" v-bind="slotProps" />
224
+ </template>
199
225
  </ChatEmbed>
200
226
  </div>
201
227
  </TelaChatSidepanelWindow>
@@ -1,4 +1,5 @@
1
1
  import type { DefineSetupFnComponent, SlotsType } from 'vue';
2
+ import type { ChatEmbedHost } from '../../types/embed.js';
2
3
  import type { ChatActionPayload } from '../../messages/composables/chat-action.js';
3
4
  import type { MessageFeedbackPayload } from '../../messages/composables/chat-feedback.js';
4
5
  import type { ChatMessageTerminalPayload } from '../../messages/composables/chat-message-terminal.js';
@@ -22,6 +23,8 @@ type MeistrariChatEmbedEmits = {
22
23
  'messageTerminal': (payload: ChatMessageTerminalPayload) => any;
23
24
  /** Fired once the server accepts a user message, including retries of a failed answer. */
24
25
  'messageSent': (payload: ChatMessageSentPayload) => any;
26
+ /** Fired after an in-place reset; the conversation ID is unchanged. */
27
+ 'conversationCleared': (conversationId: string) => any;
25
28
  };
26
29
 
27
30
  type MeistrariChatEmbedSlots = SlotsType<{
@@ -33,6 +36,8 @@ type MeistrariChatEmbedSlots = SlotsType<{
33
36
  'composer-bottom'?: () => any;
34
37
  /** Rendered inside a failed assistant message's error box, next to "Tentar novamente"; receives the failed message. */
35
38
  'failed-message-actions'?: (props: { message: Message }) => any;
39
+ /** Replaces the text of a user message, keeping its attachments, sender and timestamp; render nothing to keep the default text. */
40
+ 'user-message'?: (props: { message: Message }) => any;
36
41
  }>;
37
42
 
38
43
  /**
@@ -61,7 +66,8 @@ type MeistrariChatEmbedSlots = SlotsType<{
61
66
  * </template>
62
67
  * ```
63
68
  */
64
- declare const MeistrariChatEmbed: DefineSetupFnComponent<MeistrariChatEmbedProps, MeistrariChatEmbedEmits, MeistrariChatEmbedSlots>;
69
+ type EmbedComponent = DefineSetupFnComponent<MeistrariChatEmbedProps, MeistrariChatEmbedEmits, MeistrariChatEmbedSlots>;
70
+ declare const MeistrariChatEmbed: EmbedComponent & { new (): InstanceType<EmbedComponent> & ChatEmbedHost };
65
71
 
66
72
  export default MeistrariChatEmbed;
67
73
  export type { AvailableChatModels, ChatActionPayload, ChatEmbedVariant, ChatEmbedVariantProps, ChatFeatureConfig, ChatMessageSentPayload, ChatMessageTerminalPayload, ChatSidebarConfig, ChatSuggestion, DefaultChatEmbedProps, MeistrariChatEmbedProps, Message, MessageFeedbackPayload, TelaAgentChatEmbedProps };
@@ -3,12 +3,12 @@ type __VLS_Props = {
3
3
  };
4
4
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
5
5
  close: () => any;
6
- dismiss: () => any;
7
6
  newConversation: () => any;
7
+ dismiss: () => any;
8
8
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
9
  onClose?: (() => any) | undefined;
10
- onDismiss?: (() => any) | undefined;
11
10
  onNewConversation?: (() => any) | undefined;
11
+ onDismiss?: (() => any) | undefined;
12
12
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
13
  declare const _default: typeof __VLS_export;
14
14
  export default _default;
@@ -3,12 +3,12 @@ type __VLS_Props = {
3
3
  };
4
4
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
5
5
  close: () => any;
6
- dismiss: () => any;
7
6
  newConversation: () => any;
7
+ dismiss: () => any;
8
8
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
9
  onClose?: (() => any) | undefined;
10
- onDismiss?: (() => any) | undefined;
11
10
  onNewConversation?: (() => any) | undefined;
11
+ onDismiss?: (() => any) | undefined;
12
12
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
13
  declare const _default: typeof __VLS_export;
14
14
  export default _default;
@@ -52,13 +52,13 @@ type __VLS_ModelProps = {
52
52
  'selectedReasoningEffort'?: ChatReasoningEffort | null;
53
53
  };
54
54
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
55
- declare var __VLS_54: {}, __VLS_100: {}, __VLS_102: {};
55
+ declare var __VLS_54: {}, __VLS_101: {}, __VLS_103: {};
56
56
  type __VLS_Slots = {} & {
57
57
  loading?: (props: typeof __VLS_54) => any;
58
58
  } & {
59
- empty?: (props: typeof __VLS_100) => any;
59
+ empty?: (props: typeof __VLS_101) => any;
60
60
  } & {
61
- default?: (props: typeof __VLS_102) => any;
61
+ default?: (props: typeof __VLS_103) => any;
62
62
  };
63
63
  declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
64
64
  "update:selectedModel": (value: string | null) => any;
@@ -74,9 +74,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
74
74
  selectConversation: (id: string) => any;
75
75
  send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
76
76
  settings: () => any;
77
+ newConversation: (groupId?: string | null | undefined) => any;
77
78
  selectExecutionProfile: (profile: ChatExecutionProfile) => any;
78
79
  tabChange: (tab: TopbarTab) => any;
79
- newConversation: (groupId?: string | null | undefined) => any;
80
80
  switchWorkspace: () => any;
81
81
  signout: () => any;
82
82
  cancelEditing: () => any;
@@ -96,11 +96,11 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
96
96
  onSelectConversation?: ((id: string) => any) | undefined;
97
97
  onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
98
98
  onSettings?: (() => any) | undefined;
99
+ onNewConversation?: ((groupId?: string | null | undefined) => any) | undefined;
99
100
  onSelectExecutionProfile?: ((profile: ChatExecutionProfile) => any) | undefined;
100
101
  "onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
101
102
  "onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
102
103
  onTabChange?: ((tab: TopbarTab) => any) | undefined;
103
- onNewConversation?: ((groupId?: string | null | undefined) => any) | undefined;
104
104
  onSwitchWorkspace?: (() => any) | undefined;
105
105
  onSignout?: (() => any) | undefined;
106
106
  onCancelEditing?: (() => any) | undefined;
@@ -253,6 +253,7 @@ function startNewConversationFromOutdatedSettings() {
253
253
  :execution-controls-disabled="executionControlsDisabled"
254
254
  centered
255
255
  @select-execution-profile="emit('selectExecutionProfile', $event)"
256
+ @new-conversation="emit('newConversation')"
256
257
  @send="(content, files, agentInputs) => emit('send', content, files, agentInputs)"
257
258
  @cancel="emit('cancel')"
258
259
  />
@@ -52,13 +52,13 @@ type __VLS_ModelProps = {
52
52
  'selectedReasoningEffort'?: ChatReasoningEffort | null;
53
53
  };
54
54
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
55
- declare var __VLS_54: {}, __VLS_100: {}, __VLS_102: {};
55
+ declare var __VLS_54: {}, __VLS_101: {}, __VLS_103: {};
56
56
  type __VLS_Slots = {} & {
57
57
  loading?: (props: typeof __VLS_54) => any;
58
58
  } & {
59
- empty?: (props: typeof __VLS_100) => any;
59
+ empty?: (props: typeof __VLS_101) => any;
60
60
  } & {
61
- default?: (props: typeof __VLS_102) => any;
61
+ default?: (props: typeof __VLS_103) => any;
62
62
  };
63
63
  declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
64
64
  "update:selectedModel": (value: string | null) => any;
@@ -74,9 +74,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
74
74
  selectConversation: (id: string) => any;
75
75
  send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
76
76
  settings: () => any;
77
+ newConversation: (groupId?: string | null | undefined) => any;
77
78
  selectExecutionProfile: (profile: ChatExecutionProfile) => any;
78
79
  tabChange: (tab: TopbarTab) => any;
79
- newConversation: (groupId?: string | null | undefined) => any;
80
80
  switchWorkspace: () => any;
81
81
  signout: () => any;
82
82
  cancelEditing: () => any;
@@ -96,11 +96,11 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
96
96
  onSelectConversation?: ((id: string) => any) | undefined;
97
97
  onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
98
98
  onSettings?: (() => any) | undefined;
99
+ onNewConversation?: ((groupId?: string | null | undefined) => any) | undefined;
99
100
  onSelectExecutionProfile?: ((profile: ChatExecutionProfile) => any) | undefined;
100
101
  "onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
101
102
  "onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
102
103
  onTabChange?: ((tab: TopbarTab) => any) | undefined;
103
- onNewConversation?: ((groupId?: string | null | undefined) => any) | undefined;
104
104
  onSwitchWorkspace?: (() => any) | undefined;
105
105
  onSignout?: (() => any) | undefined;
106
106
  onCancelEditing?: (() => any) | undefined;
@@ -14,4 +14,5 @@ export declare function useConversationFiles(conversationId: Ref<string | null |
14
14
  documentFiles: any;
15
15
  otherFiles: any;
16
16
  fetchFiles: () => Promise<void>;
17
+ clearFiles: () => void;
17
18
  };
@@ -4,7 +4,8 @@ function useConversationFilesState() {
4
4
  return {
5
5
  files: useState("conversationFiles", () => ({})),
6
6
  loading: useState("conversationFilesLoading", () => ({})),
7
- error: useState("conversationFilesError", () => ({}))
7
+ error: useState("conversationFilesError", () => ({})),
8
+ revisions: useState("conversationFilesRevisions", () => ({}))
8
9
  };
9
10
  }
10
11
  function addFilesToConversationState(state, conversationId, newFiles, messageId, messageRole) {
@@ -57,6 +58,7 @@ export function useConversationFiles(conversationId) {
57
58
  const id = conversationId.value;
58
59
  if (!id)
59
60
  return;
61
+ const revision = state.revisions.value[id] ?? 0;
60
62
  const fetchId = ++currentFetchId;
61
63
  state.loading.value = { ...state.loading.value, [id]: true };
62
64
  state.error.value = { ...state.error.value, [id]: null };
@@ -65,11 +67,11 @@ export function useConversationFiles(conversationId) {
65
67
  chatApi.path(`/conversations/${id}/files`),
66
68
  chatApi.withChatHeaders()
67
69
  );
68
- if (fetchId === currentFetchId) {
70
+ if (fetchId === currentFetchId && revision === (state.revisions.value[id] ?? 0)) {
69
71
  state.files.value = { ...state.files.value, [id]: response.files };
70
72
  }
71
73
  } catch (err) {
72
- if (fetchId === currentFetchId) {
74
+ if (fetchId === currentFetchId && revision === (state.revisions.value[id] ?? 0)) {
73
75
  state.error.value = {
74
76
  ...state.error.value,
75
77
  [id]: err instanceof Error ? err.message : "Falha ao buscar arquivos"
@@ -77,11 +79,21 @@ export function useConversationFiles(conversationId) {
77
79
  state.files.value = { ...state.files.value, [id]: [] };
78
80
  }
79
81
  } finally {
80
- if (fetchId === currentFetchId) {
82
+ if (fetchId === currentFetchId && revision === (state.revisions.value[id] ?? 0)) {
81
83
  state.loading.value = { ...state.loading.value, [id]: false };
82
84
  }
83
85
  }
84
86
  }
87
+ function clearFiles() {
88
+ currentFetchId++;
89
+ const id = conversationId.value;
90
+ if (!id)
91
+ return;
92
+ state.revisions.value = { ...state.revisions.value, [id]: (state.revisions.value[id] ?? 0) + 1 };
93
+ state.files.value = { ...state.files.value, [id]: [] };
94
+ state.loading.value = { ...state.loading.value, [id]: false };
95
+ state.error.value = { ...state.error.value, [id]: null };
96
+ }
85
97
  const hasFiles = computed(() => files.value.length > 0);
86
98
  const imageFiles = computed(
87
99
  () => files.value.filter((f) => f.mimeType.startsWith("image/"))
@@ -108,6 +120,7 @@ export function useConversationFiles(conversationId) {
108
120
  imageFiles,
109
121
  documentFiles,
110
122
  otherFiles,
111
- fetchFiles
123
+ fetchFiles,
124
+ clearFiles
112
125
  };
113
126
  }
@@ -1,8 +1,12 @@
1
- declare var __VLS_10: string, __VLS_11: any;
1
+ declare var __VLS_11: string, __VLS_12: any;
2
2
  type __VLS_Slots = {} & {
3
- [K in NonNullable<typeof __VLS_10>]?: (props: typeof __VLS_11) => any;
3
+ [K in NonNullable<typeof __VLS_11>]?: (props: typeof __VLS_12) => any;
4
4
  };
5
- declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {
6
+ clear: () => any;
7
+ sendMessage: (content: string, messageId?: string) => any;
8
+ cancel: () => any;
9
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
10
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
11
  declare const _default: typeof __VLS_export;
8
12
  export default _default;
@@ -4,6 +4,12 @@ import { provideChatHarnessAdapter } from "../../../embed/composables/harness-ad
4
4
  import { useChat } from "../../messages/composables/chat";
5
5
  import { useLightweightModelSelection } from "../composables/model-selection";
6
6
  defineOptions({ inheritAttrs: false });
7
+ const host = ref(null);
8
+ defineExpose({
9
+ clear: () => host.value?.clear() ?? Promise.resolve(false),
10
+ sendMessage: (content, messageId) => host.value?.sendMessage(content, messageId) ?? Promise.resolve(false),
11
+ cancel: () => host.value?.cancel() ?? Promise.resolve()
12
+ });
7
13
  provideChatHarnessAdapter({
8
14
  useChat,
9
15
  useModelSelection: useLightweightModelSelection,
@@ -12,7 +18,7 @@ provideChatHarnessAdapter({
12
18
  </script>
13
19
 
14
20
  <template>
15
- <ChatEmbedInner v-bind="$attrs">
21
+ <ChatEmbedInner ref="host" v-bind="$attrs">
16
22
  <template v-for="(_, name) in $slots" #[name]="slotProps">
17
23
  <slot :name="name" v-bind="slotProps || {}" />
18
24
  </template>
@@ -1,8 +1,12 @@
1
- declare var __VLS_10: string, __VLS_11: any;
1
+ declare var __VLS_11: string, __VLS_12: any;
2
2
  type __VLS_Slots = {} & {
3
- [K in NonNullable<typeof __VLS_10>]?: (props: typeof __VLS_11) => any;
3
+ [K in NonNullable<typeof __VLS_11>]?: (props: typeof __VLS_12) => any;
4
4
  };
5
- declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {
6
+ clear: () => any;
7
+ sendMessage: (content: string, messageId?: string) => any;
8
+ cancel: () => any;
9
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
10
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
11
  declare const _default: typeof __VLS_export;
8
12
  export default _default;
@@ -34,7 +34,7 @@ export declare function useChat(options?: Parameters<typeof useSharedChat>[0]):
34
34
  wakeSessionId?: string;
35
35
  defer?: boolean;
36
36
  executionSettings?: ExecutionSettings;
37
- }>, queuedSend?: import("../../../messages/composables/outbound-queue.js").QueuedSend) => Promise<Message | null>;
37
+ }>, queuedSend?: import("../../../messages/composables/outbound-queue.js").QueuedSend, messageId?: string) => Promise<Message | null>;
38
38
  flushOutboundQueue: (wakeSessionId?: string) => Promise<Message | null>;
39
39
  failOutboundQueue: (message: string) => boolean;
40
40
  retryMessage: (messageId: string, prepare?: () => Promise<{
@@ -20,6 +20,13 @@ type __VLS_Slots = {
20
20
  'failed-message-actions'?: (props: {
21
21
  message: Message;
22
22
  }) => unknown;
23
+ /**
24
+ * Replaces the text of a user message. Attachments, sender and timestamp
25
+ * stay in place, and rendering nothing keeps the default text.
26
+ */
27
+ 'user-message'?: (props: {
28
+ message: Message;
29
+ }) => unknown;
23
30
  };
24
31
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
25
32
  retry: (messageId: string) => any;
@@ -196,10 +196,12 @@ const attachedFiles = computed(() => {
196
196
  </template>
197
197
  </div>
198
198
  <div body-16-regular whitespace-pre-wrap break-words>
199
- <template v-for="(segment, index) in userContentSegments" :key="`${index}-${segment.type === 'citation' ? segment.citation.raw : 'text'}`">
200
- <ChatCitation v-if="segment.type === 'citation'" :citation="segment.citation" :label="segment.label" />
201
- <span v-else v-html="renderUserText(segment.content)" />
202
- </template>
199
+ <slot name="user-message" :message="message">
200
+ <template v-for="(segment, index) in userContentSegments" :key="`${index}-${segment.type === 'citation' ? segment.citation.raw : 'text'}`">
201
+ <ChatCitation v-if="segment.type === 'citation'" :citation="segment.citation" :label="segment.label" />
202
+ <span v-else v-html="renderUserText(segment.content)" />
203
+ </template>
204
+ </slot>
203
205
  </div>
204
206
  </TelaChatMessageContent>
205
207
 
@@ -20,6 +20,13 @@ type __VLS_Slots = {
20
20
  'failed-message-actions'?: (props: {
21
21
  message: Message;
22
22
  }) => unknown;
23
+ /**
24
+ * Replaces the text of a user message. Attachments, sender and timestamp
25
+ * stay in place, and rendering nothing keeps the default text.
26
+ */
27
+ 'user-message'?: (props: {
28
+ message: Message;
29
+ }) => unknown;
23
30
  };
24
31
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
25
32
  retry: (messageId: string) => any;
@@ -51,10 +51,12 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
51
51
  } & {
52
52
  cancel: () => any;
53
53
  send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
54
+ newConversation: () => any;
54
55
  selectExecutionProfile: (profile: ChatExecutionProfile) => any;
55
56
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
56
57
  onCancel?: (() => any) | undefined;
57
58
  onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
59
+ onNewConversation?: (() => any) | undefined;
58
60
  onSelectExecutionProfile?: ((profile: ChatExecutionProfile) => any) | undefined;
59
61
  "onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
60
62
  "onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
@@ -35,7 +35,7 @@ const props = defineProps({
35
35
  sendBlocked: { type: Boolean, required: false },
36
36
  draftStorageKey: { type: [String, null], required: false }
37
37
  });
38
- const emit = defineEmits(["send", "cancel", "selectExecutionProfile"]);
38
+ const emit = defineEmits(["send", "newConversation", "cancel", "selectExecutionProfile"]);
39
39
  const selectedModel = defineModel("selectedModel", { type: [String, null], ...{ default: null } });
40
40
  const composerExtension = inject(CHAT_COMPOSER_EXTENSION_KEY, null);
41
41
  const selectedReasoningEffort = defineModel("selectedReasoningEffort", { type: null, ...{ default: null } });
@@ -125,7 +125,10 @@ const skillItems = computed(() => {
125
125
  }
126
126
  return items;
127
127
  });
128
- const commandItems = computed(() => composerExtension?.commands ?? []);
128
+ const commandItems = computed(() => [
129
+ { id: "new", label: "/new", description: "Criar uma nova conversa", icon: "i-ph-plus", run: startNewConversation },
130
+ ...(composerExtension?.commands ?? []).filter((command) => command.id !== "new")
131
+ ]);
129
132
  const mentionAutocomplete = useMentionAutocomplete({
130
133
  editorRef,
131
134
  fileItems,
@@ -134,8 +137,18 @@ const mentionAutocomplete = useMentionAutocomplete({
134
137
  disabled: isLocked,
135
138
  onInsert: handleInput
136
139
  });
140
+ function startNewConversation() {
141
+ if (isLocked.value || props.sendBlocked || uploading.value)
142
+ return;
143
+ setDraft("");
144
+ pendingFiles.value = [];
145
+ agentInputTexts.value = {};
146
+ mentionAutocomplete.resetMentions();
147
+ inputHistory.resetNavigation();
148
+ emit("newConversation");
149
+ }
137
150
  const canSend = computed(
138
- () => content.value.trim().length > 0 && !isLocked.value && !props.sendBlocked && !uploading.value && !props.agentInputSchemaLoading && agentInputReadiness.value.ready
151
+ () => content.value.trim().length > 0 && !isLocked.value && !props.sendBlocked && !uploading.value && (content.value.trim() === "/new" || !props.agentInputSchemaLoading && agentInputReadiness.value.ready)
139
152
  );
140
153
  const submitStatus = computed(() => {
141
154
  if (uploading.value)
@@ -278,6 +291,10 @@ function buildAgentInputs(files, queuedFiles) {
278
291
  async function handleSend() {
279
292
  if (!canSend.value)
280
293
  return;
294
+ if (content.value.trim() === "/new") {
295
+ startNewConversation();
296
+ return;
297
+ }
281
298
  let files = [];
282
299
  const queuedFiles = [...pendingFiles.value];
283
300
  if (queuedFiles.length > 0) {
@@ -51,10 +51,12 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
51
51
  } & {
52
52
  cancel: () => any;
53
53
  send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
54
+ newConversation: () => any;
54
55
  selectExecutionProfile: (profile: ChatExecutionProfile) => any;
55
56
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
56
57
  onCancel?: (() => any) | undefined;
57
58
  onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
59
+ onNewConversation?: (() => any) | undefined;
58
60
  onSelectExecutionProfile?: ((profile: ChatExecutionProfile) => any) | undefined;
59
61
  "onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
60
62
  "onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
@@ -22,6 +22,9 @@ type __VLS_Slots = {
22
22
  'failed-message-actions'?: (props: {
23
23
  message: Message;
24
24
  }) => unknown;
25
+ 'user-message'?: (props: {
26
+ message: Message;
27
+ }) => unknown;
25
28
  };
26
29
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
27
30
  isPinned: import("vue").Ref<boolean, boolean>;
@@ -153,6 +153,9 @@ defineExpose({
153
153
  <template v-if="$slots['failed-message-actions']" #failed-message-actions="slotProps">
154
154
  <slot name="failed-message-actions" v-bind="slotProps" />
155
155
  </template>
156
+ <template v-if="$slots['user-message']" #user-message="slotProps">
157
+ <slot name="user-message" v-bind="slotProps" />
158
+ </template>
156
159
  </ChatMessageBubble>
157
160
  </template>
158
161
  <p
@@ -22,6 +22,9 @@ type __VLS_Slots = {
22
22
  'failed-message-actions'?: (props: {
23
23
  message: Message;
24
24
  }) => unknown;
25
+ 'user-message'?: (props: {
26
+ message: Message;
27
+ }) => unknown;
25
28
  };
26
29
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
27
30
  isPinned: import("vue").Ref<boolean, boolean>;
@@ -72,7 +72,7 @@ export declare function useChat(options?: {
72
72
  wakeSessionId?: string;
73
73
  defer?: boolean;
74
74
  executionSettings?: ExecutionSettings;
75
- }>, queuedSend?: QueuedSend) => Promise<Message | null>;
75
+ }>, queuedSend?: QueuedSend, messageId?: string) => Promise<Message | null>;
76
76
  flushOutboundQueue: (wakeSessionId?: string) => Promise<Message | null>;
77
77
  failOutboundQueue: (message: string) => boolean;
78
78
  retryMessage: (messageId: string, prepare?: () => Promise<{
@@ -165,8 +165,9 @@ export function useChat(options = {}, adapter = {}) {
165
165
  const streamingMessageId = ref(null);
166
166
  const pendingStreamReconciliationIds = ref(/* @__PURE__ */ new Set());
167
167
  const pendingRealResultIds = ref(/* @__PURE__ */ new Set());
168
+ const clearing = ref(false);
168
169
  const composerLocked = computed(
169
- () => hasPendingMessage.value || runtimeBlocked.value || pendingRealResultIds.value.size > 0 || outbound.occupied.value
170
+ () => clearing.value || hasPendingMessage.value || runtimeBlocked.value || pendingRealResultIds.value.size > 0 || outbound.occupied.value
170
171
  );
171
172
  const queuedMessageContent = computed(() => outbound.slot.value?.content ?? null);
172
173
  let requestDurableRecovery = () => {
@@ -562,7 +563,9 @@ export function useChat(options = {}, adapter = {}) {
562
563
  loading.value = false;
563
564
  }
564
565
  };
565
- const sendMessage = async (content, files, agentInputs, prepare, queuedSend) => {
566
+ const sendMessage = async (content, files, agentInputs, prepare, queuedSend, messageId) => {
567
+ if (clearing.value)
568
+ return null;
566
569
  if (!currentConversation.value && !prepare)
567
570
  return null;
568
571
  if (outboundQueueEnabled.value && outbound.occupied.value && !queuedSend)
@@ -579,6 +582,7 @@ export function useChat(options = {}, adapter = {}) {
579
582
  const existingOptimisticUserMessage = queuedSend ? messages.value.find((message) => message.id === queuedSend.optimisticIds.user) : void 0;
580
583
  const existingOptimisticAssistantMessage = queuedSend ? messages.value.find((message) => message.id === queuedSend.optimisticIds.assistant) : void 0;
581
584
  const optimisticPair = createOptimisticMessagePair({
585
+ messageId,
582
586
  conversationId,
583
587
  content,
584
588
  files: filesWithSource,
@@ -957,30 +961,48 @@ export function useChat(options = {}, adapter = {}) {
957
961
  }
958
962
  };
959
963
  const clearConversationThread = async () => {
960
- if (!currentConversation.value)
964
+ const conversationId = currentConversation.value?.id;
965
+ if (!conversationId || clearing.value || hasPendingMessage.value || sending.value || outbound.occupied.value)
961
966
  return false;
967
+ const generation = loadGeneration;
968
+ const ownsView = () => generation === loadGeneration && currentConversation.value?.id === conversationId;
969
+ clearing.value = true;
962
970
  error.value = null;
971
+ stopPolling();
963
972
  try {
964
973
  const updated = await $fetch(
965
- chatApi.path(`/conversations/${currentConversation.value.id}/clear`),
974
+ chatApi.path(`/conversations/${conversationId}/clear`),
966
975
  chatApi.withChatHeaders({ method: "POST" })
967
976
  );
968
- currentConversation.value = {
969
- ...currentConversation.value,
970
- ...updated,
971
- messages: []
972
- };
977
+ if (!ownsView())
978
+ return true;
979
+ stopPolling();
980
+ loadGeneration++;
981
+ loading.value = false;
982
+ gateController?.abort();
983
+ outbound.clear();
984
+ uncertainSend = null;
985
+ clearPreparingSteps();
986
+ directStream.disconnect();
987
+ currentConversation.value = { ...currentConversation.value, ...updated, messages: [] };
973
988
  messages.value = [];
989
+ runtimeBlocked.value = false;
974
990
  pendingStreamReconciliationIds.value = /* @__PURE__ */ new Set();
975
991
  pendingRealResultIds.value = /* @__PURE__ */ new Set();
976
992
  streamingMessageId.value = null;
977
- directStream.disconnect();
993
+ agentSessionStatus.value = null;
994
+ agentSessionExecutable.value = false;
995
+ agentSessionRewakeable.value = false;
978
996
  return true;
979
997
  } catch (err) {
980
- const message = err instanceof Error ? err.message : "Erro ao limpar conversa";
981
- error.value = message;
998
+ if (ownsView()) {
999
+ error.value = err instanceof Error ? err.message : "Erro ao limpar conversa";
1000
+ startPolling();
1001
+ }
982
1002
  Sentry.captureException(err);
983
1003
  return false;
1004
+ } finally {
1005
+ clearing.value = false;
984
1006
  }
985
1007
  };
986
1008
  const retryMessage = async (messageId, prepare) => {
@@ -8,6 +8,7 @@ export declare function createOptimisticMessagePair(input: {
8
8
  files: Message['files'];
9
9
  createdBy: string | null;
10
10
  now?: Date;
11
+ messageId?: string;
11
12
  createId?: () => string;
12
13
  }): {
13
14
  user: Message;
@@ -7,7 +7,7 @@ export function createOptimisticMessagePair(input) {
7
7
  const assistantCreatedAt = new Date(userCreatedAt.getTime() + 1);
8
8
  return {
9
9
  user: {
10
- id: createId(),
10
+ id: input.messageId ?? createId(),
11
11
  conversationId: input.conversationId,
12
12
  role: "user",
13
13
  content: input.content,
@@ -11,3 +11,11 @@ export type { ConversationEvent, Message } from './schemas/chat/conversations.js
11
11
  export type { MessageOverviewResponse } from './schemas/chat/messages.js';
12
12
  export type { ConversationCreatorFilter } from './schemas/chat/primitives.js';
13
13
  export type { TelaAgentExecutionInput } from './schemas/integrations/tela-agent-api.js';
14
+ /** Host controls for a ready Claude/v4 embed; these add no recording UI. */
15
+ export interface ChatEmbedHost {
16
+ /** True transfers ownership to chat, including pending network reconciliation; do not submit it again. */
17
+ sendMessage: (content: string, messageId?: string) => Promise<boolean>;
18
+ /** Clear history and runtime bindings while preserving the conversation ID. */
19
+ clear: () => Promise<boolean>;
20
+ cancel: () => Promise<void>;
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "4.12.0-rc.1",
3
+ "version": "4.12.0-rc.2",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {