@raisindb/client 0.5.9 → 0.5.11

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.
Files changed (40) hide show
  1. package/README.md +32 -0
  2. package/dist/{chunk-IFV4ECHC.mjs → chunk-ASOWGNQZ.mjs} +3 -3
  3. package/dist/{chunk-ZUUSBLVP.mjs → chunk-FYWYSNQG.mjs} +3 -3
  4. package/dist/{chunk-765DLJMZ.mjs → chunk-NOPZAMIV.mjs} +31 -2
  5. package/dist/chunk-NOPZAMIV.mjs.map +1 -0
  6. package/dist/{chunk-AULHRX5C.mjs → chunk-TPZNJNZR.mjs} +2 -2
  7. package/dist/{chunk-VMUT6PIO.mjs → chunk-ZCRYQCXS.mjs} +2 -2
  8. package/dist/{conversation-list-store-CHaqGtSi.d.mts → conversation-list-store-BNYosL8k.d.mts} +221 -3
  9. package/dist/{conversation-list-store-CHaqGtSi.d.ts → conversation-list-store-BNYosL8k.d.ts} +221 -3
  10. package/dist/{index-DiL2gplR.d.ts → index-GaJToZKx.d.ts} +1 -1
  11. package/dist/{index-AobOnS4A.d.mts → index-UlXuwMoL.d.mts} +1 -1
  12. package/dist/index.d.mts +4 -4
  13. package/dist/index.d.ts +4 -4
  14. package/dist/index.js +405 -6
  15. package/dist/index.js.map +1 -1
  16. package/dist/index.mjs +378 -10
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/react.d.mts +2 -2
  19. package/dist/react.d.ts +2 -2
  20. package/dist/react.js +30 -1
  21. package/dist/react.js.map +1 -1
  22. package/dist/react.mjs +3 -3
  23. package/dist/{svelte-sql-FQqis53r.d.ts → svelte-sql-bROcapdk.d.ts} +1 -1
  24. package/dist/{svelte-sql-BUrNcoHR.d.mts → svelte-sql-yLeGoj89.d.mts} +1 -1
  25. package/dist/svelte.d.mts +2 -2
  26. package/dist/svelte.d.ts +2 -2
  27. package/dist/svelte.js +30 -1
  28. package/dist/svelte.js.map +1 -1
  29. package/dist/svelte.mjs +3 -3
  30. package/dist/vue.d.mts +2 -2
  31. package/dist/vue.d.ts +2 -2
  32. package/dist/vue.js +30 -1
  33. package/dist/vue.js.map +1 -1
  34. package/dist/vue.mjs +2 -2
  35. package/package.json +1 -1
  36. package/dist/chunk-765DLJMZ.mjs.map +0 -1
  37. /package/dist/{chunk-IFV4ECHC.mjs.map → chunk-ASOWGNQZ.mjs.map} +0 -0
  38. /package/dist/{chunk-ZUUSBLVP.mjs.map → chunk-FYWYSNQG.mjs.map} +0 -0
  39. /package/dist/{chunk-AULHRX5C.mjs.map → chunk-TPZNJNZR.mjs.map} +0 -0
  40. /package/dist/{chunk-VMUT6PIO.mjs.map → chunk-ZCRYQCXS.mjs.map} +0 -0
package/dist/index.js CHANGED
@@ -99,6 +99,7 @@ __export(src_exports, {
99
99
  UploadErrorCode: () => UploadErrorCode,
100
100
  UploadManager: () => UploadManager,
101
101
  Uploader: () => Uploader,
102
+ VisitorChatError: () => VisitorChatError,
102
103
  WorkspaceClient: () => WorkspaceClient,
103
104
  WorkspaceManager: () => WorkspaceManager,
104
105
  calculateChunkCount: () => calculateChunkCount,
@@ -120,6 +121,7 @@ __export(src_exports, {
120
121
  createSqlHandler: () => createSqlHandler,
121
122
  createSubscriptionAdapter: () => createSubscriptionAdapter,
122
123
  decodeMessage: () => decodeMessage,
124
+ defaultVisitorSessionStore: () => defaultVisitorSessionStore,
123
125
  encodeMessage: () => encodeMessage,
124
126
  getChunkRange: () => getChunkRange,
125
127
  getContentType: () => getContentType,
@@ -872,6 +874,7 @@ var RequestType = /* @__PURE__ */ ((RequestType2) => {
872
874
  RequestType2["NodeUpdate"] = "node_update";
873
875
  RequestType2["NodeDelete"] = "node_delete";
874
876
  RequestType2["NodeGet"] = "node_get";
877
+ RequestType2["NodeGetByLocalizedPath"] = "node_get_by_localized_path";
875
878
  RequestType2["NodeQuery"] = "node_query";
876
879
  RequestType2["NodeQueryByPath"] = "node_query_by_path";
877
880
  RequestType2["NodeQueryByProperty"] = "node_query_by_property";
@@ -973,6 +976,8 @@ var RequestType = /* @__PURE__ */ ((RequestType2) => {
973
976
  RequestType2["ScheduledInvocationCancel"] = "scheduled_invocation_cancel";
974
977
  RequestType2["ScheduledInvocationList"] = "scheduled_invocation_list";
975
978
  RequestType2["ScheduledInvocationGet"] = "scheduled_invocation_get";
979
+ RequestType2["VisitorChatStart"] = "visitor_chat_start";
980
+ RequestType2["VisitorChatSend"] = "visitor_chat_send";
976
981
  return RequestType2;
977
982
  })(RequestType || {});
978
983
  var ResponseStatus = /* @__PURE__ */ ((ResponseStatus2) => {
@@ -1256,7 +1261,8 @@ var EventHandler = class {
1256
1261
  this.filterSubscriptions.set(hash, {
1257
1262
  serverId: subscriptionId,
1258
1263
  filters: {},
1259
- callbacks: /* @__PURE__ */ new Set([callback])
1264
+ callbacks: /* @__PURE__ */ new Set([callback]),
1265
+ synthetic: true
1260
1266
  });
1261
1267
  this.subscriptions.set(subscriptionId, hash);
1262
1268
  this.callbackToHash.set(callback, hash);
@@ -1291,11 +1297,12 @@ var EventHandler = class {
1291
1297
  async restoreSubscriptions(options) {
1292
1298
  if (!options?.retry) {
1293
1299
  for (const entry of this.filterSubscriptions.values()) {
1300
+ if (entry.synthetic) continue;
1294
1301
  this.subscriptions.delete(entry.serverId);
1295
1302
  entry.restorePending = true;
1296
1303
  }
1297
1304
  }
1298
- const entriesToRestore = Array.from(this.filterSubscriptions.entries()).filter(([, entry]) => entry.restorePending);
1305
+ const entriesToRestore = Array.from(this.filterSubscriptions.entries()).filter(([, entry]) => entry.restorePending && !entry.synthetic);
1299
1306
  let failureCount = 0;
1300
1307
  let lastError;
1301
1308
  for (const [hash, entry] of entriesToRestore) {
@@ -1528,6 +1535,27 @@ var NodeOperations = class {
1528
1535
  const result = await this.sendRequest(payload, "node_history");
1529
1536
  return result ?? [];
1530
1537
  }
1538
+ /**
1539
+ * Get a node by its localized URL path (plan Phase 12).
1540
+ *
1541
+ * @param locale - Locale code (`fr`, `fr-CA`)
1542
+ * @param path - The node's path in that locale (`/produits/chaise`)
1543
+ * @returns The translated node with `canonical_path`,
1544
+ * `canonical_localized_path`, hreflang `alternates` and a `redirect`
1545
+ * hint, or `null` when nothing resolves (missing, hidden in the locale
1546
+ * or not readable — the same answer)
1547
+ *
1548
+ * @example
1549
+ * ```typescript
1550
+ * const hit = await ws.nodes().getByLocalizedPath('fr', '/produits/chaise');
1551
+ * if (hit?.redirect) redirect301(`/fr${hit.canonical_localized_path}`);
1552
+ * ```
1553
+ */
1554
+ async getByLocalizedPath(locale, path) {
1555
+ const payload = { locale, path };
1556
+ const result = await this.sendRequest(payload, "node_get_by_localized_path");
1557
+ return result ?? null;
1558
+ }
1531
1559
  /**
1532
1560
  * List a node's revision history by path (newest first).
1533
1561
  *
@@ -4487,7 +4515,8 @@ function normalizeHomePath(home) {
4487
4515
  // src/conversations.ts
4488
4516
  var DEFAULT_SEND_MESSAGE_INACTIVITY_TIMEOUT_MS = 12e4;
4489
4517
  var ConversationManager = class {
4490
- constructor(baseUrl, repository, authManager, options = {}, executeSql) {
4518
+ constructor(baseUrl, repository, authManager, options = {}, executeSql, visitor) {
4519
+ this.visitor = visitor;
4491
4520
  this.baseUrl = baseUrl.replace(/\/$/, "");
4492
4521
  this.repository = repository;
4493
4522
  this.authManager = authManager;
@@ -4608,6 +4637,45 @@ var ConversationManager = class {
4608
4637
  ]
4609
4638
  };
4610
4639
  }
4640
+ /**
4641
+ * Start (or resume) an ANONYMOUS conversation with an agent — a public
4642
+ * website's chat, without login.
4643
+ *
4644
+ * Works on an anonymous connection only, and only with an agent that allows
4645
+ * anonymous visitors (`anonymous.enabled: true` on its raisin:AIAgent). The
4646
+ * server keeps the conversation in an ephemeral visitor session that only
4647
+ * this browser session can read; the session secret is kept in
4648
+ * sessionStorage (override with `options.store`), so a reload continues the
4649
+ * same conversation. Then use `sendMessage`, `getMessages`, `subscribe` and
4650
+ * `ConversationStore` / `useConversation` exactly as for a signed-in user.
4651
+ *
4652
+ * @example
4653
+ * ```typescript
4654
+ * const convo = await db.conversations.startAnonymous('/agents/website-assistant');
4655
+ * for await (const ev of db.conversations.sendMessage(convo.conversationPath, 'Hello')) {
4656
+ * if (ev.type === 'text_chunk') append(ev.text);
4657
+ * if (ev.type === 'failed') showLimit(ev.code); // e.g. RATE_LIMITED, LIMIT_REACHED
4658
+ * }
4659
+ * ```
4660
+ */
4661
+ async startAnonymous(agent, options) {
4662
+ if (!this.visitor) {
4663
+ throw new Error("Anonymous chat needs a WebSocket database: use client.database(repo).");
4664
+ }
4665
+ return this.visitor.start(this.normalizeAgentPath(agent), options);
4666
+ }
4667
+ /** Forget the stored anonymous session for `agent` (the next start begins a new one). */
4668
+ forgetAnonymous(agent, options) {
4669
+ this.visitor?.forget(this.normalizeAgentPath(agent), options);
4670
+ }
4671
+ /** Whether this tab remembers an anonymous conversation with `agent`. */
4672
+ hasStoredAnonymous(agent, options) {
4673
+ return this.visitor?.remembers(this.normalizeAgentPath(agent), options) ?? false;
4674
+ }
4675
+ /** Whether `conversationPath` is an anonymous conversation of this client. */
4676
+ isAnonymous(conversationPath) {
4677
+ return this.visitor?.isAnonymous(conversationPath) ?? false;
4678
+ }
4611
4679
  /**
4612
4680
  * Open an existing conversation by path.
4613
4681
  * Returns null if not found.
@@ -4653,6 +4721,7 @@ var ConversationManager = class {
4653
4721
  * Mark a conversation as read by setting unread_count to 0.
4654
4722
  */
4655
4723
  async markAsRead(conversationPath) {
4724
+ if (this.isAnonymous(conversationPath)) return;
4656
4725
  const workspace = "raisin:access_control";
4657
4726
  await this.sqlQuery(
4658
4727
  `UPDATE '${workspace}' SET properties = jsonb_set(properties, '{unread_count}', '0'::jsonb) WHERE path = $1 AND node_type = 'raisin:Conversation'`,
@@ -4663,6 +4732,7 @@ var ConversationManager = class {
4663
4732
  * Mark a single message as read by the current user.
4664
4733
  */
4665
4734
  async markMessageAsRead(messagePath) {
4735
+ if (messagePath.startsWith("/visitors/")) return;
4666
4736
  const workspace = "raisin:access_control";
4667
4737
  const now = (/* @__PURE__ */ new Date()).toISOString();
4668
4738
  await this.sqlQuery(
@@ -4687,6 +4757,18 @@ var ConversationManager = class {
4687
4757
  */
4688
4758
  async *sendMessage(conversationPath, content, options) {
4689
4759
  const shouldStream = options?.stream !== false;
4760
+ if (this.visitor?.isAnonymous(conversationPath)) {
4761
+ if (!shouldStream) {
4762
+ await this.visitor.post(conversationPath, content);
4763
+ yield { type: "waiting", timestamp: (/* @__PURE__ */ new Date()).toISOString() };
4764
+ return;
4765
+ }
4766
+ yield* this.visitor.stream(conversationPath, content, {
4767
+ signal: options?.signal,
4768
+ inactivityTimeoutMs: options?.inactivityTimeoutMs ?? DEFAULT_SEND_MESSAGE_INACTIVITY_TIMEOUT_MS
4769
+ });
4770
+ return;
4771
+ }
4690
4772
  logger.debug("[ConversationManager] Starting SSE stream for message send");
4691
4773
  if (!shouldStream) {
4692
4774
  await this.createUserMessage(conversationPath, content);
@@ -4774,6 +4856,10 @@ var ConversationManager = class {
4774
4856
  */
4775
4857
  async createUserMessage(conversationPath, content) {
4776
4858
  logger.debug("[ConversationManager] Creating user message", { path: conversationPath });
4859
+ if (this.visitor?.isAnonymous(conversationPath)) {
4860
+ await this.visitor.post(conversationPath, content);
4861
+ return;
4862
+ }
4777
4863
  const workspace = "raisin:access_control";
4778
4864
  const properties = await this.loadConversationProperties(conversationPath, workspace);
4779
4865
  const flowInstanceId = typeof properties?.flow_instance_id === "string" ? properties.flow_instance_id.trim() : "";
@@ -4825,6 +4911,12 @@ var ConversationManager = class {
4825
4911
  * initiated messages, etc.). Auto-reconnects on disconnect.
4826
4912
  */
4827
4913
  subscribe(conversationPath, onEvent, options) {
4914
+ if (this.visitor?.isAnonymous(conversationPath)) {
4915
+ const stop = this.visitor.listen(conversationPath, onEvent);
4916
+ options?.signal?.addEventListener("abort", stop, { once: true });
4917
+ return { unsubscribe: stop, waitUntilConnected: async () => {
4918
+ } };
4919
+ }
4828
4920
  logger.info("[ConversationManager] Subscribing to SSE", { path: conversationPath });
4829
4921
  const sseUrl = `${this.baseUrl}/api/conversations/${this.repository}/events`;
4830
4922
  const headers = {};
@@ -5264,6 +5356,272 @@ var ConversationManager = class {
5264
5356
  }
5265
5357
  };
5266
5358
 
5359
+ // src/visitor-chat.ts
5360
+ function defaultVisitorSessionStore() {
5361
+ const memory = /* @__PURE__ */ new Map();
5362
+ const storage = () => {
5363
+ try {
5364
+ return typeof sessionStorage !== "undefined" ? sessionStorage : null;
5365
+ } catch {
5366
+ return null;
5367
+ }
5368
+ };
5369
+ return {
5370
+ get(key) {
5371
+ try {
5372
+ return storage()?.getItem(key) ?? memory.get(key) ?? null;
5373
+ } catch {
5374
+ return memory.get(key) ?? null;
5375
+ }
5376
+ },
5377
+ set(key, value) {
5378
+ memory.set(key, value);
5379
+ try {
5380
+ storage()?.setItem(key, value);
5381
+ } catch {
5382
+ }
5383
+ },
5384
+ remove(key) {
5385
+ memory.delete(key);
5386
+ try {
5387
+ storage()?.removeItem(key);
5388
+ } catch {
5389
+ }
5390
+ }
5391
+ };
5392
+ }
5393
+ var VisitorChatError = class extends Error {
5394
+ constructor(message, code) {
5395
+ super(message);
5396
+ this.name = "VisitorChatError";
5397
+ this.code = code;
5398
+ }
5399
+ };
5400
+ var REBIND_CODES = /* @__PURE__ */ new Set(["NO_SESSION", "UNKNOWN_CONVERSATION"]);
5401
+ function asError(err) {
5402
+ const e = err;
5403
+ return new VisitorChatError(e?.message ?? String(err), e?.code ?? "UNAVAILABLE");
5404
+ }
5405
+ var VisitorChat = class {
5406
+ constructor(repository, send, events, onReconnected) {
5407
+ this.repository = repository;
5408
+ this.send = send;
5409
+ this.events = events;
5410
+ this.byPath = /* @__PURE__ */ new Map();
5411
+ this.token = null;
5412
+ onReconnected?.(() => {
5413
+ void this.rebindAll();
5414
+ });
5415
+ }
5416
+ /** Whether `conversationPath` is a visitor conversation started here. */
5417
+ isAnonymous(conversationPath) {
5418
+ return this.byPath.has(conversationPath);
5419
+ }
5420
+ storeOf(options) {
5421
+ if (options.store === false) return null;
5422
+ return options.store ?? defaultVisitorSessionStore();
5423
+ }
5424
+ keyOf(agent, options) {
5425
+ return options.storageKey ?? `raisindb:visitor:${this.repository}:${agent}`;
5426
+ }
5427
+ /** Start (or resume) a visitor conversation with `agent` (`/agents/<name>`). */
5428
+ async start(agent, options = {}) {
5429
+ const store = this.storeOf(options);
5430
+ const key = this.keyOf(agent, options);
5431
+ const sessionToken = options.sessionToken ?? this.token ?? store?.get(`${key}:session`) ?? void 0;
5432
+ const conversationId = options.newConversation ? void 0 : options.conversationId ?? store?.get(`${key}:conversation`) ?? void 0;
5433
+ let res;
5434
+ try {
5435
+ res = await this.send(
5436
+ { agent, session_token: sessionToken, conversation_id: conversationId },
5437
+ "visitor_chat_start"
5438
+ );
5439
+ } catch (err) {
5440
+ const e = asError(err);
5441
+ if ((conversationId || sessionToken) && !options.conversationId && !options.sessionToken && ["UNKNOWN_CONVERSATION", "INVALID_CONVERSATION", "SESSION_EXPIRED", "SESSION_MISMATCH"].includes(e.code)) {
5442
+ store?.remove(`${key}:conversation`);
5443
+ if (e.code !== "UNKNOWN_CONVERSATION" && e.code !== "INVALID_CONVERSATION") {
5444
+ store?.remove(`${key}:session`);
5445
+ this.token = null;
5446
+ }
5447
+ return this.start(agent, { ...options, newConversation: true });
5448
+ }
5449
+ throw e;
5450
+ }
5451
+ const token = res.session_token ?? sessionToken ?? this.token;
5452
+ if (!token) throw new VisitorChatError("the server returned no session", "UNAVAILABLE");
5453
+ this.token = token;
5454
+ store?.set(`${key}:session`, token);
5455
+ store?.set(`${key}:conversation`, res.conversation_id);
5456
+ const path = res.conversation_path;
5457
+ let entry = this.byPath.get(path);
5458
+ if (!entry) {
5459
+ entry = {
5460
+ agent: res.agent,
5461
+ conversationId: res.conversation_id,
5462
+ subscriptionId: res.subscription_id,
5463
+ listeners: /* @__PURE__ */ new Set(),
5464
+ options
5465
+ };
5466
+ this.byPath.set(path, entry);
5467
+ }
5468
+ this.route(entry);
5469
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5470
+ return {
5471
+ id: res.conversation_id,
5472
+ type: "ai_chat",
5473
+ agentRef: res.agent,
5474
+ conversationPath: path,
5475
+ conversationWorkspace: res.conversation_workspace,
5476
+ anonymous: true,
5477
+ conversationId: res.conversation_id,
5478
+ sessionToken: token,
5479
+ limits: res.limits,
5480
+ initialEvents: [{ type: "waiting", timestamp: now }]
5481
+ };
5482
+ }
5483
+ /** Route the server's forwarded events for this conversation to its listeners. */
5484
+ route(entry) {
5485
+ this.events.removeFlowEventListener(entry.subscriptionId);
5486
+ this.events.addFlowEventListener(entry.subscriptionId, (msg) => {
5487
+ const event = msg.payload;
5488
+ if (!event || typeof event.type !== "string") return;
5489
+ for (const listener of entry.listeners) {
5490
+ try {
5491
+ listener(event);
5492
+ } catch (err) {
5493
+ logger.error("[VisitorChat] listener failed", err);
5494
+ }
5495
+ }
5496
+ });
5497
+ }
5498
+ /** Re-prove the session on a new connection, for every conversation started here. */
5499
+ async rebindAll() {
5500
+ for (const entry of this.byPath.values()) {
5501
+ try {
5502
+ await this.send(
5503
+ { agent: entry.agent, session_token: this.token, conversation_id: entry.conversationId },
5504
+ "visitor_chat_start"
5505
+ );
5506
+ this.route(entry);
5507
+ } catch (err) {
5508
+ logger.warn("[VisitorChat] could not resume a conversation after reconnect", err);
5509
+ }
5510
+ }
5511
+ }
5512
+ /** Listen to a visitor conversation's events. Returns an unsubscribe function. */
5513
+ listen(conversationPath, listener) {
5514
+ const entry = this.require(conversationPath);
5515
+ entry.listeners.add(listener);
5516
+ return () => {
5517
+ entry.listeners.delete(listener);
5518
+ };
5519
+ }
5520
+ require(conversationPath) {
5521
+ const entry = this.byPath.get(conversationPath);
5522
+ if (!entry) {
5523
+ throw new VisitorChatError(
5524
+ "not a visitor conversation of this client; call startAnonymous first",
5525
+ "UNKNOWN_CONVERSATION"
5526
+ );
5527
+ }
5528
+ return entry;
5529
+ }
5530
+ /** Make sure this connection is bound (after a reconnect nothing else may read the home). */
5531
+ async ensureBound(conversationPath) {
5532
+ const entry = this.require(conversationPath);
5533
+ await this.send(
5534
+ { agent: entry.agent, session_token: this.token, conversation_id: entry.conversationId },
5535
+ "visitor_chat_start"
5536
+ ).catch((err) => {
5537
+ throw asError(err);
5538
+ });
5539
+ }
5540
+ /** Send one visitor message. Events arrive through {@link listen}. */
5541
+ async post(conversationPath, text) {
5542
+ const entry = this.require(conversationPath);
5543
+ const once = async () => await this.send(
5544
+ { conversation_id: entry.conversationId, text },
5545
+ "visitor_chat_send"
5546
+ );
5547
+ try {
5548
+ const res = await once();
5549
+ return { messageId: res.message_id, messagesLeft: res.messages_left };
5550
+ } catch (err) {
5551
+ const e = asError(err);
5552
+ if (!REBIND_CODES.has(e.code)) throw e;
5553
+ await this.ensureBound(conversationPath);
5554
+ this.route(entry);
5555
+ const res = await once().catch((again) => {
5556
+ throw asError(again);
5557
+ });
5558
+ return { messageId: res.message_id, messagesLeft: res.messages_left };
5559
+ }
5560
+ }
5561
+ /**
5562
+ * Send one visitor message and stream the turn: `text_chunk`,
5563
+ * `tool_call_started`, `tool_call_completed`, … until `done`. A refusal
5564
+ * ends the stream with `failed` (its `code` says why). No event for
5565
+ * `inactivityTimeoutMs` ends it with a synthetic `waiting`.
5566
+ */
5567
+ async *stream(conversationPath, text, options = {}) {
5568
+ const queue = [];
5569
+ let wake = null;
5570
+ const push = (event) => {
5571
+ queue.push(event);
5572
+ wake?.();
5573
+ };
5574
+ const stop = this.listen(conversationPath, push);
5575
+ const onAbort = () => push({ type: "failed", error: "aborted", code: "ABORTED", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
5576
+ options.signal?.addEventListener("abort", onAbort, { once: true });
5577
+ const timeout = options.inactivityTimeoutMs ?? 12e4;
5578
+ try {
5579
+ try {
5580
+ await this.post(conversationPath, text);
5581
+ } catch (err) {
5582
+ const e = asError(err);
5583
+ yield { type: "failed", error: e.message, code: e.code, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
5584
+ return;
5585
+ }
5586
+ for (; ; ) {
5587
+ if (!queue.length) {
5588
+ const idle = await new Promise((resolve) => {
5589
+ const timer = timeout > 0 ? setTimeout(() => resolve(true), timeout) : null;
5590
+ wake = () => {
5591
+ if (timer) clearTimeout(timer);
5592
+ resolve(false);
5593
+ };
5594
+ });
5595
+ wake = null;
5596
+ if (idle && !queue.length) {
5597
+ yield { type: "waiting", timestamp: (/* @__PURE__ */ new Date()).toISOString() };
5598
+ return;
5599
+ }
5600
+ }
5601
+ const event = queue.shift();
5602
+ yield event;
5603
+ if (event.type === "done" || event.type === "failed" || event.type === "waiting") return;
5604
+ }
5605
+ } finally {
5606
+ stop();
5607
+ options.signal?.removeEventListener("abort", onAbort);
5608
+ }
5609
+ }
5610
+ /** Whether the store remembers a conversation with `agent`. */
5611
+ remembers(agent, options = {}) {
5612
+ const store = this.storeOf(options);
5613
+ return !!store?.get(`${this.keyOf(agent, options)}:conversation`);
5614
+ }
5615
+ /** Forget the stored session (e.g. a "new chat" button that should start a new session). */
5616
+ forget(agent, options = {}) {
5617
+ const store = this.storeOf(options);
5618
+ const key = this.keyOf(agent, options);
5619
+ store?.remove(`${key}:session`);
5620
+ store?.remove(`${key}:conversation`);
5621
+ this.token = null;
5622
+ }
5623
+ };
5624
+
5267
5625
  // src/flow-client.ts
5268
5626
  var FlowClient = class _FlowClient {
5269
5627
  /**
@@ -6156,7 +6514,13 @@ var Database = class _Database {
6156
6514
  this.repository,
6157
6515
  this.httpOptions.authManager,
6158
6516
  {},
6159
- (query, params) => this.executeSql(query, params)
6517
+ (query, params) => this.executeSql(query, params),
6518
+ new VisitorChat(
6519
+ this.repository,
6520
+ (payload, requestType) => this.sendRequest(payload, requestType),
6521
+ this.eventHandler,
6522
+ this.httpOptions.onReconnected
6523
+ )
6160
6524
  );
6161
6525
  }
6162
6526
  return this._conversationManager;
@@ -10023,7 +10387,11 @@ var _RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
10023
10387
  void 0,
10024
10388
  () => this.getUploadManager(),
10025
10389
  (options) => this.signAssetUrl(options),
10026
- { httpBaseUrl: this._httpBaseUrl, authManager: this.authManager }
10390
+ {
10391
+ httpBaseUrl: this._httpBaseUrl,
10392
+ authManager: this.authManager,
10393
+ onReconnected: (callback) => this.onReconnected(callback)
10394
+ }
10027
10395
  );
10028
10396
  }
10029
10397
  /**
@@ -11403,6 +11771,34 @@ var ConversationStore = class {
11403
11771
  this._watchdogIntervalMs = options.watchdogIntervalMs ?? 3e4;
11404
11772
  if (this._conversationPath) {
11405
11773
  this.ensureSubscription();
11774
+ } else if (this.createOptions?.anonymous) {
11775
+ void this.resumeAnonymous();
11776
+ }
11777
+ }
11778
+ anonymousOptions() {
11779
+ const a = this.createOptions?.anonymous;
11780
+ if (!a) return null;
11781
+ return a === true ? {} : a;
11782
+ }
11783
+ /**
11784
+ * Resume the visitor conversation this tab already has (the session secret
11785
+ * and conversation id live in sessionStorage), and load its history. A tab
11786
+ * without one starts nothing until the first message.
11787
+ */
11788
+ async resumeAnonymous() {
11789
+ const opts = this.anonymousOptions();
11790
+ if (!opts || !this.createOptions || this._conversationPath) return;
11791
+ const agent = this.createOptions.participant;
11792
+ try {
11793
+ if (!this.manager.hasStoredAnonymous(agent, opts)) return;
11794
+ const convo = await this.manager.startAnonymous(agent, opts);
11795
+ if (this._conversationPath) return;
11796
+ this._conversationPath = convo.conversationPath;
11797
+ this._conversationType = convo.type;
11798
+ this.ensureSubscription();
11799
+ await this.loadMessages();
11800
+ } catch (err) {
11801
+ logger.debug("[ConversationStore] No visitor conversation to resume", err);
11406
11802
  }
11407
11803
  }
11408
11804
  // ==========================================================================
@@ -11457,7 +11853,8 @@ var ConversationStore = class {
11457
11853
  throw new Error("No conversationPath and no createOptions provided");
11458
11854
  }
11459
11855
  try {
11460
- const convo = await this.manager.create({
11856
+ const anonymous = this.anonymousOptions();
11857
+ const convo = anonymous ? await this.manager.startAnonymous(this.createOptions.participant, anonymous) : await this.manager.create({
11461
11858
  participant: this.createOptions.participant,
11462
11859
  input: this.createOptions.input
11463
11860
  });
@@ -13345,6 +13742,7 @@ function createRaisinVue(vue) {
13345
13742
  UploadErrorCode,
13346
13743
  UploadManager,
13347
13744
  Uploader,
13745
+ VisitorChatError,
13348
13746
  WorkspaceClient,
13349
13747
  WorkspaceManager,
13350
13748
  calculateChunkCount,
@@ -13366,6 +13764,7 @@ function createRaisinVue(vue) {
13366
13764
  createSqlHandler,
13367
13765
  createSubscriptionAdapter,
13368
13766
  decodeMessage,
13767
+ defaultVisitorSessionStore,
13369
13768
  encodeMessage,
13370
13769
  getChunkRange,
13371
13770
  getContentType,