@meistrari/chat-nuxt 3.9.2 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -440
- package/dist/module.d.mts +1 -1
- package/dist/module.json +1 -1
- package/dist/runtime/assets/css/markstream.css +1 -1
- package/dist/runtime/conversations/components/chat-conversation-creator-filter.vue +11 -67
- package/dist/runtime/conversations/components/chat-conversation-item.d.vue.ts +2 -1
- package/dist/runtime/conversations/components/chat-conversation-item.vue +29 -33
- package/dist/runtime/conversations/components/chat-conversation-item.vue.d.ts +2 -1
- package/dist/runtime/conversations/components/chat-conversation-list.d.vue.ts +0 -1
- package/dist/runtime/conversations/components/chat-conversation-list.vue +117 -158
- package/dist/runtime/conversations/components/chat-conversation-list.vue.d.ts +0 -1
- package/dist/runtime/conversations/components/chat-conversation-new-group-button.vue +34 -73
- package/dist/runtime/conversations/components/chat-conversation-search.vue +1 -21
- package/dist/runtime/conversations/components/chat-widget-history-list.d.vue.ts +12 -0
- package/dist/runtime/conversations/components/chat-widget-history-list.vue +27 -0
- package/dist/runtime/conversations/components/chat-widget-history-list.vue.d.ts +12 -0
- package/dist/runtime/conversations/composables/conversation-groups.d.ts +1 -0
- package/dist/runtime/conversations/composables/conversation-groups.js +2 -0
- package/dist/runtime/conversations/composables/conversation-item.js +4 -4
- package/dist/runtime/conversations/composables/conversation-list.js +14 -5
- package/dist/runtime/embed/components/chat-configuration-modal.d.vue.ts +3 -3
- package/dist/runtime/embed/components/chat-configuration-modal.vue.d.ts +3 -3
- package/dist/runtime/embed/components/chat-embed-inner.d.vue.ts +6 -3
- package/dist/runtime/embed/components/chat-embed-inner.vue +442 -174
- package/dist/runtime/embed/components/chat-embed-inner.vue.d.ts +6 -3
- package/dist/runtime/embed/components/chat-embed.d.vue.ts +31 -4
- package/dist/runtime/embed/components/chat-embed.vue +10 -4
- package/dist/runtime/embed/components/chat-embed.vue.d.ts +31 -4
- package/dist/runtime/embed/components/chat-topbar.vue +83 -85
- package/dist/runtime/embed/components/meistrari-chat-embed.d.vue.ts +5 -3
- package/dist/runtime/embed/components/meistrari-chat-embed.vue +118 -57
- package/dist/runtime/embed/components/meistrari-chat-embed.vue.d.ts +5 -3
- package/dist/runtime/embed/components/mobile/chat-mobile-drawer.d.vue.ts +0 -1
- package/dist/runtime/embed/components/mobile/chat-mobile-drawer.vue +2 -45
- package/dist/runtime/embed/components/mobile/chat-mobile-drawer.vue.d.ts +0 -1
- package/dist/runtime/embed/components/mobile/chat-mobile-home-suggestions.d.vue.ts +9 -5
- package/dist/runtime/embed/components/mobile/chat-mobile-home-suggestions.vue +14 -25
- package/dist/runtime/embed/components/mobile/chat-mobile-home-suggestions.vue.d.ts +9 -5
- package/dist/runtime/embed/components/mobile/chat-mobile-shell.d.vue.ts +4 -4
- package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue +2 -4
- package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue.d.ts +4 -4
- package/dist/runtime/embed/composables/use-agent-wake.d.ts +23 -0
- package/dist/runtime/embed/composables/use-agent-wake.js +182 -0
- package/dist/runtime/embed/feature-config.d.ts +7 -0
- package/dist/runtime/embed/feature-config.js +2 -1
- package/dist/runtime/embed/loading-messages.d.ts +16 -0
- package/dist/runtime/embed/loading-messages.js +10 -0
- package/dist/runtime/embed/types.d.ts +47 -8
- package/dist/runtime/files/icons.d.ts +1 -1
- package/dist/runtime/files/icons.js +1 -1
- package/dist/runtime/messages/components/chat-mention-dropdown.d.vue.ts +6 -1
- package/dist/runtime/messages/components/chat-mention-dropdown.vue +46 -22
- package/dist/runtime/messages/components/chat-mention-dropdown.vue.d.ts +6 -1
- package/dist/runtime/messages/components/chat-message-bubble.d.vue.ts +10 -1
- package/dist/runtime/messages/components/chat-message-bubble.vue +69 -76
- package/dist/runtime/messages/components/chat-message-bubble.vue.d.ts +10 -1
- package/dist/runtime/messages/components/chat-message-feedback.vue +25 -229
- package/dist/runtime/messages/components/chat-message-input.vue +176 -134
- package/dist/runtime/messages/components/chat-message-list.d.vue.ts +12 -1
- package/dist/runtime/messages/components/chat-message-list.vue +37 -24
- package/dist/runtime/messages/components/chat-message-list.vue.d.ts +12 -1
- package/dist/runtime/messages/components/chat-model-selector.vue +10 -22
- package/dist/runtime/messages/components/reasoning/chat-reasoning-message.vue +2 -3
- package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.d.vue.ts +2 -1
- package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue +69 -77
- package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue.d.ts +2 -1
- package/dist/runtime/messages/components/reasoning/chat-thinking-indicator.vue +3 -9
- package/dist/runtime/messages/components/widgets/chat-bash-widget.vue +3 -12
- package/dist/runtime/messages/components/widgets/chat-doc-sdk-widget.vue +3 -12
- package/dist/runtime/messages/components/widgets/chat-generic-tool-widget.vue +3 -12
- package/dist/runtime/messages/components/widgets/chat-grep-widget.vue +7 -17
- package/dist/runtime/messages/components/widgets/chat-read-widget.vue +7 -17
- package/dist/runtime/messages/components/widgets/chat-tela-skill-widget.vue +4 -13
- package/dist/runtime/messages/components/widgets/chat-thinking-widget.vue +11 -22
- package/dist/runtime/messages/components/widgets/chat-web-search-widget.vue +13 -40
- package/dist/runtime/messages/components/widgets/chat-workstation-query-widget.vue +52 -65
- package/dist/runtime/messages/components/widgets/chat-write-widget.vue +8 -18
- package/dist/runtime/messages/composables/chat.d.ts +8 -2
- package/dist/runtime/messages/composables/chat.js +180 -25
- package/dist/runtime/messages/composables/mention-autocomplete.js +4 -0
- package/dist/runtime/messages/composables/use-direct-stream.d.ts +41 -0
- package/dist/runtime/messages/composables/use-direct-stream.js +263 -0
- package/dist/runtime/messages/message-state.js +1 -0
- package/dist/runtime/messages/reasoning.d.ts +7 -0
- package/dist/runtime/messages/reasoning.js +5 -0
- package/dist/runtime/public/files/generic.svg +2 -5
- package/dist/runtime/public/files/image.svg +2 -1
- package/dist/runtime/types/embed.d.ts +1 -1
- package/dist/runtime/types/schemas/chat/conversations.d.ts +4 -0
- package/dist/runtime/types/schemas/chat/conversations.js +3 -0
- package/dist/runtime/types/schemas/chat/messages.d.ts +43 -0
- package/dist/runtime/types/schemas/chat/messages.js +31 -0
- package/dist/runtime/types/schemas/chat/models.d.ts +3 -2
- package/dist/runtime/types/schemas/chat/models.js +2 -1
- package/dist/runtime/types/schemas/chat/ui.d.ts +10 -0
- package/dist/runtime/types/schemas/chat/ui.js +1 -0
- package/dist/runtime/types/schemas/integrations/tela-agent-api.d.ts +35 -0
- package/dist/runtime/types/schemas/integrations/tela-agent-api.js +17 -0
- package/dist/types.d.mts +1 -1
- package/package.json +5 -4
- package/dist/runtime/conversations/components/chat-conversation-new-group-popover-content.d.vue.ts +0 -23
- package/dist/runtime/conversations/components/chat-conversation-new-group-popover-content.vue +0 -75
- package/dist/runtime/conversations/components/chat-conversation-new-group-popover-content.vue.d.ts +0 -23
- package/dist/runtime/messages/components/reasoning/chat-reasoning-step-item.d.vue.ts +0 -37
- package/dist/runtime/messages/components/reasoning/chat-reasoning-step-item.vue +0 -125
- package/dist/runtime/messages/components/reasoning/chat-reasoning-step-item.vue.d.ts +0 -37
|
@@ -335,86 +335,73 @@ const displayName = computed(() => {
|
|
|
335
335
|
</script>
|
|
336
336
|
|
|
337
337
|
<template>
|
|
338
|
-
<
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
</p>
|
|
348
|
-
</div>
|
|
349
|
-
<TelaStatus
|
|
350
|
-
:variant="statusVariant"
|
|
351
|
-
:label="statusLabel"
|
|
352
|
-
:is-icon-animated="!toolResult"
|
|
353
|
-
/>
|
|
354
|
-
</div>
|
|
338
|
+
<TelaChatWidgetCard
|
|
339
|
+
title="Consulta de Dados"
|
|
340
|
+
:status-variant="statusVariant"
|
|
341
|
+
:status-label="statusLabel ?? ''"
|
|
342
|
+
:is-icon-animated="!toolResult"
|
|
343
|
+
>
|
|
344
|
+
<template v-if="displayName !== 'Workstation'" #subtitle>
|
|
345
|
+
{{ displayName }}
|
|
346
|
+
</template>
|
|
355
347
|
|
|
356
348
|
<!-- Filters preview (when collapsed) -->
|
|
357
349
|
<div
|
|
358
350
|
v-if="!expanded && canExpand && (formattedFilters || formattedOutputQuery)"
|
|
359
|
-
class="
|
|
351
|
+
class="flex flex-wrap items-center gap-6px"
|
|
360
352
|
>
|
|
361
|
-
<
|
|
362
|
-
<template v-if="formattedFilters">
|
|
363
|
-
<span
|
|
364
|
-
v-for="filter in formattedFilters.slice(0, 3)"
|
|
365
|
-
:key="filter"
|
|
366
|
-
class="text-11px px-8px py-3px rounded-6px bg-gray-100 text-gray-600"
|
|
367
|
-
>
|
|
368
|
-
{{ filter }}
|
|
369
|
-
</span>
|
|
370
|
-
</template>
|
|
371
|
-
<template v-if="formattedOutputQuery">
|
|
372
|
-
<span
|
|
373
|
-
v-for="query in formattedOutputQuery.slice(0, 3)"
|
|
374
|
-
:key="query"
|
|
375
|
-
class="text-11px px-8px py-3px rounded-6px bg-blue-100 text-blue-700"
|
|
376
|
-
>
|
|
377
|
-
{{ query }}
|
|
378
|
-
</span>
|
|
379
|
-
</template>
|
|
353
|
+
<template v-if="formattedFilters">
|
|
380
354
|
<span
|
|
381
|
-
v-
|
|
382
|
-
|
|
355
|
+
v-for="filter in formattedFilters.slice(0, 3)"
|
|
356
|
+
:key="filter"
|
|
357
|
+
class="text-11px px-8px py-3px rounded-6px bg-gray-100 text-gray-600"
|
|
383
358
|
>
|
|
384
|
-
|
|
359
|
+
{{ filter }}
|
|
385
360
|
</span>
|
|
386
|
-
</
|
|
361
|
+
</template>
|
|
362
|
+
<template v-if="formattedOutputQuery">
|
|
363
|
+
<span
|
|
364
|
+
v-for="query in formattedOutputQuery.slice(0, 3)"
|
|
365
|
+
:key="query"
|
|
366
|
+
class="text-11px px-8px py-3px rounded-6px bg-blue-100 text-blue-700"
|
|
367
|
+
>
|
|
368
|
+
{{ query }}
|
|
369
|
+
</span>
|
|
370
|
+
</template>
|
|
371
|
+
<span
|
|
372
|
+
v-if="(formattedFilters?.length || 0) + (formattedOutputQuery?.length || 0) > 3"
|
|
373
|
+
class="text-11px text-gray-400"
|
|
374
|
+
>
|
|
375
|
+
+{{ (formattedFilters?.length || 0) + (formattedOutputQuery?.length || 0) - 3 }} mais
|
|
376
|
+
</span>
|
|
387
377
|
</div>
|
|
388
378
|
|
|
389
379
|
<!-- Launch message (when only have launching output) -->
|
|
390
|
-
<div v-if="toolResult && isLaunchMessage" class="px-16px
|
|
391
|
-
<div class="
|
|
392
|
-
<div class="
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
</p>
|
|
397
|
-
</div>
|
|
380
|
+
<div v-if="toolResult && isLaunchMessage" class="bg-blue-50 rounded-xl border-0.5px border-blue-200 px-16px py-12px">
|
|
381
|
+
<div class="flex items-center gap-8px">
|
|
382
|
+
<div class="i-ph-clock text-16px text-blue-600 animate-pulse" />
|
|
383
|
+
<p class="text-13px text-blue-700 m-0">
|
|
384
|
+
Aguardando resultados da consulta...
|
|
385
|
+
</p>
|
|
398
386
|
</div>
|
|
399
387
|
</div>
|
|
400
388
|
|
|
401
389
|
<!-- Expand/Collapse button -->
|
|
402
|
-
<
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
</div>
|
|
390
|
+
<button
|
|
391
|
+
v-if="toolResult && !isLaunchMessage"
|
|
392
|
+
type="button"
|
|
393
|
+
class="w-full flex items-center justify-center gap-6px px-12px py-8px text-13px font-medium text-gray-600 hover:text-gray-900 hover:bg-gray-50 rounded-8px transition-colors"
|
|
394
|
+
@click="expanded = !expanded"
|
|
395
|
+
>
|
|
396
|
+
<span>{{ expanded ? "Ocultar detalhes" : "Ver detalhes" }}</span>
|
|
397
|
+
<div
|
|
398
|
+
class="i-ph-caret-down text-12px transition-transform"
|
|
399
|
+
:class="expanded && 'rotate-180'"
|
|
400
|
+
/>
|
|
401
|
+
</button>
|
|
415
402
|
|
|
416
403
|
<!-- Expanded content -->
|
|
417
|
-
<
|
|
404
|
+
<template v-if="expanded && toolResult && !isLaunchMessage">
|
|
418
405
|
<!-- Filters section -->
|
|
419
406
|
<div v-if="formattedFilters || formattedOutputQuery">
|
|
420
407
|
<div class="text-14px font-580 leading-16px tracking-[-0.15px] text-gray-700 mb-2">
|
|
@@ -548,6 +535,6 @@ const displayName = computed(() => {
|
|
|
548
535
|
<pre class="text-14px text-primary whitespace-pre-wrap break-words m-0">{{ toolResult.output }}</pre>
|
|
549
536
|
</div>
|
|
550
537
|
</div>
|
|
551
|
-
</
|
|
552
|
-
</
|
|
538
|
+
</template>
|
|
539
|
+
</TelaChatWidgetCard>
|
|
553
540
|
</template>
|
|
@@ -90,23 +90,13 @@ const hasMoreLines = computed(() => {
|
|
|
90
90
|
</script>
|
|
91
91
|
|
|
92
92
|
<template>
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
Write
|
|
99
|
-
</h3>
|
|
100
|
-
<p class="text-12px text-secondary truncate mt-2px">
|
|
101
|
-
{{ fileName }}
|
|
102
|
-
<span v-if="parentDir" class="opacity-60">em {{ parentDir }}</span>
|
|
103
|
-
</p>
|
|
104
|
-
</div>
|
|
105
|
-
<TelaStatus :variant="statusVariant" :label="statusLabel" />
|
|
106
|
-
</div>
|
|
93
|
+
<TelaChatWidgetCard title="Write" :status-variant="statusVariant" :status-label="statusLabel">
|
|
94
|
+
<template #subtitle>
|
|
95
|
+
{{ fileName }}
|
|
96
|
+
<span v-if="parentDir" class="opacity-60">em {{ parentDir }}</span>
|
|
97
|
+
</template>
|
|
107
98
|
|
|
108
|
-
|
|
109
|
-
<div v-if="toolResult" class="px-16px pb-16px space-y-16px">
|
|
99
|
+
<template v-if="toolResult">
|
|
110
100
|
<!-- File path section -->
|
|
111
101
|
<div>
|
|
112
102
|
<div class="text-14px font-580 leading-16px tracking-[-0.15px] text-gray-700 mb-2">
|
|
@@ -156,8 +146,8 @@ const hasMoreLines = computed(() => {
|
|
|
156
146
|
</p>
|
|
157
147
|
</div>
|
|
158
148
|
</div>
|
|
159
|
-
</
|
|
160
|
-
</
|
|
149
|
+
</template>
|
|
150
|
+
</TelaChatWidgetCard>
|
|
161
151
|
</template>
|
|
162
152
|
|
|
163
153
|
<style scoped>
|
|
@@ -4,6 +4,7 @@ import type { FileUploadInput } from '../../types/schemas/chat/files.js';
|
|
|
4
4
|
import type { SendMessageBody } from '../../types/schemas/chat/messages.js';
|
|
5
5
|
import type { TelaAgentExecutionInput } from '../../types/schemas/integrations/tela-agent-api.js';
|
|
6
6
|
export declare function resolveTelaAgentInputs(messageInputs: TelaAgentExecutionInput[] | undefined, configuredInputs: DeepReadonly<TelaAgentExecutionInput[]> | null | undefined): SendMessageBody['agentInputs'];
|
|
7
|
+
export declare function resolveStreamingContent(authoritative: string, provisional: string): string;
|
|
7
8
|
export declare function useChat(): {
|
|
8
9
|
currentConversation: any;
|
|
9
10
|
messages: any;
|
|
@@ -12,15 +13,20 @@ export declare function useChat(): {
|
|
|
12
13
|
error: any;
|
|
13
14
|
isPolling: any;
|
|
14
15
|
hasPendingMessage: any;
|
|
16
|
+
streamingActiveMessageId: any;
|
|
17
|
+
preparingSteps: any;
|
|
15
18
|
cancelling: any;
|
|
16
19
|
generatingTitle: any;
|
|
17
20
|
animatedTitle: any;
|
|
18
21
|
loadConversation: (id: string) => Promise<void>;
|
|
19
22
|
setConversation: (conv: Conversation | ConversationWithMessages) => void;
|
|
23
|
+
adoptConversation: (conv: Conversation) => void;
|
|
20
24
|
setConversationModel: (model: string | null) => void;
|
|
21
25
|
pendingModelSelection: any;
|
|
22
|
-
sendMessage: (content: string, files?: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[]) => Promise<
|
|
23
|
-
|
|
26
|
+
sendMessage: (content: string, files?: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[], prepare?: (signal: AbortSignal) => Promise<{
|
|
27
|
+
wakeSessionId?: string;
|
|
28
|
+
}>) => Promise<Message | null>;
|
|
29
|
+
retryMessage: (messageId: string, wakeSessionId?: string) => Promise<boolean>;
|
|
24
30
|
cancelConversation: () => Promise<boolean>;
|
|
25
31
|
startPolling: () => void;
|
|
26
32
|
stopPolling: () => void;
|
|
@@ -20,13 +20,22 @@ import { getChatApiErrorCode, getChatApiErrorMessage } from "../../shared/chat-a
|
|
|
20
20
|
import { useChatApi } from "../../shared/composables/chat-api.js";
|
|
21
21
|
import { useConversationFileActions } from "../../files/composables/conversation-files.js";
|
|
22
22
|
import { useEmbedConfig } from "../../embed/composables/embed-config.js";
|
|
23
|
+
import { CHAT_PREPARING_STEPS, nextChatPreparingStepDelay } from "../../embed/loading-messages.js";
|
|
23
24
|
import { usePolling } from "../../shared/composables/polling.js";
|
|
24
25
|
import { terminalMessageTransitions, useChatMessageTerminal } from "./chat-message-terminal.js";
|
|
26
|
+
import { useDirectStream } from "./use-direct-stream.js";
|
|
25
27
|
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.";
|
|
26
28
|
export function resolveTelaAgentInputs(messageInputs, configuredInputs) {
|
|
27
29
|
const inputs = messageInputs ?? configuredInputs;
|
|
28
30
|
return inputs === void 0 || inputs === null ? void 0 : inputs.map((input) => ({ ...input }));
|
|
29
31
|
}
|
|
32
|
+
export function resolveStreamingContent(authoritative, provisional) {
|
|
33
|
+
if (provisional.startsWith(authoritative))
|
|
34
|
+
return provisional;
|
|
35
|
+
if (authoritative.startsWith(provisional))
|
|
36
|
+
return authoritative;
|
|
37
|
+
return authoritative || provisional;
|
|
38
|
+
}
|
|
30
39
|
export function useChat() {
|
|
31
40
|
const chatApi = useChatApi();
|
|
32
41
|
const chatMessageTerminal = useChatMessageTerminal();
|
|
@@ -60,6 +69,34 @@ export function useChat() {
|
|
|
60
69
|
});
|
|
61
70
|
const loading = ref(false);
|
|
62
71
|
const sending = ref(false);
|
|
72
|
+
const gateHolding = ref(false);
|
|
73
|
+
const preparingSteps = ref([]);
|
|
74
|
+
let preparingTimer;
|
|
75
|
+
let gateController = null;
|
|
76
|
+
let sendStartedAt = 0;
|
|
77
|
+
let standInOutputAtMs = null;
|
|
78
|
+
function clearPreparingSteps() {
|
|
79
|
+
if (preparingTimer)
|
|
80
|
+
clearTimeout(preparingTimer);
|
|
81
|
+
preparingTimer = void 0;
|
|
82
|
+
preparingSteps.value = [];
|
|
83
|
+
standInOutputAtMs = null;
|
|
84
|
+
}
|
|
85
|
+
function schedulePreparingStep(index) {
|
|
86
|
+
if (index >= CHAT_PREPARING_STEPS.length)
|
|
87
|
+
return;
|
|
88
|
+
const delay = nextChatPreparingStepDelay(index);
|
|
89
|
+
if (index === 0)
|
|
90
|
+
standInOutputAtMs = delay;
|
|
91
|
+
preparingTimer = setTimeout(() => {
|
|
92
|
+
preparingSteps.value = CHAT_PREPARING_STEPS.slice(0, index + 1);
|
|
93
|
+
schedulePreparingStep(index + 1);
|
|
94
|
+
}, delay);
|
|
95
|
+
}
|
|
96
|
+
function schedulePreparingSteps() {
|
|
97
|
+
clearPreparingSteps();
|
|
98
|
+
schedulePreparingStep(0);
|
|
99
|
+
}
|
|
63
100
|
const error = ref(null);
|
|
64
101
|
const titleGenerated = ref(false);
|
|
65
102
|
const generatingTitle = ref(false);
|
|
@@ -67,12 +104,29 @@ export function useChat() {
|
|
|
67
104
|
const hasPendingMessage = computed(
|
|
68
105
|
() => messages.value.some((m) => m.status === "pending")
|
|
69
106
|
);
|
|
107
|
+
const streamingMessageId = ref(null);
|
|
108
|
+
let requestDurableRecovery = () => {
|
|
109
|
+
};
|
|
110
|
+
const directStream = useDirectStream({
|
|
111
|
+
onResult: () => requestDurableRecovery(),
|
|
112
|
+
onDisconnect: () => requestDurableRecovery()
|
|
113
|
+
});
|
|
114
|
+
const streamingActiveMessageId = computed(
|
|
115
|
+
() => directStream.activity.value === "active" ? streamingMessageId.value : null
|
|
116
|
+
);
|
|
70
117
|
const reasoningCache = createReasoningCache();
|
|
71
|
-
const processedMessages = computed(() =>
|
|
118
|
+
const processedMessages = computed(() => {
|
|
119
|
+
const resolved = resolveReasoningDisplayMessages(messages.value, reasoningCache);
|
|
120
|
+
const messageId = streamingMessageId.value;
|
|
121
|
+
const content = directStream.streamingText.value;
|
|
122
|
+
if (!messageId || !content)
|
|
123
|
+
return resolved;
|
|
124
|
+
return resolved.map((message) => message.id === messageId && message.status === "pending" ? { ...message, content: resolveStreamingContent(message.content, content) } : message);
|
|
125
|
+
});
|
|
72
126
|
const stopCondition = computed(() => !hasPendingMessage.value);
|
|
73
127
|
let pollGeneration = 0;
|
|
74
128
|
const pollMessages = async () => {
|
|
75
|
-
if (!currentConversation.value)
|
|
129
|
+
if (!currentConversation.value || gateHolding.value)
|
|
76
130
|
return;
|
|
77
131
|
const generation = pollGeneration;
|
|
78
132
|
try {
|
|
@@ -84,6 +138,13 @@ export function useChat() {
|
|
|
84
138
|
return;
|
|
85
139
|
const terminalMessages = terminalMessageTransitions(messages.value, response.messages);
|
|
86
140
|
messages.value = response.messages;
|
|
141
|
+
const latestSessionId = response.messages.findLast((message) => message.agentSessionId)?.agentSessionId;
|
|
142
|
+
if (latestSessionId && currentConversation.value.threadSessionId === null) {
|
|
143
|
+
currentConversation.value = {
|
|
144
|
+
...currentConversation.value,
|
|
145
|
+
threadSessionId: latestSessionId
|
|
146
|
+
};
|
|
147
|
+
}
|
|
87
148
|
for (const terminalMessage of terminalMessages)
|
|
88
149
|
chatMessageTerminal.emit(terminalMessage);
|
|
89
150
|
if (response.newFiles && response.newFiles.length > 0) {
|
|
@@ -123,6 +184,10 @@ export function useChat() {
|
|
|
123
184
|
pollGeneration++;
|
|
124
185
|
endPolling();
|
|
125
186
|
}
|
|
187
|
+
requestDurableRecovery = () => {
|
|
188
|
+
startPolling();
|
|
189
|
+
void pollMessages();
|
|
190
|
+
};
|
|
126
191
|
const pendingModelBuckets = useState("chat-pending-model-selection", () => ({}));
|
|
127
192
|
const pendingModelSelection = computed({
|
|
128
193
|
get: () => {
|
|
@@ -142,7 +207,15 @@ export function useChat() {
|
|
|
142
207
|
return;
|
|
143
208
|
currentConversation.value = { ...conversation, model };
|
|
144
209
|
};
|
|
210
|
+
const adoptConversation = (conv) => {
|
|
211
|
+
currentConversation.value = { ...conv, messages: messages.value };
|
|
212
|
+
titleGenerated.value = false;
|
|
213
|
+
};
|
|
145
214
|
const setConversation = (conv) => {
|
|
215
|
+
if (currentConversation.value && currentConversation.value.id !== conv.id) {
|
|
216
|
+
directStream.disconnect();
|
|
217
|
+
streamingMessageId.value = null;
|
|
218
|
+
}
|
|
146
219
|
const conversationMessages = "messages" in conv ? conv.messages : [];
|
|
147
220
|
currentConversation.value = { ...conv, messages: conversationMessages };
|
|
148
221
|
messages.value = conversationMessages;
|
|
@@ -163,8 +236,14 @@ export function useChat() {
|
|
|
163
236
|
if (shouldTreatLoadedConversationTitleAsGenerated(data.messages)) {
|
|
164
237
|
titleGenerated.value = true;
|
|
165
238
|
}
|
|
166
|
-
|
|
239
|
+
const pendingAssistant = data.messages.findLast((m) => m.role === "assistant" && m.status === "pending");
|
|
240
|
+
if (pendingAssistant) {
|
|
241
|
+
streamingMessageId.value = pendingAssistant.id;
|
|
242
|
+
directStream.beginMessage();
|
|
167
243
|
startPolling();
|
|
244
|
+
} else {
|
|
245
|
+
streamingMessageId.value = null;
|
|
246
|
+
directStream.disconnect();
|
|
168
247
|
}
|
|
169
248
|
} catch (err) {
|
|
170
249
|
currentConversation.value = null;
|
|
@@ -176,15 +255,18 @@ export function useChat() {
|
|
|
176
255
|
loading.value = false;
|
|
177
256
|
}
|
|
178
257
|
};
|
|
179
|
-
const sendMessage = async (content, files, agentInputs) => {
|
|
180
|
-
if (!currentConversation.value)
|
|
258
|
+
const sendMessage = async (content, files, agentInputs, prepare) => {
|
|
259
|
+
if (!currentConversation.value && !prepare)
|
|
181
260
|
return null;
|
|
182
261
|
sending.value = true;
|
|
183
262
|
error.value = null;
|
|
263
|
+
sendStartedAt = Date.now();
|
|
264
|
+
schedulePreparingSteps();
|
|
184
265
|
const filesWithSource = files?.map((f) => ({ ...f, source: "user" })) ?? null;
|
|
266
|
+
const conversationId = currentConversation.value?.id ?? "";
|
|
185
267
|
const optimisticUserMessage = {
|
|
186
268
|
id: crypto.randomUUID(),
|
|
187
|
-
conversationId
|
|
269
|
+
conversationId,
|
|
188
270
|
role: "user",
|
|
189
271
|
content,
|
|
190
272
|
status: "completed",
|
|
@@ -198,7 +280,7 @@ export function useChat() {
|
|
|
198
280
|
};
|
|
199
281
|
const optimisticAssistantMessage = {
|
|
200
282
|
id: crypto.randomUUID(),
|
|
201
|
-
conversationId
|
|
283
|
+
conversationId,
|
|
202
284
|
role: "assistant",
|
|
203
285
|
content: "",
|
|
204
286
|
status: "pending",
|
|
@@ -210,28 +292,68 @@ export function useChat() {
|
|
|
210
292
|
createdAt: /* @__PURE__ */ new Date(),
|
|
211
293
|
updatedAt: null
|
|
212
294
|
};
|
|
295
|
+
streamingMessageId.value = optimisticAssistantMessage.id;
|
|
296
|
+
directStream.beginMessage();
|
|
213
297
|
messages.value = [...messages.value, optimisticUserMessage, optimisticAssistantMessage];
|
|
298
|
+
const controller = new AbortController();
|
|
299
|
+
gateController = controller;
|
|
214
300
|
try {
|
|
301
|
+
let wakeSessionId;
|
|
302
|
+
if (prepare) {
|
|
303
|
+
gateHolding.value = true;
|
|
304
|
+
try {
|
|
305
|
+
wakeSessionId = (await prepare(controller.signal)).wakeSessionId;
|
|
306
|
+
} finally {
|
|
307
|
+
gateHolding.value = false;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (controller.signal.aborted)
|
|
311
|
+
throw new DOMException("Aborted", "AbortError");
|
|
312
|
+
if (!currentConversation.value)
|
|
313
|
+
throw new Error("Erro ao enviar mensagem");
|
|
215
314
|
const resolvedAgentInputs = resolveTelaAgentInputs(agentInputs, embedConfig?.telaAgentInputs?.value);
|
|
216
315
|
const body = {
|
|
217
316
|
content,
|
|
218
317
|
files,
|
|
219
|
-
...resolvedAgentInputs !== void 0 ? { agentInputs: resolvedAgentInputs } : {}
|
|
318
|
+
...resolvedAgentInputs !== void 0 ? { agentInputs: resolvedAgentInputs } : {},
|
|
319
|
+
...wakeSessionId ? { wakeSessionId } : {},
|
|
320
|
+
clientTiming: {
|
|
321
|
+
// Everything the gate held goes in here: whatever the server
|
|
322
|
+
// measures, the user has already been waiting this long.
|
|
323
|
+
sinceSendMs: Math.max(0, Date.now() - sendStartedAt),
|
|
324
|
+
...standInOutputAtMs !== null ? { standInOutputAtMs } : {}
|
|
325
|
+
}
|
|
220
326
|
};
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
327
|
+
const acceptMessagePair = (response2) => {
|
|
328
|
+
messages.value = messages.value.map((msg) => {
|
|
329
|
+
if (msg.id === optimisticUserMessage.id)
|
|
330
|
+
return response2.userMessage;
|
|
331
|
+
if (msg.id === optimisticAssistantMessage.id)
|
|
332
|
+
return response2.assistantMessage;
|
|
333
|
+
return msg;
|
|
334
|
+
});
|
|
335
|
+
streamingMessageId.value = response2.assistantMessage.id;
|
|
336
|
+
};
|
|
337
|
+
let response;
|
|
338
|
+
let acceptedByStream = false;
|
|
339
|
+
try {
|
|
340
|
+
response = await directStream.connectMessage(
|
|
341
|
+
chatApi.path(`/conversations/${currentConversation.value.id}/messages/stream`),
|
|
342
|
+
chatApi.withChatHeaders({
|
|
343
|
+
method: "POST",
|
|
344
|
+
headers: { "Content-Type": "application/json" },
|
|
345
|
+
body: JSON.stringify(body)
|
|
346
|
+
}),
|
|
347
|
+
acceptMessagePair
|
|
348
|
+
);
|
|
349
|
+
acceptedByStream = true;
|
|
350
|
+
} catch {
|
|
351
|
+
response = await $fetch(
|
|
352
|
+
chatApi.path(`/conversations/${currentConversation.value.id}/messages`),
|
|
353
|
+
chatApi.withChatHeaders({ method: "POST", body })
|
|
354
|
+
);
|
|
355
|
+
acceptMessagePair(response);
|
|
356
|
+
}
|
|
235
357
|
if (files && files.length > 0) {
|
|
236
358
|
addFilesToConversation(
|
|
237
359
|
currentConversation.value.id,
|
|
@@ -240,29 +362,46 @@ export function useChat() {
|
|
|
240
362
|
"user"
|
|
241
363
|
);
|
|
242
364
|
}
|
|
243
|
-
|
|
365
|
+
if (!acceptedByStream)
|
|
366
|
+
startPolling();
|
|
244
367
|
return response.userMessage;
|
|
245
368
|
} catch (err) {
|
|
246
369
|
messages.value = messages.value.filter(
|
|
247
370
|
(msg) => msg.id !== optimisticUserMessage.id && msg.id !== optimisticAssistantMessage.id
|
|
248
371
|
);
|
|
372
|
+
streamingMessageId.value = null;
|
|
373
|
+
clearPreparingSteps();
|
|
374
|
+
directStream.disconnect();
|
|
249
375
|
stopPolling();
|
|
376
|
+
if (err instanceof DOMException && err.name === "AbortError")
|
|
377
|
+
return null;
|
|
250
378
|
const message = getChatApiErrorCode(err) === "workspace_agent_not_ready" ? WORKSPACE_AGENT_NOT_READY_TEXT : err instanceof Error ? err.message : "Erro ao enviar mensagem";
|
|
251
379
|
error.value = message;
|
|
252
380
|
Sentry.captureException(err);
|
|
253
381
|
return null;
|
|
254
382
|
} finally {
|
|
255
383
|
sending.value = false;
|
|
384
|
+
gateHolding.value = false;
|
|
385
|
+
if (gateController === controller)
|
|
386
|
+
gateController = null;
|
|
256
387
|
}
|
|
257
388
|
};
|
|
258
389
|
const clearConversation = () => {
|
|
259
390
|
stopPolling();
|
|
391
|
+
gateController?.abort();
|
|
392
|
+
streamingMessageId.value = null;
|
|
393
|
+
clearPreparingSteps();
|
|
394
|
+
directStream.disconnect();
|
|
260
395
|
currentConversation.value = null;
|
|
261
396
|
messages.value = [];
|
|
262
397
|
error.value = null;
|
|
263
398
|
};
|
|
264
399
|
const cancelling = ref(false);
|
|
265
400
|
const cancelConversation = async () => {
|
|
401
|
+
if (gateHolding.value) {
|
|
402
|
+
gateController?.abort();
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
266
405
|
if (!currentConversation.value || !hasPendingMessage.value)
|
|
267
406
|
return false;
|
|
268
407
|
cancelling.value = true;
|
|
@@ -278,6 +417,8 @@ export function useChat() {
|
|
|
278
417
|
return false;
|
|
279
418
|
}
|
|
280
419
|
messages.value = markPendingAssistantMessagesInterrupted(messages.value);
|
|
420
|
+
streamingMessageId.value = null;
|
|
421
|
+
directStream.disconnect();
|
|
281
422
|
stopPolling();
|
|
282
423
|
return true;
|
|
283
424
|
} catch (err) {
|
|
@@ -304,6 +445,8 @@ export function useChat() {
|
|
|
304
445
|
messages: []
|
|
305
446
|
};
|
|
306
447
|
messages.value = [];
|
|
448
|
+
streamingMessageId.value = null;
|
|
449
|
+
directStream.disconnect();
|
|
307
450
|
return true;
|
|
308
451
|
} catch (err) {
|
|
309
452
|
const message = err instanceof Error ? err.message : "Erro ao limpar conversa";
|
|
@@ -312,7 +455,7 @@ export function useChat() {
|
|
|
312
455
|
return false;
|
|
313
456
|
}
|
|
314
457
|
};
|
|
315
|
-
const retryMessage = async (messageId) => {
|
|
458
|
+
const retryMessage = async (messageId, wakeSessionId) => {
|
|
316
459
|
if (!currentConversation.value)
|
|
317
460
|
return false;
|
|
318
461
|
error.value = null;
|
|
@@ -320,15 +463,22 @@ export function useChat() {
|
|
|
320
463
|
if (!retry)
|
|
321
464
|
return false;
|
|
322
465
|
messages.value = retry.messages;
|
|
466
|
+
streamingMessageId.value = messageId;
|
|
467
|
+
directStream.beginMessage();
|
|
323
468
|
try {
|
|
324
469
|
await $fetch(
|
|
325
470
|
chatApi.path(`/conversations/${currentConversation.value.id}/messages/${messageId}/retry`),
|
|
326
|
-
chatApi.withChatHeaders({
|
|
471
|
+
chatApi.withChatHeaders({
|
|
472
|
+
method: "POST",
|
|
473
|
+
body: wakeSessionId ? { wakeSessionId } : {}
|
|
474
|
+
})
|
|
327
475
|
);
|
|
328
476
|
startPolling();
|
|
329
477
|
return true;
|
|
330
478
|
} catch (err) {
|
|
331
479
|
messages.value = restoreAssistantMessageAfterRetryFailure(messages.value, retry.originalMessage);
|
|
480
|
+
streamingMessageId.value = null;
|
|
481
|
+
directStream.disconnect();
|
|
332
482
|
const errorMessage = getChatApiErrorCode(err) === "workspace_agent_not_ready" ? WORKSPACE_AGENT_NOT_READY_TEXT : err instanceof Error ? err.message : "Erro ao reenviar mensagem";
|
|
333
483
|
error.value = errorMessage;
|
|
334
484
|
Sentry.captureException(err);
|
|
@@ -400,6 +550,8 @@ export function useChat() {
|
|
|
400
550
|
};
|
|
401
551
|
watch(hasPendingMessage, (isPending, wasPending) => {
|
|
402
552
|
if (wasPending && !isPending) {
|
|
553
|
+
streamingMessageId.value = null;
|
|
554
|
+
directStream.disconnect();
|
|
403
555
|
void generateTitle();
|
|
404
556
|
}
|
|
405
557
|
});
|
|
@@ -414,11 +566,14 @@ export function useChat() {
|
|
|
414
566
|
error: readonly(error),
|
|
415
567
|
isPolling: readonly(isPolling),
|
|
416
568
|
hasPendingMessage,
|
|
569
|
+
streamingActiveMessageId,
|
|
570
|
+
preparingSteps: readonly(preparingSteps),
|
|
417
571
|
cancelling: readonly(cancelling),
|
|
418
572
|
generatingTitle: readonly(generatingTitle),
|
|
419
573
|
animatedTitle: readonly(animatedTitle),
|
|
420
574
|
loadConversation,
|
|
421
575
|
setConversation,
|
|
576
|
+
adoptConversation,
|
|
422
577
|
setConversationModel,
|
|
423
578
|
pendingModelSelection,
|
|
424
579
|
sendMessage,
|
|
@@ -27,6 +27,7 @@ export function useMentionAutocomplete(options) {
|
|
|
27
27
|
const query = ref("");
|
|
28
28
|
const highlightedIndex = ref(-1);
|
|
29
29
|
const lastQuery = ref("");
|
|
30
|
+
const triggerCaretRect = ref(null);
|
|
30
31
|
let triggerTextOffset = -1;
|
|
31
32
|
const isOpen = computed(() => activeTrigger.value !== null);
|
|
32
33
|
const filteredItems = computed(() => {
|
|
@@ -89,10 +90,12 @@ export function useMentionAutocomplete(options) {
|
|
|
89
90
|
const mentionTrigger = findMentionTrigger(getTextBeforeCursor());
|
|
90
91
|
if (!mentionTrigger) {
|
|
91
92
|
activeTrigger.value = null;
|
|
93
|
+
triggerCaretRect.value = null;
|
|
92
94
|
return;
|
|
93
95
|
}
|
|
94
96
|
activeTrigger.value = mentionTrigger.trigger;
|
|
95
97
|
triggerTextOffset = mentionTrigger.offset;
|
|
98
|
+
triggerCaretRect.value = getCaretRangeAtOffset(triggerTextOffset)?.getBoundingClientRect() ?? null;
|
|
96
99
|
if (mentionTrigger.query !== lastQuery.value) {
|
|
97
100
|
highlightedIndex.value = -1;
|
|
98
101
|
lastQuery.value = mentionTrigger.query;
|
|
@@ -314,6 +317,7 @@ ${varLines}
|
|
|
314
317
|
isOpen,
|
|
315
318
|
activeTrigger: readonly(activeTrigger),
|
|
316
319
|
query: readonly(query),
|
|
320
|
+
triggerCaretRect: readonly(triggerCaretRect),
|
|
317
321
|
filteredItems,
|
|
318
322
|
highlightedIndex,
|
|
319
323
|
handleKeydown,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { SendMessageResponse } from '../../types/schemas/chat/messages.js';
|
|
2
|
+
export type DirectStreamActivity = 'idle' | 'connected' | 'active';
|
|
3
|
+
export type SseFrame = {
|
|
4
|
+
event?: string;
|
|
5
|
+
data: string;
|
|
6
|
+
};
|
|
7
|
+
export type MessagePartsEvent = {
|
|
8
|
+
kind: 'message-parts';
|
|
9
|
+
runId: string;
|
|
10
|
+
deltaSeq: number;
|
|
11
|
+
parentToolUseId?: string | null;
|
|
12
|
+
event: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
export type MessagePartsAccumulator = {
|
|
15
|
+
runId: string | null;
|
|
16
|
+
lastDeltaSeq: number;
|
|
17
|
+
textByIndex: Record<number, string>;
|
|
18
|
+
thinkingByIndex: Record<number, string>;
|
|
19
|
+
};
|
|
20
|
+
export declare function parseSseFrames(source: string): {
|
|
21
|
+
frames: SseFrame[];
|
|
22
|
+
rest: string;
|
|
23
|
+
};
|
|
24
|
+
export declare function createMessagePartsAccumulator(runId?: string | null): MessagePartsAccumulator;
|
|
25
|
+
export declare function applyMessagePartsEvent(accumulator: MessagePartsAccumulator, input: MessagePartsEvent): {
|
|
26
|
+
text: string;
|
|
27
|
+
thinking: string;
|
|
28
|
+
changed: boolean;
|
|
29
|
+
};
|
|
30
|
+
export declare function useDirectStream(options?: {
|
|
31
|
+
onResult?: () => void;
|
|
32
|
+
onDisconnect?: () => void;
|
|
33
|
+
}): {
|
|
34
|
+
activity: any;
|
|
35
|
+
streamingText: any;
|
|
36
|
+
streamingThinking: any;
|
|
37
|
+
activeRunId: any;
|
|
38
|
+
connectMessage: (url: string, init: RequestInit, onAccepted: (response: SendMessageResponse) => void) => Promise<SendMessageResponse>;
|
|
39
|
+
beginMessage: () => void;
|
|
40
|
+
disconnect: (reset?: boolean) => void;
|
|
41
|
+
};
|