@vinaverse/client 0.1.0-alpha.6 → 0.1.0-alpha.8

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 (49) hide show
  1. package/README.md +69 -7
  2. package/dist/agent/index.cjs +526 -75
  3. package/dist/agent/index.cjs.map +1 -1
  4. package/dist/agent/index.d.cts +3 -3
  5. package/dist/agent/index.d.ts +3 -3
  6. package/dist/agent/index.js +2 -2
  7. package/dist/backend/index.cjs +268 -0
  8. package/dist/backend/index.cjs.map +1 -0
  9. package/dist/backend/index.d.cts +72 -0
  10. package/dist/backend/index.d.ts +72 -0
  11. package/dist/backend/index.js +250 -0
  12. package/dist/backend/index.js.map +1 -0
  13. package/dist/{chunk-ZWG2TMUL.js → chunk-46D3FSLU.js} +24 -2
  14. package/dist/chunk-46D3FSLU.js.map +1 -0
  15. package/dist/chunk-DI4DPUNA.js +843 -0
  16. package/dist/chunk-DI4DPUNA.js.map +1 -0
  17. package/dist/chunk-YNUHXJD6.js +9 -0
  18. package/dist/{chunk-VSJDVLCP.js.map → chunk-YNUHXJD6.js.map} +1 -1
  19. package/dist/contract-DmQs8amJ.d.cts +6 -0
  20. package/dist/contract-DmQs8amJ.d.ts +6 -0
  21. package/dist/conversation/index.cjs +24 -2
  22. package/dist/conversation/index.cjs.map +1 -1
  23. package/dist/conversation/index.d.cts +5 -5
  24. package/dist/conversation/index.d.ts +5 -5
  25. package/dist/conversation/index.js +3 -3
  26. package/dist/index.cjs +2 -2
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +2 -2
  29. package/dist/index.d.ts +2 -2
  30. package/dist/index.js +1 -1
  31. package/dist/{runtime-DLJbJkSr.d.ts → runtime-A4ovUdrc.d.cts} +147 -21
  32. package/dist/{runtime-CErMdYxJ.d.cts → runtime-B6_l8--U.d.ts} +147 -21
  33. package/dist/{session-8W3fRNeb.d.cts → session-CPz4pcPu.d.cts} +246 -7
  34. package/dist/{session-8W3fRNeb.d.ts → session-CPz4pcPu.d.ts} +246 -7
  35. package/dist/{store-C7FtS5AY.d.cts → store-BxiicOKG.d.cts} +32 -2
  36. package/dist/{store-DFk0ayh8.d.ts → store-DZka9T2-.d.ts} +32 -2
  37. package/dist/testing/index.cjs +38 -1
  38. package/dist/testing/index.cjs.map +1 -1
  39. package/dist/testing/index.d.cts +15 -4
  40. package/dist/testing/index.d.ts +15 -4
  41. package/dist/testing/index.js +40 -3
  42. package/dist/testing/index.js.map +1 -1
  43. package/package.json +41 -2
  44. package/dist/chunk-VSJDVLCP.js +0 -9
  45. package/dist/chunk-XPES5PZ3.js +0 -416
  46. package/dist/chunk-XPES5PZ3.js.map +0 -1
  47. package/dist/chunk-ZWG2TMUL.js.map +0 -1
  48. package/dist/contract-_NMdPjgN.d.cts +0 -6
  49. package/dist/contract-_NMdPjgN.d.ts +0 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vinaverse/client",
3
- "version": "0.1.0-alpha.6",
3
+ "version": "0.1.0-alpha.8",
4
4
  "description": "Vinaverse for apps: the realtime session client (WebSocket with SSE fallback, resume, typed events and cards). No UI.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Vinaverse, Inc.",
@@ -23,6 +23,10 @@
23
23
  },
24
24
  "exports": {
25
25
  ".": {
26
+ "react-native": {
27
+ "types": "./dist/index.d.cts",
28
+ "default": "./dist/index.cjs"
29
+ },
26
30
  "import": {
27
31
  "types": "./dist/index.d.ts",
28
32
  "default": "./dist/index.js"
@@ -33,6 +37,10 @@
33
37
  }
34
38
  },
35
39
  "./conversation": {
40
+ "react-native": {
41
+ "types": "./dist/conversation/index.d.cts",
42
+ "default": "./dist/conversation/index.cjs"
43
+ },
36
44
  "import": {
37
45
  "types": "./dist/conversation/index.d.ts",
38
46
  "default": "./dist/conversation/index.js"
@@ -43,6 +51,10 @@
43
51
  }
44
52
  },
45
53
  "./agent": {
54
+ "react-native": {
55
+ "types": "./dist/agent/index.d.cts",
56
+ "default": "./dist/agent/index.cjs"
57
+ },
46
58
  "import": {
47
59
  "types": "./dist/agent/index.d.ts",
48
60
  "default": "./dist/agent/index.js"
@@ -52,7 +64,25 @@
52
64
  "default": "./dist/agent/index.cjs"
53
65
  }
54
66
  },
67
+ "./backend": {
68
+ "react-native": {
69
+ "types": "./dist/backend/index.d.cts",
70
+ "default": "./dist/backend/index.cjs"
71
+ },
72
+ "import": {
73
+ "types": "./dist/backend/index.d.ts",
74
+ "default": "./dist/backend/index.js"
75
+ },
76
+ "require": {
77
+ "types": "./dist/backend/index.d.cts",
78
+ "default": "./dist/backend/index.cjs"
79
+ }
80
+ },
55
81
  "./testing": {
82
+ "react-native": {
83
+ "types": "./dist/testing/index.d.cts",
84
+ "default": "./dist/testing/index.cjs"
85
+ },
56
86
  "import": {
57
87
  "types": "./dist/testing/index.d.ts",
58
88
  "default": "./dist/testing/index.js"
@@ -77,6 +107,9 @@
77
107
  ],
78
108
  "testing": [
79
109
  "./dist/testing/index.d.cts"
110
+ ],
111
+ "backend": [
112
+ "./dist/backend/index.d.cts"
80
113
  ]
81
114
  }
82
115
  },
@@ -104,7 +137,13 @@
104
137
  "name": "@vinaverse/client/agent (ESM, everything, the conversation store included; docs/architecture/agent-runtime.md)",
105
138
  "path": "dist/agent/index.js",
106
139
  "import": "*",
107
- "limit": "15 kB"
140
+ "limit": "17.5 kB"
141
+ },
142
+ {
143
+ "name": "@vinaverse/client/backend (ESM, everything: backendFromFetch and decodeBackendFailure)",
144
+ "path": "dist/backend/index.js",
145
+ "import": "*",
146
+ "limit": "2.25 kB"
108
147
  }
109
148
  ],
