@meistrari/chat-nuxt 3.11.0 → 3.12.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/module.json +1 -1
- package/dist/runtime/assets/css/markstream.css +1 -1
- package/dist/runtime/embed/components/chat-embed-inner.vue +76 -36
- package/dist/runtime/embed/composables/use-agent-wake.d.ts +1 -2
- package/dist/runtime/embed/composables/use-agent-wake.js +19 -13
- package/dist/runtime/embed/feature-config.d.ts +2 -0
- package/dist/runtime/embed/feature-config.js +2 -1
- package/dist/runtime/embed/loading-messages.d.ts +1 -1
- package/dist/runtime/embed/loading-messages.js +1 -1
- package/dist/runtime/messages/components/chat-message-bubble.vue +4 -4
- package/dist/runtime/messages/components/chat-message-input.d.vue.ts +8 -2
- package/dist/runtime/messages/components/chat-message-input.vue +51 -2
- package/dist/runtime/messages/components/chat-message-input.vue.d.ts +8 -2
- package/dist/runtime/messages/components/chat-message-list.vue +3 -5
- package/dist/runtime/messages/components/reasoning/chat-reasoning-steps.vue +1 -1
- package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue +14 -66
- package/dist/runtime/messages/composables/chat-message-terminal.js +3 -3
- package/dist/runtime/messages/composables/chat.d.ts +17 -3
- package/dist/runtime/messages/composables/chat.js +246 -23
- package/dist/runtime/messages/composables/outbound-queue.d.ts +23 -0
- package/dist/runtime/messages/composables/outbound-queue.js +28 -0
- package/dist/runtime/messages/composables/use-direct-stream.d.ts +7 -1
- package/dist/runtime/messages/composables/use-direct-stream.js +30 -7
- package/dist/runtime/server/utils/chat-api-proxy.js +3 -1
- package/dist/runtime/shared/chat-api-error.d.ts +7 -0
- package/dist/runtime/shared/chat-api-error.js +24 -4
- package/dist/runtime/types/schemas/chat/chat-config.d.ts +1 -0
- package/dist/runtime/types/schemas/chat/chat-config.js +1 -0
- package/dist/runtime/types/schemas/chat/messages.d.ts +14 -0
- package/dist/runtime/types/schemas/chat/messages.js +6 -1
- package/dist/runtime/types/schemas/chat/ui.d.ts +2 -2
- package/dist/runtime/types/schemas/chat/ui.js +2 -2
- package/dist/runtime/types/schemas/index.d.ts +1 -0
- package/dist/runtime/types/schemas/index.js +1 -0
- package/dist/runtime/types/schemas/integrations/tela-agent-api.d.ts +46 -0
- package/dist/runtime/types/schemas/integrations/tela-agent-api.js +26 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -101,7 +101,7 @@ Installing `@meistrari/chat-nuxt` installs the runtime packages used by the laye
|
|
|
101
101
|
|
|
102
102
|
| Package | Version | Purpose |
|
|
103
103
|
|---------|---------|---------|
|
|
104
|
-
| `@meistrari/auth-nuxt` | `3.
|
|
104
|
+
| `@meistrari/auth-nuxt` | `3.31.0` | Authentication (required) |
|
|
105
105
|
| `@meistrari/logger` | `^2.1.3` | Structured logging for the server proxy routes |
|
|
106
106
|
| `@meistrari/tela-build` | `^1.30.0` | UI component library (Nuxt layer) |
|
|
107
107
|
| `@sentry/nuxt` | `^10.0.0` | Client-side error capture used by chat composables |
|
package/dist/module.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.markstream-vue{color:#171717;font-family:Inter,"Inter Fallback: Arial",sans-serif;font-size:16px;font-weight:400;letter-spacing:-.15px;line-height:24px;--list-item-marker:#a3a3a3}.markstream-vue code,.markstream-vue kbd,.markstream-vue pre,.markstream-vue samp{font-family:Geist Mono Variable,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:13px;font-variation-settings:normal;line-height:1.5}.markstream-vue ol,.markstream-vue ul{margin:8px 0;padding-left:1.5em}.markstream-vue .hr-node{border-color:#e5e5e5;border-top-width:.5px;margin-bottom:24px;margin-top:24px}.markstream-vue .strong-node{font-weight:580}.markstream-vue .list-node.list-node{margin-bottom:8px;margin-left:4px;margin-top:8px;padding-left:1em}.markstream-vue ul{list-style:disc;list-style-position:outside}.markstream-vue ol{list-style:decimal;list-style-position:outside}.markstream-vue li{margin:4px 0}.markstream-vue .paragraph-node{margin:0}.markstream-vue .my-8{margin-bottom:8px;margin-top:8px}.markstream-vue .heading-node.heading-1,.markstream-vue h1{font-size:24px;font-weight:540;letter-spacing:-.8px;line-height:28px;margin:24px 0 16px}.markstream-vue .heading-node.heading-2,.markstream-vue h2{font-size:20px;font-weight:540;letter-spacing:-.5px;line-height:24px;margin:20px 0 12px}.markstream-vue .heading-node.heading-3,.markstream-vue h3{font-size:20px;font-weight:540;letter-spacing:-.5px;line-height:24px;margin:16px 0 8px}.markstream-vue .heading-node.heading-4,.markstream-vue h4{font-size:16px;font-weight:540;letter-spacing:-.2px;line-height:20px;margin:12px 0 6px}.markstream-vue .heading-node.heading-5,.markstream-vue h5{font-size:14px;font-weight:540;letter-spacing:-.15px;line-height:16px;margin:8px 0 4px}.markstream-vue .heading-node.heading-6,.markstream-vue h6{font-size:12px;font-weight:540;letter-spacing:-.1px;line-height:14px;margin:8px 0 4px}.markstream-vue [class*=tooltip],[class*=tooltip-element]{background-color:var(--neutral-900,#1a1a1a)!important;border-radius:4px;color:#fff!important;font-size:12px;padding:4px 8px}.mention-tag{align-items:center;border-radius:9999px;display:inline-flex;font-size:.875em;font-weight:500;gap:4px;padding:2px 8px;vertical-align:middle;white-space:nowrap}.mention-tag-file{background-color:rgba(127,189,247,.12);color:#7fbdf7}.mention-tag-skill{background-color:rgba(155,111,222,.12);color:#9b6fde}.mention-tag-canvas{background-color:rgba(234,161,65,.12);color:#d4882e}.mention-tag>span[class*=i-ph]{flex-shrink:0}
|
|
1
|
+
.markstream-vue{color:#171717;font-family:Inter,"Inter Fallback: Arial",sans-serif;font-size:16px;font-weight:400;letter-spacing:-.15px;line-height:24px;--list-item-marker:#a3a3a3;--list-item-counter-marker:#262626}.markstream-vue code,.markstream-vue kbd,.markstream-vue pre,.markstream-vue samp{font-family:Geist Mono Variable,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:13px;font-variation-settings:normal;line-height:1.5}.markstream-vue ol,.markstream-vue ul{margin:8px 0;padding-left:1.5em}.markstream-vue .hr-node{border-color:#e5e5e5;border-top-width:.5px;margin-bottom:24px;margin-top:24px}.markstream-vue .strong-node{font-weight:580}.markstream-vue .list-node.list-node{margin-bottom:8px;margin-left:4px;margin-top:8px;padding-left:1em}.markstream-vue ul{list-style:disc;list-style-position:outside}.markstream-vue ol{list-style:decimal;list-style-position:outside}.markstream-vue li{margin:4px 0}.markstream-vue .paragraph-node{margin:0}.markstream-vue .my-8{margin-bottom:8px;margin-top:8px}.markstream-vue .heading-node.heading-1,.markstream-vue h1{font-size:24px;font-weight:540;letter-spacing:-.8px;line-height:28px;margin:24px 0 16px}.markstream-vue .heading-node.heading-2,.markstream-vue h2{font-size:20px;font-weight:540;letter-spacing:-.5px;line-height:24px;margin:20px 0 12px}.markstream-vue .heading-node.heading-3,.markstream-vue h3{font-size:20px;font-weight:540;letter-spacing:-.5px;line-height:24px;margin:16px 0 8px}.markstream-vue .heading-node.heading-4,.markstream-vue h4{font-size:16px;font-weight:540;letter-spacing:-.2px;line-height:20px;margin:12px 0 6px}.markstream-vue .heading-node.heading-5,.markstream-vue h5{font-size:14px;font-weight:540;letter-spacing:-.15px;line-height:16px;margin:8px 0 4px}.markstream-vue .heading-node.heading-6,.markstream-vue h6{font-size:12px;font-weight:540;letter-spacing:-.1px;line-height:14px;margin:8px 0 4px}.markstream-vue [class*=tooltip],[class*=tooltip-element]{background-color:var(--neutral-900,#1a1a1a)!important;border-radius:4px;color:#fff!important;font-size:12px;padding:4px 8px}.mention-tag{align-items:center;border-radius:9999px;display:inline-flex;font-size:.875em;font-weight:500;gap:4px;padding:2px 8px;vertical-align:middle;white-space:nowrap}.mention-tag-file{background-color:rgba(127,189,247,.12);color:#7fbdf7}.mention-tag-skill{background-color:rgba(155,111,222,.12);color:#9b6fde}.mention-tag-canvas{background-color:rgba(234,161,65,.12);color:#d4882e}.mention-tag>span[class*=i-ph]{flex-shrink:0}
|
|
@@ -143,6 +143,9 @@ function applySuggestion(prompt) {
|
|
|
143
143
|
const breakpoints = useBreakpoints({ mobile: CHAT_MOBILE_BREAKPOINT });
|
|
144
144
|
const isMobile = breakpoints.smaller("mobile");
|
|
145
145
|
const drawerOpen = ref(false);
|
|
146
|
+
function restoreQueuedSend(send) {
|
|
147
|
+
messageInputRef.value?.restoreDraft(send);
|
|
148
|
+
}
|
|
146
149
|
const { isDragging, setupDropZone } = useFileDrop({
|
|
147
150
|
onDrop: (files) => {
|
|
148
151
|
messageInputRef.value?.addFiles(files);
|
|
@@ -155,7 +158,10 @@ const {
|
|
|
155
158
|
loading,
|
|
156
159
|
generatingTitle,
|
|
157
160
|
animatedTitle,
|
|
158
|
-
|
|
161
|
+
composerLocked,
|
|
162
|
+
outboundOccupied,
|
|
163
|
+
sessionReady,
|
|
164
|
+
agentSessionRewakeable,
|
|
159
165
|
streamingActiveMessageId,
|
|
160
166
|
preparingSteps,
|
|
161
167
|
loadConversation,
|
|
@@ -164,12 +170,18 @@ const {
|
|
|
164
170
|
setConversationModel,
|
|
165
171
|
pendingModelSelection,
|
|
166
172
|
sendMessage,
|
|
173
|
+
flushOutboundQueue,
|
|
174
|
+
failOutboundQueue,
|
|
167
175
|
retryMessage,
|
|
168
176
|
cancelConversation,
|
|
169
177
|
startPolling,
|
|
170
178
|
clearConversation,
|
|
171
179
|
error: chatError
|
|
172
|
-
} = useChat(
|
|
180
|
+
} = useChat({
|
|
181
|
+
outboundQueueEnabled: computed(() => resolvedFeatures.value.chatOutboundQueue),
|
|
182
|
+
onQueuedSendCancelled: restoreQueuedSend,
|
|
183
|
+
onQueuedSendFailed: restoreQueuedSend
|
|
184
|
+
});
|
|
173
185
|
const agentWake = useAgentWake();
|
|
174
186
|
const modelUpdateInFlight = ref(null);
|
|
175
187
|
watch(chatError, (message) => {
|
|
@@ -186,9 +198,11 @@ const selectedModel = computed({
|
|
|
186
198
|
if (conversation && conversation.model !== value) {
|
|
187
199
|
const previousModel = conversation.model;
|
|
188
200
|
setConversationModel(value);
|
|
189
|
-
const update = updateConversationModel(conversation.id, value).then((updated) => {
|
|
201
|
+
const update = updateConversationModel(conversation.id, value).then(async (updated) => {
|
|
190
202
|
if (!updated && currentConversation.value?.id === conversation.id && currentConversation.value.model === value) {
|
|
191
203
|
setConversationModel(previousModel);
|
|
204
|
+
} else if (updated && value) {
|
|
205
|
+
await agentWake.wake(conversation.threadSessionId ?? void 0, value);
|
|
192
206
|
}
|
|
193
207
|
return updated;
|
|
194
208
|
});
|
|
@@ -199,11 +213,6 @@ const selectedModel = computed({
|
|
|
199
213
|
}
|
|
200
214
|
}
|
|
201
215
|
});
|
|
202
|
-
watch(selectedModel, (model, previousModel) => {
|
|
203
|
-
if (!isReady.value || model === previousModel || currentConversation.value?.threadSessionId)
|
|
204
|
-
return;
|
|
205
|
-
void agentWake.wake(model);
|
|
206
|
-
});
|
|
207
216
|
const filesCount = computed(() => total.value);
|
|
208
217
|
const hasMessages = computed(() => messages.value.length > 0);
|
|
209
218
|
const mobilePreviewFile = computed(() => {
|
|
@@ -244,8 +253,8 @@ function updateConversationId(nextConversationId) {
|
|
|
244
253
|
async function ensureConversationLoaded(conversationId) {
|
|
245
254
|
if (currentConversation.value?.id === conversationId) {
|
|
246
255
|
void agentWake.wake(
|
|
247
|
-
currentConversation.value.
|
|
248
|
-
|
|
256
|
+
currentConversation.value.threadSessionId ?? void 0,
|
|
257
|
+
selectedModel.value
|
|
249
258
|
);
|
|
250
259
|
startPolling();
|
|
251
260
|
return;
|
|
@@ -265,8 +274,8 @@ async function ensureConversationLoaded(conversationId) {
|
|
|
265
274
|
return;
|
|
266
275
|
}
|
|
267
276
|
void agentWake.wake(
|
|
268
|
-
currentConversation.value.
|
|
269
|
-
|
|
277
|
+
currentConversation.value.threadSessionId ?? void 0,
|
|
278
|
+
selectedModel.value
|
|
270
279
|
);
|
|
271
280
|
startPolling();
|
|
272
281
|
} finally {
|
|
@@ -277,7 +286,7 @@ async function ensureConversationLoaded(conversationId) {
|
|
|
277
286
|
}
|
|
278
287
|
function resetConversation(shouldEmit = true) {
|
|
279
288
|
clearConversation();
|
|
280
|
-
void agentWake.wake(selectedModel.value);
|
|
289
|
+
void agentWake.wake(void 0, selectedModel.value);
|
|
281
290
|
activeTab.value = "chat";
|
|
282
291
|
closeFilesPanel();
|
|
283
292
|
closePreview();
|
|
@@ -460,29 +469,60 @@ async function handleSend(content, files, agentInputs) {
|
|
|
460
469
|
const conversation = currentConversation.value;
|
|
461
470
|
if (!conversation)
|
|
462
471
|
throw new Error("Erro ao preparar conversa");
|
|
463
|
-
if (conversation.threadSessionId) {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
)
|
|
468
|
-
|
|
472
|
+
if (conversation.threadSessionId && resolvedFeatures.value.chatOutboundQueue && sessionReady.value === false) {
|
|
473
|
+
if (!agentSessionRewakeable.value)
|
|
474
|
+
return { defer: true };
|
|
475
|
+
const wake = await agentWake.wake(conversation.threadSessionId, selectedModel.value);
|
|
476
|
+
if (wake.status !== "ready")
|
|
477
|
+
throw new Error("N\xE3o foi poss\xEDvel acordar o agente. Tente novamente.");
|
|
478
|
+
return {};
|
|
479
|
+
}
|
|
480
|
+
if (!conversation.threadSessionId) {
|
|
469
481
|
await agentWake.waitUntilSettled(signal);
|
|
482
|
+
if (agentWake.state.value === "idle")
|
|
483
|
+
await agentWake.wake(void 0, selectedModel.value);
|
|
484
|
+
else if (agentWake.state.value === "ready")
|
|
485
|
+
await agentWake.wake(void 0, selectedModel.value);
|
|
470
486
|
}
|
|
471
487
|
claimedWakeSessionId = claimableWakeSessionId();
|
|
488
|
+
if (!conversation.threadSessionId && !claimedWakeSessionId)
|
|
489
|
+
throw new Error("N\xE3o foi poss\xEDvel acordar o agente. Tente novamente.");
|
|
472
490
|
return claimedWakeSessionId ? { wakeSessionId: claimedWakeSessionId } : {};
|
|
473
491
|
});
|
|
474
492
|
if (sent && claimedWakeSessionId)
|
|
475
493
|
agentWake.consume();
|
|
494
|
+
if (!sent)
|
|
495
|
+
restoreQueuedSend({ content, files, agentInputs: resolvedAgentInputs });
|
|
496
|
+
}
|
|
497
|
+
watch(
|
|
498
|
+
[outboundOccupied, agentWake.state],
|
|
499
|
+
async ([occupied, wakeState]) => {
|
|
500
|
+
if (!occupied)
|
|
501
|
+
return;
|
|
502
|
+
if (wakeState === "ready") {
|
|
503
|
+
const wakeSessionId = claimableWakeSessionId();
|
|
504
|
+
const sent = await flushOutboundQueue(wakeSessionId);
|
|
505
|
+
if (sent && wakeSessionId)
|
|
506
|
+
agentWake.consume();
|
|
507
|
+
} else if (wakeState === "unavailable" && (agentSessionRewakeable.value || sessionReady.value === null)) {
|
|
508
|
+
failOutboundQueue("N\xE3o foi poss\xEDvel acordar o agente. Tente novamente.");
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
);
|
|
512
|
+
watch(
|
|
513
|
+
[outboundOccupied, agentSessionRewakeable],
|
|
514
|
+
([occupied, rewakeable]) => {
|
|
515
|
+
const sessionId = currentConversation.value?.threadSessionId;
|
|
516
|
+
if (occupied && rewakeable && sessionId && agentWake.state.value !== "waking")
|
|
517
|
+
void agentWake.wake(sessionId, selectedModel.value);
|
|
518
|
+
}
|
|
519
|
+
);
|
|
520
|
+
async function handleCancel() {
|
|
521
|
+
await cancelConversation();
|
|
476
522
|
}
|
|
477
523
|
async function handleRetry(messageId) {
|
|
478
|
-
if (currentConversation.value?.threadSessionId)
|
|
479
|
-
await agentWake.wake(
|
|
480
|
-
currentConversation.value.model,
|
|
481
|
-
currentConversation.value.threadSessionId
|
|
482
|
-
);
|
|
483
|
-
} else {
|
|
524
|
+
if (!currentConversation.value?.threadSessionId)
|
|
484
525
|
await agentWake.waitUntilSettled();
|
|
485
|
-
}
|
|
486
526
|
await retryMessage(messageId, claimableWakeSessionId());
|
|
487
527
|
}
|
|
488
528
|
async function handleNewConversation(groupId) {
|
|
@@ -770,7 +810,7 @@ async function handleDelete() {
|
|
|
770
810
|
:files-count="filesCount"
|
|
771
811
|
:generating-title="generatingTitle"
|
|
772
812
|
:has-messages="hasMessages"
|
|
773
|
-
:has-pending-message="
|
|
813
|
+
:has-pending-message="composerLocked"
|
|
774
814
|
:hide-settings="hideSettings"
|
|
775
815
|
:hide-sidebar="hideSidebar"
|
|
776
816
|
:is-editing="isEditing"
|
|
@@ -789,7 +829,7 @@ async function handleDelete() {
|
|
|
789
829
|
:workspace-logo="workspaceLogo"
|
|
790
830
|
:workspace-name="workspaceName"
|
|
791
831
|
:workspace-settings="effectiveWorkspaceSettings"
|
|
792
|
-
@cancel="
|
|
832
|
+
@cancel="handleCancel"
|
|
793
833
|
@cancel-editing="cancelEditing"
|
|
794
834
|
@copy-link="handleShare"
|
|
795
835
|
@debug="showDebugModal = true"
|
|
@@ -848,7 +888,7 @@ async function handleDelete() {
|
|
|
848
888
|
<ChatMessageInput
|
|
849
889
|
ref="messageInputRef"
|
|
850
890
|
v-model:selected-model="selectedModel"
|
|
851
|
-
:loading="
|
|
891
|
+
:loading="composerLocked"
|
|
852
892
|
:creator-name="currentUser?.name ?? void 0"
|
|
853
893
|
:creator-email="
|
|
854
894
|
currentConversation?.createdBy ?? currentUser?.email ?? void 0
|
|
@@ -864,7 +904,7 @@ async function handleDelete() {
|
|
|
864
904
|
:show-cancel-button="resolvedFeatures.showCancelButton"
|
|
865
905
|
centered
|
|
866
906
|
@send="handleSend"
|
|
867
|
-
@cancel="
|
|
907
|
+
@cancel="handleCancel"
|
|
868
908
|
/>
|
|
869
909
|
</template>
|
|
870
910
|
</TelaChatEmptyState>
|
|
@@ -881,7 +921,7 @@ async function handleDelete() {
|
|
|
881
921
|
<ChatMessageInput
|
|
882
922
|
ref="messageInputRef"
|
|
883
923
|
v-model:selected-model="selectedModel"
|
|
884
|
-
:loading="
|
|
924
|
+
:loading="composerLocked"
|
|
885
925
|
:creator-name="currentUser?.name ?? void 0"
|
|
886
926
|
:creator-email="
|
|
887
927
|
currentConversation?.createdBy ?? currentUser?.email ?? void 0
|
|
@@ -897,7 +937,7 @@ async function handleDelete() {
|
|
|
897
937
|
:show-cancel-button="resolvedFeatures.showCancelButton"
|
|
898
938
|
has-disclaimer
|
|
899
939
|
@send="handleSend"
|
|
900
|
-
@cancel="
|
|
940
|
+
@cancel="handleCancel"
|
|
901
941
|
/>
|
|
902
942
|
</ChatMobileShell>
|
|
903
943
|
|
|
@@ -1045,7 +1085,7 @@ async function handleDelete() {
|
|
|
1045
1085
|
<ChatMessageInput
|
|
1046
1086
|
ref="messageInputRef"
|
|
1047
1087
|
v-model:selected-model="selectedModel"
|
|
1048
|
-
:loading="
|
|
1088
|
+
:loading="composerLocked"
|
|
1049
1089
|
:creator-name="currentUser?.name ?? void 0"
|
|
1050
1090
|
:creator-email="
|
|
1051
1091
|
currentConversation?.createdBy ?? currentUser?.email ?? void 0
|
|
@@ -1061,7 +1101,7 @@ async function handleDelete() {
|
|
|
1061
1101
|
:show-cancel-button="resolvedFeatures.showCancelButton"
|
|
1062
1102
|
centered
|
|
1063
1103
|
@send="handleSend"
|
|
1064
|
-
@cancel="
|
|
1104
|
+
@cancel="handleCancel"
|
|
1065
1105
|
/>
|
|
1066
1106
|
</template>
|
|
1067
1107
|
</TelaChatEmptyState>
|
|
@@ -1081,7 +1121,7 @@ async function handleDelete() {
|
|
|
1081
1121
|
<ChatMessageInput
|
|
1082
1122
|
ref="messageInputRef"
|
|
1083
1123
|
v-model:selected-model="selectedModel"
|
|
1084
|
-
:loading="
|
|
1124
|
+
:loading="composerLocked"
|
|
1085
1125
|
:creator-name="currentUser?.name ?? void 0"
|
|
1086
1126
|
:creator-email="
|
|
1087
1127
|
currentConversation?.createdBy ?? currentUser?.email ?? void 0
|
|
@@ -1097,7 +1137,7 @@ async function handleDelete() {
|
|
|
1097
1137
|
:show-cancel-button="resolvedFeatures.showCancelButton"
|
|
1098
1138
|
has-disclaimer
|
|
1099
1139
|
@send="handleSend"
|
|
1100
|
-
@cancel="
|
|
1140
|
+
@cancel="handleCancel"
|
|
1101
1141
|
/>
|
|
1102
1142
|
</TelaChatBody>
|
|
1103
1143
|
|
|
@@ -2,7 +2,6 @@ import type { TelaAgentWakeResponse } from '../../types/schemas/integrations/tel
|
|
|
2
2
|
export type AgentWakeSnapshot = {
|
|
3
3
|
state: TelaAgentWakeResponse['status'] | 'idle';
|
|
4
4
|
sessionId?: string;
|
|
5
|
-
model?: string;
|
|
6
5
|
};
|
|
7
6
|
export declare function pollAgentWake(options: {
|
|
8
7
|
request: () => Promise<TelaAgentWakeResponse>;
|
|
@@ -16,7 +15,7 @@ export declare function pollAgentWake(options: {
|
|
|
16
15
|
export declare function useAgentWake(): {
|
|
17
16
|
state: any;
|
|
18
17
|
sessionId: any;
|
|
19
|
-
wake: (model?: string | null
|
|
18
|
+
wake: (requestedSessionId?: string, model?: string | null) => Promise<TelaAgentWakeResponse>;
|
|
20
19
|
skip: () => void;
|
|
21
20
|
consume: () => void;
|
|
22
21
|
waitUntilSettled: (signal?: AbortSignal) => Promise<AgentWakeSnapshot>;
|
|
@@ -21,21 +21,30 @@ export async function pollAgentWake(options) {
|
|
|
21
21
|
else
|
|
22
22
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
23
23
|
}));
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
let unavailableAttempts = 0;
|
|
25
|
+
while (!options.signal?.aborted && now() <= deadline) {
|
|
26
|
+
try {
|
|
26
27
|
const response = await options.request();
|
|
27
28
|
options.onResponse?.(response);
|
|
29
|
+
if (response.status === "unavailable" && unavailableAttempts < 2) {
|
|
30
|
+
unavailableAttempts += 1;
|
|
31
|
+
await wait(Math.min(500 * 2 ** (unavailableAttempts - 1), Math.max(0, deadline - now())), options.signal);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
28
34
|
if (response.status !== "waking")
|
|
29
35
|
return response;
|
|
30
36
|
const remaining = deadline - now();
|
|
31
37
|
if (remaining <= 0)
|
|
32
38
|
break;
|
|
33
39
|
await wait(Math.min(options.pollIntervalMs ?? WAKE_POLL_INTERVAL_MS, remaining), options.signal);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (error instanceof DOMException && error.name === "AbortError")
|
|
42
|
+
throw error;
|
|
43
|
+
if (unavailableAttempts >= 2)
|
|
44
|
+
return { status: "unavailable" };
|
|
45
|
+
unavailableAttempts += 1;
|
|
46
|
+
await wait(Math.min(500 * 2 ** (unavailableAttempts - 1), Math.max(0, deadline - now())), options.signal);
|
|
34
47
|
}
|
|
35
|
-
} catch (error) {
|
|
36
|
-
if (error instanceof DOMException && error.name === "AbortError")
|
|
37
|
-
throw error;
|
|
38
|
-
return { status: "unavailable" };
|
|
39
48
|
}
|
|
40
49
|
return { status: "unavailable" };
|
|
41
50
|
}
|
|
@@ -67,7 +76,7 @@ export function useAgentWake() {
|
|
|
67
76
|
activeWake = null;
|
|
68
77
|
setSnapshot({ state: "idle" });
|
|
69
78
|
}
|
|
70
|
-
async function wake(
|
|
79
|
+
async function wake(requestedSessionId, model) {
|
|
71
80
|
generation++;
|
|
72
81
|
const currentGeneration = generation;
|
|
73
82
|
abortController?.abort();
|
|
@@ -76,8 +85,7 @@ export function useAgentWake() {
|
|
|
76
85
|
let wakeSessionId = requestedSessionId ?? snapshot.value.sessionId;
|
|
77
86
|
setSnapshot({
|
|
78
87
|
state: "waking",
|
|
79
|
-
...wakeSessionId ? { sessionId: wakeSessionId } : {}
|
|
80
|
-
...model ? { model } : {}
|
|
88
|
+
...wakeSessionId ? { sessionId: wakeSessionId } : {}
|
|
81
89
|
});
|
|
82
90
|
if (import.meta.server) {
|
|
83
91
|
const unavailable = { status: "unavailable" };
|
|
@@ -105,16 +113,14 @@ export function useAgentWake() {
|
|
|
105
113
|
wakeSessionId = response.sessionId ?? wakeSessionId;
|
|
106
114
|
setSnapshot({
|
|
107
115
|
state: response.status,
|
|
108
|
-
...wakeSessionId ? { sessionId: wakeSessionId } : {}
|
|
109
|
-
...model ? { model } : {}
|
|
116
|
+
...wakeSessionId ? { sessionId: wakeSessionId } : {}
|
|
110
117
|
});
|
|
111
118
|
}
|
|
112
119
|
});
|
|
113
120
|
if (currentGeneration === generation) {
|
|
114
121
|
setSnapshot({
|
|
115
122
|
state: result.status,
|
|
116
|
-
...result.sessionId ? { sessionId: result.sessionId } : {}
|
|
117
|
-
...model ? { model } : {}
|
|
123
|
+
...result.sessionId ? { sessionId: result.sessionId } : {}
|
|
118
124
|
});
|
|
119
125
|
}
|
|
120
126
|
return result;
|
|
@@ -8,11 +8,13 @@ export type ChatFeatureConfig = {
|
|
|
8
8
|
showCancelButton?: boolean;
|
|
9
9
|
/** Time to first token shown in assistant message metadata. Defaults to true. */
|
|
10
10
|
showTtft?: boolean;
|
|
11
|
+
chatOutboundQueue?: boolean;
|
|
11
12
|
};
|
|
12
13
|
export type ResolvedChatFeatureConfig = {
|
|
13
14
|
showUsageTab: boolean;
|
|
14
15
|
showDebugOption: boolean;
|
|
15
16
|
showCancelButton: boolean;
|
|
16
17
|
showTtft: boolean;
|
|
18
|
+
chatOutboundQueue: boolean;
|
|
17
19
|
};
|
|
18
20
|
export declare function resolveChatFeatures(features?: Partial<ChatFeatureConfig>): ResolvedChatFeatureConfig;
|
|
@@ -3,6 +3,7 @@ export function resolveChatFeatures(features) {
|
|
|
3
3
|
showUsageTab: features?.showUsageTab ?? false,
|
|
4
4
|
showDebugOption: features?.showDebugOption ?? false,
|
|
5
5
|
showCancelButton: features?.showCancelButton ?? false,
|
|
6
|
-
showTtft: features?.showTtft ?? true
|
|
6
|
+
showTtft: features?.showTtft ?? true,
|
|
7
|
+
chatOutboundQueue: features?.chatOutboundQueue ?? false
|
|
7
8
|
};
|
|
8
9
|
}
|
|
@@ -5,7 +5,7 @@ export declare const DEFAULT_CHAT_LOADING_MESSAGES: readonly ["Analisando sua pe
|
|
|
5
5
|
* than as a loading placeholder. The user message is already delivered at this
|
|
6
6
|
* point, so the wait belongs to the answer.
|
|
7
7
|
*/
|
|
8
|
-
export declare const CHAT_PREPARING_STEPS: readonly ["Preparando
|
|
8
|
+
export declare const CHAT_PREPARING_STEPS: readonly ["Preparando a resposta", "Analisando a mensagem"];
|
|
9
9
|
/**
|
|
10
10
|
* The first delay is measured from the send, every later one from the step
|
|
11
11
|
* before it — so the sequence paces itself instead of racing a shared clock.
|
|
@@ -13,7 +13,7 @@ export const DEFAULT_CHAT_LOADING_MESSAGES = [
|
|
|
13
13
|
"Estruturando resposta"
|
|
14
14
|
];
|
|
15
15
|
export const CHAT_PREPARING_STEPS = [
|
|
16
|
-
"Preparando
|
|
16
|
+
"Preparando a resposta",
|
|
17
17
|
"Analisando a mensagem"
|
|
18
18
|
];
|
|
19
19
|
export const CHAT_PREPARING_STEP_DELAYS_MS = [2500, 4e3];
|
|
@@ -111,13 +111,13 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
111
111
|
<template>
|
|
112
112
|
<TelaChatMessage
|
|
113
113
|
:from="message.role === 'user' ? 'user' : 'assistant'"
|
|
114
|
-
:align="
|
|
114
|
+
:align="message.role === 'user' ? 'end' : 'start'"
|
|
115
115
|
>
|
|
116
116
|
<template v-if="message.role === 'user'">
|
|
117
117
|
<div
|
|
118
118
|
max-w-prose
|
|
119
119
|
flex="~ col"
|
|
120
|
-
|
|
120
|
+
items-end
|
|
121
121
|
>
|
|
122
122
|
<div
|
|
123
123
|
v-if="!isOwnMessage"
|
|
@@ -138,7 +138,7 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
138
138
|
|
|
139
139
|
<TelaChatMessageContent
|
|
140
140
|
variant="contained"
|
|
141
|
-
|
|
141
|
+
align="end"
|
|
142
142
|
:class="isPending && 'op-80'"
|
|
143
143
|
>
|
|
144
144
|
<div v-if="hasFiles" flex="~ col" gap-4px mb-8px>
|
|
@@ -171,7 +171,7 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
171
171
|
mt-8px
|
|
172
172
|
body-10-regular
|
|
173
173
|
text-tertiary
|
|
174
|
-
|
|
174
|
+
self-end
|
|
175
175
|
>
|
|
176
176
|
{{ formatTime(message.createdAt) }}
|
|
177
177
|
</div>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { DeepReadonly } from 'vue';
|
|
2
2
|
import type { Message } from '../../types/schemas/chat/conversations.js';
|
|
3
|
-
import type { ConversationFile } from '../../types/schemas/chat/files.js';
|
|
3
|
+
import type { ConversationFile, FileUploadInput } from '../../types/schemas/chat/files.js';
|
|
4
4
|
import type { WorkspaceSettingsResponse as WorkspaceSettings } from '../../types/schemas/chat/workspace-settings.js';
|
|
5
|
-
import type { TelaAgentContextFile, TelaAgentInputVariable, TelaAgentSkill } from '../../types/schemas/integrations/tela-agent-api.js';
|
|
5
|
+
import type { TelaAgentContextFile, TelaAgentExecutionInput, TelaAgentInputVariable, TelaAgentSkill } from '../../types/schemas/integrations/tela-agent-api.js';
|
|
6
6
|
type __VLS_Props = {
|
|
7
7
|
disabled?: boolean;
|
|
8
8
|
loading?: boolean;
|
|
@@ -21,6 +21,11 @@ type __VLS_Props = {
|
|
|
21
21
|
showCancelButton?: boolean;
|
|
22
22
|
};
|
|
23
23
|
declare function addFiles(files: File[], inputName?: string): void;
|
|
24
|
+
declare function restoreDraft(draft: {
|
|
25
|
+
content: string;
|
|
26
|
+
files?: FileUploadInput[];
|
|
27
|
+
agentInputs?: TelaAgentExecutionInput[];
|
|
28
|
+
}): void;
|
|
24
29
|
declare function setDraft(text: string): void;
|
|
25
30
|
type __VLS_ModelProps = {
|
|
26
31
|
'selectedModel'?: string | null;
|
|
@@ -28,6 +33,7 @@ type __VLS_ModelProps = {
|
|
|
28
33
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
29
34
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
30
35
|
addFiles: typeof addFiles;
|
|
36
|
+
restoreDraft: typeof restoreDraft;
|
|
31
37
|
setDraft: typeof setDraft;
|
|
32
38
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
33
39
|
"update:selectedModel": (value: string | null) => any;
|
|
@@ -196,6 +196,15 @@ function handleFileSelect(event) {
|
|
|
196
196
|
function createPendingFile(file, inputName) {
|
|
197
197
|
return {
|
|
198
198
|
id: `${file.name}-${file.lastModified}-${file.size}-${Math.random().toString(36).slice(2)}`,
|
|
199
|
+
source: "browser",
|
|
200
|
+
file,
|
|
201
|
+
inputName
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function createUploadedPendingFile(file, inputName) {
|
|
205
|
+
return {
|
|
206
|
+
id: `${file.vaultReference ?? file.url}-${Math.random().toString(36).slice(2)}`,
|
|
207
|
+
source: "uploaded",
|
|
199
208
|
file,
|
|
200
209
|
inputName
|
|
201
210
|
};
|
|
@@ -231,6 +240,22 @@ function handleVariableDrop(event, inputName) {
|
|
|
231
240
|
function getVaultRef(file) {
|
|
232
241
|
return file.vaultReference?.trim() || (file.url.startsWith("vault://") ? file.url : null);
|
|
233
242
|
}
|
|
243
|
+
function getPendingFileMimeType(file) {
|
|
244
|
+
return file.source === "browser" ? file.file.type : file.file.mimeType;
|
|
245
|
+
}
|
|
246
|
+
async function resolvePendingFiles(files) {
|
|
247
|
+
const browserFiles = files.filter((file) => file.source === "browser");
|
|
248
|
+
const uploadedBrowserFiles = browserFiles.length > 0 ? await uploadFiles(browserFiles.map((file) => file.file)) : [];
|
|
249
|
+
let uploadedIndex = 0;
|
|
250
|
+
return files.map((file) => {
|
|
251
|
+
if (file.source === "uploaded")
|
|
252
|
+
return file.file;
|
|
253
|
+
const uploaded = uploadedBrowserFiles[uploadedIndex++];
|
|
254
|
+
if (!uploaded)
|
|
255
|
+
throw new Error("Falha ao preparar anexo");
|
|
256
|
+
return uploaded;
|
|
257
|
+
});
|
|
258
|
+
}
|
|
234
259
|
function buildAgentInputs(files, queuedFiles) {
|
|
235
260
|
if (!hasAgentInputs.value)
|
|
236
261
|
return [];
|
|
@@ -266,7 +291,7 @@ async function handleSend() {
|
|
|
266
291
|
const queuedFiles = [...pendingFiles.value];
|
|
267
292
|
if (queuedFiles.length > 0) {
|
|
268
293
|
try {
|
|
269
|
-
files = await
|
|
294
|
+
files = await resolvePendingFiles(queuedFiles);
|
|
270
295
|
} catch {
|
|
271
296
|
return;
|
|
272
297
|
}
|
|
@@ -470,6 +495,29 @@ function addFiles(files, inputName) {
|
|
|
470
495
|
});
|
|
471
496
|
queueFiles(validFiles, inputName);
|
|
472
497
|
}
|
|
498
|
+
function restoreDraft(draft) {
|
|
499
|
+
setDraft(draft.content);
|
|
500
|
+
const textInputs = (draft.agentInputs ?? []).filter((input) => input.type === "text");
|
|
501
|
+
agentInputTexts.value = {
|
|
502
|
+
...agentInputTexts.value,
|
|
503
|
+
...Object.fromEntries(textInputs.map((input) => [input.name, input.content]))
|
|
504
|
+
};
|
|
505
|
+
const availableSlots = MAX_FILES - pendingFiles.value.length;
|
|
506
|
+
if (availableSlots <= 0 || !draft.files?.length)
|
|
507
|
+
return;
|
|
508
|
+
const fileInputs = (draft.agentInputs ?? []).filter((input) => input.type === "file");
|
|
509
|
+
const claimedInputs = /* @__PURE__ */ new Set();
|
|
510
|
+
const restoredFiles = draft.files.slice(0, availableSlots).map((file) => {
|
|
511
|
+
const vaultRef = getVaultRef(file);
|
|
512
|
+
const inputIndex = fileInputs.findIndex(
|
|
513
|
+
(input, index) => !claimedInputs.has(index) && input.vaultRef === vaultRef
|
|
514
|
+
);
|
|
515
|
+
if (inputIndex >= 0)
|
|
516
|
+
claimedInputs.add(inputIndex);
|
|
517
|
+
return createUploadedPendingFile(file, inputIndex >= 0 ? fileInputs[inputIndex]?.name : void 0);
|
|
518
|
+
});
|
|
519
|
+
pendingFiles.value = [...pendingFiles.value, ...restoredFiles];
|
|
520
|
+
}
|
|
473
521
|
function setDraft(text) {
|
|
474
522
|
content.value = text;
|
|
475
523
|
if (editorRef.value) {
|
|
@@ -488,6 +536,7 @@ function setDraft(text) {
|
|
|
488
536
|
}
|
|
489
537
|
defineExpose({
|
|
490
538
|
addFiles,
|
|
539
|
+
restoreDraft,
|
|
491
540
|
setDraft
|
|
492
541
|
});
|
|
493
542
|
</script>
|
|
@@ -641,7 +690,7 @@ defineExpose({
|
|
|
641
690
|
v-for="pendingFile in pendingFiles"
|
|
642
691
|
:key="pendingFile.id"
|
|
643
692
|
:name="pendingFile.file.name"
|
|
644
|
-
:icon="getFileIcon(pendingFile
|
|
693
|
+
:icon="getFileIcon(getPendingFileMimeType(pendingFile))"
|
|
645
694
|
:meta="formatFileSize(pendingFile.file.size)"
|
|
646
695
|
remove-label="Remover anexo"
|
|
647
696
|
@remove="removeFile(pendingFile.id)"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { DeepReadonly } from 'vue';
|
|
2
2
|
import type { Message } from '../../types/schemas/chat/conversations.js';
|
|
3
|
-
import type { ConversationFile } from '../../types/schemas/chat/files.js';
|
|
3
|
+
import type { ConversationFile, FileUploadInput } from '../../types/schemas/chat/files.js';
|
|
4
4
|
import type { WorkspaceSettingsResponse as WorkspaceSettings } from '../../types/schemas/chat/workspace-settings.js';
|
|
5
|
-
import type { TelaAgentContextFile, TelaAgentInputVariable, TelaAgentSkill } from '../../types/schemas/integrations/tela-agent-api.js';
|
|
5
|
+
import type { TelaAgentContextFile, TelaAgentExecutionInput, TelaAgentInputVariable, TelaAgentSkill } from '../../types/schemas/integrations/tela-agent-api.js';
|
|
6
6
|
type __VLS_Props = {
|
|
7
7
|
disabled?: boolean;
|
|
8
8
|
loading?: boolean;
|
|
@@ -21,6 +21,11 @@ type __VLS_Props = {
|
|
|
21
21
|
showCancelButton?: boolean;
|
|
22
22
|
};
|
|
23
23
|
declare function addFiles(files: File[], inputName?: string): void;
|
|
24
|
+
declare function restoreDraft(draft: {
|
|
25
|
+
content: string;
|
|
26
|
+
files?: FileUploadInput[];
|
|
27
|
+
agentInputs?: TelaAgentExecutionInput[];
|
|
28
|
+
}): void;
|
|
24
29
|
declare function setDraft(text: string): void;
|
|
25
30
|
type __VLS_ModelProps = {
|
|
26
31
|
'selectedModel'?: string | null;
|
|
@@ -28,6 +33,7 @@ type __VLS_ModelProps = {
|
|
|
28
33
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
29
34
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
30
35
|
addFiles: typeof addFiles;
|
|
36
|
+
restoreDraft: typeof restoreDraft;
|
|
31
37
|
setDraft: typeof setDraft;
|
|
32
38
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
33
39
|
"update:selectedModel": (value: string | null) => any;
|
|
@@ -18,9 +18,6 @@ const skeletonMessages = [
|
|
|
18
18
|
function scrollToBottom() {
|
|
19
19
|
conversationRef.value?.scrollToBottom();
|
|
20
20
|
}
|
|
21
|
-
const hasPendingMessage = computed(
|
|
22
|
-
() => props.messages.some((message) => message.status === "pending")
|
|
23
|
-
);
|
|
24
21
|
watch(
|
|
25
22
|
() => [props.messages.length, props.messages.at(-1)?.content.length ?? 0],
|
|
26
23
|
scrollToBottom
|
|
@@ -36,7 +33,7 @@ onMounted(scrollToBottom);
|
|
|
36
33
|
'pt-72px pb-24px px-16px': variant === 'mobile'
|
|
37
34
|
}"
|
|
38
35
|
>
|
|
39
|
-
<TelaChatConversationContent class="max-w-700px!">
|
|
36
|
+
<TelaChatConversationContent class="max-w-700px! min-h-full">
|
|
40
37
|
<template v-if="loading">
|
|
41
38
|
<div
|
|
42
39
|
v-for="message in skeletonMessages"
|
|
@@ -59,7 +56,8 @@ onMounted(scrollToBottom);
|
|
|
59
56
|
@retry="emit('retry', $event)"
|
|
60
57
|
/>
|
|
61
58
|
<p
|
|
62
|
-
v-if="messages.length > 0
|
|
59
|
+
v-if="messages.length > 0"
|
|
60
|
+
mt-auto mb--6px
|
|
63
61
|
text-center body-11-regular text-tertiary
|
|
64
62
|
>
|
|
65
63
|
As respostas podem conter erros. Verifique documentos originais.
|
|
@@ -24,7 +24,7 @@ defineExpose({
|
|
|
24
24
|
</script>
|
|
25
25
|
|
|
26
26
|
<template>
|
|
27
|
-
<div v-if="groupedReasoning.length > 0" flex="~ col" w-full
|
|
27
|
+
<div v-if="groupedReasoning.length > 0" flex="~ col" w-full>
|
|
28
28
|
<ChatReasoningMessage
|
|
29
29
|
v-for="(step, index) in groupedReasoning"
|
|
30
30
|
:key="getStepKey(step, index)"
|