@meistrari/chat-nuxt 3.12.0-rc.0 → 3.12.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/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "3.12.0-rc.0",
4
+ "version": "3.12.0-rc.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -286,6 +286,7 @@ async function ensureConversationLoaded(conversationId) {
286
286
  }
287
287
  function resetConversation(shouldEmit = true) {
288
288
  clearConversation();
289
+ agentWake.consume();
289
290
  void agentWake.wake(void 0, selectedModel.value);
290
291
  activeTab.value = "chat";
291
292
  closeFilesPanel();
@@ -481,8 +482,6 @@ async function handleSend(content, files, agentInputs) {
481
482
  await agentWake.waitUntilSettled(signal);
482
483
  if (agentWake.state.value === "idle")
483
484
  await agentWake.wake(void 0, selectedModel.value);
484
- else if (agentWake.state.value === "ready")
485
- await agentWake.wake(void 0, selectedModel.value);
486
485
  }
487
486
  claimedWakeSessionId = claimableWakeSessionId();
488
487
  if (!conversation.threadSessionId && !claimedWakeSessionId)
@@ -5,6 +5,7 @@ export type AgentWakeSnapshot = {
5
5
  };
6
6
  export declare function pollAgentWake(options: {
7
7
  request: () => Promise<TelaAgentWakeResponse>;
8
+ requestStatus?: (sessionId: string) => Promise<TelaAgentWakeResponse>;
8
9
  onResponse?: (response: TelaAgentWakeResponse) => void;
9
10
  signal?: AbortSignal;
10
11
  pollIntervalMs?: number;
@@ -22,9 +22,11 @@ export async function pollAgentWake(options) {
22
22
  signal?.addEventListener("abort", onAbort, { once: true });
23
23
  }));
24
24
  let unavailableAttempts = 0;
25
+ let wakeSessionId;
25
26
  while (!options.signal?.aborted && now() <= deadline) {
26
27
  try {
27
- const response = await options.request();
28
+ const response = wakeSessionId && options.requestStatus ? await options.requestStatus(wakeSessionId) : await options.request();
29
+ wakeSessionId = response.sessionId ?? wakeSessionId;
28
30
  options.onResponse?.(response);
29
31
  if (response.status === "unavailable" && unavailableAttempts < 2) {
30
32
  unavailableAttempts += 1;
@@ -107,6 +109,13 @@ export function useAgentWake() {
107
109
  }
108
110
  })
109
111
  ),
112
+ requestStatus: (sessionId2) => $fetch(
113
+ chatApi.path(`/agent/wake/${encodeURIComponent(sessionId2)}/status`),
114
+ chatApi.withChatHeaders({
115
+ method: "GET",
116
+ signal: controller.signal
117
+ })
118
+ ),
110
119
  onResponse: (response) => {
111
120
  if (currentGeneration !== generation)
112
121
  return;
@@ -129,18 +129,16 @@ export function useChat(options = {}) {
129
129
  return null;
130
130
  return agentSessionStatus.value === "waiting_messages" && agentSessionExecutable.value;
131
131
  });
132
- const composerLocked = computed(() => hasPendingMessage.value || outbound.occupied.value);
133
- const queuedMessageContent = computed(() => outbound.slot.value?.content ?? null);
134
132
  const streamingMessageId = ref(null);
135
133
  const pendingStreamReconciliationIds = ref(/* @__PURE__ */ new Set());
134
+ const pendingRealResultIds = ref(/* @__PURE__ */ new Set());
135
+ const composerLocked = computed(
136
+ () => hasPendingMessage.value || pendingRealResultIds.value.size > 0 || outbound.occupied.value
137
+ );
138
+ const queuedMessageContent = computed(() => outbound.slot.value?.content ?? null);
136
139
  let requestDurableRecovery = () => {
137
140
  };
138
141
  function optimisticallyCompleteStreamedMessage(result) {
139
- if (result.status === "waiting_messages") {
140
- agentSessionStatus.value = "waiting_messages";
141
- agentSessionExecutable.value = true;
142
- agentSessionRewakeable.value = false;
143
- }
144
142
  const messageId = streamingMessageId.value;
145
143
  if (!messageId)
146
144
  return;
@@ -153,6 +151,10 @@ export function useChat(options = {}) {
153
151
  ...pendingStreamReconciliationIds.value,
154
152
  messageId
155
153
  ]);
154
+ pendingRealResultIds.value = /* @__PURE__ */ new Set([
155
+ ...pendingRealResultIds.value,
156
+ messageId
157
+ ]);
156
158
  messages.value = messages.value.map((message) => message.id === messageId ? {
157
159
  ...message,
158
160
  content: resolveStreamingContent(message.content, result.text),
@@ -164,8 +166,22 @@ export function useChat(options = {}) {
164
166
  status: ChatMessageStatus.Completed
165
167
  });
166
168
  }
169
+ function confirmStreamedResult(result) {
170
+ if (result.status === "waiting_messages") {
171
+ agentSessionStatus.value = "waiting_messages";
172
+ agentSessionExecutable.value = true;
173
+ agentSessionRewakeable.value = false;
174
+ }
175
+ const messageId = streamingMessageId.value;
176
+ if (!messageId || !pendingRealResultIds.value.has(messageId))
177
+ return;
178
+ const remaining = new Set(pendingRealResultIds.value);
179
+ remaining.delete(messageId);
180
+ pendingRealResultIds.value = remaining;
181
+ }
167
182
  const directStream = useDirectStream({
168
- onResult: optimisticallyCompleteStreamedMessage,
183
+ onOptimisticResult: optimisticallyCompleteStreamedMessage,
184
+ onResult: confirmStreamedResult,
169
185
  onTerminal: () => requestDurableRecovery(),
170
186
  onDisconnect: () => requestDurableRecovery()
171
187
  });
@@ -207,16 +223,20 @@ export function useChat(options = {}) {
207
223
  return;
208
224
  const currentMessagesById = new Map(messages.value.map((message) => [message.id, message]));
209
225
  const remainingReconciliationIds = new Set(pendingStreamReconciliationIds.value);
226
+ const remainingRealResultIds = new Set(pendingRealResultIds.value);
210
227
  const durableMessageIds = new Set(response.messages.map((message) => message.id));
211
228
  for (const messageId of remainingReconciliationIds) {
212
- if (!durableMessageIds.has(messageId))
229
+ if (!durableMessageIds.has(messageId)) {
213
230
  remainingReconciliationIds.delete(messageId);
231
+ remainingRealResultIds.delete(messageId);
232
+ }
214
233
  }
215
234
  const reconciledMessages = response.messages.map((message) => {
216
235
  if (!remainingReconciliationIds.has(message.id))
217
236
  return message;
218
237
  if (message.status === ChatMessageStatus.Completed || message.status === ChatMessageStatus.Failed) {
219
238
  remainingReconciliationIds.delete(message.id);
239
+ remainingRealResultIds.delete(message.id);
220
240
  return message;
221
241
  }
222
242
  const optimisticMessage = currentMessagesById.get(message.id);
@@ -224,6 +244,7 @@ export function useChat(options = {}) {
224
244
  });
225
245
  const terminalMessages = terminalMessageTransitions(messages.value, reconciledMessages);
226
246
  pendingStreamReconciliationIds.value = remainingReconciliationIds;
247
+ pendingRealResultIds.value = remainingRealResultIds;
227
248
  messages.value = reconciledMessages;
228
249
  const latestSessionId = reconciledMessages.findLast((message) => message.agentSessionId)?.agentSessionId;
229
250
  if (latestSessionId && currentConversation.value.threadSessionId === null) {
@@ -265,6 +286,7 @@ export function useChat(options = {}) {
265
286
  });
266
287
  const terminalMessages = terminalMessageTransitions(previousMessages, failedMessages);
267
288
  pendingStreamReconciliationIds.value = /* @__PURE__ */ new Set();
289
+ pendingRealResultIds.value = /* @__PURE__ */ new Set();
268
290
  messages.value = failedMessages;
269
291
  for (const terminalMessage of terminalMessages)
270
292
  chatMessageTerminal.emit(terminalMessage);
@@ -286,6 +308,11 @@ export function useChat(options = {}) {
286
308
  pollGeneration++;
287
309
  endPolling();
288
310
  }
311
+ function suspendPollingForSend() {
312
+ pollGeneration++;
313
+ if (isPolling.value)
314
+ endPolling();
315
+ }
289
316
  requestDurableRecovery = () => {
290
317
  startPolling();
291
318
  void pollMessages();
@@ -322,6 +349,7 @@ export function useChat(options = {}) {
322
349
  currentConversation.value = { ...conv, messages: conversationMessages };
323
350
  messages.value = conversationMessages;
324
351
  pendingStreamReconciliationIds.value = /* @__PURE__ */ new Set();
352
+ pendingRealResultIds.value = /* @__PURE__ */ new Set();
325
353
  agentSessionStatus.value = null;
326
354
  agentSessionExecutable.value = false;
327
355
  agentSessionRewakeable.value = false;
@@ -340,6 +368,7 @@ export function useChat(options = {}) {
340
368
  currentConversation.value = data;
341
369
  messages.value = data.messages;
342
370
  pendingStreamReconciliationIds.value = /* @__PURE__ */ new Set();
371
+ pendingRealResultIds.value = /* @__PURE__ */ new Set();
343
372
  if (shouldTreatLoadedConversationTitleAsGenerated(data.messages)) {
344
373
  titleGenerated.value = true;
345
374
  }
@@ -367,6 +396,9 @@ export function useChat(options = {}) {
367
396
  return null;
368
397
  if (outboundQueueEnabled.value && outbound.occupied.value && !queuedSend)
369
398
  return null;
399
+ if (!queuedSend && (sending.value || hasPendingMessage.value || pendingRealResultIds.value.size > 0))
400
+ return null;
401
+ suspendPollingForSend();
370
402
  sending.value = true;
371
403
  error.value = null;
372
404
  sendStartedAt = Date.now();
@@ -463,6 +495,11 @@ export function useChat(options = {}) {
463
495
  return msg;
464
496
  });
465
497
  streamingMessageId.value = response2.assistantMessage.id;
498
+ if (currentConversation.value?.threadSessionId) {
499
+ agentSessionStatus.value = "running";
500
+ agentSessionExecutable.value = false;
501
+ agentSessionRewakeable.value = false;
502
+ }
466
503
  };
467
504
  let response;
468
505
  let acceptedByStream = false;
@@ -481,6 +518,10 @@ export function useChat(options = {}) {
481
518
  const statusCode = getChatApiErrorStatus(streamError);
482
519
  if (statusCode !== null && statusCode >= 400 && statusCode < 500)
483
520
  throw streamError;
521
+ if (outboundQueueEnabled.value) {
522
+ startPolling();
523
+ return optimisticUserMessage;
524
+ }
484
525
  response = await $fetch(
485
526
  chatApi.path(`/conversations/${currentConversation.value.id}/messages`),
486
527
  chatApi.withChatHeaders({ method: "POST", body })
@@ -592,6 +633,7 @@ export function useChat(options = {}) {
592
633
  currentConversation.value = null;
593
634
  messages.value = [];
594
635
  pendingStreamReconciliationIds.value = /* @__PURE__ */ new Set();
636
+ pendingRealResultIds.value = /* @__PURE__ */ new Set();
595
637
  error.value = null;
596
638
  agentSessionStatus.value = null;
597
639
  agentSessionExecutable.value = false;
@@ -660,6 +702,7 @@ export function useChat(options = {}) {
660
702
  };
661
703
  messages.value = [];
662
704
  pendingStreamReconciliationIds.value = /* @__PURE__ */ new Set();
705
+ pendingRealResultIds.value = /* @__PURE__ */ new Set();
663
706
  streamingMessageId.value = null;
664
707
  directStream.disconnect();
665
708
  return true;
@@ -33,6 +33,7 @@ export declare function applyMessagePartsEvent(accumulator: MessagePartsAccumula
33
33
  changed: boolean;
34
34
  };
35
35
  export declare function useDirectStream(options?: {
36
+ onOptimisticResult?: (result: DirectStreamResult) => void;
36
37
  onResult?: (result: DirectStreamResult) => void;
37
38
  onTerminal?: () => void;
38
39
  onDisconnect?: () => void;
@@ -178,18 +178,51 @@ export function useDirectStream(options = {}) {
178
178
  rejectAccepted = reject;
179
179
  });
180
180
  let accepted = false;
181
- let terminal = false;
182
- function settleTerminal(status) {
183
- if (terminal)
181
+ let optimisticResultReported = false;
182
+ let terminalEventObserved = false;
183
+ let terminalReported = false;
184
+ let recoveryRequested = false;
185
+ function requestRecovery() {
186
+ if (recoveryRequested)
184
187
  return;
185
- terminal = true;
188
+ recoveryRequested = true;
189
+ options.onDisconnect?.();
190
+ }
191
+ function reportTerminal() {
192
+ if (terminalReported)
193
+ return;
194
+ terminalReported = true;
195
+ recoveryRequested = true;
196
+ options.onTerminal?.();
197
+ }
198
+ function reportOptimisticResult(status) {
186
199
  if (status === "completed" || status === "waiting_messages") {
187
- options.onResult?.({
200
+ const result = {
188
201
  status,
189
202
  text: streamingText.value
190
- });
203
+ };
204
+ if (!optimisticResultReported) {
205
+ optimisticResultReported = true;
206
+ options.onOptimisticResult?.(result);
207
+ }
208
+ return result;
191
209
  }
192
- options.onTerminal?.();
210
+ return null;
211
+ }
212
+ function settleResult(status) {
213
+ if (terminalEventObserved)
214
+ return;
215
+ terminalEventObserved = true;
216
+ const result = reportOptimisticResult(status);
217
+ if (result)
218
+ options.onResult?.(result);
219
+ reportTerminal();
220
+ }
221
+ function settleTerminal() {
222
+ if (terminalEventObserved)
223
+ return;
224
+ terminalEventObserved = true;
225
+ reportTerminal();
193
226
  }
194
227
  void (async () => {
195
228
  try {
@@ -231,15 +264,18 @@ export function useDirectStream(options = {}) {
231
264
  continue;
232
265
  activity.value = "active";
233
266
  if (event.kind === "result") {
234
- settleTerminal(event.status);
267
+ settleResult(event.status);
235
268
  continue;
236
269
  }
237
270
  if (event.kind === "steps" && (event.status === "completed" || event.status === "waiting_messages")) {
238
- settleTerminal(event.status);
271
+ reportOptimisticResult(event.status);
272
+ reportTerminal();
239
273
  continue;
240
274
  }
241
275
  if (event.kind === "error") {
242
- if (event.recoverable !== true) {
276
+ if (event.recoverable === true) {
277
+ requestRecovery();
278
+ } else {
243
279
  settleTerminal();
244
280
  }
245
281
  continue;
@@ -267,8 +303,8 @@ export function useDirectStream(options = {}) {
267
303
  } finally {
268
304
  if (abortController === controller)
269
305
  abortController = null;
270
- if (accepted && !terminal && !controller.signal.aborted)
271
- options.onDisconnect?.();
306
+ if (accepted && !terminalEventObserved && !controller.signal.aborted)
307
+ requestRecovery();
272
308
  }
273
309
  })();
274
310
  return acceptedPromise;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "3.12.0-rc.0",
3
+ "version": "3.12.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {