@meistrari/chat-nuxt 3.17.0-rc.6 → 3.17.0-rc.7

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/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "3.17.0-rc.6",
4
+ "version": "3.17.0-rc.7",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -219,7 +219,7 @@ function persistConversationModel(conversation, value, wakeAgent) {
219
219
  const update = updateConversationModel(conversation.id, value).then(async (updated) => {
220
220
  if (!updated && currentConversation.value?.id === conversation.id && currentConversation.value.model === value) {
221
221
  setConversationModel(previousModel);
222
- } else if (updated && value && wakeAgent) {
222
+ } else if (updated && value && wakeAgent && !chatApi.conversationV2Enabled) {
223
223
  if (conversation.threadSessionId)
224
224
  await agentWake.ensureReady(conversation.threadSessionId, value);
225
225
  else
@@ -325,6 +325,10 @@ function updateConversationId(nextConversationId) {
325
325
  }
326
326
  async function ensureConversationLoaded(conversationId) {
327
327
  if (currentConversation.value?.id === conversationId) {
328
+ if (chatApi.conversationV2Enabled) {
329
+ startPolling();
330
+ return;
331
+ }
328
332
  if (currentConversation.value.threadSessionId)
329
333
  void agentWake.ensureReady(currentConversation.value.threadSessionId, selectedModel.value);
330
334
  else
@@ -346,6 +350,10 @@ async function ensureConversationLoaded(conversationId) {
346
350
  });
347
351
  return;
348
352
  }
353
+ if (chatApi.conversationV2Enabled) {
354
+ startPolling();
355
+ return;
356
+ }
349
357
  if (currentConversation.value.threadSessionId)
350
358
  void agentWake.ensureReady(currentConversation.value.threadSessionId, selectedModel.value);
351
359
  else
@@ -360,7 +368,8 @@ async function ensureConversationLoaded(conversationId) {
360
368
  function resetConversation(shouldEmit = true) {
361
369
  clearConversation();
362
370
  agentWake.consume();
363
- void agentWake.wake(void 0, selectedModel.value);
371
+ if (!chatApi.conversationV2Enabled)
372
+ void agentWake.wake(void 0, selectedModel.value);
364
373
  activeTab.value = "chat";
365
374
  closeFilesPanel();
366
375
  closePreview();
@@ -549,6 +558,8 @@ async function handleSend(content, files, agentInputs) {
549
558
  selectedModel.value,
550
559
  (value) => persistConversationModel(conversation, value, false)
551
560
  );
561
+ if (chatApi.conversationV2Enabled)
562
+ return {};
552
563
  if (conversation.threadSessionId && resolvedFeatures.value.chatOutboundQueue) {
553
564
  const wake = await agentWake.ensureReady(conversation.threadSessionId, selectedModel.value);
554
565
  if (wake.status !== "ready") {
@@ -42,6 +42,8 @@ export function resolveStreamingContent(authoritative, provisional) {
42
42
  }
43
43
  export function useChat(options = {}) {
44
44
  const chatApi = useChatApi();
45
+ const runtimeBlocked = ref(false);
46
+ let streamConnecting = false;
45
47
  const chatMessageTerminal = useChatMessageTerminal();
46
48
  const embedConfig = useEmbedConfig();
47
49
  const { user: authUser, activeOrganization } = useChatAuth();
@@ -152,7 +154,7 @@ export function useChat(options = {}) {
152
154
  const pendingStreamReconciliationIds = ref(/* @__PURE__ */ new Set());
153
155
  const pendingRealResultIds = ref(/* @__PURE__ */ new Set());
154
156
  const composerLocked = computed(
155
- () => hasPendingMessage.value || pendingRealResultIds.value.size > 0 || outbound.occupied.value
157
+ () => hasPendingMessage.value || runtimeBlocked.value || pendingRealResultIds.value.size > 0 || outbound.occupied.value
156
158
  );
157
159
  const queuedMessageContent = computed(() => outbound.slot.value?.content ?? null);
158
160
  let requestDurableRecovery = () => {
@@ -213,8 +215,24 @@ export function useChat(options = {}) {
213
215
  return resolveReasoningDisplayMessages(overlaid, reasoningCache);
214
216
  });
215
217
  const stopCondition = computed(
216
- () => !hasPendingMessage.value && pendingStreamReconciliationIds.value.size === 0
218
+ () => !runtimeBlocked.value && !hasPendingMessage.value && pendingStreamReconciliationIds.value.size === 0
217
219
  );
220
+ async function attachConversationStream(conversationId) {
221
+ if (streamConnecting || directStream.activity.value !== "idle")
222
+ return;
223
+ streamConnecting = true;
224
+ try {
225
+ await directStream.connectConversation(
226
+ chatApi.runtimePath(`/conversations/${conversationId}/stream`),
227
+ chatApi.withChatHeaders(),
228
+ streamingMessageId.value ?? void 0
229
+ );
230
+ } catch (err) {
231
+ Sentry.captureException(err);
232
+ } finally {
233
+ streamConnecting = false;
234
+ }
235
+ }
218
236
  let pollGeneration = 0;
219
237
  const pollMessages = async () => {
220
238
  if (!currentConversation.value)
@@ -222,13 +240,20 @@ export function useChat(options = {}) {
222
240
  if (gateHolding.value && !outboundQueueEnabled.value)
223
241
  return;
224
242
  const generation = pollGeneration;
243
+ const conversationId = currentConversation.value.id;
225
244
  try {
245
+ const runtime = chatApi.conversationV2Enabled ? await $fetch(
246
+ chatApi.runtimePath(`/conversations/${conversationId}/runtime`),
247
+ chatApi.withChatHeaders()
248
+ ) : null;
226
249
  const response = await $fetch(
227
- chatApi.path(`/conversations/${currentConversation.value.id}/messages`),
250
+ chatApi.path(`/conversations/${conversationId}/messages`),
228
251
  chatApi.withChatHeaders()
229
252
  );
230
- if (generation !== pollGeneration)
253
+ if (generation !== pollGeneration || currentConversation.value?.id !== conversationId)
231
254
  return;
255
+ if (runtime)
256
+ runtimeBlocked.value = runtime.conversation.runtimeState === "blocked";
232
257
  if (response.agentSession) {
233
258
  agentSessionStatus.value = response.agentSession.status;
234
259
  agentSessionExecutable.value = response.agentSession.executable;
@@ -261,6 +286,10 @@ export function useChat(options = {}) {
261
286
  pendingStreamReconciliationIds.value = remainingReconciliationIds;
262
287
  pendingRealResultIds.value = remainingRealResultIds;
263
288
  messages.value = reconciledMessages;
289
+ if (runtime?.turn && runtimeBlocked.value && reconciledMessages.some((message) => message.id === runtime.turn?.id)) {
290
+ streamingMessageId.value = runtime.turn.id;
291
+ void attachConversationStream(conversationId);
292
+ }
264
293
  const latestSessionId = reconciledMessages.findLast((message) => message.agentSessionId)?.agentSessionId;
265
294
  if (latestSessionId && currentConversation.value.threadSessionId === null) {
266
295
  currentConversation.value = {
@@ -330,7 +359,8 @@ export function useChat(options = {}) {
330
359
  }
331
360
  requestDurableRecovery = () => {
332
361
  startPolling();
333
- void pollMessages();
362
+ if (!chatApi.conversationV2Enabled)
363
+ void pollMessages();
334
364
  };
335
365
  const pendingModelBuckets = useState("chat-pending-model-selection", () => ({}));
336
366
  const pendingModelSelection = computed({
@@ -371,6 +401,7 @@ export function useChat(options = {}) {
371
401
  currentConversation.value = { ...conv, messages: messages.value };
372
402
  };
373
403
  const setConversation = (conv) => {
404
+ runtimeBlocked.value = false;
374
405
  if (currentConversation.value && currentConversation.value.id !== conv.id) {
375
406
  directStream.disconnect();
376
407
  streamingMessageId.value = null;
@@ -386,6 +417,11 @@ export function useChat(options = {}) {
386
417
  error.value = null;
387
418
  };
388
419
  const loadConversation = async (id) => {
420
+ if (chatApi.conversationV2Enabled) {
421
+ stopPolling();
422
+ directStream.disconnect();
423
+ }
424
+ runtimeBlocked.value = false;
389
425
  loading.value = true;
390
426
  error.value = null;
391
427
  try {
@@ -404,6 +440,8 @@ export function useChat(options = {}) {
404
440
  streamingMessageId.value = pendingAssistant.id;
405
441
  directStream.beginMessage();
406
442
  startPolling();
443
+ if (chatApi.conversationV2Enabled)
444
+ await pollMessages();
407
445
  } else {
408
446
  streamingMessageId.value = null;
409
447
  directStream.disconnect();
@@ -423,7 +461,7 @@ export function useChat(options = {}) {
423
461
  return null;
424
462
  if (outboundQueueEnabled.value && outbound.occupied.value && !queuedSend)
425
463
  return null;
426
- if (!queuedSend && (sending.value || hasPendingMessage.value || pendingRealResultIds.value.size > 0))
464
+ if (!queuedSend && (sending.value || runtimeBlocked.value || hasPendingMessage.value || pendingRealResultIds.value.size > 0))
427
465
  return null;
428
466
  suspendPollingForSend();
429
467
  sending.value = true;
@@ -468,6 +506,7 @@ export function useChat(options = {}) {
468
506
  messages.value = [...messages.value, optimisticUserMessage, optimisticAssistantMessage];
469
507
  const controller = new AbortController();
470
508
  gateController = controller;
509
+ let v2AdmissionRequested = false;
471
510
  try {
472
511
  let wakeSessionId = queuedSend?.wakeSessionId;
473
512
  let defer = false;
@@ -486,7 +525,7 @@ export function useChat(options = {}) {
486
525
  if (!currentConversation.value)
487
526
  throw new Error("Erro ao enviar mensagem");
488
527
  const sessionReady2 = currentConversation.value.threadSessionId ? agentSessionStatus.value === "waiting_messages" && agentSessionExecutable.value : null;
489
- if (outboundQueueEnabled.value && !queuedSend && (defer || sessionReady2 === false)) {
528
+ if (!chatApi.conversationV2Enabled && outboundQueueEnabled.value && !queuedSend && (defer || sessionReady2 === false)) {
490
529
  outbound.enqueue({
491
530
  content,
492
531
  files,
@@ -530,29 +569,52 @@ export function useChat(options = {}) {
530
569
  startPolling();
531
570
  };
532
571
  let response;
533
- try {
534
- response = await directStream.connectMessage(
535
- chatApi.path(`/conversations/${currentConversation.value.id}/messages/stream`),
536
- chatApi.withChatHeaders({
537
- method: "POST",
538
- headers: { "Content-Type": "application/json" },
539
- body: JSON.stringify(body)
540
- }),
541
- acceptMessagePair
572
+ if (chatApi.conversationV2Enabled) {
573
+ const v2Body = {
574
+ messageId: optimisticUserMessage.id,
575
+ content,
576
+ files,
577
+ ...resolvedAgentInputs !== void 0 ? { agentInputs: resolvedAgentInputs } : {},
578
+ clientTiming: body.clientTiming
579
+ };
580
+ const conversationId2 = currentConversation.value.id;
581
+ v2AdmissionRequested = true;
582
+ const accepted = await $fetch(
583
+ chatApi.runtimePath(`/conversations/${conversationId2}/messages`),
584
+ chatApi.withChatHeaders({ method: "POST", body: v2Body, retry: 1 })
542
585
  );
543
- } catch (streamError) {
544
- const statusCode = getChatApiErrorStatus(streamError);
545
- if (statusCode !== null && statusCode >= 400 && statusCode < 500)
546
- throw streamError;
547
- if (outboundQueueEnabled.value) {
548
- startPolling();
549
- return optimisticUserMessage;
586
+ response = accepted;
587
+ if (currentConversation.value?.id !== conversationId2)
588
+ return accepted.userMessage;
589
+ runtimeBlocked.value = accepted.conversation.runtimeState === "blocked";
590
+ acceptMessagePair(accepted);
591
+ if (runtimeBlocked.value)
592
+ void attachConversationStream(conversationId2);
593
+ } else {
594
+ try {
595
+ response = await directStream.connectMessage(
596
+ chatApi.path(`/conversations/${currentConversation.value.id}/messages/stream`),
597
+ chatApi.withChatHeaders({
598
+ method: "POST",
599
+ headers: { "Content-Type": "application/json" },
600
+ body: JSON.stringify(body)
601
+ }),
602
+ acceptMessagePair
603
+ );
604
+ } catch (streamError) {
605
+ const statusCode = getChatApiErrorStatus(streamError);
606
+ if (statusCode !== null && statusCode >= 400 && statusCode < 500)
607
+ throw streamError;
608
+ if (outboundQueueEnabled.value) {
609
+ startPolling();
610
+ return optimisticUserMessage;
611
+ }
612
+ response = await $fetch(
613
+ chatApi.path(`/conversations/${currentConversation.value.id}/messages`),
614
+ chatApi.withChatHeaders({ method: "POST", body })
615
+ );
616
+ acceptMessagePair(response);
550
617
  }
551
- response = await $fetch(
552
- chatApi.path(`/conversations/${currentConversation.value.id}/messages`),
553
- chatApi.withChatHeaders({ method: "POST", body })
554
- );
555
- acceptMessagePair(response);
556
618
  }
557
619
  if (files && files.length > 0) {
558
620
  addFilesToConversation(
@@ -565,9 +627,15 @@ export function useChat(options = {}) {
565
627
  return response.userMessage;
566
628
  } catch (err) {
567
629
  const code = getChatApiErrorCode(err);
630
+ const status = getChatApiErrorStatus(err);
631
+ if (v2AdmissionRequested && !controller.signal.aborted && (status === null || status === 408 || status === 429 || status >= 500)) {
632
+ error.value = "Confirma\xE7\xE3o do envio pendente. Reconectando \xE0 conversa\u2026";
633
+ startPolling();
634
+ return optimisticUserMessage;
635
+ }
568
636
  const queuedAttempts = (queuedSend?.attempts ?? 0) + 1;
569
637
  const queuedAgeMs = Date.now() - (queuedSend?.queuedAt ?? Date.now());
570
- if (outboundQueueEnabled.value && (code === "turn_in_progress" || code === "sandbox_not_woken") && queuedAttempts < 3 && queuedAgeMs <= 9e4) {
638
+ if (!chatApi.conversationV2Enabled && outboundQueueEnabled.value && (code === "turn_in_progress" || code === "sandbox_not_woken") && queuedAttempts < 3 && queuedAgeMs <= 9e4) {
571
639
  outbound.replace({
572
640
  content,
573
641
  files,
@@ -648,6 +716,7 @@ export function useChat(options = {}) {
648
716
  void flushOutboundQueue();
649
717
  });
650
718
  const clearConversation = () => {
719
+ runtimeBlocked.value = false;
651
720
  stopPolling();
652
721
  gateController?.abort();
653
722
  streamingMessageId.value = null;
@@ -682,11 +751,30 @@ export function useChat(options = {}) {
682
751
  gateController?.abort();
683
752
  return true;
684
753
  }
685
- if (!currentConversation.value || !hasPendingMessage.value)
754
+ if (!currentConversation.value || !hasPendingMessage.value || chatApi.conversationV2Enabled && sending.value)
686
755
  return false;
687
756
  cancelling.value = true;
688
757
  error.value = null;
689
758
  try {
759
+ if (chatApi.conversationV2Enabled) {
760
+ const conversationId = currentConversation.value.id;
761
+ const runtime = await $fetch(
762
+ chatApi.runtimePath(`/conversations/${conversationId}/runtime`),
763
+ chatApi.withChatHeaders()
764
+ );
765
+ if (currentConversation.value?.id !== conversationId)
766
+ return false;
767
+ const pending = messages.value.findLast((message) => message.role === "assistant" && message.status === "pending");
768
+ if (runtime.turn?.id === pending?.id && runtime.turn?.state !== "failed") {
769
+ const result2 = await $fetch(
770
+ chatApi.runtimePath(`/conversations/${conversationId}/cancel`),
771
+ chatApi.withChatHeaders({ method: "POST" })
772
+ );
773
+ if (currentConversation.value?.id === conversationId)
774
+ await pollMessages();
775
+ return result2.cancelled;
776
+ }
777
+ }
690
778
  const result = await $fetch(
691
779
  chatApi.path(`/conversations/${currentConversation.value.id}/cancel`),
692
780
  chatApi.withChatHeaders({ method: "POST" })
@@ -43,6 +43,7 @@ export declare function useDirectStream(options?: {
43
43
  streamingThinking: any;
44
44
  activeRunId: any;
45
45
  connectMessage: (url: string, init: RequestInit, onAccepted: (response: SendMessageResponse) => void) => Promise<SendMessageResponse>;
46
+ connectConversation: (url: string, init: RequestInit, expectedAssistantMessageId?: string) => Promise<void>;
46
47
  beginMessage: () => void;
47
48
  disconnect: (reset?: boolean) => void;
48
49
  };
@@ -137,7 +137,7 @@ export function useDirectStream(options = {}) {
137
137
  if (reset)
138
138
  resetAccumulator();
139
139
  }
140
- async function connectMessage(url, init, onAccepted) {
140
+ async function connectStream(url, init, onAccepted, expectedAssistantMessageId) {
141
141
  if (import.meta.server)
142
142
  throw new Error("Message streaming is unavailable during server rendering");
143
143
  abortController?.abort();
@@ -170,6 +170,8 @@ export function useDirectStream(options = {}) {
170
170
  abortController = null;
171
171
  throw new Error("Message stream response has no body");
172
172
  }
173
+ if (controller.signal.aborted)
174
+ throw new DOMException("Aborted", "AbortError");
173
175
  activity.value = "connected";
174
176
  let resolveAccepted;
175
177
  let rejectAccepted;
@@ -177,7 +179,9 @@ export function useDirectStream(options = {}) {
177
179
  resolveAccepted = resolve;
178
180
  rejectAccepted = reject;
179
181
  });
180
- let accepted = false;
182
+ let accepted = !onAccepted;
183
+ if (accepted)
184
+ resolveAccepted();
181
185
  let optimisticResultReported = false;
182
186
  let terminalEventObserved = false;
183
187
  let terminalReported = false;
@@ -244,6 +248,8 @@ export function useDirectStream(options = {}) {
244
248
  if (frame.data === "[DONE]")
245
249
  continue;
246
250
  if (frame.event === "message-accepted") {
251
+ if (!onAccepted)
252
+ continue;
247
253
  try {
248
254
  const messagePair = reviveMessagePair(JSON.parse(frame.data));
249
255
  accepted = true;
@@ -262,6 +268,11 @@ export function useDirectStream(options = {}) {
262
268
  }
263
269
  if (!isRecord(event) || typeof event.kind !== "string")
264
270
  continue;
271
+ if (event.kind === "conversation-state" && expectedAssistantMessageId && event.assistantMessageId !== expectedAssistantMessageId) {
272
+ controller.abort();
273
+ requestRecovery();
274
+ break;
275
+ }
265
276
  activity.value = "active";
266
277
  if (event.kind === "result") {
267
278
  settleResult(event.status);
@@ -301,8 +312,11 @@ export function useDirectStream(options = {}) {
301
312
  if (!accepted)
302
313
  rejectAccepted(error);
303
314
  } finally {
304
- if (abortController === controller)
315
+ if (abortController === controller) {
305
316
  abortController = null;
317
+ if (!onAccepted)
318
+ activity.value = "idle";
319
+ }
306
320
  if (accepted && !terminalEventObserved && !controller.signal.aborted)
307
321
  requestRecovery();
308
322
  }
@@ -315,7 +329,8 @@ export function useDirectStream(options = {}) {
315
329
  streamingText: readonly(streamingText),
316
330
  streamingThinking: readonly(streamingThinking),
317
331
  activeRunId: readonly(activeRunId),
318
- connectMessage,
332
+ connectMessage: (url, init, onAccepted) => connectStream(url, init, onAccepted),
333
+ connectConversation: (url, init, expectedAssistantMessageId) => connectStream(url, { ...init, method: "GET" }, void 0, expectedAssistantMessageId),
319
334
  beginMessage,
320
335
  disconnect
321
336
  };
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ import { defineChatApiProxyHandler, prefixProxyPath } from "../../../utils/chat-api-proxy.js";
2
+ export default defineChatApiProxyHandler((path) => prefixProxyPath("conversations", path), { apiVersion: "v2" });
@@ -1,6 +1,12 @@
1
1
  import type { H3Event } from 'h3';
2
+ export type ChatApiVersion = 'v1' | 'v2';
3
+ export declare function resolveChatBasePath(basePath: string, apiVersion: ChatApiVersion): string;
2
4
  export declare function mapVaultProxyPath(path: string): string;
3
5
  export declare function mapWorkspaceProxyPath(path: string): string;
4
6
  export declare function prefixProxyPath(prefix: string, path: string): string;
5
- export declare function proxyChatApiRequest(event: H3Event, targetPath: string): Promise<any>;
6
- export declare function defineChatApiProxyHandler(resolveTargetPath: (path: string) => string): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
7
+ export declare function proxyChatApiRequest(event: H3Event, targetPath: string, options?: {
8
+ apiVersion?: ChatApiVersion;
9
+ }): Promise<any>;
10
+ export declare function defineChatApiProxyHandler(resolveTargetPath: (path: string) => string, options?: {
11
+ apiVersion?: ChatApiVersion;
12
+ }): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
@@ -1,4 +1,4 @@
1
- import { AGENT_API_V5_CONFIG_KEY, PREPARED_AGENT_RUNTIME_CONFIG_KEY, SECRETLESS_EXECUTION_CONFIG_KEY } from "../../types/schemas/chat/chat-config.js";
1
+ import { AGENT_API_V5_CONFIG_KEY, CONVERSATION_V2_CONFIG_KEY, PREPARED_AGENT_RUNTIME_CONFIG_KEY, SECRETLESS_EXECUTION_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";
@@ -43,10 +43,20 @@ function resolveChatApiUrl(event) {
43
43
  }
44
44
  return chatApiUrl;
45
45
  }
46
- function buildTargetUrl(event, targetPath) {
46
+ export function resolveChatBasePath(basePath, apiVersion) {
47
+ const versionSuffix = `${apiVersion}/chat`;
48
+ if (basePath.endsWith("v1/chat")) {
49
+ return apiVersion === "v1" ? basePath : joinProxyPath(basePath.slice(0, -"v1/chat".length), versionSuffix);
50
+ }
51
+ if (basePath.endsWith("v1")) {
52
+ return apiVersion === "v1" ? joinProxyPath(basePath, "chat") : joinProxyPath(basePath.slice(0, -"v1".length), versionSuffix);
53
+ }
54
+ return joinProxyPath(basePath, versionSuffix);
55
+ }
56
+ function buildTargetUrl(event, targetPath, apiVersion = "v1") {
47
57
  const base = new URL(resolveChatApiUrl(event));
48
58
  const basePath = normalizeProxyPath(base.pathname);
49
- const chatBasePath = basePath.endsWith("v1/chat") ? basePath : basePath.endsWith("v1") ? joinProxyPath(basePath, "chat") : joinProxyPath(basePath, "v1/chat");
59
+ const chatBasePath = resolveChatBasePath(basePath, apiVersion);
50
60
  base.pathname = `/${joinProxyPath(chatBasePath, targetPath)}`;
51
61
  base.search = getRequestURL(event).search;
52
62
  return base.toString();
@@ -60,7 +70,10 @@ function buildProxyHeaders(event) {
60
70
  });
61
71
  }
62
72
  const config = useRuntimeConfig(event);
63
- const chatConfig = { [PREPARED_AGENT_RUNTIME_CONFIG_KEY]: true };
73
+ const chatConfig = {
74
+ [PREPARED_AGENT_RUNTIME_CONFIG_KEY]: true,
75
+ [CONVERSATION_V2_CONFIG_KEY]: true
76
+ };
64
77
  if (config.chatNuxt?.secretlessExecution) {
65
78
  chatConfig[SECRETLESS_EXECUTION_CONFIG_KEY] = true;
66
79
  }
@@ -110,8 +123,8 @@ export function mapWorkspaceProxyPath(path) {
110
123
  export function prefixProxyPath(prefix, path) {
111
124
  return joinProxyPath(prefix, path);
112
125
  }
113
- export async function proxyChatApiRequest(event, targetPath) {
114
- return proxyRequest(event, buildTargetUrl(event, targetPath), {
126
+ export async function proxyChatApiRequest(event, targetPath, options = {}) {
127
+ return proxyRequest(event, buildTargetUrl(event, targetPath, options.apiVersion ?? "v1"), {
115
128
  fetchOptions: {
116
129
  redirect: "manual"
117
130
  },
@@ -119,9 +132,9 @@ export async function proxyChatApiRequest(event, targetPath) {
119
132
  streamRequest: true
120
133
  });
121
134
  }
122
- export function defineChatApiProxyHandler(resolveTargetPath) {
135
+ export function defineChatApiProxyHandler(resolveTargetPath, options = {}) {
123
136
  return defineEventHandler((event) => {
124
137
  const path = getRouterParam(event, "path") || "";
125
- return proxyChatApiRequest(event, resolveTargetPath(path));
138
+ return proxyChatApiRequest(event, resolveTargetPath(path), options);
126
139
  });
127
140
  }
@@ -1,5 +1,7 @@
1
1
  export declare function useChatApi(): {
2
2
  path: (routePath: string) => string;
3
+ runtimePath: (routePath: string) => string;
4
+ conversationV2Enabled: boolean;
3
5
  withChatHeaders: <const T extends Record<string, any>>(options?: T) => T & {
4
6
  headers?: Record<string, string>;
5
7
  };
@@ -38,6 +38,8 @@ export function useChatApi() {
38
38
  }
39
39
  return {
40
40
  path,
41
+ runtimePath: (routePath) => `/api/v2${routePath}`,
42
+ conversationV2Enabled: true,
41
43
  withChatHeaders
42
44
  };
43
45
  }
@@ -1,3 +1,4 @@
1
1
  export declare const PREPARED_AGENT_RUNTIME_CONFIG_KEY = "prepared-agent-runtime";
2
2
  export declare const SECRETLESS_EXECUTION_CONFIG_KEY = "secretless-execution";
3
3
  export declare const AGENT_API_V5_CONFIG_KEY = "agent-api-v5";
4
+ export declare const CONVERSATION_V2_CONFIG_KEY = "conversation-v2";
@@ -1,3 +1,4 @@
1
1
  export const PREPARED_AGENT_RUNTIME_CONFIG_KEY = 'prepared-agent-runtime';
2
2
  export const SECRETLESS_EXECUTION_CONFIG_KEY = 'secretless-execution';
3
3
  export const AGENT_API_V5_CONFIG_KEY = 'agent-api-v5';
4
+ export const CONVERSATION_V2_CONFIG_KEY = 'conversation-v2';
@@ -0,0 +1,239 @@
1
+ import { z } from 'zod';
2
+ export declare const conversationRuntimeStateSchema: z.ZodEnum<{
3
+ waiting_messages: "waiting_messages";
4
+ blocked: "blocked";
5
+ }>;
6
+ export type ConversationRuntimeState = z.infer<typeof conversationRuntimeStateSchema>;
7
+ export declare const turnRuntimeStateNameSchema: z.ZodEnum<{
8
+ running: "running";
9
+ completed: "completed";
10
+ failed: "failed";
11
+ cancelled: "cancelled";
12
+ accepted: "accepted";
13
+ checking_session: "checking_session";
14
+ waiting_for_wake: "waiting_for_wake";
15
+ dispatching: "dispatching";
16
+ streaming: "streaming";
17
+ }>;
18
+ export type TurnRuntimeStateName = z.infer<typeof turnRuntimeStateNameSchema>;
19
+ export declare const conversationWakeStateSchema: z.ZodEnum<{
20
+ failed: "failed";
21
+ waking: "waking";
22
+ parked: "parked";
23
+ ready: "ready";
24
+ stale: "stale";
25
+ checking: "checking";
26
+ }>;
27
+ export type ConversationWakeState = z.infer<typeof conversationWakeStateSchema>;
28
+ export declare const wakeConversationResponseSchema: z.ZodObject<{
29
+ state: z.ZodEnum<{
30
+ failed: "failed";
31
+ waking: "waking";
32
+ parked: "parked";
33
+ ready: "ready";
34
+ stale: "stale";
35
+ checking: "checking";
36
+ }>;
37
+ sessionId: z.ZodNullable<z.ZodString>;
38
+ error: z.ZodNullable<z.ZodString>;
39
+ }, z.core.$strip>;
40
+ export type WakeConversationResponse = z.infer<typeof wakeConversationResponseSchema>;
41
+ export declare const turnSnapshotSchema: z.ZodObject<{
42
+ id: z.ZodString;
43
+ state: z.ZodEnum<{
44
+ running: "running";
45
+ completed: "completed";
46
+ failed: "failed";
47
+ cancelled: "cancelled";
48
+ accepted: "accepted";
49
+ checking_session: "checking_session";
50
+ waiting_for_wake: "waiting_for_wake";
51
+ dispatching: "dispatching";
52
+ streaming: "streaming";
53
+ }>;
54
+ sessionId: z.ZodNullable<z.ZodString>;
55
+ error: z.ZodNullable<z.ZodString>;
56
+ }, z.core.$strip>;
57
+ export type TurnSnapshot = z.infer<typeof turnSnapshotSchema>;
58
+ export declare const sendMessageV2BodySchema: z.ZodObject<{
59
+ messageId: z.ZodString;
60
+ content: z.ZodString;
61
+ files: z.ZodOptional<z.ZodArray<z.ZodObject<{
62
+ url: z.ZodString;
63
+ name: z.ZodString;
64
+ mimeType: z.ZodOptional<z.ZodString>;
65
+ vaultReference: z.ZodOptional<z.ZodString>;
66
+ size: z.ZodOptional<z.ZodNumber>;
67
+ }, z.core.$strip>>>;
68
+ agentInputs: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
69
+ type: z.ZodLiteral<"file">;
70
+ name: z.ZodString;
71
+ vaultRef: z.ZodString;
72
+ filename: z.ZodString;
73
+ metadata: z.ZodOptional<z.ZodString>;
74
+ }, z.core.$strip>, z.ZodObject<{
75
+ type: z.ZodLiteral<"text">;
76
+ name: z.ZodString;
77
+ content: z.ZodString;
78
+ }, z.core.$strip>], "type">>>;
79
+ clientTiming: z.ZodOptional<z.ZodObject<{
80
+ sinceSendMs: z.ZodNumber;
81
+ standInOutputAtMs: z.ZodOptional<z.ZodNumber>;
82
+ }, z.core.$strict>>;
83
+ }, z.core.$strict>;
84
+ export type SendMessageV2Body = z.infer<typeof sendMessageV2BodySchema>;
85
+ export declare const sendMessageV2ResponseSchema: z.ZodObject<{
86
+ userMessage: z.ZodObject<{
87
+ id: z.ZodString;
88
+ conversationId: z.ZodString;
89
+ role: z.ZodEnum<{
90
+ user: "user";
91
+ assistant: "assistant";
92
+ }>;
93
+ content: z.ZodString;
94
+ status: z.ZodNullable<z.ZodEnum<{
95
+ pending: "pending";
96
+ completed: "completed";
97
+ failed: "failed";
98
+ }>>;
99
+ failReason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
100
+ reasoningData: z.ZodUnknown;
101
+ files: z.ZodNullable<z.ZodArray<z.ZodObject<{
102
+ url: z.ZodString;
103
+ vaultReference: z.ZodOptional<z.ZodString>;
104
+ name: z.ZodString;
105
+ mimeType: z.ZodOptional<z.ZodString>;
106
+ source: z.ZodEnum<{
107
+ user: "user";
108
+ assistant: "assistant";
109
+ }>;
110
+ size: z.ZodOptional<z.ZodNumber>;
111
+ }, z.core.$strip>>>;
112
+ externalUuid: z.ZodNullable<z.ZodString>;
113
+ ttft: z.ZodNullable<z.ZodString>;
114
+ createdBy: z.ZodNullable<z.ZodString>;
115
+ createdAt: z.ZodDate;
116
+ updatedAt: z.ZodNullable<z.ZodDate>;
117
+ }, z.core.$strip>;
118
+ assistantMessage: z.ZodObject<{
119
+ id: z.ZodString;
120
+ conversationId: z.ZodString;
121
+ role: z.ZodEnum<{
122
+ user: "user";
123
+ assistant: "assistant";
124
+ }>;
125
+ content: z.ZodString;
126
+ status: z.ZodNullable<z.ZodEnum<{
127
+ pending: "pending";
128
+ completed: "completed";
129
+ failed: "failed";
130
+ }>>;
131
+ failReason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
132
+ reasoningData: z.ZodUnknown;
133
+ files: z.ZodNullable<z.ZodArray<z.ZodObject<{
134
+ url: z.ZodString;
135
+ vaultReference: z.ZodOptional<z.ZodString>;
136
+ name: z.ZodString;
137
+ mimeType: z.ZodOptional<z.ZodString>;
138
+ source: z.ZodEnum<{
139
+ user: "user";
140
+ assistant: "assistant";
141
+ }>;
142
+ size: z.ZodOptional<z.ZodNumber>;
143
+ }, z.core.$strip>>>;
144
+ externalUuid: z.ZodNullable<z.ZodString>;
145
+ ttft: z.ZodNullable<z.ZodString>;
146
+ createdBy: z.ZodNullable<z.ZodString>;
147
+ createdAt: z.ZodDate;
148
+ updatedAt: z.ZodNullable<z.ZodDate>;
149
+ }, z.core.$strip>;
150
+ turn: z.ZodNullable<z.ZodObject<{
151
+ id: z.ZodString;
152
+ state: z.ZodEnum<{
153
+ running: "running";
154
+ completed: "completed";
155
+ failed: "failed";
156
+ cancelled: "cancelled";
157
+ accepted: "accepted";
158
+ checking_session: "checking_session";
159
+ waiting_for_wake: "waiting_for_wake";
160
+ dispatching: "dispatching";
161
+ streaming: "streaming";
162
+ }>;
163
+ sessionId: z.ZodNullable<z.ZodString>;
164
+ error: z.ZodNullable<z.ZodString>;
165
+ }, z.core.$strip>>;
166
+ conversation: z.ZodObject<{
167
+ runtimeState: z.ZodEnum<{
168
+ waiting_messages: "waiting_messages";
169
+ blocked: "blocked";
170
+ }>;
171
+ }, z.core.$strip>;
172
+ }, z.core.$strip>;
173
+ export type SendMessageV2Response = z.infer<typeof sendMessageV2ResponseSchema>;
174
+ export declare const conversationRuntimeV2ResponseSchema: z.ZodObject<{
175
+ conversation: z.ZodObject<{
176
+ runtimeState: z.ZodEnum<{
177
+ waiting_messages: "waiting_messages";
178
+ blocked: "blocked";
179
+ }>;
180
+ }, z.core.$strip>;
181
+ turn: z.ZodNullable<z.ZodObject<{
182
+ id: z.ZodString;
183
+ state: z.ZodEnum<{
184
+ running: "running";
185
+ completed: "completed";
186
+ failed: "failed";
187
+ cancelled: "cancelled";
188
+ accepted: "accepted";
189
+ checking_session: "checking_session";
190
+ waiting_for_wake: "waiting_for_wake";
191
+ dispatching: "dispatching";
192
+ streaming: "streaming";
193
+ }>;
194
+ sessionId: z.ZodNullable<z.ZodString>;
195
+ error: z.ZodNullable<z.ZodString>;
196
+ }, z.core.$strip>>;
197
+ }, z.core.$strip>;
198
+ export type ConversationRuntimeV2Response = z.infer<typeof conversationRuntimeV2ResponseSchema>;
199
+ export declare const cancelConversationV2ResponseSchema: z.ZodObject<{
200
+ success: z.ZodLiteral<true>;
201
+ cancelled: z.ZodBoolean;
202
+ turn: z.ZodNullable<z.ZodObject<{
203
+ id: z.ZodString;
204
+ state: z.ZodEnum<{
205
+ running: "running";
206
+ completed: "completed";
207
+ failed: "failed";
208
+ cancelled: "cancelled";
209
+ accepted: "accepted";
210
+ checking_session: "checking_session";
211
+ waiting_for_wake: "waiting_for_wake";
212
+ dispatching: "dispatching";
213
+ streaming: "streaming";
214
+ }>;
215
+ sessionId: z.ZodNullable<z.ZodString>;
216
+ error: z.ZodNullable<z.ZodString>;
217
+ }, z.core.$strip>>;
218
+ }, z.core.$strip>;
219
+ export type CancelConversationV2Response = z.infer<typeof cancelConversationV2ResponseSchema>;
220
+ export declare const conversationRuntimeWebhookPayloadSchema: z.ZodObject<{
221
+ eventType: z.ZodEnum<{
222
+ "session.started": "session.started";
223
+ "session.completed": "session.completed";
224
+ "session.failed": "session.failed";
225
+ "session.cancelled": "session.cancelled";
226
+ "session.waiting_messages": "session.waiting_messages";
227
+ }>;
228
+ sessionId: z.ZodString;
229
+ status: z.ZodOptional<z.ZodString>;
230
+ error: z.ZodOptional<z.ZodNullable<z.ZodString>>;
231
+ failReason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
232
+ result: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>>;
233
+ deliveryId: z.ZodOptional<z.ZodString>;
234
+ }, z.core.$catchall<z.ZodUnknown>>;
235
+ export type ConversationRuntimeWebhookPayload = z.infer<typeof conversationRuntimeWebhookPayloadSchema>;
236
+ export declare const conversationRuntimeWebhookResponseSchema: z.ZodObject<{
237
+ handled: z.ZodBoolean;
238
+ }, z.core.$strip>;
239
+ export type ConversationRuntimeWebhookResponse = z.infer<typeof conversationRuntimeWebhookResponseSchema>;
@@ -0,0 +1,69 @@
1
+ import { z } from 'zod';
2
+ import { telaAgentExecutionInputSchema } from '../integrations/tela-agent-api';
3
+ import { messageSchema } from './conversations';
4
+ import { fileUploadInputSchema } from './files';
5
+ import { sendMessageClientTimingSchema } from './messages';
6
+ export const conversationRuntimeStateSchema = z.enum(['blocked', 'waiting_messages']);
7
+ export const turnRuntimeStateNameSchema = z.enum([
8
+ 'accepted',
9
+ 'checking_session',
10
+ 'waiting_for_wake',
11
+ 'dispatching',
12
+ 'running',
13
+ 'streaming',
14
+ 'completed',
15
+ 'failed',
16
+ 'cancelled',
17
+ ]);
18
+ export const conversationWakeStateSchema = z.enum(['stale', 'checking', 'waking', 'ready', 'parked', 'failed']);
19
+ export const wakeConversationResponseSchema = z.object({
20
+ state: conversationWakeStateSchema,
21
+ sessionId: z.string().nullable(),
22
+ error: z.string().nullable(),
23
+ });
24
+ export const turnSnapshotSchema = z.object({
25
+ id: z.string(),
26
+ state: turnRuntimeStateNameSchema,
27
+ sessionId: z.string().nullable(),
28
+ error: z.string().nullable(),
29
+ });
30
+ export const sendMessageV2BodySchema = z.strictObject({
31
+ messageId: z.string().uuid(),
32
+ content: z.string(),
33
+ files: z.array(fileUploadInputSchema).optional(),
34
+ agentInputs: z.array(telaAgentExecutionInputSchema).optional(),
35
+ clientTiming: sendMessageClientTimingSchema.optional(),
36
+ });
37
+ export const sendMessageV2ResponseSchema = z.object({
38
+ userMessage: messageSchema,
39
+ assistantMessage: messageSchema,
40
+ turn: turnSnapshotSchema.nullable(),
41
+ conversation: z.object({ runtimeState: conversationRuntimeStateSchema }),
42
+ });
43
+ export const conversationRuntimeV2ResponseSchema = z.object({
44
+ conversation: z.object({ runtimeState: conversationRuntimeStateSchema }),
45
+ turn: turnSnapshotSchema.nullable(),
46
+ });
47
+ export const cancelConversationV2ResponseSchema = z.object({
48
+ success: z.literal(true),
49
+ cancelled: z.boolean(),
50
+ turn: turnSnapshotSchema.nullable(),
51
+ });
52
+ export const conversationRuntimeWebhookPayloadSchema = z.object({
53
+ eventType: z.enum([
54
+ 'session.started',
55
+ 'session.completed',
56
+ 'session.failed',
57
+ 'session.cancelled',
58
+ 'session.waiting_messages',
59
+ ]),
60
+ sessionId: z.string().min(1),
61
+ status: z.string().optional(),
62
+ error: z.string().nullable().optional(),
63
+ failReason: z.string().nullable().optional(),
64
+ result: z.union([z.string(), z.record(z.string(), z.unknown())]).nullable().optional(),
65
+ deliveryId: z.string().optional(),
66
+ }).catchall(z.unknown());
67
+ export const conversationRuntimeWebhookResponseSchema = z.object({
68
+ handled: z.boolean(),
69
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "3.17.0-rc.6",
3
+ "version": "3.17.0-rc.7",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -32,6 +32,8 @@
32
32
  "test:types": "pnpm build && tsc -p tsconfig.type-tests.json --noEmit",
33
33
  "smoke:pack": "./scripts/smoke-pack-consumer.sh",
34
34
  "smoke:browser": "./scripts/smoke-pack-consumer.sh --browser",
35
+ "smoke:browser:v1": "./scripts/smoke-pack-consumer.sh --browser-v1",
36
+ "smoke:browser:v2": "./scripts/smoke-pack-consumer.sh --browser-v2",
35
37
  "lint": "eslint .",
36
38
  "lint:fix": "eslint . --fix",
37
39
  "lint:attrs": "! grep -rn '[a-z0-9-]![[:space:]>@\"'\"'\"']' --include='*.vue' src/runtime/ 2>/dev/null",