@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.
- package/README.md +32 -0
- package/dist/{chunk-IFV4ECHC.mjs → chunk-ASOWGNQZ.mjs} +3 -3
- package/dist/{chunk-ZUUSBLVP.mjs → chunk-FYWYSNQG.mjs} +3 -3
- package/dist/{chunk-765DLJMZ.mjs → chunk-NOPZAMIV.mjs} +31 -2
- package/dist/chunk-NOPZAMIV.mjs.map +1 -0
- package/dist/{chunk-AULHRX5C.mjs → chunk-TPZNJNZR.mjs} +2 -2
- package/dist/{chunk-VMUT6PIO.mjs → chunk-ZCRYQCXS.mjs} +2 -2
- package/dist/{conversation-list-store-CHaqGtSi.d.mts → conversation-list-store-BNYosL8k.d.mts} +221 -3
- package/dist/{conversation-list-store-CHaqGtSi.d.ts → conversation-list-store-BNYosL8k.d.ts} +221 -3
- package/dist/{index-DiL2gplR.d.ts → index-GaJToZKx.d.ts} +1 -1
- package/dist/{index-AobOnS4A.d.mts → index-UlXuwMoL.d.mts} +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +405 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +378 -10
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +30 -1
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +3 -3
- package/dist/{svelte-sql-FQqis53r.d.ts → svelte-sql-bROcapdk.d.ts} +1 -1
- package/dist/{svelte-sql-BUrNcoHR.d.mts → svelte-sql-yLeGoj89.d.mts} +1 -1
- package/dist/svelte.d.mts +2 -2
- package/dist/svelte.d.ts +2 -2
- package/dist/svelte.js +30 -1
- package/dist/svelte.js.map +1 -1
- package/dist/svelte.mjs +3 -3
- package/dist/vue.d.mts +2 -2
- package/dist/vue.d.ts +2 -2
- package/dist/vue.js +30 -1
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +2 -2
- package/package.json +1 -1
- package/dist/chunk-765DLJMZ.mjs.map +0 -1
- /package/dist/{chunk-IFV4ECHC.mjs.map → chunk-ASOWGNQZ.mjs.map} +0 -0
- /package/dist/{chunk-ZUUSBLVP.mjs.map → chunk-FYWYSNQG.mjs.map} +0 -0
- /package/dist/{chunk-AULHRX5C.mjs.map → chunk-TPZNJNZR.mjs.map} +0 -0
- /package/dist/{chunk-VMUT6PIO.mjs.map → chunk-ZCRYQCXS.mjs.map} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
useConversation,
|
|
4
4
|
useConversationList,
|
|
5
5
|
useFlow
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ASOWGNQZ.mjs";
|
|
7
7
|
import {
|
|
8
8
|
RAISIN_CONTEXT_KEY,
|
|
9
9
|
createConversationAdapter,
|
|
10
10
|
createConversationListAdapter,
|
|
11
11
|
createFlowAdapter
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FYWYSNQG.mjs";
|
|
13
13
|
import {
|
|
14
14
|
FlowClient,
|
|
15
15
|
RaisinAbortError,
|
|
@@ -20,10 +20,10 @@ import {
|
|
|
20
20
|
RaisinTimeoutError,
|
|
21
21
|
SSEClient,
|
|
22
22
|
classifyHttpError
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-TPZNJNZR.mjs";
|
|
24
24
|
import {
|
|
25
25
|
createRaisinVue
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-ZCRYQCXS.mjs";
|
|
27
27
|
import {
|
|
28
28
|
createAuthAdapter,
|
|
29
29
|
createConnectionAdapter,
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
normalizeHomePath,
|
|
49
49
|
projectPlansFromMessages,
|
|
50
50
|
setLogLevel
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-NOPZAMIV.mjs";
|
|
52
52
|
|
|
53
53
|
// src/client.ts
|
|
54
54
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
@@ -405,6 +405,7 @@ var RequestType = /* @__PURE__ */ ((RequestType2) => {
|
|
|
405
405
|
RequestType2["NodeUpdate"] = "node_update";
|
|
406
406
|
RequestType2["NodeDelete"] = "node_delete";
|
|
407
407
|
RequestType2["NodeGet"] = "node_get";
|
|
408
|
+
RequestType2["NodeGetByLocalizedPath"] = "node_get_by_localized_path";
|
|
408
409
|
RequestType2["NodeQuery"] = "node_query";
|
|
409
410
|
RequestType2["NodeQueryByPath"] = "node_query_by_path";
|
|
410
411
|
RequestType2["NodeQueryByProperty"] = "node_query_by_property";
|
|
@@ -506,6 +507,8 @@ var RequestType = /* @__PURE__ */ ((RequestType2) => {
|
|
|
506
507
|
RequestType2["ScheduledInvocationCancel"] = "scheduled_invocation_cancel";
|
|
507
508
|
RequestType2["ScheduledInvocationList"] = "scheduled_invocation_list";
|
|
508
509
|
RequestType2["ScheduledInvocationGet"] = "scheduled_invocation_get";
|
|
510
|
+
RequestType2["VisitorChatStart"] = "visitor_chat_start";
|
|
511
|
+
RequestType2["VisitorChatSend"] = "visitor_chat_send";
|
|
509
512
|
return RequestType2;
|
|
510
513
|
})(RequestType || {});
|
|
511
514
|
var ResponseStatus = /* @__PURE__ */ ((ResponseStatus2) => {
|
|
@@ -789,7 +792,8 @@ var EventHandler = class {
|
|
|
789
792
|
this.filterSubscriptions.set(hash, {
|
|
790
793
|
serverId: subscriptionId,
|
|
791
794
|
filters: {},
|
|
792
|
-
callbacks: /* @__PURE__ */ new Set([callback])
|
|
795
|
+
callbacks: /* @__PURE__ */ new Set([callback]),
|
|
796
|
+
synthetic: true
|
|
793
797
|
});
|
|
794
798
|
this.subscriptions.set(subscriptionId, hash);
|
|
795
799
|
this.callbackToHash.set(callback, hash);
|
|
@@ -824,11 +828,12 @@ var EventHandler = class {
|
|
|
824
828
|
async restoreSubscriptions(options) {
|
|
825
829
|
if (!options?.retry) {
|
|
826
830
|
for (const entry of this.filterSubscriptions.values()) {
|
|
831
|
+
if (entry.synthetic) continue;
|
|
827
832
|
this.subscriptions.delete(entry.serverId);
|
|
828
833
|
entry.restorePending = true;
|
|
829
834
|
}
|
|
830
835
|
}
|
|
831
|
-
const entriesToRestore = Array.from(this.filterSubscriptions.entries()).filter(([, entry]) => entry.restorePending);
|
|
836
|
+
const entriesToRestore = Array.from(this.filterSubscriptions.entries()).filter(([, entry]) => entry.restorePending && !entry.synthetic);
|
|
832
837
|
let failureCount = 0;
|
|
833
838
|
let lastError;
|
|
834
839
|
for (const [hash, entry] of entriesToRestore) {
|
|
@@ -1061,6 +1066,27 @@ var NodeOperations = class {
|
|
|
1061
1066
|
const result = await this.sendRequest(payload, "node_history");
|
|
1062
1067
|
return result ?? [];
|
|
1063
1068
|
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Get a node by its localized URL path (plan Phase 12).
|
|
1071
|
+
*
|
|
1072
|
+
* @param locale - Locale code (`fr`, `fr-CA`)
|
|
1073
|
+
* @param path - The node's path in that locale (`/produits/chaise`)
|
|
1074
|
+
* @returns The translated node with `canonical_path`,
|
|
1075
|
+
* `canonical_localized_path`, hreflang `alternates` and a `redirect`
|
|
1076
|
+
* hint, or `null` when nothing resolves (missing, hidden in the locale
|
|
1077
|
+
* or not readable — the same answer)
|
|
1078
|
+
*
|
|
1079
|
+
* @example
|
|
1080
|
+
* ```typescript
|
|
1081
|
+
* const hit = await ws.nodes().getByLocalizedPath('fr', '/produits/chaise');
|
|
1082
|
+
* if (hit?.redirect) redirect301(`/fr${hit.canonical_localized_path}`);
|
|
1083
|
+
* ```
|
|
1084
|
+
*/
|
|
1085
|
+
async getByLocalizedPath(locale, path) {
|
|
1086
|
+
const payload = { locale, path };
|
|
1087
|
+
const result = await this.sendRequest(payload, "node_get_by_localized_path");
|
|
1088
|
+
return result ?? null;
|
|
1089
|
+
}
|
|
1064
1090
|
/**
|
|
1065
1091
|
* List a node's revision history by path (newest first).
|
|
1066
1092
|
*
|
|
@@ -3585,7 +3611,8 @@ var SearchApi = class {
|
|
|
3585
3611
|
// src/conversations.ts
|
|
3586
3612
|
var DEFAULT_SEND_MESSAGE_INACTIVITY_TIMEOUT_MS = 12e4;
|
|
3587
3613
|
var ConversationManager = class {
|
|
3588
|
-
constructor(baseUrl, repository, authManager, options = {}, executeSql) {
|
|
3614
|
+
constructor(baseUrl, repository, authManager, options = {}, executeSql, visitor) {
|
|
3615
|
+
this.visitor = visitor;
|
|
3589
3616
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
3590
3617
|
this.repository = repository;
|
|
3591
3618
|
this.authManager = authManager;
|
|
@@ -3706,6 +3733,45 @@ var ConversationManager = class {
|
|
|
3706
3733
|
]
|
|
3707
3734
|
};
|
|
3708
3735
|
}
|
|
3736
|
+
/**
|
|
3737
|
+
* Start (or resume) an ANONYMOUS conversation with an agent — a public
|
|
3738
|
+
* website's chat, without login.
|
|
3739
|
+
*
|
|
3740
|
+
* Works on an anonymous connection only, and only with an agent that allows
|
|
3741
|
+
* anonymous visitors (`anonymous.enabled: true` on its raisin:AIAgent). The
|
|
3742
|
+
* server keeps the conversation in an ephemeral visitor session that only
|
|
3743
|
+
* this browser session can read; the session secret is kept in
|
|
3744
|
+
* sessionStorage (override with `options.store`), so a reload continues the
|
|
3745
|
+
* same conversation. Then use `sendMessage`, `getMessages`, `subscribe` and
|
|
3746
|
+
* `ConversationStore` / `useConversation` exactly as for a signed-in user.
|
|
3747
|
+
*
|
|
3748
|
+
* @example
|
|
3749
|
+
* ```typescript
|
|
3750
|
+
* const convo = await db.conversations.startAnonymous('/agents/website-assistant');
|
|
3751
|
+
* for await (const ev of db.conversations.sendMessage(convo.conversationPath, 'Hello')) {
|
|
3752
|
+
* if (ev.type === 'text_chunk') append(ev.text);
|
|
3753
|
+
* if (ev.type === 'failed') showLimit(ev.code); // e.g. RATE_LIMITED, LIMIT_REACHED
|
|
3754
|
+
* }
|
|
3755
|
+
* ```
|
|
3756
|
+
*/
|
|
3757
|
+
async startAnonymous(agent, options) {
|
|
3758
|
+
if (!this.visitor) {
|
|
3759
|
+
throw new Error("Anonymous chat needs a WebSocket database: use client.database(repo).");
|
|
3760
|
+
}
|
|
3761
|
+
return this.visitor.start(this.normalizeAgentPath(agent), options);
|
|
3762
|
+
}
|
|
3763
|
+
/** Forget the stored anonymous session for `agent` (the next start begins a new one). */
|
|
3764
|
+
forgetAnonymous(agent, options) {
|
|
3765
|
+
this.visitor?.forget(this.normalizeAgentPath(agent), options);
|
|
3766
|
+
}
|
|
3767
|
+
/** Whether this tab remembers an anonymous conversation with `agent`. */
|
|
3768
|
+
hasStoredAnonymous(agent, options) {
|
|
3769
|
+
return this.visitor?.remembers(this.normalizeAgentPath(agent), options) ?? false;
|
|
3770
|
+
}
|
|
3771
|
+
/** Whether `conversationPath` is an anonymous conversation of this client. */
|
|
3772
|
+
isAnonymous(conversationPath) {
|
|
3773
|
+
return this.visitor?.isAnonymous(conversationPath) ?? false;
|
|
3774
|
+
}
|
|
3709
3775
|
/**
|
|
3710
3776
|
* Open an existing conversation by path.
|
|
3711
3777
|
* Returns null if not found.
|
|
@@ -3751,6 +3817,7 @@ var ConversationManager = class {
|
|
|
3751
3817
|
* Mark a conversation as read by setting unread_count to 0.
|
|
3752
3818
|
*/
|
|
3753
3819
|
async markAsRead(conversationPath) {
|
|
3820
|
+
if (this.isAnonymous(conversationPath)) return;
|
|
3754
3821
|
const workspace = "raisin:access_control";
|
|
3755
3822
|
await this.sqlQuery(
|
|
3756
3823
|
`UPDATE '${workspace}' SET properties = jsonb_set(properties, '{unread_count}', '0'::jsonb) WHERE path = $1 AND node_type = 'raisin:Conversation'`,
|
|
@@ -3761,6 +3828,7 @@ var ConversationManager = class {
|
|
|
3761
3828
|
* Mark a single message as read by the current user.
|
|
3762
3829
|
*/
|
|
3763
3830
|
async markMessageAsRead(messagePath) {
|
|
3831
|
+
if (messagePath.startsWith("/visitors/")) return;
|
|
3764
3832
|
const workspace = "raisin:access_control";
|
|
3765
3833
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3766
3834
|
await this.sqlQuery(
|
|
@@ -3785,6 +3853,18 @@ var ConversationManager = class {
|
|
|
3785
3853
|
*/
|
|
3786
3854
|
async *sendMessage(conversationPath, content, options) {
|
|
3787
3855
|
const shouldStream = options?.stream !== false;
|
|
3856
|
+
if (this.visitor?.isAnonymous(conversationPath)) {
|
|
3857
|
+
if (!shouldStream) {
|
|
3858
|
+
await this.visitor.post(conversationPath, content);
|
|
3859
|
+
yield { type: "waiting", timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3860
|
+
return;
|
|
3861
|
+
}
|
|
3862
|
+
yield* this.visitor.stream(conversationPath, content, {
|
|
3863
|
+
signal: options?.signal,
|
|
3864
|
+
inactivityTimeoutMs: options?.inactivityTimeoutMs ?? DEFAULT_SEND_MESSAGE_INACTIVITY_TIMEOUT_MS
|
|
3865
|
+
});
|
|
3866
|
+
return;
|
|
3867
|
+
}
|
|
3788
3868
|
logger.debug("[ConversationManager] Starting SSE stream for message send");
|
|
3789
3869
|
if (!shouldStream) {
|
|
3790
3870
|
await this.createUserMessage(conversationPath, content);
|
|
@@ -3872,6 +3952,10 @@ var ConversationManager = class {
|
|
|
3872
3952
|
*/
|
|
3873
3953
|
async createUserMessage(conversationPath, content) {
|
|
3874
3954
|
logger.debug("[ConversationManager] Creating user message", { path: conversationPath });
|
|
3955
|
+
if (this.visitor?.isAnonymous(conversationPath)) {
|
|
3956
|
+
await this.visitor.post(conversationPath, content);
|
|
3957
|
+
return;
|
|
3958
|
+
}
|
|
3875
3959
|
const workspace = "raisin:access_control";
|
|
3876
3960
|
const properties = await this.loadConversationProperties(conversationPath, workspace);
|
|
3877
3961
|
const flowInstanceId = typeof properties?.flow_instance_id === "string" ? properties.flow_instance_id.trim() : "";
|
|
@@ -3923,6 +4007,12 @@ var ConversationManager = class {
|
|
|
3923
4007
|
* initiated messages, etc.). Auto-reconnects on disconnect.
|
|
3924
4008
|
*/
|
|
3925
4009
|
subscribe(conversationPath, onEvent, options) {
|
|
4010
|
+
if (this.visitor?.isAnonymous(conversationPath)) {
|
|
4011
|
+
const stop = this.visitor.listen(conversationPath, onEvent);
|
|
4012
|
+
options?.signal?.addEventListener("abort", stop, { once: true });
|
|
4013
|
+
return { unsubscribe: stop, waitUntilConnected: async () => {
|
|
4014
|
+
} };
|
|
4015
|
+
}
|
|
3926
4016
|
logger.info("[ConversationManager] Subscribing to SSE", { path: conversationPath });
|
|
3927
4017
|
const sseUrl = `${this.baseUrl}/api/conversations/${this.repository}/events`;
|
|
3928
4018
|
const headers = {};
|
|
@@ -4362,6 +4452,272 @@ var ConversationManager = class {
|
|
|
4362
4452
|
}
|
|
4363
4453
|
};
|
|
4364
4454
|
|
|
4455
|
+
// src/visitor-chat.ts
|
|
4456
|
+
function defaultVisitorSessionStore() {
|
|
4457
|
+
const memory = /* @__PURE__ */ new Map();
|
|
4458
|
+
const storage = () => {
|
|
4459
|
+
try {
|
|
4460
|
+
return typeof sessionStorage !== "undefined" ? sessionStorage : null;
|
|
4461
|
+
} catch {
|
|
4462
|
+
return null;
|
|
4463
|
+
}
|
|
4464
|
+
};
|
|
4465
|
+
return {
|
|
4466
|
+
get(key) {
|
|
4467
|
+
try {
|
|
4468
|
+
return storage()?.getItem(key) ?? memory.get(key) ?? null;
|
|
4469
|
+
} catch {
|
|
4470
|
+
return memory.get(key) ?? null;
|
|
4471
|
+
}
|
|
4472
|
+
},
|
|
4473
|
+
set(key, value) {
|
|
4474
|
+
memory.set(key, value);
|
|
4475
|
+
try {
|
|
4476
|
+
storage()?.setItem(key, value);
|
|
4477
|
+
} catch {
|
|
4478
|
+
}
|
|
4479
|
+
},
|
|
4480
|
+
remove(key) {
|
|
4481
|
+
memory.delete(key);
|
|
4482
|
+
try {
|
|
4483
|
+
storage()?.removeItem(key);
|
|
4484
|
+
} catch {
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4487
|
+
};
|
|
4488
|
+
}
|
|
4489
|
+
var VisitorChatError = class extends Error {
|
|
4490
|
+
constructor(message, code) {
|
|
4491
|
+
super(message);
|
|
4492
|
+
this.name = "VisitorChatError";
|
|
4493
|
+
this.code = code;
|
|
4494
|
+
}
|
|
4495
|
+
};
|
|
4496
|
+
var REBIND_CODES = /* @__PURE__ */ new Set(["NO_SESSION", "UNKNOWN_CONVERSATION"]);
|
|
4497
|
+
function asError(err) {
|
|
4498
|
+
const e = err;
|
|
4499
|
+
return new VisitorChatError(e?.message ?? String(err), e?.code ?? "UNAVAILABLE");
|
|
4500
|
+
}
|
|
4501
|
+
var VisitorChat = class {
|
|
4502
|
+
constructor(repository, send, events, onReconnected) {
|
|
4503
|
+
this.repository = repository;
|
|
4504
|
+
this.send = send;
|
|
4505
|
+
this.events = events;
|
|
4506
|
+
this.byPath = /* @__PURE__ */ new Map();
|
|
4507
|
+
this.token = null;
|
|
4508
|
+
onReconnected?.(() => {
|
|
4509
|
+
void this.rebindAll();
|
|
4510
|
+
});
|
|
4511
|
+
}
|
|
4512
|
+
/** Whether `conversationPath` is a visitor conversation started here. */
|
|
4513
|
+
isAnonymous(conversationPath) {
|
|
4514
|
+
return this.byPath.has(conversationPath);
|
|
4515
|
+
}
|
|
4516
|
+
storeOf(options) {
|
|
4517
|
+
if (options.store === false) return null;
|
|
4518
|
+
return options.store ?? defaultVisitorSessionStore();
|
|
4519
|
+
}
|
|
4520
|
+
keyOf(agent, options) {
|
|
4521
|
+
return options.storageKey ?? `raisindb:visitor:${this.repository}:${agent}`;
|
|
4522
|
+
}
|
|
4523
|
+
/** Start (or resume) a visitor conversation with `agent` (`/agents/<name>`). */
|
|
4524
|
+
async start(agent, options = {}) {
|
|
4525
|
+
const store = this.storeOf(options);
|
|
4526
|
+
const key = this.keyOf(agent, options);
|
|
4527
|
+
const sessionToken = options.sessionToken ?? this.token ?? store?.get(`${key}:session`) ?? void 0;
|
|
4528
|
+
const conversationId = options.newConversation ? void 0 : options.conversationId ?? store?.get(`${key}:conversation`) ?? void 0;
|
|
4529
|
+
let res;
|
|
4530
|
+
try {
|
|
4531
|
+
res = await this.send(
|
|
4532
|
+
{ agent, session_token: sessionToken, conversation_id: conversationId },
|
|
4533
|
+
"visitor_chat_start"
|
|
4534
|
+
);
|
|
4535
|
+
} catch (err) {
|
|
4536
|
+
const e = asError(err);
|
|
4537
|
+
if ((conversationId || sessionToken) && !options.conversationId && !options.sessionToken && ["UNKNOWN_CONVERSATION", "INVALID_CONVERSATION", "SESSION_EXPIRED", "SESSION_MISMATCH"].includes(e.code)) {
|
|
4538
|
+
store?.remove(`${key}:conversation`);
|
|
4539
|
+
if (e.code !== "UNKNOWN_CONVERSATION" && e.code !== "INVALID_CONVERSATION") {
|
|
4540
|
+
store?.remove(`${key}:session`);
|
|
4541
|
+
this.token = null;
|
|
4542
|
+
}
|
|
4543
|
+
return this.start(agent, { ...options, newConversation: true });
|
|
4544
|
+
}
|
|
4545
|
+
throw e;
|
|
4546
|
+
}
|
|
4547
|
+
const token = res.session_token ?? sessionToken ?? this.token;
|
|
4548
|
+
if (!token) throw new VisitorChatError("the server returned no session", "UNAVAILABLE");
|
|
4549
|
+
this.token = token;
|
|
4550
|
+
store?.set(`${key}:session`, token);
|
|
4551
|
+
store?.set(`${key}:conversation`, res.conversation_id);
|
|
4552
|
+
const path = res.conversation_path;
|
|
4553
|
+
let entry = this.byPath.get(path);
|
|
4554
|
+
if (!entry) {
|
|
4555
|
+
entry = {
|
|
4556
|
+
agent: res.agent,
|
|
4557
|
+
conversationId: res.conversation_id,
|
|
4558
|
+
subscriptionId: res.subscription_id,
|
|
4559
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
4560
|
+
options
|
|
4561
|
+
};
|
|
4562
|
+
this.byPath.set(path, entry);
|
|
4563
|
+
}
|
|
4564
|
+
this.route(entry);
|
|
4565
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4566
|
+
return {
|
|
4567
|
+
id: res.conversation_id,
|
|
4568
|
+
type: "ai_chat",
|
|
4569
|
+
agentRef: res.agent,
|
|
4570
|
+
conversationPath: path,
|
|
4571
|
+
conversationWorkspace: res.conversation_workspace,
|
|
4572
|
+
anonymous: true,
|
|
4573
|
+
conversationId: res.conversation_id,
|
|
4574
|
+
sessionToken: token,
|
|
4575
|
+
limits: res.limits,
|
|
4576
|
+
initialEvents: [{ type: "waiting", timestamp: now }]
|
|
4577
|
+
};
|
|
4578
|
+
}
|
|
4579
|
+
/** Route the server's forwarded events for this conversation to its listeners. */
|
|
4580
|
+
route(entry) {
|
|
4581
|
+
this.events.removeFlowEventListener(entry.subscriptionId);
|
|
4582
|
+
this.events.addFlowEventListener(entry.subscriptionId, (msg) => {
|
|
4583
|
+
const event = msg.payload;
|
|
4584
|
+
if (!event || typeof event.type !== "string") return;
|
|
4585
|
+
for (const listener of entry.listeners) {
|
|
4586
|
+
try {
|
|
4587
|
+
listener(event);
|
|
4588
|
+
} catch (err) {
|
|
4589
|
+
logger.error("[VisitorChat] listener failed", err);
|
|
4590
|
+
}
|
|
4591
|
+
}
|
|
4592
|
+
});
|
|
4593
|
+
}
|
|
4594
|
+
/** Re-prove the session on a new connection, for every conversation started here. */
|
|
4595
|
+
async rebindAll() {
|
|
4596
|
+
for (const entry of this.byPath.values()) {
|
|
4597
|
+
try {
|
|
4598
|
+
await this.send(
|
|
4599
|
+
{ agent: entry.agent, session_token: this.token, conversation_id: entry.conversationId },
|
|
4600
|
+
"visitor_chat_start"
|
|
4601
|
+
);
|
|
4602
|
+
this.route(entry);
|
|
4603
|
+
} catch (err) {
|
|
4604
|
+
logger.warn("[VisitorChat] could not resume a conversation after reconnect", err);
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
}
|
|
4608
|
+
/** Listen to a visitor conversation's events. Returns an unsubscribe function. */
|
|
4609
|
+
listen(conversationPath, listener) {
|
|
4610
|
+
const entry = this.require(conversationPath);
|
|
4611
|
+
entry.listeners.add(listener);
|
|
4612
|
+
return () => {
|
|
4613
|
+
entry.listeners.delete(listener);
|
|
4614
|
+
};
|
|
4615
|
+
}
|
|
4616
|
+
require(conversationPath) {
|
|
4617
|
+
const entry = this.byPath.get(conversationPath);
|
|
4618
|
+
if (!entry) {
|
|
4619
|
+
throw new VisitorChatError(
|
|
4620
|
+
"not a visitor conversation of this client; call startAnonymous first",
|
|
4621
|
+
"UNKNOWN_CONVERSATION"
|
|
4622
|
+
);
|
|
4623
|
+
}
|
|
4624
|
+
return entry;
|
|
4625
|
+
}
|
|
4626
|
+
/** Make sure this connection is bound (after a reconnect nothing else may read the home). */
|
|
4627
|
+
async ensureBound(conversationPath) {
|
|
4628
|
+
const entry = this.require(conversationPath);
|
|
4629
|
+
await this.send(
|
|
4630
|
+
{ agent: entry.agent, session_token: this.token, conversation_id: entry.conversationId },
|
|
4631
|
+
"visitor_chat_start"
|
|
4632
|
+
).catch((err) => {
|
|
4633
|
+
throw asError(err);
|
|
4634
|
+
});
|
|
4635
|
+
}
|
|
4636
|
+
/** Send one visitor message. Events arrive through {@link listen}. */
|
|
4637
|
+
async post(conversationPath, text) {
|
|
4638
|
+
const entry = this.require(conversationPath);
|
|
4639
|
+
const once = async () => await this.send(
|
|
4640
|
+
{ conversation_id: entry.conversationId, text },
|
|
4641
|
+
"visitor_chat_send"
|
|
4642
|
+
);
|
|
4643
|
+
try {
|
|
4644
|
+
const res = await once();
|
|
4645
|
+
return { messageId: res.message_id, messagesLeft: res.messages_left };
|
|
4646
|
+
} catch (err) {
|
|
4647
|
+
const e = asError(err);
|
|
4648
|
+
if (!REBIND_CODES.has(e.code)) throw e;
|
|
4649
|
+
await this.ensureBound(conversationPath);
|
|
4650
|
+
this.route(entry);
|
|
4651
|
+
const res = await once().catch((again) => {
|
|
4652
|
+
throw asError(again);
|
|
4653
|
+
});
|
|
4654
|
+
return { messageId: res.message_id, messagesLeft: res.messages_left };
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4657
|
+
/**
|
|
4658
|
+
* Send one visitor message and stream the turn: `text_chunk`,
|
|
4659
|
+
* `tool_call_started`, `tool_call_completed`, … until `done`. A refusal
|
|
4660
|
+
* ends the stream with `failed` (its `code` says why). No event for
|
|
4661
|
+
* `inactivityTimeoutMs` ends it with a synthetic `waiting`.
|
|
4662
|
+
*/
|
|
4663
|
+
async *stream(conversationPath, text, options = {}) {
|
|
4664
|
+
const queue = [];
|
|
4665
|
+
let wake = null;
|
|
4666
|
+
const push = (event) => {
|
|
4667
|
+
queue.push(event);
|
|
4668
|
+
wake?.();
|
|
4669
|
+
};
|
|
4670
|
+
const stop = this.listen(conversationPath, push);
|
|
4671
|
+
const onAbort = () => push({ type: "failed", error: "aborted", code: "ABORTED", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
4672
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
4673
|
+
const timeout = options.inactivityTimeoutMs ?? 12e4;
|
|
4674
|
+
try {
|
|
4675
|
+
try {
|
|
4676
|
+
await this.post(conversationPath, text);
|
|
4677
|
+
} catch (err) {
|
|
4678
|
+
const e = asError(err);
|
|
4679
|
+
yield { type: "failed", error: e.message, code: e.code, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4680
|
+
return;
|
|
4681
|
+
}
|
|
4682
|
+
for (; ; ) {
|
|
4683
|
+
if (!queue.length) {
|
|
4684
|
+
const idle = await new Promise((resolve) => {
|
|
4685
|
+
const timer = timeout > 0 ? setTimeout(() => resolve(true), timeout) : null;
|
|
4686
|
+
wake = () => {
|
|
4687
|
+
if (timer) clearTimeout(timer);
|
|
4688
|
+
resolve(false);
|
|
4689
|
+
};
|
|
4690
|
+
});
|
|
4691
|
+
wake = null;
|
|
4692
|
+
if (idle && !queue.length) {
|
|
4693
|
+
yield { type: "waiting", timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4694
|
+
return;
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
const event = queue.shift();
|
|
4698
|
+
yield event;
|
|
4699
|
+
if (event.type === "done" || event.type === "failed" || event.type === "waiting") return;
|
|
4700
|
+
}
|
|
4701
|
+
} finally {
|
|
4702
|
+
stop();
|
|
4703
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4706
|
+
/** Whether the store remembers a conversation with `agent`. */
|
|
4707
|
+
remembers(agent, options = {}) {
|
|
4708
|
+
const store = this.storeOf(options);
|
|
4709
|
+
return !!store?.get(`${this.keyOf(agent, options)}:conversation`);
|
|
4710
|
+
}
|
|
4711
|
+
/** Forget the stored session (e.g. a "new chat" button that should start a new session). */
|
|
4712
|
+
forget(agent, options = {}) {
|
|
4713
|
+
const store = this.storeOf(options);
|
|
4714
|
+
const key = this.keyOf(agent, options);
|
|
4715
|
+
store?.remove(`${key}:session`);
|
|
4716
|
+
store?.remove(`${key}:conversation`);
|
|
4717
|
+
this.token = null;
|
|
4718
|
+
}
|
|
4719
|
+
};
|
|
4720
|
+
|
|
4365
4721
|
// src/inbox.ts
|
|
4366
4722
|
var InboxApi = class {
|
|
4367
4723
|
constructor(baseUrl, repository, authManager, options = {}) {
|
|
@@ -4816,7 +5172,13 @@ var Database = class _Database {
|
|
|
4816
5172
|
this.repository,
|
|
4817
5173
|
this.httpOptions.authManager,
|
|
4818
5174
|
{},
|
|
4819
|
-
(query, params) => this.executeSql(query, params)
|
|
5175
|
+
(query, params) => this.executeSql(query, params),
|
|
5176
|
+
new VisitorChat(
|
|
5177
|
+
this.repository,
|
|
5178
|
+
(payload, requestType) => this.sendRequest(payload, requestType),
|
|
5179
|
+
this.eventHandler,
|
|
5180
|
+
this.httpOptions.onReconnected
|
|
5181
|
+
)
|
|
4820
5182
|
);
|
|
4821
5183
|
}
|
|
4822
5184
|
return this._conversationManager;
|
|
@@ -8683,7 +9045,11 @@ var _RaisinClient = class _RaisinClient extends EventEmitter2 {
|
|
|
8683
9045
|
void 0,
|
|
8684
9046
|
() => this.getUploadManager(),
|
|
8685
9047
|
(options) => this.signAssetUrl(options),
|
|
8686
|
-
{
|
|
9048
|
+
{
|
|
9049
|
+
httpBaseUrl: this._httpBaseUrl,
|
|
9050
|
+
authManager: this.authManager,
|
|
9051
|
+
onReconnected: (callback) => this.onReconnected(callback)
|
|
9052
|
+
}
|
|
8687
9053
|
);
|
|
8688
9054
|
}
|
|
8689
9055
|
/**
|
|
@@ -9940,6 +10306,7 @@ export {
|
|
|
9940
10306
|
UploadErrorCode,
|
|
9941
10307
|
UploadManager,
|
|
9942
10308
|
Uploader,
|
|
10309
|
+
VisitorChatError,
|
|
9943
10310
|
WorkspaceClient,
|
|
9944
10311
|
WorkspaceManager,
|
|
9945
10312
|
calculateChunkCount,
|
|
@@ -9961,6 +10328,7 @@ export {
|
|
|
9961
10328
|
createSqlHandler,
|
|
9962
10329
|
createSubscriptionAdapter,
|
|
9963
10330
|
decodeMessage,
|
|
10331
|
+
defaultVisitorSessionStore,
|
|
9964
10332
|
encodeMessage,
|
|
9965
10333
|
getChunkRange,
|
|
9966
10334
|
getContentType,
|