@ai-accounts/vue-headless 0.3.0-alpha.1 → 0.3.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/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -25,8 +35,13 @@ __export(index_exports, {
25
35
  useAccountWizard: () => useAccountWizard,
26
36
  useAiAccounts: () => useAiAccounts,
27
37
  useBackendRegistry: () => useBackendRegistry,
38
+ useConversation: () => useConversation,
28
39
  useLoginSession: () => useLoginSession,
29
40
  useOnboarding: () => useOnboarding,
41
+ useProcessGroups: () => useProcessGroups,
42
+ useSmartChat: () => useSmartChat,
43
+ useSmartScroll: () => useSmartScroll,
44
+ useStreamingParser: () => useStreamingParser,
30
45
  version: () => version
31
46
  });
32
47
  module.exports = __toCommonJS(index_exports);
@@ -173,6 +188,7 @@ function useLoginSession() {
173
188
  const accountId = (0, import_vue5.ref)(null);
174
189
  const urlPrompt = (0, import_vue5.ref)(null);
175
190
  const textPrompt = (0, import_vue5.ref)(null);
191
+ const menuPrompt = (0, import_vue5.ref)(null);
176
192
  const stdoutLines = (0, import_vue5.ref)([]);
177
193
  const errorCode = (0, import_vue5.ref)(null);
178
194
  const errorMessage = (0, import_vue5.ref)(null);
@@ -181,15 +197,33 @@ function useLoginSession() {
181
197
  status.value = "running";
182
198
  urlPrompt.value = null;
183
199
  textPrompt.value = null;
200
+ menuPrompt.value = null;
184
201
  stdoutLines.value = [];
185
202
  errorCode.value = null;
186
203
  errorMessage.value = null;
187
- const { session_id } = await client.beginLogin(id, flow, inputs);
188
- sessionId.value = session_id;
189
- emit({ type: "login.started", sessionId: session_id, backendKind: "", flow });
190
- for await (const event of client.streamLogin(id, session_id)) {
191
- dispatch(event);
192
- if (status.value !== "running") return;
204
+ try {
205
+ const { session_id } = await client.beginLogin(id, flow, inputs);
206
+ sessionId.value = session_id;
207
+ emit({ type: "login.started", sessionId: session_id, backendKind: "", flow });
208
+ for await (const event of client.streamLogin(id, session_id)) {
209
+ dispatch(event);
210
+ if (status.value !== "running") return;
211
+ }
212
+ if (status.value === "running") {
213
+ status.value = "failed";
214
+ errorCode.value = "stream_ended";
215
+ errorMessage.value = "Login stream ended unexpectedly";
216
+ }
217
+ } catch (err) {
218
+ status.value = "failed";
219
+ errorCode.value = "network_error";
220
+ errorMessage.value = err instanceof Error ? err.message : String(err);
221
+ emit({
222
+ type: "login.failed",
223
+ sessionId: sessionId.value ?? "",
224
+ code: "network_error",
225
+ message: errorMessage.value
226
+ });
193
227
  }
194
228
  }
195
229
  function dispatch(event) {
@@ -202,6 +236,10 @@ function useLoginSession() {
202
236
  textPrompt.value = event;
203
237
  emit({ type: "login.prompt", sessionId: sessionId.value, promptKind: "text" });
204
238
  break;
239
+ case "menu_prompt":
240
+ menuPrompt.value = event;
241
+ emit({ type: "login.prompt", sessionId: sessionId.value, promptKind: "menu" });
242
+ break;
205
243
  case "stdout":
206
244
  stdoutLines.value = [...stdoutLines.value, event.text];
207
245
  break;
@@ -230,9 +268,12 @@ function useLoginSession() {
230
268
  }
231
269
  }
232
270
  async function respond(answer) {
233
- if (!sessionId.value || !accountId.value || !textPrompt.value) return;
234
- const promptId = textPrompt.value.prompt_id;
271
+ if (!sessionId.value || !accountId.value) return;
272
+ const activePrompt = textPrompt.value ?? menuPrompt.value;
273
+ if (!activePrompt) return;
274
+ const promptId = activePrompt.prompt_id;
235
275
  textPrompt.value = null;
276
+ menuPrompt.value = null;
236
277
  await client.respondLogin(accountId.value, sessionId.value, promptId, answer);
237
278
  }
238
279
  async function cancel() {
@@ -246,6 +287,7 @@ function useLoginSession() {
246
287
  accountId,
247
288
  urlPrompt,
248
289
  textPrompt,
290
+ menuPrompt,
249
291
  stdoutLines,
250
292
  errorCode,
251
293
  errorMessage,
@@ -255,6 +297,595 @@ function useLoginSession() {
255
297
  };
256
298
  }
257
299
 
300
+ // src/composables/useConversation.ts
301
+ var import_vue6 = require("vue");
302
+ function useConversation(client) {
303
+ const sessionId = (0, import_vue6.ref)(null);
304
+ const messages = (0, import_vue6.shallowRef)([]);
305
+ const isStreaming = (0, import_vue6.ref)(false);
306
+ const streamingText = (0, import_vue6.ref)("");
307
+ const error = (0, import_vue6.ref)(null);
308
+ async function create(backendId, model) {
309
+ error.value = null;
310
+ const session = await client.createConversation({ backend_id: backendId, model });
311
+ sessionId.value = session.id;
312
+ messages.value = [];
313
+ }
314
+ async function load(id) {
315
+ error.value = null;
316
+ const detail = await client.getConversation(id);
317
+ sessionId.value = detail.id;
318
+ messages.value = detail.messages;
319
+ }
320
+ async function send(content) {
321
+ if (!sessionId.value) {
322
+ error.value = "No active session";
323
+ return;
324
+ }
325
+ error.value = null;
326
+ isStreaming.value = true;
327
+ streamingText.value = "";
328
+ const userMsg = {
329
+ id: `pending-${Date.now()}`,
330
+ role: "user",
331
+ content,
332
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
333
+ model: null,
334
+ tokens_in: null,
335
+ tokens_out: null
336
+ };
337
+ messages.value = [...messages.value, userMsg];
338
+ try {
339
+ let accumulated = "";
340
+ for await (const delta of client.streamChat(sessionId.value, content)) {
341
+ if (delta.kind === "token" && delta.text) {
342
+ accumulated += delta.text;
343
+ streamingText.value = accumulated;
344
+ } else if (delta.kind === "error") {
345
+ error.value = delta.text ?? "Unknown error";
346
+ }
347
+ }
348
+ if (accumulated) {
349
+ const assistantMsg = {
350
+ id: `msg-${Date.now()}`,
351
+ role: "assistant",
352
+ content: accumulated,
353
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
354
+ model: null,
355
+ tokens_in: null,
356
+ tokens_out: null
357
+ };
358
+ messages.value = [...messages.value, assistantMsg];
359
+ }
360
+ } catch (e) {
361
+ error.value = e instanceof Error ? e.message : "Stream failed";
362
+ } finally {
363
+ isStreaming.value = false;
364
+ streamingText.value = "";
365
+ }
366
+ }
367
+ return { sessionId, messages, isStreaming, streamingText, error, create, send, load };
368
+ }
369
+
370
+ // src/composables/useSmartChat.ts
371
+ var import_vue8 = require("vue");
372
+
373
+ // src/composables/useProcessGroups.ts
374
+ var import_vue7 = require("vue");
375
+ var AUTO_COLLAPSE_MS = {
376
+ tool_call: 4e3,
377
+ reasoning: 2e3,
378
+ code_execution: 2e3
379
+ };
380
+ function useProcessGroups() {
381
+ const groups = (0, import_vue7.ref)(/* @__PURE__ */ new Map());
382
+ function _triggerReactivity() {
383
+ groups.value = new Map(groups.value);
384
+ }
385
+ function addGroup(group) {
386
+ groups.value.set(group.id, { ...group, isExpanded: true });
387
+ _triggerReactivity();
388
+ }
389
+ function removeGroup(id) {
390
+ groups.value.delete(id);
391
+ _triggerReactivity();
392
+ }
393
+ function toggleGroup(id) {
394
+ const g = groups.value.get(id);
395
+ if (g) {
396
+ g.isExpanded = !g.isExpanded;
397
+ _triggerReactivity();
398
+ }
399
+ }
400
+ function collapseGroup(id) {
401
+ const g = groups.value.get(id);
402
+ if (g) {
403
+ g.isExpanded = false;
404
+ _triggerReactivity();
405
+ }
406
+ }
407
+ function expandGroup(id) {
408
+ const g = groups.value.get(id);
409
+ if (g) {
410
+ g.isExpanded = true;
411
+ _triggerReactivity();
412
+ }
413
+ }
414
+ function updateGroupContent(id, content) {
415
+ const g = groups.value.get(id);
416
+ if (g) {
417
+ g.content += content;
418
+ _triggerReactivity();
419
+ }
420
+ }
421
+ function clearGroups() {
422
+ groups.value.clear();
423
+ _triggerReactivity();
424
+ }
425
+ function processToolCallDelta(delta) {
426
+ const existing = groups.value.get(delta.id);
427
+ if (existing) {
428
+ if (delta.arguments) {
429
+ existing.content += delta.arguments;
430
+ _triggerReactivity();
431
+ }
432
+ return;
433
+ }
434
+ const type = delta.group_type ?? "tool_call";
435
+ addGroup({
436
+ id: delta.id,
437
+ type,
438
+ label: delta.name ?? delta.id,
439
+ content: delta.arguments ?? "",
440
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
441
+ autoCollapseMs: AUTO_COLLAPSE_MS[type]
442
+ });
443
+ }
444
+ return {
445
+ groups,
446
+ addGroup,
447
+ removeGroup,
448
+ toggleGroup,
449
+ collapseGroup,
450
+ expandGroup,
451
+ updateGroupContent,
452
+ clearGroups,
453
+ processToolCallDelta
454
+ };
455
+ }
456
+
457
+ // src/composables/useSmartChat.ts
458
+ var HEARTBEAT_TIMEOUT_MS = 9e4;
459
+ function useSmartChat() {
460
+ const { client } = useAiAccounts();
461
+ const sessionId = (0, import_vue8.ref)(null);
462
+ const messages = (0, import_vue8.shallowRef)([]);
463
+ const isStreaming = (0, import_vue8.ref)(false);
464
+ const streamingContent = (0, import_vue8.ref)("");
465
+ const error = (0, import_vue8.ref)(null);
466
+ const chatMode = (0, import_vue8.ref)("single");
467
+ const backendResponses = (0, import_vue8.ref)(/* @__PURE__ */ new Map());
468
+ const synthesisState = (0, import_vue8.ref)(null);
469
+ const selectedBackend = (0, import_vue8.ref)(null);
470
+ const selectedAccount = (0, import_vue8.ref)(null);
471
+ const selectedModel = (0, import_vue8.ref)(null);
472
+ const processGroups = useProcessGroups();
473
+ const canFinalize = (0, import_vue8.ref)(false);
474
+ const isFinalizing = (0, import_vue8.ref)(false);
475
+ const detectedConfig = (0, import_vue8.ref)(null);
476
+ let configParser = null;
477
+ function setConfigParser(parser) {
478
+ configParser = parser;
479
+ }
480
+ async function finalize() {
481
+ if (!canFinalize.value) return null;
482
+ isFinalizing.value = true;
483
+ try {
484
+ return detectedConfig.value;
485
+ } finally {
486
+ isFinalizing.value = false;
487
+ }
488
+ }
489
+ let lastSeq = 0;
490
+ let heartbeatTimer = null;
491
+ let activeAbort = null;
492
+ function clearHeartbeat() {
493
+ if (heartbeatTimer) {
494
+ clearTimeout(heartbeatTimer);
495
+ heartbeatTimer = null;
496
+ }
497
+ }
498
+ function resetHeartbeat() {
499
+ clearHeartbeat();
500
+ heartbeatTimer = setTimeout(() => {
501
+ error.value = "Connection lost \u2014 no activity from server";
502
+ isStreaming.value = false;
503
+ heartbeatTimer = null;
504
+ if (activeAbort) {
505
+ activeAbort.abort(new DOMException("heartbeat-timeout", "AbortError"));
506
+ }
507
+ }, HEARTBEAT_TIMEOUT_MS);
508
+ }
509
+ async function createSession(backendId, model) {
510
+ error.value = null;
511
+ const session = await client.createChatSession(backendId, model);
512
+ sessionId.value = session.id;
513
+ messages.value = [];
514
+ }
515
+ async function loadSession(id) {
516
+ error.value = null;
517
+ const detail = await client.getConversation(id);
518
+ sessionId.value = detail.id;
519
+ messages.value = detail.messages;
520
+ }
521
+ async function send(content) {
522
+ if (!sessionId.value) {
523
+ error.value = "No active session";
524
+ return;
525
+ }
526
+ error.value = null;
527
+ isStreaming.value = true;
528
+ streamingContent.value = "";
529
+ backendResponses.value = /* @__PURE__ */ new Map();
530
+ synthesisState.value = null;
531
+ canFinalize.value = false;
532
+ detectedConfig.value = null;
533
+ processGroups.clearGroups();
534
+ lastSeq = 0;
535
+ activeAbort = new AbortController();
536
+ resetHeartbeat();
537
+ const userMsg = {
538
+ id: `pending-${Date.now()}`,
539
+ role: "user",
540
+ content,
541
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
542
+ model: null,
543
+ tokens_in: null,
544
+ tokens_out: null
545
+ };
546
+ messages.value = [...messages.value, userMsg];
547
+ try {
548
+ const req = { session_id: sessionId.value, content, mode: chatMode.value };
549
+ if (selectedBackend.value) req.backend_kind = selectedBackend.value;
550
+ if (selectedAccount.value) req.account_id = selectedAccount.value;
551
+ if (selectedModel.value) req.model = selectedModel.value;
552
+ for await (const event of client.sendChat(req, { signal: activeAbort.signal })) {
553
+ dispatch(event);
554
+ }
555
+ if (chatMode.value === "single" && streamingContent.value) {
556
+ const assistantMsg = {
557
+ id: `msg-${Date.now()}`,
558
+ role: "assistant",
559
+ content: streamingContent.value,
560
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
561
+ model: null,
562
+ tokens_in: null,
563
+ tokens_out: null
564
+ };
565
+ messages.value = [...messages.value, assistantMsg];
566
+ }
567
+ if (configParser) {
568
+ const lastMsg = messages.value[messages.value.length - 1];
569
+ if (lastMsg && lastMsg.role === "assistant") {
570
+ try {
571
+ const cfg = configParser(lastMsg.content);
572
+ if (cfg) {
573
+ detectedConfig.value = cfg;
574
+ canFinalize.value = true;
575
+ }
576
+ } catch (e) {
577
+ console.error("[useSmartChat] configParser threw \u2014 canFinalize stays false", e);
578
+ }
579
+ }
580
+ }
581
+ } catch (e) {
582
+ if (e instanceof DOMException && e.name === "AbortError") {
583
+ } else if (e instanceof Error) {
584
+ error.value = `${e.name}: ${e.message}`;
585
+ } else {
586
+ error.value = `Chat failed: ${String(e)}`;
587
+ }
588
+ } finally {
589
+ clearHeartbeat();
590
+ activeAbort = null;
591
+ isStreaming.value = false;
592
+ streamingContent.value = "";
593
+ }
594
+ }
595
+ function dispatch(event) {
596
+ if (typeof event._seq === "number") {
597
+ if (event._seq <= lastSeq) return;
598
+ if (lastSeq > 0 && event._seq > lastSeq + 1) {
599
+ console.warn(
600
+ "[useSmartChat] seq gap detected: lastSeq=%d incoming=%d (lost %d events)",
601
+ lastSeq,
602
+ event._seq,
603
+ event._seq - lastSeq - 1
604
+ );
605
+ }
606
+ lastSeq = event._seq;
607
+ }
608
+ resetHeartbeat();
609
+ if (event.kind === "done" || event.kind === "error" || event.kind === "synthesis_complete" || event.kind === "synthesis_error") {
610
+ clearHeartbeat();
611
+ }
612
+ switch (event.kind) {
613
+ // Single mode
614
+ case "token":
615
+ streamingContent.value += event.payload;
616
+ break;
617
+ case "done":
618
+ break;
619
+ case "error":
620
+ error.value = event.payload ?? "Unknown error";
621
+ break;
622
+ case "tool_call": {
623
+ const delta = { id: event.id };
624
+ if (event.name !== void 0) delta.name = event.name;
625
+ if (event.arguments !== void 0) delta.arguments = event.arguments;
626
+ if (event.group_type !== void 0) delta.group_type = event.group_type;
627
+ processGroups.processToolCallDelta(delta);
628
+ break;
629
+ }
630
+ // All/compound mode — backend events
631
+ case "backend_delta": {
632
+ const existing = backendResponses.value.get(event.backend);
633
+ const updated = new Map(backendResponses.value);
634
+ updated.set(event.backend, {
635
+ backend: event.backend,
636
+ content: (existing?.content ?? "") + (event.text ?? ""),
637
+ status: "streaming"
638
+ });
639
+ backendResponses.value = updated;
640
+ break;
641
+ }
642
+ case "backend_complete": {
643
+ const existing = backendResponses.value.get(event.backend);
644
+ if (existing) {
645
+ const updated = new Map(backendResponses.value);
646
+ updated.set(event.backend, { ...existing, status: "complete" });
647
+ backendResponses.value = updated;
648
+ }
649
+ break;
650
+ }
651
+ case "backend_error": {
652
+ const existing = backendResponses.value.get(event.backend);
653
+ const updated = new Map(backendResponses.value);
654
+ updated.set(event.backend, {
655
+ backend: event.backend,
656
+ content: existing?.content ?? "",
657
+ status: "error",
658
+ error: event.error
659
+ });
660
+ backendResponses.value = updated;
661
+ break;
662
+ }
663
+ case "backend_timeout": {
664
+ const existing = backendResponses.value.get(event.backend);
665
+ const updated = new Map(backendResponses.value);
666
+ updated.set(event.backend, {
667
+ backend: event.backend,
668
+ content: existing?.content ?? "",
669
+ status: "timeout"
670
+ });
671
+ backendResponses.value = updated;
672
+ break;
673
+ }
674
+ // Compound synthesis
675
+ case "synthesis_start": {
676
+ synthesisState.value = {
677
+ status: "streaming",
678
+ content: "",
679
+ primaryBackend: event.primary_backend,
680
+ backendsCollected: event.backends_collected ?? []
681
+ };
682
+ break;
683
+ }
684
+ case "synthesis_delta": {
685
+ if (synthesisState.value) {
686
+ synthesisState.value = {
687
+ ...synthesisState.value,
688
+ content: synthesisState.value.content + (event.text ?? "")
689
+ };
690
+ }
691
+ break;
692
+ }
693
+ case "synthesis_complete":
694
+ if (synthesisState.value) {
695
+ synthesisState.value = { ...synthesisState.value, status: "complete" };
696
+ }
697
+ break;
698
+ case "synthesis_error": {
699
+ if (synthesisState.value) {
700
+ synthesisState.value = { ...synthesisState.value, status: "error", error: event.error };
701
+ } else {
702
+ synthesisState.value = {
703
+ status: "error",
704
+ content: "",
705
+ primaryBackend: "",
706
+ backendsCollected: [],
707
+ error: event.error
708
+ };
709
+ }
710
+ break;
711
+ }
712
+ }
713
+ }
714
+ function setMode(mode) {
715
+ chatMode.value = mode;
716
+ }
717
+ function selectBackend(kind) {
718
+ selectedBackend.value = kind;
719
+ selectedAccount.value = null;
720
+ selectedModel.value = null;
721
+ }
722
+ return {
723
+ sessionId,
724
+ messages,
725
+ isStreaming,
726
+ streamingContent,
727
+ error,
728
+ chatMode,
729
+ backendResponses,
730
+ synthesisState,
731
+ selectedBackend,
732
+ selectedAccount,
733
+ selectedModel,
734
+ createSession,
735
+ loadSession,
736
+ send,
737
+ setMode,
738
+ selectBackend,
739
+ processGroups,
740
+ canFinalize,
741
+ isFinalizing,
742
+ detectedConfig,
743
+ finalize,
744
+ setConfigParser
745
+ };
746
+ }
747
+
748
+ // src/composables/useSmartScroll.ts
749
+ var import_vue9 = require("vue");
750
+ var THRESHOLD = 32;
751
+ function useSmartScroll() {
752
+ const containerRef = (0, import_vue9.ref)(null);
753
+ const isNearBottom = (0, import_vue9.ref)(true);
754
+ const showScrollButton = (0, import_vue9.ref)(false);
755
+ function check() {
756
+ const el = containerRef.value;
757
+ if (!el) return;
758
+ const near = el.scrollHeight - el.scrollTop - el.clientHeight < THRESHOLD;
759
+ isNearBottom.value = near;
760
+ showScrollButton.value = !near;
761
+ }
762
+ function scrollToBottom() {
763
+ containerRef.value?.scrollTo({ top: containerRef.value.scrollHeight, behavior: "smooth" });
764
+ }
765
+ let observer = null;
766
+ (0, import_vue9.onMounted)(() => {
767
+ const el = containerRef.value;
768
+ if (!el) return;
769
+ el.addEventListener("scroll", check, { passive: true });
770
+ observer = new MutationObserver(() => {
771
+ if (isNearBottom.value) scrollToBottom();
772
+ });
773
+ observer.observe(el, { childList: true, subtree: true });
774
+ });
775
+ (0, import_vue9.onUnmounted)(() => {
776
+ containerRef.value?.removeEventListener("scroll", check);
777
+ observer?.disconnect();
778
+ });
779
+ return { containerRef, isNearBottom, showScrollButton, scrollToBottom };
780
+ }
781
+
782
+ // src/composables/useStreamingParser.ts
783
+ var DEFAULT_MAX_PENDING = 1e6;
784
+ var smdResolutionWarned = false;
785
+ async function resolveSmd() {
786
+ const injected = globalThis.__smd;
787
+ if (injected) return injected;
788
+ try {
789
+ const mod = await import(
790
+ /* @vite-ignore */
791
+ "streaming-markdown"
792
+ );
793
+ return mod;
794
+ } catch (err) {
795
+ if (!smdResolutionWarned) {
796
+ smdResolutionWarned = true;
797
+ console.warn(
798
+ '[useStreamingParser] Could not load "streaming-markdown" peer dep; markdown rendering disabled. Install it or inject globalThis.__smd.',
799
+ err
800
+ );
801
+ }
802
+ return null;
803
+ }
804
+ }
805
+ function useStreamingParser(options = {}) {
806
+ const maxPending = options.maxPendingChars ?? DEFAULT_MAX_PENDING;
807
+ let smd = null;
808
+ let parser = null;
809
+ let pending = [];
810
+ let pendingChars = 0;
811
+ let rafId = null;
812
+ let initToken = 0;
813
+ function flush() {
814
+ rafId = null;
815
+ if (!smd || !parser || pending.length === 0) return;
816
+ const text = pending.join("");
817
+ pending = [];
818
+ pendingChars = 0;
819
+ try {
820
+ smd.parser_write(parser, text);
821
+ } catch (err) {
822
+ console.error("[useStreamingParser] parser_write threw; chunk discarded", err);
823
+ }
824
+ options.onFlush?.();
825
+ }
826
+ function init(container) {
827
+ destroy();
828
+ container.textContent = "";
829
+ const token = ++initToken;
830
+ resolveSmd().then((resolved) => {
831
+ if (token !== initToken) return;
832
+ if (!resolved) return;
833
+ smd = resolved;
834
+ const renderer = resolved.default_renderer(container);
835
+ parser = resolved.parser(renderer);
836
+ if (pending.length > 0) {
837
+ const text = pending.join("");
838
+ pending = [];
839
+ pendingChars = 0;
840
+ try {
841
+ resolved.parser_write(parser, text);
842
+ } catch (err) {
843
+ console.error("[useStreamingParser] initial parser_write threw; buffered chunk discarded", err);
844
+ }
845
+ }
846
+ }).catch((err) => {
847
+ console.error("[useStreamingParser] resolveSmd unexpectedly rejected", err);
848
+ });
849
+ }
850
+ function write(text) {
851
+ if (initToken === 0) return;
852
+ if (pendingChars + text.length > maxPending) {
853
+ console.warn(
854
+ "[useStreamingParser] dropping write \u2014 pending buffer exceeded maxPendingChars (%d). smd may never have resolved.",
855
+ maxPending
856
+ );
857
+ return;
858
+ }
859
+ pending.push(text);
860
+ pendingChars += text.length;
861
+ if (rafId === null && typeof requestAnimationFrame !== "undefined") {
862
+ rafId = requestAnimationFrame(flush);
863
+ } else if (rafId === null) {
864
+ queueMicrotask(flush);
865
+ }
866
+ }
867
+ function finalize() {
868
+ if (rafId !== null) {
869
+ if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(rafId);
870
+ rafId = null;
871
+ }
872
+ if (pending.length > 0) flush();
873
+ if (smd && parser) {
874
+ try {
875
+ smd.parser_end(parser);
876
+ } catch (err) {
877
+ console.error("[useStreamingParser] parser_end threw", err);
878
+ }
879
+ }
880
+ parser = null;
881
+ smd = null;
882
+ }
883
+ function destroy() {
884
+ finalize();
885
+ }
886
+ return { init, write, finalize, destroy };
887
+ }
888
+
258
889
  // src/index.ts
259
890
  var version = "0.3.0-alpha.1";
260
891
  // Annotate the CommonJS export names for ESM import in node:
@@ -264,7 +895,12 @@ var version = "0.3.0-alpha.1";
264
895
  useAccountWizard,
265
896
  useAiAccounts,
266
897
  useBackendRegistry,
898
+ useConversation,
267
899
  useLoginSession,
268
900
  useOnboarding,
901
+ useProcessGroups,
902
+ useSmartChat,
903
+ useSmartScroll,
904
+ useStreamingParser,
269
905
  version
270
906
  });