@meistrari/chat-nuxt 4.10.0-rc.2 → 4.10.0-rc.21

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 (135) hide show
  1. package/README.md +8 -6
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +2 -0
  4. package/dist/runtime/conversations/components/chat-conversation-export-modal.d.vue.ts +10 -0
  5. package/dist/runtime/conversations/components/chat-conversation-export-modal.vue +126 -0
  6. package/dist/runtime/conversations/components/chat-conversation-export-modal.vue.d.ts +10 -0
  7. package/dist/runtime/conversations/components/chat-conversation-list.vue +8 -1
  8. package/dist/runtime/{rendering/components/openui/bar-chart.d.vue.ts → conversations/components/chat-export-document.d.vue.ts} +2 -5
  9. package/dist/runtime/conversations/components/chat-export-document.vue +26 -0
  10. package/dist/runtime/{rendering/components/openui/bar-chart.vue.d.ts → conversations/components/chat-export-document.vue.d.ts} +2 -5
  11. package/dist/runtime/{rendering/components/openui/checkbox.d.vue.ts → conversations/components/chat-export-file.d.vue.ts} +1 -1
  12. package/dist/runtime/conversations/components/chat-export-file.vue +12 -0
  13. package/dist/runtime/{rendering/components/openui/checkbox.vue.d.ts → conversations/components/chat-export-file.vue.d.ts} +1 -1
  14. package/dist/runtime/{rendering/components/openui/buttons.d.vue.ts → conversations/components/chat-export-message.d.vue.ts} +2 -3
  15. package/dist/runtime/conversations/components/chat-export-message.vue +84 -0
  16. package/dist/runtime/{rendering/components/openui/bar.vue.d.ts → conversations/components/chat-export-message.vue.d.ts} +2 -5
  17. package/dist/runtime/{rendering/components/openui/bar.d.vue.ts → conversations/components/chat-export-openui.d.vue.ts} +3 -4
  18. package/dist/runtime/conversations/components/chat-export-openui.vue +19 -0
  19. package/dist/runtime/conversations/components/chat-export-openui.vue.d.ts +9 -0
  20. package/dist/runtime/{rendering/components/openui/callout.d.vue.ts → conversations/components/chat-export-questions.d.vue.ts} +12 -4
  21. package/dist/runtime/conversations/components/chat-export-questions.vue +22 -0
  22. package/dist/runtime/{rendering/components/openui/callout.vue.d.ts → conversations/components/chat-export-questions.vue.d.ts} +12 -4
  23. package/dist/runtime/conversations/composables/conversation-list.d.ts +2 -1
  24. package/dist/runtime/conversations/composables/conversation-list.js +4 -13
  25. package/dist/runtime/conversations/export-document.d.ts +1 -0
  26. package/dist/runtime/conversations/export-document.js +154 -0
  27. package/dist/runtime/conversations/export.d.ts +4 -0
  28. package/dist/runtime/conversations/export.js +69 -4
  29. package/dist/runtime/embed/components/chat-embed-inner.vue +81 -7
  30. package/dist/runtime/embed/components/mobile/chat-mobile-shell.d.vue.ts +3 -2
  31. package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue +2 -0
  32. package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue.d.ts +3 -2
  33. package/dist/runtime/embed/execution-controls.d.ts +2 -1
  34. package/dist/runtime/embed/execution-controls.js +2 -2
  35. package/dist/runtime/messages/components/chat-execution-mode-selector.vue +10 -6
  36. package/dist/runtime/messages/components/chat-message-bubble.vue +7 -0
  37. package/dist/runtime/messages/components/chat-message-input.d.vue.ts +2 -0
  38. package/dist/runtime/messages/components/chat-message-input.vue +18 -1
  39. package/dist/runtime/messages/components/chat-message-input.vue.d.ts +2 -0
  40. package/dist/runtime/messages/composables/chat.d.ts +15 -0
  41. package/dist/runtime/messages/composables/chat.js +49 -5
  42. package/dist/runtime/messages/composer-draft.d.ts +16 -0
  43. package/dist/runtime/messages/composer-draft.js +61 -0
  44. package/dist/runtime/messages/composer-pending-send.d.ts +8 -0
  45. package/dist/runtime/messages/composer-pending-send.js +34 -0
  46. package/dist/runtime/messages/conversation-timeline.js +1 -1
  47. package/dist/runtime/rendering/components/openui/{markdown.d.vue.ts → markdown-prose.d.vue.ts} +1 -3
  48. package/dist/runtime/rendering/components/openui/{markdown.vue → markdown-prose.vue} +3 -3
  49. package/dist/runtime/rendering/components/openui/{markdown.vue.d.ts → markdown-prose.vue.d.ts} +1 -3
  50. package/dist/runtime/rendering/components/openui-block.vue +26 -52
  51. package/dist/runtime/rendering/openui/context.d.ts +2 -17
  52. package/dist/runtime/rendering/openui/context.js +0 -2
  53. package/dist/runtime/server/utils/chat-api-proxy.d.ts +1 -1
  54. package/dist/runtime/server/utils/chat-api-proxy.js +3 -2
  55. package/dist/runtime/types/composer.d.ts +6 -0
  56. package/dist/runtime/types/schemas/chat/chat-config.d.ts +6 -2
  57. package/dist/runtime/types/schemas/chat/chat-config.js +1 -0
  58. package/dist/runtime/types/schemas/chat/conversation-runtime.d.ts +8 -0
  59. package/dist/runtime/types/schemas/chat/conversations.d.ts +8 -0
  60. package/dist/runtime/types/schemas/chat/messages.d.ts +12 -0
  61. package/dist/runtime/types/schemas/chat/ui-language.d.ts +19 -2
  62. package/dist/runtime/types/schemas/chat/ui-language.js +8 -3
  63. package/dist/runtime/types/schemas/chat/v3.d.ts +160 -2
  64. package/dist/runtime/types/schemas/chat/v3.js +91 -5
  65. package/dist/runtime/types/schemas/integrations/slack-apps.d.ts +53 -0
  66. package/dist/runtime/types/schemas/integrations/slack-apps.js +6 -0
  67. package/dist/runtime/types/schemas/integrations/slack-identity.d.ts +26 -0
  68. package/dist/runtime/types/schemas/integrations/slack-identity.js +23 -0
  69. package/dist/runtime/types/schemas/integrations/tela-agent-api.d.ts +40 -0
  70. package/dist/runtime/types/schemas/integrations/tela-agent-api.js +3 -0
  71. package/dist/runtime/types/schemas/integrations/tela-api.d.ts +11 -0
  72. package/dist/runtime/types/schemas/integrations/tela-api.js +8 -0
  73. package/dist/runtime/types/schemas/integrations/tela-auth.d.ts +39 -0
  74. package/dist/runtime/types/schemas/integrations/tela-auth.js +12 -0
  75. package/dist/runtime/types/schemas/slack/apps.d.ts +144 -0
  76. package/dist/runtime/types/schemas/slack/apps.js +53 -0
  77. package/dist/runtime/types/schemas/slack/identities.d.ts +58 -0
  78. package/dist/runtime/types/schemas/slack/identities.js +19 -0
  79. package/dist/runtime/types/schemas/slack/installations.d.ts +39 -0
  80. package/dist/runtime/types/schemas/slack/installations.js +18 -0
  81. package/package.json +2 -2
  82. package/dist/runtime/rendering/components/openui/bar-chart.vue +0 -39
  83. package/dist/runtime/rendering/components/openui/bar.vue +0 -12
  84. package/dist/runtime/rendering/components/openui/button.d.vue.ts +0 -11
  85. package/dist/runtime/rendering/components/openui/button.vue +0 -30
  86. package/dist/runtime/rendering/components/openui/button.vue.d.ts +0 -11
  87. package/dist/runtime/rendering/components/openui/buttons.vue +0 -14
  88. package/dist/runtime/rendering/components/openui/buttons.vue.d.ts +0 -8
  89. package/dist/runtime/rendering/components/openui/callout.vue +0 -27
  90. package/dist/runtime/rendering/components/openui/card.d.vue.ts +0 -9
  91. package/dist/runtime/rendering/components/openui/card.vue +0 -19
  92. package/dist/runtime/rendering/components/openui/card.vue.d.ts +0 -9
  93. package/dist/runtime/rendering/components/openui/checkbox.vue +0 -26
  94. package/dist/runtime/rendering/components/openui/col.d.vue.ts +0 -10
  95. package/dist/runtime/rendering/components/openui/col.vue +0 -16
  96. package/dist/runtime/rendering/components/openui/col.vue.d.ts +0 -10
  97. package/dist/runtime/rendering/components/openui/form.d.vue.ts +0 -10
  98. package/dist/runtime/rendering/components/openui/form.vue +0 -60
  99. package/dist/runtime/rendering/components/openui/form.vue.d.ts +0 -10
  100. package/dist/runtime/rendering/components/openui/input.d.vue.ts +0 -10
  101. package/dist/runtime/rendering/components/openui/input.vue +0 -23
  102. package/dist/runtime/rendering/components/openui/input.vue.d.ts +0 -10
  103. package/dist/runtime/rendering/components/openui/metric.d.vue.ts +0 -11
  104. package/dist/runtime/rendering/components/openui/metric.vue +0 -20
  105. package/dist/runtime/rendering/components/openui/metric.vue.d.ts +0 -11
  106. package/dist/runtime/rendering/components/openui/option.d.vue.ts +0 -10
  107. package/dist/runtime/rendering/components/openui/option.vue +0 -12
  108. package/dist/runtime/rendering/components/openui/option.vue.d.ts +0 -10
  109. package/dist/runtime/rendering/components/openui/question.d.vue.ts +0 -11
  110. package/dist/runtime/rendering/components/openui/question.vue +0 -15
  111. package/dist/runtime/rendering/components/openui/question.vue.d.ts +0 -11
  112. package/dist/runtime/rendering/components/openui/questions.d.vue.ts +0 -9
  113. package/dist/runtime/rendering/components/openui/questions.vue +0 -91
  114. package/dist/runtime/rendering/components/openui/questions.vue.d.ts +0 -9
  115. package/dist/runtime/rendering/components/openui/select.d.vue.ts +0 -10
  116. package/dist/runtime/rendering/components/openui/select.vue +0 -27
  117. package/dist/runtime/rendering/components/openui/select.vue.d.ts +0 -10
  118. package/dist/runtime/rendering/components/openui/stack.d.vue.ts +0 -9
  119. package/dist/runtime/rendering/components/openui/stack.vue +0 -18
  120. package/dist/runtime/rendering/components/openui/stack.vue.d.ts +0 -9
  121. package/dist/runtime/rendering/components/openui/table.d.vue.ts +0 -9
  122. package/dist/runtime/rendering/components/openui/table.vue +0 -41
  123. package/dist/runtime/rendering/components/openui/table.vue.d.ts +0 -9
  124. package/dist/runtime/rendering/openui/actions.d.ts +0 -15
  125. package/dist/runtime/rendering/openui/actions.js +0 -40
  126. package/dist/runtime/rendering/openui/form-fields.d.ts +0 -10
  127. package/dist/runtime/rendering/openui/form-fields.js +0 -30
  128. package/dist/runtime/rendering/openui/program.d.ts +0 -15
  129. package/dist/runtime/rendering/openui/program.js +0 -74
  130. package/dist/runtime/rendering/openui/registry.d.ts +0 -5
  131. package/dist/runtime/rendering/openui/registry.js +0 -44
  132. package/dist/runtime/rendering/openui/render.d.ts +0 -21
  133. package/dist/runtime/rendering/openui/render.js +0 -45
  134. package/dist/runtime/rendering/openui/use-openui-context.d.ts +0 -2
  135. package/dist/runtime/rendering/openui/use-openui-context.js +0 -8
@@ -5,6 +5,7 @@ import type { Conversation, ConversationWithMessages, Message } from '../../type
5
5
  import { type ChatReasoningEffort } from '../../types/schemas/chat/models.js';
6
6
  import type { FileUploadInput, MessageFile } from '../../types/schemas/chat/files.js';
7
7
  import type { ExecutionSettings, SendMessageBody } from '../../types/schemas/chat/messages.js';
8
+ import type { SendMessageV2Body } from '../../types/schemas/chat/conversation-runtime.js';
8
9
  import type { TelaAgentExecutionInput } from '../../types/schemas/integrations/tela-agent-api.js';
9
10
  import { useDirectStream } from './use-direct-stream.js';
10
11
  import { type QueuedSend } from './outbound-queue.js';
@@ -23,6 +24,20 @@ export declare function useChat(options?: {
23
24
  outboundQueueEnabled?: MaybeRefOrGetter<boolean>;
24
25
  onQueuedSendCancelled?: (send: QueuedSend) => void;
25
26
  onQueuedSendFailed?: (send: QueuedSend) => void;
27
+ /** The server recorded this message (its acknowledgement or a later poll); the composer draft can go. */
28
+ onSendConfirmed?: (send: {
29
+ conversationId: string;
30
+ content: string;
31
+ messageId: string;
32
+ }) => void;
33
+ onSendPrepared?: (send: {
34
+ conversationId: string;
35
+ body: SendMessageV2Body;
36
+ }) => string;
37
+ onMessagesReceived?: (snapshot: {
38
+ conversationId: string;
39
+ messages: Message[];
40
+ }) => void;
26
41
  }, adapter?: ChatStateAdapter): {
27
42
  currentConversation: any;
28
43
  messages: any;
@@ -37,6 +37,8 @@ const POLL_INTERVAL_MS = 500;
37
37
  const POLL_MAX_BACKOFF_MS = 5e3;
38
38
  const CHAT_REQUEST_TIMEOUT_MS = 15e3;
39
39
  const CHAT_UNAVAILABLE_TEXT = "Chat temporariamente indispon\xEDvel. Tente novamente.";
40
+ const SEND_PENDING_TEXT = "Confirma\xE7\xE3o do envio pendente. Reconectando \xE0 conversa\u2026";
41
+ const SEND_UNCONFIRMED_TEXT = "Sua mensagem n\xE3o foi confirmada pelo servidor. Mantenha esta conversa aberta e use Reenviar para tentar de novo.";
40
42
  const WORKSPACE_AGENT_NOT_READY_TEXT = "O agente deste workspace ainda n\xE3o foi configurado no Tela. Abra as configura\xE7\xF5es do workspace para criar o agente.";
41
43
  export function resolveTelaAgentInputs(messageInputs, configuredInputs) {
42
44
  const inputs = messageInputs ?? configuredInputs;
@@ -249,6 +251,22 @@ export function useChat(options = {}, adapter = {}) {
249
251
  let consecutivePollFailures = 0;
250
252
  let pollOutageStartedAt = null;
251
253
  const pollInterval = ref(POLL_INTERVAL_MS);
254
+ let uncertainSend = null;
255
+ function confirmSend(conversationId, content, messageId) {
256
+ uncertainSend = null;
257
+ options.onSendConfirmed?.({ conversationId, content, messageId });
258
+ }
259
+ function confirmUncertainSend(conversationId, durableMessages) {
260
+ const pending = uncertainSend;
261
+ if (!pending || pending.conversationId !== conversationId)
262
+ return;
263
+ const recorded = durableMessages.find((message) => message.role === "user" && (pending.messageId ? message.externalUuid === pending.messageId : !pending.knownIds.has(message.id) && message.content === pending.content));
264
+ if (!recorded)
265
+ return;
266
+ if (error.value === SEND_PENDING_TEXT)
267
+ error.value = null;
268
+ confirmSend(conversationId, pending.content, pending.messageId ?? recorded.id);
269
+ }
252
270
  const pollMessages = async () => {
253
271
  if (!currentConversation.value)
254
272
  return;
@@ -268,7 +286,7 @@ export function useChat(options = {}, adapter = {}) {
268
286
  if (generation !== pollGeneration || currentConversation.value?.id !== conversationId)
269
287
  return;
270
288
  resetPollOutage();
271
- if (error.value === CHAT_UNAVAILABLE_TEXT)
289
+ if (error.value === CHAT_UNAVAILABLE_TEXT || error.value === SEND_UNCONFIRMED_TEXT)
272
290
  error.value = null;
273
291
  if (runtime)
274
292
  runtimeBlocked.value = runtime.conversation.runtimeState === "blocked";
@@ -277,9 +295,11 @@ export function useChat(options = {}, adapter = {}) {
277
295
  agentSessionExecutable.value = response.agentSession.executable;
278
296
  agentSessionRewakeable.value = response.agentSession.rewakeable ?? false;
279
297
  }
298
+ options.onMessagesReceived?.({ conversationId, messages: response.messages });
299
+ confirmUncertainSend(conversationId, response.messages);
280
300
  const queuedV2Send = chatApi.conversationV2Enabled ? outbound.slot.value : null;
281
301
  if (queuedV2Send) {
282
- const admitted = response.messages.some((message) => message.id === queuedV2Send.optimisticIds.user);
302
+ const admitted = response.messages.some((message) => message.role === "user" && message.externalUuid === queuedV2Send.optimisticIds.user);
283
303
  if (admitted) {
284
304
  outbound.clear();
285
305
  gateHolding.value = false;
@@ -354,7 +374,7 @@ export function useChat(options = {}, adapter = {}) {
354
374
  if (terminalError || outageExhausted) {
355
375
  const message = statusCode === 501 || !terminalError ? CHAT_UNAVAILABLE_TEXT : getChatApiErrorMessage(err) ?? "Erro ao atualizar mensagens";
356
376
  if (!terminalError || statusCode === 501 || outbound.occupied.value) {
357
- error.value = message;
377
+ error.value = outbound.occupied.value ? SEND_UNCONFIRMED_TEXT : message;
358
378
  stopPolling();
359
379
  return;
360
380
  }
@@ -505,8 +525,10 @@ export function useChat(options = {}, adapter = {}) {
505
525
  );
506
526
  if (generation !== loadGeneration)
507
527
  return false;
528
+ options.onMessagesReceived?.({ conversationId: id, messages: data.messages });
508
529
  currentConversation.value = data;
509
530
  messages.value = data.messages;
531
+ confirmUncertainSend(id, data.messages);
510
532
  pendingStreamReconciliationIds.value = /* @__PURE__ */ new Set();
511
533
  pendingRealResultIds.value = /* @__PURE__ */ new Set();
512
534
  if (shouldTreatLoadedConversationTitleAsGenerated(data.messages))
@@ -622,6 +644,7 @@ export function useChat(options = {}, adapter = {}) {
622
644
  if (sentEmitted)
623
645
  return;
624
646
  sentEmitted = true;
647
+ confirmSend(response2.userMessage.conversationId, content, chatApi.conversationV2Enabled ? optimisticUserMessage.id : response2.userMessage.id);
625
648
  chatMessageSent.emit({
626
649
  conversationId: response2.userMessage.conversationId,
627
650
  messageId: response2.userMessage.id,
@@ -658,6 +681,13 @@ export function useChat(options = {}, adapter = {}) {
658
681
  clientTiming: body.clientTiming
659
682
  };
660
683
  const conversationId2 = currentConversation.value.id;
684
+ const preparedMessageId = options.onSendPrepared?.({ conversationId: conversationId2, body: v2Body });
685
+ if (preparedMessageId && preparedMessageId !== optimisticUserMessage.id) {
686
+ const previousId = optimisticUserMessage.id;
687
+ optimisticUserMessage.id = preparedMessageId;
688
+ v2Body.messageId = preparedMessageId;
689
+ messages.value = messages.value.map((message) => message.id === previousId ? optimisticUserMessage : message);
690
+ }
661
691
  v2AdmissionRequested = true;
662
692
  const accepted = await $fetch(
663
693
  chatApi.runtimePath(`/conversations/${conversationId2}/messages`),
@@ -687,6 +717,12 @@ export function useChat(options = {}, adapter = {}) {
687
717
  if (statusCode !== null && statusCode >= 400 && statusCode < 500)
688
718
  throw streamError;
689
719
  if (outboundQueueEnabled.value) {
720
+ uncertainSend = {
721
+ conversationId: currentConversation.value.id,
722
+ content,
723
+ messageId: null,
724
+ knownIds: new Set(messages.value.map((message) => message.id))
725
+ };
690
726
  startPolling();
691
727
  return optimisticUserMessage;
692
728
  }
@@ -725,12 +761,14 @@ export function useChat(options = {}, adapter = {}) {
725
761
  attempts
726
762
  });
727
763
  gateHolding.value = true;
728
- error.value = "Confirma\xE7\xE3o do envio pendente. Reconectando \xE0 conversa\u2026";
764
+ error.value = SEND_PENDING_TEXT;
765
+ uncertainSend = { conversationId: currentConversation.value?.id ?? conversationId, content, messageId: optimisticUserMessage.id, knownIds: /* @__PURE__ */ new Set() };
729
766
  startPolling();
730
767
  return optimisticUserMessage;
731
768
  }
732
769
  } else {
733
- error.value = "Confirma\xE7\xE3o do envio pendente. Reconectando \xE0 conversa\u2026";
770
+ error.value = SEND_PENDING_TEXT;
771
+ uncertainSend = { conversationId: currentConversation.value?.id ?? conversationId, content, messageId: optimisticUserMessage.id, knownIds: /* @__PURE__ */ new Set() };
734
772
  startPolling();
735
773
  return optimisticUserMessage;
736
774
  }
@@ -766,6 +804,8 @@ export function useChat(options = {}, adapter = {}) {
766
804
  clearPreparingSteps();
767
805
  directStream.disconnect();
768
806
  stopPolling();
807
+ if (uncertainSend?.messageId === optimisticUserMessage.id)
808
+ uncertainSend = null;
769
809
  if (err instanceof DOMException && err.name === "AbortError")
770
810
  return null;
771
811
  const message = getChatApiErrorCode(err) === "workspace_agent_not_ready" ? WORKSPACE_AGENT_NOT_READY_TEXT : err instanceof Error ? err.message : "Erro ao enviar mensagem";
@@ -810,6 +850,8 @@ export function useChat(options = {}, adapter = {}) {
810
850
  clearPreparingSteps();
811
851
  directStream.disconnect();
812
852
  stopPolling();
853
+ if (uncertainSend?.messageId === queued.optimisticIds.user)
854
+ uncertainSend = null;
813
855
  error.value = message;
814
856
  options.onQueuedSendFailed?.(queued);
815
857
  return true;
@@ -853,6 +895,8 @@ export function useChat(options = {}, adapter = {}) {
853
895
  clearPreparingSteps();
854
896
  directStream.disconnect();
855
897
  stopPolling();
898
+ if (uncertainSend?.messageId === queued.optimisticIds.user)
899
+ uncertainSend = null;
856
900
  options.onQueuedSendCancelled?.(queued);
857
901
  return true;
858
902
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Where the composer keeps the text of one conversation until the server
3
+ * confirms it recorded the message. Scoped to the user and workspace so a
4
+ * shared browser never shows one person's draft to another; `null` (no user
5
+ * yet) disables persistence.
6
+ */
7
+ export declare function composerDraftStorageKey(owner: {
8
+ userId?: string | null;
9
+ workspaceId?: string | null;
10
+ telaAgentId?: string | null;
11
+ conversationScope?: string | null;
12
+ }, conversationId: string | null): string | null;
13
+ export declare function readComposerDraft(key: string | null): string;
14
+ /** Empty text removes the draft. False when nothing was stored (no key, denied or full storage). */
15
+ export declare function writeComposerDraft(key: string | null, text: string): boolean;
16
+ export declare function composerDraftText(editor: HTMLElement | null): string;
@@ -0,0 +1,61 @@
1
+ import { normalizeConversationScope, normalizeTelaAgentId } from "../conversations/scope.js";
2
+ const DRAFT_KEY_PREFIX = "chat-composer-draft";
3
+ export function composerDraftStorageKey(owner, conversationId) {
4
+ if (!owner.userId || !owner.workspaceId)
5
+ return null;
6
+ return `${DRAFT_KEY_PREFIX}:v2:${encodeURIComponent(JSON.stringify([owner.workspaceId, owner.userId, normalizeTelaAgentId(owner.telaAgentId), normalizeConversationScope(owner.conversationScope), conversationId]))}`;
7
+ }
8
+ export function readComposerDraft(key) {
9
+ if (!key)
10
+ return "";
11
+ try {
12
+ const stored = localStorage.getItem(key);
13
+ if (stored !== null)
14
+ return stored;
15
+ if (!key.startsWith(`${DRAFT_KEY_PREFIX}:v2:`))
16
+ return "";
17
+ const [workspaceId, userId, , , conversationId] = JSON.parse(decodeURIComponent(key.slice(`${DRAFT_KEY_PREFIX}:v2:`.length)));
18
+ const legacyKey = `${DRAFT_KEY_PREFIX}:${workspaceId}:${userId}:${conversationId ?? "new"}`;
19
+ const legacy = localStorage.getItem(legacyKey) ?? "";
20
+ if (legacy && writeComposerDraft(key, legacy))
21
+ localStorage.removeItem(legacyKey);
22
+ return legacy;
23
+ } catch {
24
+ return "";
25
+ }
26
+ }
27
+ export function writeComposerDraft(key, text) {
28
+ if (!key)
29
+ return false;
30
+ try {
31
+ if (text.trim())
32
+ localStorage.setItem(key, text);
33
+ else
34
+ localStorage.removeItem(key);
35
+ return true;
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+ export function composerDraftText(editor) {
41
+ if (!editor)
42
+ return "";
43
+ let accumulated = false;
44
+ function read(node) {
45
+ if (node.nodeType === 3) {
46
+ const text = node.textContent ?? "";
47
+ accumulated ||= !!text;
48
+ return text;
49
+ }
50
+ const element = node;
51
+ if (node.nodeName === "BR") {
52
+ if (element.dataset.sentinel)
53
+ return "";
54
+ accumulated = true;
55
+ return "\n";
56
+ }
57
+ const prefix = node.nodeName === "DIV" && accumulated ? "\n" : "";
58
+ return prefix + Array.from(node.childNodes).map(read).join("");
59
+ }
60
+ return Array.from(editor.childNodes).map(read).join("").replace(/\u00A0/g, " ");
61
+ }
@@ -0,0 +1,8 @@
1
+ import type { SendMessageV2Body } from '../types/schemas/chat/conversation-runtime.js';
2
+ export declare function prepareComposerSend(key: string | null, text: string, request: SendMessageV2Body): string;
3
+ export declare function confirmPendingComposerSend(key: string | null, messageId: string): void;
4
+ export declare function reconcileComposerSend(key: string | null, messages: readonly {
5
+ id: string;
6
+ role: string;
7
+ externalUuid: string | null;
8
+ }[]): void;
@@ -0,0 +1,34 @@
1
+ import { readComposerDraft, writeComposerDraft } from "./composer-draft.js";
2
+ function readPendingSend(key) {
3
+ if (!key)
4
+ return null;
5
+ try {
6
+ const value = JSON.parse(readComposerDraft(`${key}:pending-send`));
7
+ return typeof value?.messageId === "string" && typeof value.text === "string" && typeof value.fingerprint === "string" ? value : null;
8
+ } catch {
9
+ return null;
10
+ }
11
+ }
12
+ export function prepareComposerSend(key, text, request) {
13
+ if (!key || !text)
14
+ return request.messageId;
15
+ const fingerprint = JSON.stringify([request.content, request.files ?? [], request.agentInputs ?? null, request.executionSettings ?? null]);
16
+ const pending = readPendingSend(key);
17
+ if (pending?.fingerprint === fingerprint && pending.text === text)
18
+ return pending.messageId;
19
+ writeComposerDraft(`${key}:pending-send`, JSON.stringify({ messageId: request.messageId, text, fingerprint }));
20
+ return request.messageId;
21
+ }
22
+ export function confirmPendingComposerSend(key, messageId) {
23
+ const pending = readPendingSend(key);
24
+ if (!key || !pending || pending.messageId !== messageId)
25
+ return;
26
+ if (readComposerDraft(key) === pending.text)
27
+ writeComposerDraft(key, "");
28
+ writeComposerDraft(`${key}:pending-send`, "");
29
+ }
30
+ export function reconcileComposerSend(key, messages) {
31
+ const pending = readPendingSend(key);
32
+ if (pending && messages.some((message) => message.role === "user" && message.externalUuid === pending.messageId))
33
+ confirmPendingComposerSend(key, pending.messageId);
34
+ }
@@ -51,7 +51,7 @@ export function mergeConversationTimeline(messages, events = [], controls) {
51
51
  }
52
52
  if (controls?.mode === "simple") {
53
53
  const seen = /* @__PURE__ */ new Set();
54
- const labels = { default: "Padr\xE3o", deep: "Aprofundar", custom: "Personalizado" };
54
+ const labels = { default: "Padr\xE3o", deep: "Aprofundado", intensive: "Intensivo", custom: "Personalizado" };
55
55
  return items.flatMap((item) => {
56
56
  if (item.kind !== "event" || !["model_changed", "reasoning_effort_changed"].includes(item.value.type))
57
57
  return [item];
@@ -1,7 +1,5 @@
1
1
  type __VLS_Props = {
2
- props: {
3
- text: string;
4
- };
2
+ text: string;
5
3
  partial?: boolean;
6
4
  };
7
5
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -2,13 +2,13 @@
2
2
  import { OPENUI_MARKDOWN_COMPONENTS_KEY } from "../../openui/context";
3
3
  import { useWidgetProse } from "../../openui/use-widget-prose";
4
4
  const props = defineProps({
5
- props: { type: Object, required: true },
5
+ text: { type: String, required: true },
6
6
  partial: { type: Boolean, required: false }
7
7
  });
8
8
  const customComponents = inject(OPENUI_MARKDOWN_COMPONENTS_KEY, ref({}));
9
- const content = useWidgetProse(() => props.props.text);
9
+ const content = useWidgetProse(() => props.text);
10
10
  </script>
11
11
 
12
12
  <template>
13
- <AppMarkdownRender :content="content" :final="!partial" :typewriter="false" :custom-components="customComponents" data-openui="markdown" />
13
+ <AppMarkdownRender :content="content" :final="!partial" :typewriter="false" :custom-components="customComponents" />
14
14
  </template>
@@ -1,7 +1,5 @@
1
1
  type __VLS_Props = {
2
- props: {
3
- text: string;
4
- };
2
+ text: string;
5
3
  partial?: boolean;
6
4
  };
7
5
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -1,73 +1,47 @@
1
1
  <script setup>
2
- import { formatWidgetContext, isSafeUrl } from "@meistrari/chat-ui-lang";
3
2
  import { CHAT_MESSAGE_CONTEXT_KEY, CHAT_MESSAGE_SEND_KEY } from "../../types/messages";
4
3
  import { useStatusToast } from "../../shared/composables/status-toast";
5
- import { copyWidgetText, runActionPlan } from "../openui/actions";
6
- import { OPENUI_CONTEXT_KEY, OPENUI_ERROR_REPORTER_KEY } from "../openui/context";
7
- import { useOpenuiProgram } from "../openui/program";
8
- import { renderNode } from "../openui/render";
4
+ import { OPENUI_ERROR_REPORTER_KEY } from "../openui/context";
5
+ import OpenuiCitation from "./openui/citation.vue";
6
+ import OpenuiFileCard from "./openui/file-card.vue";
7
+ import OpenuiImage from "./openui/image.vue";
8
+ import OpenuiMarkdownProse from "./openui/markdown-prose.vue";
9
9
  const props = defineProps({
10
10
  node: { type: Object, required: true },
11
11
  loading: { type: Boolean, required: false }
12
12
  });
13
13
  const code = computed(() => props.node.code ?? props.node.content ?? "");
14
14
  const complete = computed(() => !props.loading && !props.node.loading);
15
- const program = useOpenuiProgram(code, complete);
16
15
  const send = inject(CHAT_MESSAGE_SEND_KEY, null);
17
16
  const message = inject(CHAT_MESSAGE_CONTEXT_KEY, null);
18
17
  const reportErrors = inject(OPENUI_ERROR_REPORTER_KEY, null);
19
- const answered = ref(false);
20
18
  const statusToast = useStatusToast();
21
19
  const interactive = computed(() => Boolean(
22
- send && complete.value && !program.incomplete.value && !answered.value && (!message || message.role === "assistant" && message.isLatestMessage.value && !message.streaming.value)
20
+ send && (!message || message.role === "assistant" && message.isLatestMessage.value && !message.streaming.value)
23
21
  ));
24
- async function triggerAction(label, plan) {
25
- if (!send || !interactive.value)
26
- return false;
27
- let sent = false;
28
- const accepted = await runActionPlan(label, plan, {
29
- send: async (text, context) => {
30
- const ok = await send(`${text}${formatWidgetContext(context, { onDrop: (drop) => console.warn("[chat-nuxt] widget context dropped", drop) })}`);
31
- sent = sent || ok;
32
- return ok;
33
- },
34
- openUrl: (url) => {
35
- if (isSafeUrl(url))
36
- window.open(url, "_blank", "noopener");
37
- },
38
- custom: runCustomAction,
39
- store: program.store,
40
- stateDefaults: program.stateDefaults.value
41
- });
42
- if (sent)
43
- answered.value = true;
44
- return accepted;
22
+ const host = {
23
+ prose: OpenuiMarkdownProse,
24
+ components: { FileCard: OpenuiFileCard, Image: OpenuiImage, Citation: OpenuiCitation }
25
+ };
26
+ async function sendMessage(text) {
27
+ return send ? await send(text) : false;
45
28
  }
46
- async function runCustomAction(name, params) {
47
- if (name === "CopyText" && typeof params.text === "string") {
48
- const copied = await copyWidgetText(params.text, (text) => navigator.clipboard.writeText(text));
49
- if (!copied)
50
- statusToast.update({ text: "N\xE3o foi poss\xEDvel copiar", icon: "i-ph-warning" });
51
- return copied;
52
- }
53
- console.warn("[chat-nuxt] unsupported widget action", { name });
54
- return false;
29
+ function onErrors(errors, source) {
30
+ reportErrors?.(errors, source);
31
+ }
32
+ function onCopyFailed() {
33
+ statusToast.update({ text: "N\xE3o foi poss\xEDvel copiar", icon: "i-ph-warning" });
55
34
  }
56
- provide(OPENUI_CONTEXT_KEY, { renderNode, interactive, triggerAction });
57
- watch([program.errors, complete], ([errors, done]) => {
58
- if (done && errors.length)
59
- reportErrors?.(errors, code.value);
60
- }, { immediate: true });
61
- const Root = () => renderNode(program.root.value);
62
35
  </script>
63
36
 
64
37
  <template>
65
- <div my-12px data-openui="block" :data-openui-incomplete="program.incomplete.value || void 0">
66
- <Root v-if="program.root.value" />
67
- <div v-else-if="!complete" flex flex-col gap-8px>
68
- <TelaSkeleton h-20px w-40per />
69
- <TelaSkeleton h-80px w-full />
70
- </div>
71
- <pre v-else body-12-regular text-secondary whitespace-pre-wrap data-openui="fallback">{{ code }}</pre>
72
- </div>
38
+ <OpenuiWidget
39
+ :code="code"
40
+ :complete="complete"
41
+ :interactive="interactive"
42
+ :send="sendMessage"
43
+ :host="host"
44
+ @errors="onErrors"
45
+ @copy-failed="onCopyFailed"
46
+ />
73
47
  </template>
@@ -1,22 +1,7 @@
1
- import type { ActionPlan, OpenUIError } from '@openuidev/lang-core';
2
- import type { Component, InjectionKey, Ref, VNode } from 'vue';
3
- export type RenderedNode = VNode | string | null | Array<VNode | string | null>;
4
- export interface OpenuiContext {
5
- renderNode: (value: unknown) => RenderedNode;
6
- /** Clicks are honoured only on the latest assistant message, after streaming ends. */
7
- interactive: Ref<boolean>;
8
- /** Resolves once the action finished; returns false when it was rejected. A widget answers once: after an accepted send, `interactive` turns false. */
9
- triggerAction: (label: string, plan?: ActionPlan) => Promise<boolean>;
10
- }
11
- export declare const OPENUI_CONTEXT_KEY: InjectionKey<OpenuiContext>;
1
+ import type { OpenUIError } from '@openuidev/lang-core';
2
+ import type { Component, InjectionKey, Ref } from 'vue';
12
3
  export type OpenuiErrorReporter = (errors: OpenUIError[], code: string) => void;
13
4
  /** Optional: hosts inject this to collect parse and runtime errors per widget. */
14
5
  export declare const OPENUI_ERROR_REPORTER_KEY: InjectionKey<OpenuiErrorReporter>;
15
6
  /** Markdown node overrides of the surrounding message (vault links, citations), so prose inside widgets renders the same way. */
16
7
  export declare const OPENUI_MARKDOWN_COMPONENTS_KEY: InjectionKey<Ref<Record<string, Component>>>;
17
- export interface OpenuiFormState {
18
- get: (name: string) => unknown;
19
- set: (name: string, value: unknown) => void;
20
- }
21
- /** Provided by Form so its fields share one state bag. */
22
- export declare const OPENUI_FORM_KEY: InjectionKey<OpenuiFormState>;
@@ -1,4 +1,2 @@
1
- export const OPENUI_CONTEXT_KEY = Symbol("openui-context");
2
1
  export const OPENUI_ERROR_REPORTER_KEY = Symbol("openui-error-reporter");
3
2
  export const OPENUI_MARKDOWN_COMPONENTS_KEY = Symbol("openui-markdown-components");
4
- export const OPENUI_FORM_KEY = Symbol("openui-form");
@@ -1,7 +1,7 @@
1
1
  import type { H3Event } from 'h3';
2
2
  export declare function isEdgeHeader(name: string): boolean;
3
3
  export declare const fetchWithoutEdgeHeaders: typeof fetch;
4
- export type ChatApiVersion = 'v1' | 'v2';
4
+ export type ChatApiVersion = 'v1' | 'v2' | 'v3';
5
5
  export declare function resolveChatBasePath(basePath: string, apiVersion: ChatApiVersion): string;
6
6
  export declare function mapVaultProxyPath(path: string): string;
7
7
  export declare function mapWorkspaceProxyPath(path: string): string;
@@ -1,4 +1,4 @@
1
- import { AGENT_API_V5_CONFIG_KEY, PRE_WAKE_CACHE_CONFIG_KEY, CONVERSATION_V2_CONFIG_KEY, PREPARED_AGENT_RUNTIME_CONFIG_KEY, ROUTE_CLAUDE_VIA_GATEWAY_CONFIG_KEY, SECRETLESS_EXECUTION_CONFIG_KEY } from "../../types/schemas/chat/chat-config.js";
1
+ import { AGENT_API_V5_CONFIG_KEY, PRE_WAKE_CACHE_CONFIG_KEY, CONVERSATION_V2_CONFIG_KEY, PREPARED_AGENT_RUNTIME_CONFIG_KEY, ROUTE_CLAUDE_VIA_GATEWAY_CONFIG_KEY, SECRETLESS_EXECUTION_CONFIG_KEY, UI_LANGUAGE_CONFIG_KEY } from "../../types/schemas/chat/chat-config.js";
2
2
  import { createError, defineEventHandler, getHeader, getRequestURL, getRouterParam, proxyRequest } from "h3";
3
3
  import { useRuntimeConfig } from "nitropack/runtime";
4
4
  import { resolveAuthToken } from "./auth-token.js";
@@ -93,7 +93,8 @@ function buildProxyHeaders(event) {
93
93
  const config = useRuntimeConfig(event);
94
94
  const chatConfig = {
95
95
  [PREPARED_AGENT_RUNTIME_CONFIG_KEY]: true,
96
- [CONVERSATION_V2_CONFIG_KEY]: true
96
+ [CONVERSATION_V2_CONFIG_KEY]: true,
97
+ [UI_LANGUAGE_CONFIG_KEY]: "web"
97
98
  };
98
99
  if (config.chatNuxt?.secretlessExecution) {
99
100
  chatConfig[SECRETLESS_EXECUTION_CONFIG_KEY] = true;
@@ -1,4 +1,5 @@
1
1
  import type { Component, MaybeRef } from 'vue';
2
+ import type { ChatExecutionProfile } from '../embed/execution-controls.js';
2
3
  /** Stable string key, also compatible with hosts providing the literal key. */
3
4
  export declare const CHAT_COMPOSER_EXTENSION_KEY = "chat-composer-extension";
4
5
  /** A synchronous, per-send snapshot owned by the host application. */
@@ -22,6 +23,11 @@ export interface ChatComposerCommand {
22
23
  }
23
24
  /** Provide above MeistrariChatEmbed to extend its composer without replacing it. */
24
25
  export interface ChatComposerExtension {
26
+ /** Consume a host draft after a new conversation's composer is ready; return null thereafter. */
27
+ takeInitialDraft?: () => {
28
+ content: string;
29
+ executionProfile?: ChatExecutionProfile;
30
+ } | null;
25
31
  /** Action next to the file menu. Receives the composer's `disabled` state. */
26
32
  component?: Component;
27
33
  /** Items inside the shared attachment strip. Receives `disabled` as well. */
@@ -1,3 +1,4 @@
1
+ import type { UiProfile } from './ui-language';
1
2
  export declare const PREPARED_AGENT_RUNTIME_CONFIG_KEY = "prepared-agent-runtime";
2
3
  export declare const SECRETLESS_EXECUTION_CONFIG_KEY = "secretless-execution";
3
4
  export declare const AGENT_API_V5_CONFIG_KEY = "agent-api-v5";
@@ -6,7 +7,10 @@ export declare const PRE_WAKE_CACHE_CONFIG_KEY = "pre-wake-cache";
6
7
  export declare const ROUTE_CLAUDE_VIA_GATEWAY_CONFIG_KEY = "route-claude-via-gateway";
7
8
  export type ChatHarness = 'claude' | 'lightweight';
8
9
  export type ChatConfig = {
9
- harness?: ChatHarness;
10
- [key: string]: boolean | ChatHarness | undefined;
10
+ 'harness'?: ChatHarness;
11
+ /** Catalogue profile of the client's surface; widgets are requested only when it is set. */
12
+ 'ui-language'?: UiProfile;
13
+ [key: string]: boolean | ChatHarness | UiProfile | undefined;
11
14
  };
12
15
  export declare function resolveChatHarness(config?: Pick<ChatConfig, 'harness'>): ChatHarness;
16
+ export declare const UI_LANGUAGE_CONFIG_KEY = "ui-language";
@@ -7,3 +7,4 @@ export const ROUTE_CLAUDE_VIA_GATEWAY_CONFIG_KEY = 'route-claude-via-gateway';
7
7
  export function resolveChatHarness(config) {
8
8
  return config?.harness === 'lightweight' ? 'lightweight' : 'claude';
9
9
  }
10
+ export const UI_LANGUAGE_CONFIG_KEY = 'ui-language';
@@ -133,6 +133,10 @@ export declare const sendMessageV2ResponseSchema: z.ZodObject<{
133
133
  id: z.ZodEnum<{
134
134
  openui: "openui";
135
135
  }>;
136
+ profile: z.ZodEnum<{
137
+ web: "web";
138
+ slack: "slack";
139
+ }>;
136
140
  libraryVersion: z.ZodOptional<z.ZodString>;
137
141
  }, z.core.$strip>>>;
138
142
  }, z.core.$strip>;
@@ -174,6 +178,10 @@ export declare const sendMessageV2ResponseSchema: z.ZodObject<{
174
178
  id: z.ZodEnum<{
175
179
  openui: "openui";
176
180
  }>;
181
+ profile: z.ZodEnum<{
182
+ web: "web";
183
+ slack: "slack";
184
+ }>;
177
185
  libraryVersion: z.ZodOptional<z.ZodString>;
178
186
  }, z.core.$strip>>>;
179
187
  }, z.core.$strip>;
@@ -117,6 +117,10 @@ export declare const messageSchema: z.ZodObject<{
117
117
  id: z.ZodEnum<{
118
118
  openui: "openui";
119
119
  }>;
120
+ profile: z.ZodEnum<{
121
+ web: "web";
122
+ slack: "slack";
123
+ }>;
120
124
  libraryVersion: z.ZodOptional<z.ZodString>;
121
125
  }, z.core.$strip>>>;
122
126
  }, z.core.$strip>;
@@ -238,6 +242,10 @@ export declare const conversationWithMessagesSchema: z.ZodObject<{
238
242
  id: z.ZodEnum<{
239
243
  openui: "openui";
240
244
  }>;
245
+ profile: z.ZodEnum<{
246
+ web: "web";
247
+ slack: "slack";
248
+ }>;
241
249
  libraryVersion: z.ZodOptional<z.ZodString>;
242
250
  }, z.core.$strip>>>;
243
251
  }, z.core.$strip>>;
@@ -160,6 +160,10 @@ export declare const sendMessageResponseSchema: z.ZodObject<{
160
160
  id: z.ZodEnum<{
161
161
  openui: "openui";
162
162
  }>;
163
+ profile: z.ZodEnum<{
164
+ web: "web";
165
+ slack: "slack";
166
+ }>;
163
167
  libraryVersion: z.ZodOptional<z.ZodString>;
164
168
  }, z.core.$strip>>>;
165
169
  }, z.core.$strip>;
@@ -201,6 +205,10 @@ export declare const sendMessageResponseSchema: z.ZodObject<{
201
205
  id: z.ZodEnum<{
202
206
  openui: "openui";
203
207
  }>;
208
+ profile: z.ZodEnum<{
209
+ web: "web";
210
+ slack: "slack";
211
+ }>;
204
212
  libraryVersion: z.ZodOptional<z.ZodString>;
205
213
  }, z.core.$strip>>>;
206
214
  }, z.core.$strip>;
@@ -245,6 +253,10 @@ export declare const messagePollResponseSchema: z.ZodObject<{
245
253
  id: z.ZodEnum<{
246
254
  openui: "openui";
247
255
  }>;
256
+ profile: z.ZodEnum<{
257
+ web: "web";
258
+ slack: "slack";
259
+ }>;
248
260
  libraryVersion: z.ZodOptional<z.ZodString>;
249
261
  }, z.core.$strip>>>;
250
262
  }, z.core.$strip>>;