110
149
  "scripts": {
@@ -1,9 +0,0 @@
1
- // src/contract.ts
2
- var CONTRACT = {
3
- api: "v1-draft.20",
4
- commit: "5f6f41bdbfbdd6eadf21180eed1ffddfa5310355"
5
- };
6
-
7
- export { CONTRACT };
8
- //# sourceMappingURL=chunk-VSJDVLCP.js.map
9
- //# sourceMappingURL=chunk-VSJDVLCP.js.map
@@ -1,416 +0,0 @@
1
- import { createConversation } from './chunk-ZWG2TMUL.js';
2
-
3
- // src/agent/runtime.ts
4
- var AgentRefusal = class extends Error {
5
- constructor(kind, options = {}) {
6
- super(
7
- options.message ?? (kind === "ended" ? "The conversation is over." : "No agent now.")
8
- );
9
- this.kind = kind;
10
- this.name = "AgentRefusal";
11
- this.reason = options.reason ?? (kind === "ended" ? "ended" : "unknown");
12
- this.code = options.code ?? null;
13
- }
14
- kind;
15
- reason;
16
- code;
17
- };
18
- var NOT_KEPT = /* @__PURE__ */ new Set(["starting", "unavailable", "unauthorized", "closed"]);
19
- var NO_FEATURES = {
20
- history: false,
21
- memory: false,
22
- businessAgent: false,
23
- businessSetup: false
24
- };
25
- var SYSTEM_CLOCK = { now: () => Date.now() };
26
- function agentKey(key) {
27
- return `${key.subject ?? "guest"}|${key.mode}|${key.scope ?? "-"}|${key.thread ?? "chat"}`;
28
- }
29
- var samePlace = (a, b) => a?.screen === b?.screen && (a?.entity?.type ?? null) === (b?.entity?.type ?? null) && (a?.entity?.id ?? null) === (b?.entity?.id ?? null) && JSON.stringify(a?.filters ?? null) === JSON.stringify(b?.filters ?? null);
30
- function usable(session, insecure) {
31
- return typeof session.id === "string" && session.id !== "" && (insecure.realtime === true || /^wss:\/\//i.test(session.realtime_url ?? "")) && (insecure.turns === true || /^https:\/\//i.test(session.turns_url ?? "")) && Boolean(session.client_secret);
32
- }
33
- function warnInsecure(insecure, warn) {
34
- if (!(insecure.realtime || insecure.turns)) return;
35
- if (globalThis.__DEV__ === true) return;
36
- warn(
37
- "@vinaverse/client/agent: insecureAddresses lets sessions use plaintext ws:// or http:// addresses. It is for mocks and local development only; remove it from release builds."
38
- );
39
- }
40
- function createAgentRuntime(options) {
41
- const { backend } = options;
42
- const policy = {
43
- delays: options.retry?.delays ?? [500, 1500, 3500],
44
- isRetryable: options.retry?.isRetryable ?? retryable
45
- };
46
- const clock = options.clock ?? SYSTEM_CLOCK;
47
- const keepAlive = options.keepAlive ?? "session";
48
- const staleMs = options.statusStaleMs ?? 5 * 6e4;
49
- const insecure = options.insecureAddresses ?? {};
50
- warnInsecure(insecure, options.onWarning ?? ((m) => console.warn(m)));
51
- const live = /* @__PURE__ */ new Map();
52
- const current = /* @__PURE__ */ new Map();
53
- const listeners = /* @__PURE__ */ new Set();
54
- let status = null;
55
- let reading = null;
56
- let sessionName = null;
57
- const emit = (event) => options.onEvent?.(event);
58
- const changed = () => {
59
- for (const l of [...listeners]) l();
60
- };
61
- const update = (entry, next) => {
62
- entry.state = { ...entry.state, ...next };
63
- for (const l of [...entry.listeners]) l();
64
- };
65
- const end = (k) => {
66
- const entry = live.get(k);
67
- if (!entry) return;
68
- live.delete(k);
69
- entry.store.dispose();
70
- emit({ type: "conversation_ended", key: k });
71
- changed();
72
- };
73
- const patiently = (run, signal, key) => retrying(
74
- run,
75
- policy,
76
- signal,
77
- (attempt) => emit({ type: "session_retried", key, attempt })
78
- );
79
- async function open(entry, key, request) {
80
- if (live.get(entry.key) !== entry) throw new AgentRefusal("unavailable");
81
- const told = entry.place;
82
- const conversationId = request.conversationId ?? current.get(entry.key) ?? null;
83
- let session;
84
- try {
85
- session = await patiently(
86
- () => backend.openSession({
87
- ...request,
88
- conversationId,
89
- key,
90
- place: told,
91
- entryPoint: entry.entryPoint
92
- }),
93
- request.signal,
94
- entry.key
95
- );
96
- if (!usable(session, insecure))
97
- throw new AgentRefusal("unavailable", {
98
- reason: "invalid_session",
99
- message: "The session is not usable (an id, a secret, a wss:// realtime_url and an https:// turns_url)."
100
- });
101
- } catch (error) {
102
- if (live.get(entry.key) === entry) {
103
- const failure = error instanceof AgentRefusal ? error.kind === "ended" && conversationId !== null ? "ended" : "unavailable" : policy.isRetryable(error) ? "unreachable" : "unavailable";
104
- const reason = reasonOf(error, failure);
105
- const code = error instanceof AgentRefusal ? error.code : codeOf(error);
106
- update(entry, { failure, reason, code });
107
- emit({ type: "session_failed", key: entry.key, failure, reason });
108
- }
109
- throw error;
110
- }
111
- if (live.get(entry.key) !== entry) throw new AgentRefusal("unavailable");
112
- entry.told = told;
113
- entry.expiresAt = session.expires_at ? Date.parse(session.expires_at) : null;
114
- if (session.conversation_id)
115
- current.set(entry.key, session.conversation_id);
116
- const name = session.agent?.display_name?.trim();
117
- if (name && name !== sessionName) {
118
- sessionName = name;
119
- changed();
120
- }
121
- update(entry, {
122
- failure: null,
123
- reason: null,
124
- code: null,
125
- sessionId: session.id,
126
- moved: !samePlace(told, entry.place)
127
- });
128
- emit({
129
- type: conversationId === null ? "session_opened" : "session_resumed",
130
- key: entry.key,
131
- sessionId: session.id,
132
- ...entry.entryPoint ? { from: entry.entryPoint } : {}
133
- });
134
- return session;
135
- }
136
- const kept = (k) => `${options.conversation?.persistenceKey ?? "vinaverse.agent"}.${k}`;
137
- function make(key) {
138
- const k = agentKey(key);
139
- const own = options.conversation ?? {};
140
- const refresh = backend.refreshSecret?.bind(backend);
141
- const entry = {
142
- key: k,
143
- store: createConversation({
144
- ...own,
145
- ...own.persistence ? {
146
- persistenceKey: kept(k)
147
- } : {},
148
- getSession: (request) => open(entry, key, request),
149
- ...refresh ? {
150
- refreshSecret: (id) => patiently(() => refresh(id), void 0, k)
151
- } : {},
152
- clock,
153
- onError: (error) => {
154
- if (!(error instanceof AgentRefusal)) own.onError?.(error);
155
- }
156
- }),
157
- listeners: /* @__PURE__ */ new Set(),
158
- state: {
159
- failure: null,
160
- reason: null,
161
- code: null,
162
- moved: false,
163
- ratings: /* @__PURE__ */ new Map(),
164
- sessionId: null
165
- },
166
- place: null,
167
- told: null,
168
- revision: 0,
169
- entryPoint: null,
170
- expiresAt: null,
171
- shown: 0,
172
- started: false,
173
- suspended: false,
174
- sentOnce: /* @__PURE__ */ new Set()
175
- };
176
- return entry;
177
- }
178
- const over = (entry) => entry.expiresAt !== null && entry.expiresAt <= clock.now();
179
- const tell = (entry, at) => {
180
- entry.place = at.place ?? null;
181
- entry.entryPoint = at.entryPoint ?? null;
182
- const sessionId = entry.state.sessionId;
183
- const moved = sessionId !== null && !samePlace(entry.told, entry.place);
184
- update(entry, { moved });
185
- const place = entry.place;
186
- if (!moved || !place || !backend.updateContext || sessionId === null)
187
- return;
188
- const revision = ++entry.revision;
189
- backend.updateContext(sessionId, place, revision).then(
190
- () => {
191
- if (live.get(entry.key) !== entry || entry.revision !== revision)
192
- return;
193
- entry.told = place;
194
- update(entry, { moved: !samePlace(entry.told, entry.place) });
195
- emit({ type: "context_updated", key: entry.key, sessionId });
196
- },
197
- () => void 0
198
- // not served, or refused: the question about this place stays the way to say it
199
- );
200
- };
201
- function handle(entry) {
202
- return {
203
- key: entry.key,
204
- store: entry.store,
205
- show(at = {}) {
206
- if (live.get(entry.key) !== entry) return () => void 0;
207
- if (entry.shown === 0 && over(entry)) {
208
- end(entry.key);
209
- return () => void 0;
210
- }
211
- entry.shown++;
212
- if (entry.suspended) {
213
- entry.suspended = false;
214
- entry.store.setForeground(true);
215
- }
216
- tell(entry, at);
217
- if (!entry.started) {
218
- entry.started = true;
219
- entry.store.start();
220
- }
221
- let released = false;
222
- return () => {
223
- if (released) return;
224
- released = true;
225
- entry.shown--;
226
- if (entry.shown > 0 || live.get(entry.key) !== entry) return;
227
- if (keepAlive === "view" || NOT_KEPT.has(entry.store.getSnapshot().status) || entry.expiresAt === null || over(entry)) {
228
- end(entry.key);
229
- return;
230
- }
231
- entry.suspended = true;
232
- entry.store.setForeground(false);
233
- };
234
- },
235
- tell: (at) => tell(entry, at),
236
- getState: () => entry.state,
237
- subscribe(listener) {
238
- entry.listeners.add(listener);
239
- return () => entry.listeners.delete(listener);
240
- },
241
- retry() {
242
- update(entry, { failure: null, reason: null, code: null });
243
- entry.store.start();
244
- },
245
- rate(turnId, rating) {
246
- const sessionId = entry.state.sessionId;
247
- if (sessionId === null || entry.state.ratings.get(turnId) === rating)
248
- return;
249
- update(entry, {
250
- ratings: new Map(entry.state.ratings).set(turnId, rating)
251
- });
252
- emit({ type: "rated", key: entry.key, sessionId, turnId, rating });
253
- void backend.sendFeedback?.({ sessionId, turnId, rating }).catch(() => void 0);
254
- },
255
- sendOnce(text) {
256
- if (entry.sentOnce.has(text)) return;
257
- entry.sentOnce.add(text);
258
- entry.store.send(text);
259
- }
260
- };
261
- }
262
- const handles = /* @__PURE__ */ new WeakMap();
263
- const lookup = (key) => {
264
- const k = agentKey(key);
265
- let entry = live.get(k);
266
- if (!entry) {
267
- entry = make(key);
268
- live.set(k, entry);
269
- }
270
- let h = handles.get(entry);
271
- if (!h) {
272
- h = handle(entry);
273
- handles.set(entry, h);
274
- }
275
- return h;
276
- };
277
- return {
278
- conversation(key) {
279
- const k = agentKey(key);
280
- const entry = live.get(k);
281
- if (entry && entry.shown === 0 && over(entry)) end(k);
282
- return lookup(key);
283
- },
284
- peek: lookup,
285
- startNew(key) {
286
- const k = agentKey(key);
287
- current.delete(k);
288
- end(k);
289
- },
290
- dispose(key) {
291
- end(agentKey(key));
292
- },
293
- continue(key, conversationId) {
294
- const k = agentKey(key);
295
- if (current.get(k) !== conversationId) {
296
- current.set(k, conversationId);
297
- const entry = live.get(k);
298
- if (entry) {
299
- entry.store.clear();
300
- end(k);
301
- } else {
302
- try {
303
- void Promise.resolve(
304
- options.conversation?.persistence?.removeItem(kept(k))
305
- ).catch(() => void 0);
306
- } catch {
307
- }
308
- }
309
- }
310
- return lookup(key);
311
- },
312
- async signOut() {
313
- for (const entry of live.values()) entry.store.clear();
314
- live.clear();
315
- current.clear();
316
- sessionName = null;
317
- changed();
318
- await backend.endSessions?.().catch(() => void 0);
319
- },
320
- status() {
321
- if (status && clock.now() - status.at < staleMs)
322
- return Promise.resolve(status.value);
323
- reading ??= readStatus(backend).then(
324
- (value) => {
325
- reading = null;
326
- status = { value, at: clock.now() };
327
- changed();
328
- return value;
329
- },
330
- (error) => {
331
- reading = null;
332
- throw error;
333
- }
334
- );
335
- return reading;
336
- },
337
- getStatus: () => status?.value ?? null,
338
- getName: () => sessionName ?? status?.value.displayName ?? null,
339
- subscribe(listener) {
340
- listeners.add(listener);
341
- return () => listeners.delete(listener);
342
- },
343
- emit
344
- };
345
- }
346
- async function readStatus(backend) {
347
- const read = backend.status ? await backend.status() : { available: true };
348
- const available = read.available === true;
349
- const features = read.features ?? NO_FEATURES;
350
- return {
351
- available,
352
- launcher: available && read.launcher === true,
353
- displayName: read.displayName?.trim() || null,
354
- avatarUrl: read.avatarUrl?.startsWith("https://") ? read.avatarUrl : null,
355
- features: {
356
- history: available && features.history === true,
357
- memory: available && features.memory === true,
358
- businessAgent: available && features.businessAgent === true,
359
- businessSetup: available && features.businessSetup === true
360
- }
361
- };
362
- }
363
- var field = (error, name) => typeof error === "object" && error !== null ? error[name] : void 0;
364
- var retryable = (error) => field(error, "retryable") === true;
365
- var REASONS = /* @__PURE__ */ new Set([
366
- "ended",
367
- "auth",
368
- "off",
369
- "rate_limited",
370
- "network",
371
- "server",
372
- "invalid_session",
373
- "unknown"
374
- ]);
375
- function reasonOf(error, failure) {
376
- if (error instanceof AgentRefusal) return error.reason;
377
- const given = field(error, "reason");
378
- if (typeof given === "string" && REASONS.has(given))
379
- return given;
380
- const status = field(error, "status");
381
- if (status === 401 || status === 403) return "auth";
382
- if (status === 429) return "rate_limited";
383
- if (typeof status === "number" && status >= 500) return "server";
384
- return failure === "unreachable" ? "network" : "unknown";
385
- }
386
- function codeOf(error) {
387
- const code = field(error, "code");
388
- return typeof code === "string" ? code : null;
389
- }
390
- async function retrying(run, policy, signal, onRetry) {
391
- for (let attempt = 0; ; attempt++) {
392
- try {
393
- return await run();
394
- } catch (error) {
395
- const wait = policy.delays[attempt];
396
- if (wait === void 0 || !policy.isRetryable(error) || signal?.aborted)
397
- throw error;
398
- await new Promise((resolve, reject) => {
399
- const timer = setTimeout(() => {
400
- signal?.removeEventListener("abort", stop);
401
- resolve();
402
- }, wait);
403
- function stop() {
404
- clearTimeout(timer);
405
- reject(error);
406
- }
407
- signal?.addEventListener("abort", stop, { once: true });
408
- });
409
- onRetry(attempt + 1);
410
- }
411
- }
412
- }
413
-
414
- export { AgentRefusal, agentKey, createAgentRuntime };
415
- //# sourceMappingURL=chunk-XPES5PZ3.js.map
416
- //# sourceMappingURL=chunk-XPES5PZ3.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/agent/runtime.ts"],"names":[],"mappings":";;;AAmEO,IAAM,YAAA,GAAN,cAA2B,KAAA,CAAM;AAAA,EAGtC,WAAA,CACW,IAAA,EACT,OAAA,GAII,EAAC,EACL;AACA,IAAA,KAAA;AAAA,MACE,OAAA,CAAQ,OAAA,KACL,IAAA,KAAS,OAAA,GAAU,2BAAA,GAA8B,eAAA;AAAA,KACtD;AAVS,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAWT,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,OAAA,CAAQ,MAAA,KAAW,IAAA,KAAS,UAAU,OAAA,GAAU,SAAA,CAAA;AAC9D,IAAA,IAAA,CAAK,IAAA,GAAO,QAAQ,IAAA,IAAQ,IAAA;AAAA,EAC9B;AAAA,EAdW,IAAA;AAAA,EAHF,MAAA;AAAA,EACA,IAAA;AAiBX;AA8MA,IAAM,QAAA,uBAAe,GAAA,CAAI,CAAC,YAAY,aAAA,EAAe,cAAA,EAAgB,QAAQ,CAAC,CAAA;AAC9E,IAAM,WAAA,GAAuC;AAAA,EAC3C,OAAA,EAAS,KAAA;AAAA,EACT,MAAA,EAAQ,KAAA;AAAA,EACR,aAAA,EAAe,KAAA;AAAA,EACf,aAAA,EAAe;AACjB,CAAA;AACA,IAAM,eAA2B,EAAE,GAAA,EAAK,MAAM,IAAA,CAAK,KAAI,EAAE;AAGlD,SAAS,SAAS,GAAA,EAAuB;AAC9C,EAAA,OAAO,CAAA,EAAG,GAAA,CAAI,OAAA,IAAW,OAAO,IAAI,GAAA,CAAI,IAAI,CAAA,CAAA,EAAI,GAAA,CAAI,KAAA,IAAS,GAAG,CAAA,CAAA,EAAI,GAAA,CAAI,UAAU,MAAM,CAAA,CAAA;AAC1F;AAEA,IAAM,SAAA,GAAY,CAAC,CAAA,EAAsB,CAAA,KACvC,GAAG,MAAA,KAAW,CAAA,EAAG,MAAA,IAAA,CAChB,CAAA,EAAG,QAAQ,IAAA,IAAQ,IAAA,OAAW,CAAA,EAAG,MAAA,EAAQ,QAAQ,IAAA,CAAA,IAAA,CACjD,CAAA,EAAG,MAAA,EAAQ,EAAA,IAAM,IAAA,OAAW,CAAA,EAAG,MAAA,EAAQ,EAAA,IAAM,SAC9C,IAAA,CAAK,SAAA,CAAU,CAAA,EAAG,OAAA,IAAW,IAAI,CAAA,KAAM,IAAA,CAAK,SAAA,CAAU,CAAA,EAAG,WAAW,IAAI,CAAA;AAG1E,SAAS,MAAA,CACP,SACA,QAAA,EACS;AACT,EAAA,OACE,OAAO,OAAA,CAAQ,EAAA,KAAO,QAAA,IACtB,OAAA,CAAQ,EAAA,KAAO,EAAA,KACd,QAAA,CAAS,QAAA,KAAa,IAAA,IACrB,YAAA,CAAa,IAAA,CAAK,OAAA,CAAQ,YAAA,IAAgB,EAAE,CAAA,CAAA,KAC7C,QAAA,CAAS,KAAA,KAAU,IAAA,IAAQ,cAAA,CAAe,IAAA,CAAK,OAAA,CAAQ,SAAA,IAAa,EAAE,CAAA,CAAA,IACvE,OAAA,CAAQ,OAAA,CAAQ,aAAa,CAAA;AAEjC;AAGA,SAAS,YAAA,CACP,UACA,IAAA,EACM;AACN,EAAA,IAAI,EAAE,QAAA,CAAS,QAAA,IAAY,QAAA,CAAS,KAAA,CAAA,EAAQ;AAC5C,EAAA,IAAK,UAAA,CAAqC,YAAY,IAAA,EAAM;AAC5D,EAAA,IAAA;AAAA,IACE;AAAA,GACF;AACF;AAkBO,SAAS,mBAAmB,OAAA,EAA4C;AAC7E,EAAA,MAAM,EAAE,SAAQ,GAAI,OAAA;AACpB,EAAA,MAAM,MAAA,GAAqC;AAAA,IACzC,QAAQ,OAAA,CAAQ,KAAA,EAAO,UAAU,CAAC,GAAA,EAAK,MAAM,IAAI,CAAA;AAAA,IACjD,WAAA,EAAa,OAAA,CAAQ,KAAA,EAAO,WAAA,IAAe;AAAA,GAC7C;AACA,EAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,IAAS,YAAA;AAC/B,EAAA,MAAM,SAAA,GAAY,QAAQ,SAAA,IAAa,SAAA;AACvC,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,aAAA,IAAiB,CAAA,GAAI,GAAA;AAC7C,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,iBAAA,IAAqB,EAAC;AAC/C,EAAA,YAAA,CAAa,QAAA,EAAU,QAAQ,SAAA,KAAc,CAAC,MAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,CAAE,CAAA;AACpE,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAmB;AACpC,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAoB;AACxC,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAgB;AACtC,EAAA,IAAI,MAAA,GAAoD,IAAA;AACxD,EAAA,IAAI,OAAA,GAAuC,IAAA;AAC3C,EAAA,IAAI,WAAA,GAA6B,IAAA;AAEjC,EAAA,MAAM,IAAA,GAAO,CAAC,KAAA,KAAsB,OAAA,CAAQ,UAAU,KAAK,CAAA;AAC3D,EAAA,MAAM,UAAU,MAAM;AACpB,IAAA,KAAA,MAAW,CAAA,IAAK,CAAC,GAAG,SAAS,GAAG,CAAA,EAAE;AAAA,EACpC,CAAA;AACA,EAAA,MAAM,MAAA,GAAS,CAAC,KAAA,EAAc,IAAA,KAA0C;AACtE,IAAA,KAAA,CAAM,QAAQ,EAAE,GAAG,KAAA,CAAM,KAAA,EAAO,GAAG,IAAA,EAAK;AACxC,IAAA,KAAA,MAAW,KAAK,CAAC,GAAG,KAAA,CAAM,SAAS,GAAG,CAAA,EAAE;AAAA,EAC1C,CAAA;AAEA,EAAA,MAAM,GAAA,GAAM,CAAC,CAAA,KAAc;AACzB,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA;AACxB,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,IAAA,CAAK,OAAO,CAAC,CAAA;AACb,IAAA,KAAA,CAAM,MAAM,OAAA,EAAQ;AACpB,IAAA,IAAA,CAAK,EAAE,IAAA,EAAM,oBAAA,EAAsB,GAAA,EAAK,GAAG,CAAA;AAC3C,IAAA,OAAA,EAAQ;AAAA,EACV,CAAA;AAEA,EAAA,MAAM,SAAA,GAAY,CAChB,GAAA,EACA,MAAA,EACA,GAAA,KAEA,QAAA;AAAA,IAAS,GAAA;AAAA,IAAK,MAAA;AAAA,IAAQ,MAAA;AAAA,IAAQ,CAAC,YAC7B,IAAA,CAAK,EAAE,MAAM,iBAAA,EAAmB,GAAA,EAAK,SAAS;AAAA,GAChD;AAEF,EAAA,eAAe,IAAA,CACb,KAAA,EACA,GAAA,EACA,OAAA,EAC8B;AAE9B,IAAA,IAAI,IAAA,CAAK,IAAI,KAAA,CAAM,GAAG,MAAM,KAAA,EAAO,MAAM,IAAI,YAAA,CAAa,aAAa,CAAA;AACvE,IAAA,MAAM,OAAO,KAAA,CAAM,KAAA;AAEnB,IAAA,MAAM,iBACJ,OAAA,CAAQ,cAAA,IAAkB,QAAQ,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA,IAAK,IAAA;AACtD,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,MAAM,SAAA;AAAA,QACd,MACE,QAAQ,WAAA,CAAY;AAAA,UAClB,GAAG,OAAA;AAAA,UACH,cAAA;AAAA,UACA,GAAA;AAAA,UACA,KAAA,EAAO,IAAA;AAAA,UACP,YAAY,KAAA,CAAM;AAAA,SACnB,CAAA;AAAA,QACH,OAAA,CAAQ,MAAA;AAAA,QACR,KAAA,CAAM;AAAA,OACR;AACA,MAAA,IAAI,CAAC,MAAA,CAAO,OAAA,EAAS,QAAQ,CAAA;AAC3B,QAAA,MAAM,IAAI,aAAa,aAAA,EAAe;AAAA,UACpC,MAAA,EAAQ,iBAAA;AAAA,UACR,OAAA,EACE;AAAA,SACH,CAAA;AAAA,IACL,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,GAAG,MAAM,KAAA,EAAO;AACjC,QAAA,MAAM,OAAA,GACJ,KAAA,YAAiB,YAAA,GACb,KAAA,CAAM,SAAS,OAAA,IAAW,cAAA,KAAmB,IAAA,GAC3C,OAAA,GACA,aAAA,GACF,MAAA,CAAO,WAAA,CAAY,KAAK,IACtB,aAAA,GACA,aAAA;AACR,QAAA,MAAM,MAAA,GAAS,QAAA,CAAS,KAAA,EAAO,OAAO,CAAA;AACtC,QAAA,MAAM,OAAO,KAAA,YAAiB,YAAA,GAAe,KAAA,CAAM,IAAA,GAAO,OAAO,KAAK,CAAA;AACtE,QAAA,MAAA,CAAO,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,MAAM,CAAA;AACvC,QAAA,IAAA,CAAK,EAAE,MAAM,gBAAA,EAAkB,GAAA,EAAK,MAAM,GAAA,EAAK,OAAA,EAAS,QAAQ,CAAA;AAAA,MAClE;AACA,MAAA,MAAM,KAAA;AAAA,IACR;AAGA,IAAA,IAAI,IAAA,CAAK,IAAI,KAAA,CAAM,GAAG,MAAM,KAAA,EAAO,MAAM,IAAI,YAAA,CAAa,aAAa,CAAA;AACvE,IAAA,KAAA,CAAM,IAAA,GAAO,IAAA;AACb,IAAA,KAAA,CAAM,YAAY,OAAA,CAAQ,UAAA,GACtB,KAAK,KAAA,CAAM,OAAA,CAAQ,UAAU,CAAA,GAC7B,IAAA;AACJ,IAAA,IAAI,OAAA,CAAQ,eAAA;AACV,MAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,GAAA,EAAK,OAAA,CAAQ,eAAe,CAAA;AAChD,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,KAAA,EAAO,YAAA,EAAc,IAAA,EAAK;AAC/C,IAAA,IAAI,IAAA,IAAQ,SAAS,WAAA,EAAa;AAChC,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,OAAA,EAAQ;AAAA,IACV;AACA,IAAA,MAAA,CAAO,KAAA,EAAO;AAAA,MACZ,OAAA,EAAS,IAAA;AAAA,MACT,MAAA,EAAQ,IAAA;AAAA,MACR,IAAA,EAAM,IAAA;AAAA,MACN,WAAW,OAAA,CAAQ,EAAA;AAAA,MACnB,KAAA,EAAO,CAAC,SAAA,CAAU,IAAA,EAAM,MAAM,KAAK;AAAA,KACpC,CAAA;AACD,IAAA,IAAA,CAAK;AAAA,MACH,IAAA,EAAM,cAAA,KAAmB,IAAA,GAAO,gBAAA,GAAmB,iBAAA;AAAA,MACnD,KAAK,KAAA,CAAM,GAAA;AAAA,MACX,WAAW,OAAA,CAAQ,EAAA;AAAA,MACnB,GAAI,MAAM,UAAA,GAAa,EAAE,MAAM,KAAA,CAAM,UAAA,KAAe;AAAC,KACtD,CAAA;AACD,IAAA,OAAO,OAAA;AAAA,EACT;AAGA,EAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KACZ,CAAA,EAAG,QAAQ,YAAA,EAAc,cAAA,IAAkB,iBAAiB,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA;AAEnE,EAAA,SAAS,KAAK,GAAA,EAAsB;AAClC,IAAA,MAAM,CAAA,GAAI,SAAS,GAAG,CAAA;AACtB,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,YAAA,IAAgB,EAAC;AACrC,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,aAAA,EAAe,IAAA,CAAK,OAAO,CAAA;AACnD,IAAA,MAAM,KAAA,GAAe;AAAA,MACnB,GAAA,EAAK,CAAA;AAAA,MACL,OAAO,kBAAA,CAAmB;AAAA,QACxB,GAAG,GAAA;AAAA,QACH,GAAI,IAAI,WAAA,GACJ;AAAA,UACE,cAAA,EAAgB,KAAK,CAAC;AAAA,YAExB,EAAC;AAAA,QACL,YAAY,CAAC,OAAA,KAAY,IAAA,CAAK,KAAA,EAAO,KAAK,OAAO,CAAA;AAAA,QACjD,GAAI,OAAA,GACA;AAAA,UACE,aAAA,EAAe,CAAC,EAAA,KACd,SAAA,CAAU,MAAM,OAAA,CAAQ,EAAE,CAAA,EAAG,MAAA,EAAW,CAAC;AAAA,YAE7C,EAAC;AAAA,QACL,KAAA;AAAA,QACA,OAAA,EAAS,CAAC,KAAA,KAAU;AAElB,UAAA,IAAI,EAAE,KAAA,YAAiB,YAAA,CAAA,EAAe,GAAA,CAAI,UAAU,KAAK,CAAA;AAAA,QAC3D;AAAA,OACD,CAAA;AAAA,MACD,SAAA,sBAAe,GAAA,EAAI;AAAA,MACnB,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,IAAA;AAAA,QACT,MAAA,EAAQ,IAAA;AAAA,QACR,IAAA,EAAM,IAAA;AAAA,QACN,KAAA,EAAO,KAAA;AAAA,QACP,OAAA,sBAAa,GAAA,EAAI;AAAA,QACjB,SAAA,EAAW;AAAA,OACb;AAAA,MACA,KAAA,EAAO,IAAA;AAAA,MACP,IAAA,EAAM,IAAA;AAAA,MACN,QAAA,EAAU,CAAA;AAAA,MACV,UAAA,EAAY,IAAA;AAAA,MACZ,SAAA,EAAW,IAAA;AAAA,MACX,KAAA,EAAO,CAAA;AAAA,MACP,OAAA,EAAS,KAAA;AAAA,MACT,SAAA,EAAW,KAAA;AAAA,MACX,QAAA,sBAAc,GAAA;AAAI,KACpB;AACA,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,IAAA,GAAO,CAAC,KAAA,KACZ,KAAA,CAAM,cAAc,IAAA,IAAQ,KAAA,CAAM,SAAA,IAAa,KAAA,CAAM,GAAA,EAAI;AAG3D,EAAA,MAAM,IAAA,GAAO,CACX,KAAA,EACA,EAAA,KAIG;AACH,IAAA,KAAA,CAAM,KAAA,GAAQ,GAAG,KAAA,IAAS,IAAA;AAC1B,IAAA,KAAA,CAAM,UAAA,GAAa,GAAG,UAAA,IAAc,IAAA;AACpC,IAAA,MAAM,SAAA,GAAY,MAAM,KAAA,CAAM,SAAA;AAC9B,IAAA,MAAM,KAAA,GAAQ,cAAc,IAAA,IAAQ,CAAC,UAAU,KAAA,CAAM,IAAA,EAAM,MAAM,KAAK,CAAA;AACtE,IAAA,MAAA,CAAO,KAAA,EAAO,EAAE,KAAA,EAAO,CAAA;AACvB,IAAA,MAAM,QAAQ,KAAA,CAAM,KAAA;AACpB,IAAA,IAAI,CAAC,KAAA,IAAS,CAAC,SAAS,CAAC,OAAA,CAAQ,iBAAiB,SAAA,KAAc,IAAA;AAC9D,MAAA;AACF,IAAA,MAAM,QAAA,GAAW,EAAE,KAAA,CAAM,QAAA;AACzB,IAAA,OAAA,CAAQ,aAAA,CAAc,SAAA,EAAW,KAAA,EAAO,QAAQ,CAAA,CAAE,IAAA;AAAA,MAChD,MAAM;AACJ,QAAA,IAAI,KAAK,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA,KAAM,KAAA,IAAS,MAAM,QAAA,KAAa,QAAA;AACtD,UAAA;AACF,QAAA,KAAA,CAAM,IAAA,GAAO,KAAA;AACb,QAAA,MAAA,CAAO,KAAA,EAAO,EAAE,KAAA,EAAO,CAAC,SAAA,CAAU,MAAM,IAAA,EAAM,KAAA,CAAM,KAAK,CAAA,EAAG,CAAA;AAC5D,QAAA,IAAA,CAAK,EAAE,IAAA,EAAM,iBAAA,EAAmB,KAAK,KAAA,CAAM,GAAA,EAAK,WAAW,CAAA;AAAA,MAC7D,CAAA;AAAA,MACA,MAAM;AAAA;AAAA,KACR;AAAA,EACF,CAAA;AAEA,EAAA,SAAS,OAAO,KAAA,EAAiC;AAC/C,IAAA,OAAO;AAAA,MACL,KAAK,KAAA,CAAM,GAAA;AAAA,MACX,OAAO,KAAA,CAAM,KAAA;AAAA,MACb,IAAA,CAAK,EAAA,GAAK,EAAC,EAAG;AAEZ,QAAA,IAAI,KAAK,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA,KAAM,KAAA,SAAc,MAAM,MAAA;AAChD,QAAA,IAAI,KAAA,CAAM,KAAA,KAAU,CAAA,IAAK,IAAA,CAAK,KAAK,CAAA,EAAG;AACpC,UAAA,GAAA,CAAI,MAAM,GAAG,CAAA;AACb,UAAA,OAAO,MAAM,MAAA;AAAA,QACf;AACA,QAAA,KAAA,CAAM,KAAA,EAAA;AACN,QAAA,IAAI,MAAM,SAAA,EAAW;AACnB,UAAA,KAAA,CAAM,SAAA,GAAY,KAAA;AAClB,UAAA,KAAA,CAAM,KAAA,CAAM,cAAc,IAAI,CAAA;AAAA,QAChC;AACA,QAAA,IAAA,CAAK,OAAO,EAAE,CAAA;AACd,QAAA,IAAI,CAAC,MAAM,OAAA,EAAS;AAClB,UAAA,KAAA,CAAM,OAAA,GAAU,IAAA;AAChB,UAAA,KAAA,CAAM,MAAM,KAAA,EAAM;AAAA,QACpB;AACA,QAAA,IAAI,QAAA,GAAW,KAAA;AACf,QAAA,OAAO,MAAM;AACX,UAAA,IAAI,QAAA,EAAU;AACd,UAAA,QAAA,GAAW,IAAA;AACX,UAAA,KAAA,CAAM,KAAA,EAAA;AACN,UAAA,IAAI,KAAA,CAAM,QAAQ,CAAA,IAAK,IAAA,CAAK,IAAI,KAAA,CAAM,GAAG,MAAM,KAAA,EAAO;AACtD,UAAA,IACE,SAAA,KAAc,MAAA,IACd,QAAA,CAAS,GAAA,CAAI,MAAM,KAAA,CAAM,WAAA,EAAY,CAAE,MAAM,KAC7C,KAAA,CAAM,SAAA,KAAc,IAAA,IACpB,IAAA,CAAK,KAAK,CAAA,EACV;AACA,YAAA,GAAA,CAAI,MAAM,GAAG,CAAA;AACb,YAAA;AAAA,UACF;AACA,UAAA,KAAA,CAAM,SAAA,GAAY,IAAA;AAClB,UAAA,KAAA,CAAM,KAAA,CAAM,cAAc,KAAK,CAAA;AAAA,QACjC,CAAA;AAAA,MACF,CAAA;AAAA,MACA,IAAA,EAAM,CAAC,EAAA,KAAO,IAAA,CAAK,OAAO,EAAE,CAAA;AAAA,MAC5B,QAAA,EAAU,MAAM,KAAA,CAAM,KAAA;AAAA,MACtB,UAAU,QAAA,EAAU;AAClB,QAAA,KAAA,CAAM,SAAA,CAAU,IAAI,QAAQ,CAAA;AAC5B,QAAA,OAAO,MAAM,KAAA,CAAM,SAAA,CAAU,MAAA,CAAO,QAAQ,CAAA;AAAA,MAC9C,CAAA;AAAA,MACA,KAAA,GAAQ;AACN,QAAA,MAAA,CAAO,KAAA,EAAO,EAAE,OAAA,EAAS,IAAA,EAAM,QAAQ,IAAA,EAAM,IAAA,EAAM,MAAM,CAAA;AACzD,QAAA,KAAA,CAAM,MAAM,KAAA,EAAM;AAAA,MACpB,CAAA;AAAA,MACA,IAAA,CAAK,QAAQ,MAAA,EAAQ;AACnB,QAAA,MAAM,SAAA,GAAY,MAAM,KAAA,CAAM,SAAA;AAC9B,QAAA,IAAI,cAAc,IAAA,IAAQ,KAAA,CAAM,MAAM,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA,KAAM,MAAA;AAC5D,UAAA;AACF,QAAA,MAAA,CAAO,KAAA,EAAO;AAAA,UACZ,OAAA,EAAS,IAAI,GAAA,CAAI,KAAA,CAAM,MAAM,OAAO,CAAA,CAAE,GAAA,CAAI,MAAA,EAAQ,MAAM;AAAA,SACzD,CAAA;AACD,QAAA,IAAA,CAAK,EAAE,MAAM,OAAA,EAAS,GAAA,EAAK,MAAM,GAAA,EAAK,SAAA,EAAW,MAAA,EAAQ,MAAA,EAAQ,CAAA;AACjE,QAAA,KAAK,OAAA,CACF,YAAA,GAAe,EAAE,SAAA,EAAW,MAAA,EAAQ,QAAQ,CAAA,CAC5C,KAAA,CAAM,MAAM,MAAS,CAAA;AAAA,MAC1B,CAAA;AAAA,MACA,SAAS,IAAA,EAAM;AACb,QAAA,IAAI,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,IAAI,CAAA,EAAG;AAC9B,QAAA,KAAA,CAAM,QAAA,CAAS,IAAI,IAAI,CAAA;AACvB,QAAA,KAAA,CAAM,KAAA,CAAM,KAAK,IAAI,CAAA;AAAA,MACvB;AAAA,KACF;AAAA,EACF;AAEA,EAAA,MAAM,OAAA,uBAAc,OAAA,EAAkC;AAEtD,EAAA,MAAM,MAAA,GAAS,CAAC,GAAA,KAAkB;AAChC,IAAA,MAAM,CAAA,GAAI,SAAS,GAAG,CAAA;AACtB,IAAA,IAAI,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA;AACtB,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,KAAA,GAAQ,KAAK,GAAG,CAAA;AAChB,MAAA,IAAA,CAAK,GAAA,CAAI,GAAG,KAAK,CAAA;AAAA,IACnB;AACA,IAAA,IAAI,CAAA,GAAI,OAAA,CAAQ,GAAA,CAAI,KAAK,CAAA;AACzB,IAAA,IAAI,CAAC,CAAA,EAAG;AACN,MAAA,CAAA,GAAI,OAAO,KAAK,CAAA;AAChB,MAAA,OAAA,CAAQ,GAAA,CAAI,OAAO,CAAC,CAAA;AAAA,IACtB;AACA,IAAA,OAAO,CAAA;AAAA,EACT,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,aAAa,GAAA,EAAK;AAChB,MAAA,MAAM,CAAA,GAAI,SAAS,GAAG,CAAA;AACtB,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA;AACxB,MAAA,IAAI,KAAA,IAAS,MAAM,KAAA,KAAU,CAAA,IAAK,KAAK,KAAK,CAAA,MAAO,CAAC,CAAA;AACpD,MAAA,OAAO,OAAO,GAAG,CAAA;AAAA,IACnB,CAAA;AAAA,IACA,IAAA,EAAM,MAAA;AAAA,IACN,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,CAAA,GAAI,SAAS,GAAG,CAAA;AACtB,MAAA,OAAA,CAAQ,OAAO,CAAC,CAAA;AAChB,MAAA,GAAA,CAAI,CAAC,CAAA;AAAA,IACP,CAAA;AAAA,IACA,QAAQ,GAAA,EAAK;AACX,MAAA,GAAA,CAAI,QAAA,CAAS,GAAG,CAAC,CAAA;AAAA,IACnB,CAAA;AAAA,IACA,QAAA,CAAS,KAAK,cAAA,EAAgB;AAC5B,MAAA,MAAM,CAAA,GAAI,SAAS,GAAG,CAAA;AACtB,MAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,cAAA,EAAgB;AACrC,QAAA,OAAA,CAAQ,GAAA,CAAI,GAAG,cAAc,CAAA;AAE7B,QAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA;AACxB,QAAA,IAAI,KAAA,EAAO;AACT,UAAA,KAAA,CAAM,MAAM,KAAA,EAAM;AAClB,UAAA,GAAA,CAAI,CAAC,CAAA;AAAA,QACP,CAAA,MAAO;AACL,UAAA,IAAI;AACF,YAAA,KAAK,OAAA,CAAQ,OAAA;AAAA,cACX,QAAQ,YAAA,EAAc,WAAA,EAAa,UAAA,CAAW,IAAA,CAAK,CAAC,CAAC;AAAA,aACvD,CAAE,KAAA,CAAM,MAAM,KAAA,CAAS,CAAA;AAAA,UACzB,CAAA,CAAA,MAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF;AACA,MAAA,OAAO,OAAO,GAAG,CAAA;AAAA,IACnB,CAAA;AAAA,IACA,MAAM,OAAA,GAAU;AACd,MAAA,KAAA,MAAW,SAAS,IAAA,CAAK,MAAA,EAAO,EAAG,KAAA,CAAM,MAAM,KAAA,EAAM;AACrD,MAAA,IAAA,CAAK,KAAA,EAAM;AACX,MAAA,OAAA,CAAQ,KAAA,EAAM;AACd,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,OAAA,EAAQ;AACR,MAAA,MAAM,OAAA,CAAQ,WAAA,IAAc,CAAE,KAAA,CAAM,MAAM,MAAS,CAAA;AAAA,IACrD,CAAA;AAAA,IACA,MAAA,GAAS;AACP,MAAA,IAAI,MAAA,IAAU,KAAA,CAAM,GAAA,EAAI,GAAI,OAAO,EAAA,GAAK,OAAA;AACtC,QAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,CAAO,KAAK,CAAA;AACrC,MAAA,OAAA,KAAY,UAAA,CAAW,OAAO,CAAA,CAAE,IAAA;AAAA,QAC9B,CAAC,KAAA,KAAU;AACT,UAAA,OAAA,GAAU,IAAA;AACV,UAAA,MAAA,GAAS,EAAE,KAAA,EAAO,EAAA,EAAI,KAAA,CAAM,KAAI,EAAE;AAClC,UAAA,OAAA,EAAQ;AACR,UAAA,OAAO,KAAA;AAAA,QACT,CAAA;AAAA,QACA,CAAC,KAAA,KAAmB;AAClB,UAAA,OAAA,GAAU,IAAA;AACV,UAAA,MAAM,KAAA;AAAA,QACR;AAAA,OACF;AACA,MAAA,OAAO,OAAA;AAAA,IACT,CAAA;AAAA,IACA,SAAA,EAAW,MAAM,MAAA,EAAQ,KAAA,IAAS,IAAA;AAAA,IAClC,OAAA,EAAS,MAAM,WAAA,IAAe,MAAA,EAAQ,MAAM,WAAA,IAAe,IAAA;AAAA,IAC3D,UAAU,QAAA,EAAU;AAClB,MAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,MAAA,OAAO,MAAM,SAAA,CAAU,MAAA,CAAO,QAAQ,CAAA;AAAA,IACxC,CAAA;AAAA,IACA;AAAA,GACF;AACF;AAEA,eAAe,WAAW,OAAA,EAA6C;AACrE,EAAA,MAAM,IAAA,GAAwB,QAAQ,MAAA,GAClC,MAAM,QAAQ,MAAA,EAAO,GACrB,EAAE,SAAA,EAAW,IAAA,EAAK;AACtB,EAAA,MAAM,SAAA,GAAY,KAAK,SAAA,KAAc,IAAA;AACrC,EAAA,MAAM,QAAA,GAAW,KAAK,QAAA,IAAY,WAAA;AAClC,EAAA,OAAO;AAAA,IACL,SAAA;AAAA,IACA,QAAA,EAAU,SAAA,IAAa,IAAA,CAAK,QAAA,KAAa,IAAA;AAAA,IACzC,WAAA,EAAa,IAAA,CAAK,WAAA,EAAa,IAAA,EAAK,IAAK,IAAA;AAAA,IACzC,WAAW,IAAA,CAAK,SAAA,EAAW,WAAW,UAAU,CAAA,GAAI,KAAK,SAAA,GAAY,IAAA;AAAA,IACrE,QAAA,EAAU;AAAA,MACR,OAAA,EAAS,SAAA,IAAa,QAAA,CAAS,OAAA,KAAY,IAAA;AAAA,MAC3C,MAAA,EAAQ,SAAA,IAAa,QAAA,CAAS,MAAA,KAAW,IAAA;AAAA,MACzC,aAAA,EAAe,SAAA,IAAa,QAAA,CAAS,aAAA,KAAkB,IAAA;AAAA,MACvD,aAAA,EAAe,SAAA,IAAa,QAAA,CAAS,aAAA,KAAkB;AAAA;AACzD,GACF;AACF;AAEA,IAAM,KAAA,GAAQ,CAAC,KAAA,EAAgB,IAAA,KAC7B,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,GAClC,KAAA,CAAkC,IAAI,CAAA,GACvC,MAAA;AAEN,IAAM,YAAY,CAAC,KAAA,KAAmB,KAAA,CAAM,KAAA,EAAO,WAAW,CAAA,KAAM,IAAA;AAEpE,IAAM,OAAA,uBAAc,GAAA,CAAwB;AAAA,EAC1C,OAAA;AAAA,EACA,MAAA;AAAA,EACA,KAAA;AAAA,EACA,cAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAC,CAAA;AAED,SAAS,QAAA,CAAS,OAAgB,OAAA,EAA2C;AAC3E,EAAA,IAAI,KAAA,YAAiB,YAAA,EAAc,OAAO,KAAA,CAAM,MAAA;AAChD,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,EAAO,QAAQ,CAAA;AACnC,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,OAAA,CAAQ,IAAI,KAA2B,CAAA;AACtE,IAAA,OAAO,KAAA;AACT,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,KAAA,EAAO,QAAQ,CAAA;AACpC,EAAA,IAAI,MAAA,KAAW,GAAA,IAAO,MAAA,KAAW,GAAA,EAAK,OAAO,MAAA;AAC7C,EAAA,IAAI,MAAA,KAAW,KAAK,OAAO,cAAA;AAC3B,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,IAAU,KAAK,OAAO,QAAA;AACxD,EAAA,OAAO,OAAA,KAAY,gBAAgB,SAAA,GAAY,SAAA;AACjD;AAEA,SAAS,OAAO,KAAA,EAA+B;AAC7C,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,KAAA,EAAO,MAAM,CAAA;AAChC,EAAA,OAAO,OAAO,IAAA,KAAS,QAAA,GAAW,IAAA,GAAO,IAAA;AAC3C;AAGA,eAAe,QAAA,CACb,GAAA,EACA,MAAA,EACA,MAAA,EACA,OAAA,EACY;AACZ,EAAA,KAAA,IAAS,OAAA,GAAU,KAAK,OAAA,EAAA,EAAW;AACjC,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,GAAA,EAAI;AAAA,IACnB,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,MAAA,CAAO,OAAO,CAAA;AAClC,MAAA,IAAI,SAAS,MAAA,IAAa,CAAC,OAAO,WAAA,CAAY,KAAK,KAAK,MAAA,EAAQ,OAAA;AAC9D,QAAA,MAAM,KAAA;AACR,MAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC3C,QAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC7B,UAAA,MAAA,EAAQ,mBAAA,CAAoB,SAAS,IAAI,CAAA;AACzC,UAAA,OAAA,EAAQ;AAAA,QACV,GAAG,IAAI,CAAA;AACP,QAAA,SAAS,IAAA,GAAO;AACd,UAAA,YAAA,CAAa,KAAK,CAAA;AAClB,UAAA,MAAA,CAAO,KAAK,CAAA;AAAA,QACd;AACA,QAAA,MAAA,EAAQ,iBAAiB,OAAA,EAAS,IAAA,EAAM,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,MACxD,CAAC,CAAA;AACD,MAAA,OAAA,CAAQ,UAAU,CAAC,CAAA;AAAA,IACrB;AAAA,EACF;AACF","file":"chunk-XPES5PZ3.js","sourcesContent":["// The agent runtime: everything an app needs around a conversation store, so the app keeps only its own API (the\n// `AgentBackend`), its router and its brand. One conversation per person, mode, business and thread, never mixed; kept\n// while a view shows it (and, by policy, across screens while its session lives); the tenant's backend waited out by a\n// retry policy, never answered with a new conversation; typed reasons when there is no session; a clock adapter for\n// the session's end; the screen context told at open and live where the backend serves it; status, name, ratings,\n// sign-out and events. No React: the kits bind to it.\n\nimport {\n type ConversationOptions,\n type ConversationSession,\n type ConversationStore,\n createConversation,\n type SessionRequest,\n} from '../conversation/store.js';\nimport type { Schemas } from '../events.js';\n\n/** Whose conversation: a person (null for a guest), the app's side, the business acted for, and the thread. */\nexport interface AgentKey {\n readonly subject: string | null;\n readonly mode: string;\n readonly scope?: string | null;\n /** Default `chat`; a flow of its own (a business setup, say) is another thread. */\n readonly thread?: string;\n}\n\ntype ScreenContext = Schemas['ScreenContext'];\n\n/**\n * Where the person asks from, in the contract's vocabulary (`ScreenContext`: the screen, the entity, the filters): told\n * to the agent as data (`Session.screen_context`), never as their words. The tenant's backend vouches for it.\n */\nexport interface AgentPlace {\n readonly screen: ScreenContext['screen'];\n readonly entity?: { readonly type: string; readonly id: string } | null;\n readonly filters?: ScreenContext['filters'];\n}\n\n/** What the runtime asks the tenant's backend for: a session, for this key, told this place. */\nexport interface AgentSessionRequest extends SessionRequest {\n readonly key: AgentKey;\n readonly place: AgentPlace | null;\n /** How the person came in (`button`, `inbox`, …): the app's own words, passed through. */\n readonly entryPoint: string | null;\n}\n\n/** Why there is no session, typed, so an app never needs sentinel errors of its own. */\nexport type AgentFailureReason =\n /** The conversation asked for is over: not theirs, deleted (404), or past the time it is kept (410). */\n | 'ended'\n /** The person's sign-in was refused by the tenant's API. */\n | 'auth'\n /** The agent is switched off for this tenant, mode or person. */\n | 'off'\n | 'rate_limited'\n /** No network, a timeout: retried, then said. */\n | 'network'\n /** The tenant's API or Vinaverse failed (5xx): retried, then said. */\n | 'server'\n /** The backend answered with a session the runtime will not use (not wss://, a field missing). */\n | 'invalid_session'\n | 'unknown';\n\n/**\n * A refusal from the backend, typed: `ended` (the conversation is over; a new one starts only when the person asks) or\n * `unavailable` with its reason. An error with `retryable: true` (and its `reason`, `network` by default) is waited out\n * by the retry policy, then said as `unreachable`.\n */\nexport class AgentRefusal extends Error {\n readonly reason: AgentFailureReason;\n readonly code: string | null;\n constructor(\n readonly kind: 'ended' | 'unavailable',\n options: {\n readonly reason?: AgentFailureReason;\n readonly code?: string;\n readonly message?: string;\n } = {},\n ) {\n super(\n options.message ??\n (kind === 'ended' ? 'The conversation is over.' : 'No agent now.'),\n );\n this.name = 'AgentRefusal';\n this.reason = options.reason ?? (kind === 'ended' ? 'ended' : 'unknown');\n this.code = options.code ?? null;\n }\n}\n\nexport interface AgentStatus {\n readonly available: boolean;\n /** The floating launcher may show. */\n readonly launcher: boolean;\n readonly displayName: string | null;\n readonly avatarUrl: string | null;\n readonly features: {\n readonly history: boolean;\n readonly memory: boolean;\n readonly businessAgent: boolean;\n readonly businessSetup: boolean;\n };\n}\n\n/** The status as the backend reads it; what it leaves out is off. */\nexport interface AgentStatusRead {\n readonly available: boolean;\n readonly launcher?: boolean;\n readonly displayName?: string | null;\n readonly avatarUrl?: string | null;\n readonly features?: Partial<AgentStatus['features']>;\n}\n\n/** The tenant's backend: the one piece of integration an app writes. */\nexport interface AgentBackend {\n /** A session from the tenant's API (it creates it at Vinaverse with its service key), or an `AgentRefusal`. */\n openSession(request: AgentSessionRequest): Promise<ConversationSession>;\n /** A fresh client secret for the same session (before its end). */\n refreshSecret?(sessionId: string): Promise<string>;\n /**\n * The person's place changed while the chat is open: the tenant's backend tells Vinaverse\n * (`PUT /v1/sessions/{id}/context`). Absent, or refused, the kit offers a question about the new place instead.\n */\n updateContext?(\n sessionId: string,\n place: AgentPlace,\n revision: number,\n ): Promise<void>;\n /** Whether the agent is offered, and what it can do. Absent: available, nothing extra. */\n status?(signal?: AbortSignal): Promise<AgentStatusRead>;\n /** A thumbs on a whole reply; best effort, never an error for the person. */\n sendFeedback?(feedback: {\n readonly sessionId: string;\n readonly turnId: string;\n readonly rating: AgentRating;\n }): Promise<void>;\n /** Sign-out: end the person's open sessions at the tenant's API. */\n endSessions?(): Promise<void>;\n}\n\nexport type AgentRating = 'up' | 'down';\nexport type AgentFailure = 'ended' | 'unreachable' | 'unavailable';\n\nexport type AgentEventType =\n | 'session_opened'\n | 'session_resumed'\n | 'session_retried'\n | 'session_failed'\n | 'conversation_ended'\n | 'context_updated'\n | 'rated'\n | 'chip_tapped'\n | 'card_action'\n | 'target_opened'\n | 'link_opened'\n | 'launcher_shown'\n | 'launcher_opened'\n | 'launcher_greeted'\n | 'greeting_closed';\n\n/** For the app's analytics: what happened, for which conversation, with nothing the person said. */\nexport interface AgentEvent {\n readonly type: AgentEventType;\n readonly key?: string;\n readonly sessionId?: string;\n readonly failure?: AgentFailure;\n readonly reason?: AgentFailureReason;\n readonly attempt?: number;\n readonly turnId?: string;\n readonly rating?: AgentRating;\n readonly cardId?: string;\n readonly kind?: string;\n /** Where the person opened from (`entryPoint`, the screen). */\n readonly from?: string;\n}\n\n/** What a UI draws around the store: why there is no session, whether the person moved, the thumbs given. */\nexport interface AgentConversationState {\n readonly failure: AgentFailure | null;\n readonly reason: AgentFailureReason | null;\n /** The API's own code for the failure (`conversation_expired`, …), when it gave one. */\n readonly code: string | null;\n /** The session was told another place than the one the person now asks from, and it could not be told live. */\n readonly moved: boolean;\n readonly ratings: ReadonlyMap<string, AgentRating>;\n readonly sessionId: string | null;\n}\n\nexport interface AgentConversation {\n /** The key as a string, for the app's own maps and the events. */\n readonly key: string;\n readonly store: ConversationStore;\n /**\n * A view attaches: kept, with its connection, while any view is attached. The first time, it starts. Returns the\n * detach: with no view attached it is suspended (no pings; closed with `closeInBackground`) while its session lives\n * (`keepAlive: 'session'`), and let go when it has none, is past its end, or `keepAlive` is `'view'`. Separate from the\n * app's background, which the platform adapter tells the store.\n */\n show(open?: {\n readonly place?: AgentPlace | null;\n readonly entryPoint?: string | null;\n }): () => void;\n /** The place and the way in changed while a view is attached: told live where the backend can, else on the next session. */\n tell(open: {\n readonly place?: AgentPlace | null;\n readonly entryPoint?: string | null;\n }): void;\n getState(): AgentConversationState;\n subscribe(listener: () => void): () => void;\n /** Asks for a session again, after the backend could not be reached. */\n retry(): void;\n /** A thumbs on a whole reply; a change of mind only is sent again. */\n rate(turnId: string, rating: AgentRating): void;\n /**\n * Sends the person's `text` once in this conversation's life (a chip tapped twice, a retried tap). It is shown as\n * theirs: only words they typed or tapped, never the app's in their name.\n */\n sendOnce(text: string): void;\n}\n\n/** When the runtime tries the backend again: before each try, a delay; `isRetryable` decides which errors are waited out. */\nexport interface AgentRetryPolicy {\n readonly delays: readonly number[];\n readonly isRetryable?: (error: unknown) => boolean;\n}\n\n/** The time the runtime judges a session's end by: the server's, when the app has it (a clock synced from responses). */\nexport interface AgentClock {\n now(): number;\n}\n\nexport interface AgentRuntimeOptions {\n readonly backend: AgentBackend;\n /** The store's own options (locale, time zone, deep-link prefixes, transport, persistence, closeInBackground…). */\n readonly conversation?: Omit<\n ConversationOptions,\n 'getSession' | 'refreshSecret' | 'clock'\n >;\n /** Default: 500, 1500, 3500 ms, for errors with `retryable: true`; applied to `openSession` and `refreshSecret`. */\n readonly retry?: AgentRetryPolicy;\n /** Default: the device's clock. Give the server's: a phone set wrong otherwise reuses a dead session or drops a live one. */\n readonly clock?: AgentClock;\n /** `session` (default): kept while its session lives with no view attached; `view`: let go when the last view goes. */\n readonly keepAlive?: 'session' | 'view';\n /** How long a status read stays fresh. Default 5 minutes. */\n readonly statusStaleMs?: number;\n /**\n * DANGEROUS: for mocks and local development only. Lets a leg's session address be plaintext (`ws://`, `http://`),\n * which would carry the person's messages and the session's secret unencrypted. Off by default, whatever transport\n * the app supplies; outside `__DEV__` the runtime warns once.\n */\n readonly insecureAddresses?: AgentInsecureAddresses;\n readonly onEvent?: (event: AgentEvent) => void;\n /** Where the runtime's warnings go (the app's logger, crash reporter); default `console.warn`. */\n readonly onWarning?: (message: string) => void;\n}\n\n/** The legs whose session address may be plaintext: `realtime` (the WebSocket), `turns` (HTTP). */\nexport interface AgentInsecureAddresses {\n readonly realtime?: boolean;\n readonly turns?: boolean;\n}\n\nexport interface AgentRuntime {\n /** The key's conversation: the same while it lives; a new one after `startNew`, `dispose` or its end. */\n conversation(key: AgentKey): AgentConversation;\n /**\n * The key's conversation as it stands, ending nothing and telling no one: for a render (`useSyncExternalStore`).\n * One past its session's end is ended when it is next shown, and subscribers then read the key's next one.\n */\n peek(key: AgentKey): AgentConversation;\n /** A new conversation for the key from the person's next opening; the old one is let go and forgotten. */\n startNew(key: AgentKey): void;\n /** Closes the key's conversation now and drops every reference to it; the next opening continues it in a new session. */\n dispose(key: AgentKey): void;\n /**\n * The key goes on with this conversation (history's \"Continue\"): pinned for the key and opened through the backend's\n * resume. One that is gone (404, 410: the backend's `AgentRefusal('ended')`) is said as `ended`, never replaced by a\n * new one silently.\n */\n continue(key: AgentKey, conversationId: string): AgentConversation;\n /** Every conversation closed and forgotten (what the device kept too), and the backend told to end the sessions. */\n signOut(): Promise<void>;\n /** The agent's status, read from the backend at most every `statusStaleMs`. */\n status(): Promise<AgentStatus>;\n getStatus(): AgentStatus | null;\n /** The agent's name: from the newest session, else from the status. */\n getName(): string | null;\n /** Told when the status or name changes, and when a key's conversation is let go (its next one is another). */\n subscribe(listener: () => void): () => void;\n /** An event from a kit or the app, passed to `onEvent`. */\n emit(event: AgentEvent): void;\n}\n\nconst NOT_KEPT = new Set(['starting', 'unavailable', 'unauthorized', 'closed']);\nconst NO_FEATURES: AgentStatus['features'] = {\n history: false,\n memory: false,\n businessAgent: false,\n businessSetup: false,\n};\nconst SYSTEM_CLOCK: AgentClock = { now: () => Date.now() };\n\n/** The key as a string: one conversation per person, mode, business and thread. */\nexport function agentKey(key: AgentKey): string {\n return `${key.subject ?? 'guest'}|${key.mode}|${key.scope ?? '-'}|${key.thread ?? 'chat'}`;\n}\n\nconst samePlace = (a: AgentPlace | null, b: AgentPlace | null) =>\n a?.screen === b?.screen &&\n (a?.entity?.type ?? null) === (b?.entity?.type ?? null) &&\n (a?.entity?.id ?? null) === (b?.entity?.id ?? null) &&\n JSON.stringify(a?.filters ?? null) === JSON.stringify(b?.filters ?? null);\n\n/** A session the runtime will use: an id, a secret, and `wss://` and `https://` addresses unless a leg is let off. */\nfunction usable(\n session: ConversationSession,\n insecure: AgentInsecureAddresses,\n): boolean {\n return (\n typeof session.id === 'string' &&\n session.id !== '' &&\n (insecure.realtime === true ||\n /^wss:\\/\\//i.test(session.realtime_url ?? '')) &&\n (insecure.turns === true || /^https:\\/\\//i.test(session.turns_url ?? '')) &&\n Boolean(session.client_secret)\n );\n}\n\n/** Once, as the runtime is made (once per app), outside development: plaintext would carry messages and the secret. */\nfunction warnInsecure(\n insecure: AgentInsecureAddresses,\n warn: (message: string) => void,\n): void {\n if (!(insecure.realtime || insecure.turns)) return;\n if ((globalThis as { __DEV__?: boolean }).__DEV__ === true) return;\n warn(\n '@vinaverse/client/agent: insecureAddresses lets sessions use plaintext ws:// or http:// addresses. It is for mocks and local development only; remove it from release builds.',\n );\n}\n\ninterface Entry {\n readonly key: string;\n readonly store: ConversationStore;\n readonly listeners: Set<() => void>;\n state: AgentConversationState;\n place: AgentPlace | null;\n told: AgentPlace | null;\n revision: number;\n entryPoint: string | null;\n expiresAt: number | null;\n shown: number;\n started: boolean;\n suspended: boolean;\n sentOnce: Set<string>;\n}\n\nexport function createAgentRuntime(options: AgentRuntimeOptions): AgentRuntime {\n const { backend } = options;\n const policy: Required<AgentRetryPolicy> = {\n delays: options.retry?.delays ?? [500, 1500, 3500],\n isRetryable: options.retry?.isRetryable ?? retryable,\n };\n const clock = options.clock ?? SYSTEM_CLOCK;\n const keepAlive = options.keepAlive ?? 'session';\n const staleMs = options.statusStaleMs ?? 5 * 60_000;\n const insecure = options.insecureAddresses ?? {};\n warnInsecure(insecure, options.onWarning ?? ((m) => console.warn(m)));\n const live = new Map<string, Entry>();\n const current = new Map<string, string>();\n const listeners = new Set<() => void>();\n let status: { value: AgentStatus; at: number } | null = null;\n let reading: Promise<AgentStatus> | null = null;\n let sessionName: string | null = null;\n\n const emit = (event: AgentEvent) => options.onEvent?.(event);\n const changed = () => {\n for (const l of [...listeners]) l();\n };\n const update = (entry: Entry, next: Partial<AgentConversationState>) => {\n entry.state = { ...entry.state, ...next };\n for (const l of [...entry.listeners]) l();\n };\n\n const end = (k: string) => {\n const entry = live.get(k);\n if (!entry) return;\n live.delete(k);\n entry.store.dispose();\n emit({ type: 'conversation_ended', key: k });\n changed(); // the key's next conversation is another one\n };\n\n const patiently = <T>(\n run: () => Promise<T>,\n signal: AbortSignal | undefined,\n key: string,\n ) =>\n retrying(run, policy, signal, (attempt) =>\n emit({ type: 'session_retried', key, attempt }),\n );\n\n async function open(\n entry: Entry,\n key: AgentKey,\n request: SessionRequest,\n ): Promise<ConversationSession> {\n // Let go meanwhile (a start new, a sign-out): nothing to open.\n if (live.get(entry.key) !== entry) throw new AgentRefusal('unavailable');\n const told = entry.place;\n // The runtime keeps which conversation the key continues: the app's adapter is a plain call.\n const conversationId =\n request.conversationId ?? current.get(entry.key) ?? null;\n let session: ConversationSession;\n try {\n session = await patiently(\n () =>\n backend.openSession({\n ...request,\n conversationId,\n key,\n place: told,\n entryPoint: entry.entryPoint,\n }),\n request.signal,\n entry.key,\n );\n if (!usable(session, insecure))\n throw new AgentRefusal('unavailable', {\n reason: 'invalid_session',\n message:\n 'The session is not usable (an id, a secret, a wss:// realtime_url and an https:// turns_url).',\n });\n } catch (error) {\n if (live.get(entry.key) === entry) {\n const failure: AgentFailure =\n error instanceof AgentRefusal\n ? error.kind === 'ended' && conversationId !== null\n ? 'ended'\n : 'unavailable'\n : policy.isRetryable(error)\n ? 'unreachable'\n : 'unavailable';\n const reason = reasonOf(error, failure);\n const code = error instanceof AgentRefusal ? error.code : codeOf(error);\n update(entry, { failure, reason, code });\n emit({ type: 'session_failed', key: entry.key, failure, reason });\n }\n throw error;\n }\n // Let go while the backend answered (a start new, a sign-out): touch nothing, or the old conversation would be\n // pinned again as the key's current one.\n if (live.get(entry.key) !== entry) throw new AgentRefusal('unavailable');\n entry.told = told;\n entry.expiresAt = session.expires_at\n ? Date.parse(session.expires_at)\n : null;\n if (session.conversation_id)\n current.set(entry.key, session.conversation_id);\n const name = session.agent?.display_name?.trim();\n if (name && name !== sessionName) {\n sessionName = name;\n changed();\n }\n update(entry, {\n failure: null,\n reason: null,\n code: null,\n sessionId: session.id,\n moved: !samePlace(told, entry.place),\n });\n emit({\n type: conversationId === null ? 'session_opened' : 'session_resumed',\n key: entry.key,\n sessionId: session.id,\n ...(entry.entryPoint ? { from: entry.entryPoint } : {}),\n });\n return session;\n }\n\n /** Where a key's conversation is kept on the device, with `persistence`. */\n const kept = (k: string) =>\n `${options.conversation?.persistenceKey ?? 'vinaverse.agent'}.${k}`;\n\n function make(key: AgentKey): Entry {\n const k = agentKey(key);\n const own = options.conversation ?? {};\n const refresh = backend.refreshSecret?.bind(backend);\n const entry: Entry = {\n key: k,\n store: createConversation({\n ...own,\n ...(own.persistence\n ? {\n persistenceKey: kept(k),\n }\n : {}),\n getSession: (request) => open(entry, key, request),\n ...(refresh\n ? {\n refreshSecret: (id: string) =>\n patiently(() => refresh(id), undefined, k),\n }\n : {}),\n clock,\n onError: (error) => {\n // A refusal is said through the state, never reported as a fault.\n if (!(error instanceof AgentRefusal)) own.onError?.(error);\n },\n }),\n listeners: new Set(),\n state: {\n failure: null,\n reason: null,\n code: null,\n moved: false,\n ratings: new Map(),\n sessionId: null,\n },\n place: null,\n told: null,\n revision: 0,\n entryPoint: null,\n expiresAt: null,\n shown: 0,\n started: false,\n suspended: false,\n sentOnce: new Set(),\n };\n return entry;\n }\n\n const over = (entry: Entry) =>\n entry.expiresAt !== null && entry.expiresAt <= clock.now();\n\n /** The new place: told live when the backend can (the session then knows it), else `moved` until the next session. */\n const tell = (\n entry: Entry,\n at: {\n readonly place?: AgentPlace | null;\n readonly entryPoint?: string | null;\n },\n ) => {\n entry.place = at.place ?? null;\n entry.entryPoint = at.entryPoint ?? null;\n const sessionId = entry.state.sessionId;\n const moved = sessionId !== null && !samePlace(entry.told, entry.place);\n update(entry, { moved });\n const place = entry.place;\n if (!moved || !place || !backend.updateContext || sessionId === null)\n return;\n const revision = ++entry.revision;\n backend.updateContext(sessionId, place, revision).then(\n () => {\n if (live.get(entry.key) !== entry || entry.revision !== revision)\n return;\n entry.told = place;\n update(entry, { moved: !samePlace(entry.told, entry.place) });\n emit({ type: 'context_updated', key: entry.key, sessionId });\n },\n () => undefined, // not served, or refused: the question about this place stays the way to say it\n );\n };\n\n function handle(entry: Entry): AgentConversation {\n return {\n key: entry.key,\n store: entry.store,\n show(at = {}) {\n // Let go meanwhile, or past its session's end with no view: nothing to show; the key's next one is read anew.\n if (live.get(entry.key) !== entry) return () => undefined;\n if (entry.shown === 0 && over(entry)) {\n end(entry.key);\n return () => undefined;\n }\n entry.shown++;\n if (entry.suspended) {\n entry.suspended = false;\n entry.store.setForeground(true);\n }\n tell(entry, at);\n if (!entry.started) {\n entry.started = true;\n entry.store.start();\n }\n let released = false;\n return () => {\n if (released) return;\n released = true;\n entry.shown--;\n if (entry.shown > 0 || live.get(entry.key) !== entry) return;\n if (\n keepAlive === 'view' ||\n NOT_KEPT.has(entry.store.getSnapshot().status) ||\n entry.expiresAt === null ||\n over(entry)\n ) {\n end(entry.key);\n return;\n }\n entry.suspended = true;\n entry.store.setForeground(false);\n };\n },\n tell: (at) => tell(entry, at),\n getState: () => entry.state,\n subscribe(listener) {\n entry.listeners.add(listener);\n return () => entry.listeners.delete(listener);\n },\n retry() {\n update(entry, { failure: null, reason: null, code: null });\n entry.store.start();\n },\n rate(turnId, rating) {\n const sessionId = entry.state.sessionId;\n if (sessionId === null || entry.state.ratings.get(turnId) === rating)\n return;\n update(entry, {\n ratings: new Map(entry.state.ratings).set(turnId, rating),\n });\n emit({ type: 'rated', key: entry.key, sessionId, turnId, rating });\n void backend\n .sendFeedback?.({ sessionId, turnId, rating })\n .catch(() => undefined);\n },\n sendOnce(text) {\n if (entry.sentOnce.has(text)) return;\n entry.sentOnce.add(text);\n entry.store.send(text);\n },\n };\n }\n\n const handles = new WeakMap<Entry, AgentConversation>();\n\n const lookup = (key: AgentKey) => {\n const k = agentKey(key);\n let entry = live.get(k);\n if (!entry) {\n entry = make(key);\n live.set(k, entry);\n }\n let h = handles.get(entry);\n if (!h) {\n h = handle(entry);\n handles.set(entry, h);\n }\n return h;\n };\n\n return {\n conversation(key) {\n const k = agentKey(key);\n const entry = live.get(k);\n if (entry && entry.shown === 0 && over(entry)) end(k);\n return lookup(key);\n },\n peek: lookup,\n startNew(key) {\n const k = agentKey(key);\n current.delete(k);\n end(k);\n },\n dispose(key) {\n end(agentKey(key));\n },\n continue(key, conversationId) {\n const k = agentKey(key);\n if (current.get(k) !== conversationId) {\n current.set(k, conversationId);\n // What the device kept is another conversation's: forgotten, so the resumed transcript is the only one drawn.\n const entry = live.get(k);\n if (entry) {\n entry.store.clear();\n end(k);\n } else {\n try {\n void Promise.resolve(\n options.conversation?.persistence?.removeItem(kept(k)),\n ).catch(() => undefined);\n } catch {\n // storage blocked: the transcript the session brings still merges by id\n }\n }\n }\n return lookup(key);\n },\n async signOut() {\n for (const entry of live.values()) entry.store.clear();\n live.clear();\n current.clear();\n sessionName = null;\n changed();\n await backend.endSessions?.().catch(() => undefined);\n },\n status() {\n if (status && clock.now() - status.at < staleMs)\n return Promise.resolve(status.value);\n reading ??= readStatus(backend).then(\n (value) => {\n reading = null;\n status = { value, at: clock.now() };\n changed();\n return value;\n },\n (error: unknown) => {\n reading = null;\n throw error;\n },\n );\n return reading;\n },\n getStatus: () => status?.value ?? null,\n getName: () => sessionName ?? status?.value.displayName ?? null,\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n emit,\n };\n}\n\nasync function readStatus(backend: AgentBackend): Promise<AgentStatus> {\n const read: AgentStatusRead = backend.status\n ? await backend.status()\n : { available: true };\n const available = read.available === true;\n const features = read.features ?? NO_FEATURES;\n return {\n available,\n launcher: available && read.launcher === true,\n displayName: read.displayName?.trim() || null,\n avatarUrl: read.avatarUrl?.startsWith('https://') ? read.avatarUrl : null,\n features: {\n history: available && features.history === true,\n memory: available && features.memory === true,\n businessAgent: available && features.businessAgent === true,\n businessSetup: available && features.businessSetup === true,\n },\n };\n}\n\nconst field = (error: unknown, name: string): unknown =>\n typeof error === 'object' && error !== null\n ? (error as Record<string, unknown>)[name]\n : undefined;\n\nconst retryable = (error: unknown) => field(error, 'retryable') === true;\n\nconst REASONS = new Set<AgentFailureReason>([\n 'ended',\n 'auth',\n 'off',\n 'rate_limited',\n 'network',\n 'server',\n 'invalid_session',\n 'unknown',\n]);\n\nfunction reasonOf(error: unknown, failure: AgentFailure): AgentFailureReason {\n if (error instanceof AgentRefusal) return error.reason;\n const given = field(error, 'reason');\n if (typeof given === 'string' && REASONS.has(given as AgentFailureReason))\n return given as AgentFailureReason;\n const status = field(error, 'status');\n if (status === 401 || status === 403) return 'auth';\n if (status === 429) return 'rate_limited';\n if (typeof status === 'number' && status >= 500) return 'server';\n return failure === 'unreachable' ? 'network' : 'unknown';\n}\n\nfunction codeOf(error: unknown): string | null {\n const code = field(error, 'code');\n return typeof code === 'string' ? code : null;\n}\n\n/** Tries again after each delay while the policy says so; an outage is said, never answered with a new conversation. */\nasync function retrying<T>(\n run: () => Promise<T>,\n policy: Required<AgentRetryPolicy>,\n signal: AbortSignal | undefined,\n onRetry: (attempt: number) => void,\n): Promise<T> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await run();\n } catch (error) {\n const wait = policy.delays[attempt];\n if (wait === undefined || !policy.isRetryable(error) || signal?.aborted)\n throw error;\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', stop);\n resolve();\n }, wait);\n function stop() {\n clearTimeout(timer);\n reject(error);\n }\n signal?.addEventListener('abort', stop, { once: true });\n });\n onRetry(attempt + 1);\n }\n }\n}\n"]}