@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/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- export { C as CONTRACT } from './contract-_NMdPjgN.cjs';
2
- export { A as AnswerInput, h as AuthMode, a as Card, d as CardKind, e as ClientErrorCode, j as ClientEvent, k as ClientSecret, l as Connection, m as ConversationEngine, n as ConversationState, F as FetchLike, o as Frame, H as HeldCard, f as HeldQuestion, M as Message, P as Proposal, g as ProposalOutcome, Q as Question, R as Reconnect, S as Schemas, p as ServerEvent, q as ServerEventOf, r as ServerEventType, s as SessionEvents, t as SessionInfo, u as SessionOptions, v as SessionStatus, w as SseTarget, T as ToolLine, x as TranscriptItem, y as TurnInput, V as VinaverseClientError, z as VinaverseSession, B as WebSocketFactory, D as WebSocketLike, E as cardActionSse, G as components, i as isKnownCard, I as operations, J as paths, K as sseFrames, L as turnSse } from './session-8W3fRNeb.cjs';
1
+ export { C as CONTRACT } from './contract-DmQs8amJ.cjs';
2
+ export { A as AnswerInput, h as AuthMode, a as Card, d as CardKind, e as ClientErrorCode, j as ClientEvent, k as ClientSecret, l as Connection, m as ConversationEngine, n as ConversationState, F as FetchLike, o as Frame, H as HeldCard, f as HeldQuestion, M as Message, P as Proposal, g as ProposalOutcome, Q as Question, R as Reconnect, S as Schemas, p as ServerEvent, q as ServerEventOf, r as ServerEventType, s as SessionEvents, t as SessionInfo, u as SessionOptions, v as SessionStatus, w as SseTarget, T as ToolLine, x as TranscriptItem, y as TurnInput, V as VinaverseClientError, z as VinaverseSession, B as WebSocketFactory, D as WebSocketLike, E as cardActionSse, G as components, i as isKnownCard, I as operations, J as paths, K as sseFrames, L as turnSse } from './session-CPz4pcPu.cjs';
3
3
 
4
4
  /**
5
5
  * The client secret, checked: an app holds only the session's `vv_cs_` secret, which the tenant's backend got from
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { C as CONTRACT } from './contract-_NMdPjgN.js';
2
- export { A as AnswerInput, h as AuthMode, a as Card, d as CardKind, e as ClientErrorCode, j as ClientEvent, k as ClientSecret, l as Connection, m as ConversationEngine, n as ConversationState, F as FetchLike, o as Frame, H as HeldCard, f as HeldQuestion, M as Message, P as Proposal, g as ProposalOutcome, Q as Question, R as Reconnect, S as Schemas, p as ServerEvent, q as ServerEventOf, r as ServerEventType, s as SessionEvents, t as SessionInfo, u as SessionOptions, v as SessionStatus, w as SseTarget, T as ToolLine, x as TranscriptItem, y as TurnInput, V as VinaverseClientError, z as VinaverseSession, B as WebSocketFactory, D as WebSocketLike, E as cardActionSse, G as components, i as isKnownCard, I as operations, J as paths, K as sseFrames, L as turnSse } from './session-8W3fRNeb.js';
1
+ export { C as CONTRACT } from './contract-DmQs8amJ.js';
2
+ export { A as AnswerInput, h as AuthMode, a as Card, d as CardKind, e as ClientErrorCode, j as ClientEvent, k as ClientSecret, l as Connection, m as ConversationEngine, n as ConversationState, F as FetchLike, o as Frame, H as HeldCard, f as HeldQuestion, M as Message, P as Proposal, g as ProposalOutcome, Q as Question, R as Reconnect, S as Schemas, p as ServerEvent, q as ServerEventOf, r as ServerEventType, s as SessionEvents, t as SessionInfo, u as SessionOptions, v as SessionStatus, w as SseTarget, T as ToolLine, x as TranscriptItem, y as TurnInput, V as VinaverseClientError, z as VinaverseSession, B as WebSocketFactory, D as WebSocketLike, E as cardActionSse, G as components, i as isKnownCard, I as operations, J as paths, K as sseFrames, L as turnSse } from './session-CPz4pcPu.js';
3
3
 
4
4
  /**
5
5
  * The client secret, checked: an app holds only the session's `vv_cs_` secret, which the tenant's backend got from
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { CONTRACT } from './chunk-VSJDVLCP.js';
1
+ export { CONTRACT } from './chunk-YNUHXJD6.js';
2
2
  export { ConversationEngine, VinaverseClientError, VinaverseSession, cardActionSse, clientSecret, isKnownCard, sseFrames, turnSse } from './chunk-GWLLUOFY.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,5 @@
1
- import { S as SessionRequest, b as ConversationSession, C as ConversationStore, a as ConversationOptions } from './store-DFk0ayh8.js';
2
- import { S as Schemas } from './session-8W3fRNeb.js';
1
+ import { S as SessionRequest, b as ConversationSession, C as ConversationStore, f as ConversationPersistence, a as ConversationOptions } from './store-BxiicOKG.cjs';
2
+ import { S as Schemas, k as ClientSecret } from './session-CPz4pcPu.cjs';
3
3
 
4
4
  /** Whose conversation: a person (null for a guest), the app's side, the business acted for, and the thread. */
5
5
  interface AgentKey {
@@ -22,17 +22,47 @@ interface AgentPlace {
22
22
  } | null;
23
23
  readonly filters?: ScreenContext['filters'];
24
24
  }
25
+ /** How the person came in: the kit's parts say `launcher` and `ask`; an app adds `deep_link` or its own words. */
26
+ type AgentEntryPoint = 'launcher' | 'ask' | 'deep_link' | (string & Record<never, never>);
25
27
  /** What the runtime asks the tenant's backend for: a session, for this key, told this place. */
26
28
  interface AgentSessionRequest extends SessionRequest {
27
29
  readonly key: AgentKey;
30
+ /**
31
+ * This session creation's id: the same through its retries, so the backend uses it as the gateway's idempotency key
32
+ * and never opens twice; a new one for every creation (a first open, a resume, continue-here, a rollover). A creation
33
+ * that could not reach the backend keeps it, and its whole request, for the next try, the person's "try again" too,
34
+ * for 24 hours. An `unknown_open` answer is final: not retried under it.
35
+ */
36
+ readonly openId: string;
37
+ /** The runtime's `agent`, for a backend that serves more than one. */
38
+ readonly agent: string | null;
28
39
  readonly place: AgentPlace | null;
29
- /** How the person came in (`button`, `inbox`, …): the app's own words, passed through. */
30
- readonly entryPoint: string | null;
40
+ /**
41
+ * The place's revision (`ScreenContext.revision`): higher than any the session was told before, and lower than any
42
+ * `updateContext` after it, so the API keeps the newest place across a session's rollover.
43
+ */
44
+ readonly placeRevision: number;
45
+ /** How the person came in (`launcher`, `ask`, `deep_link`, the app's own words), passed through. */
46
+ readonly entryPoint: AgentEntryPoint | null;
31
47
  }
32
48
  /** Why there is no session, typed, so an app never needs sentinel errors of its own. */
33
49
  type AgentFailureReason =
34
- /** The conversation asked for is over: not theirs, deleted (404), or past the time it is kept (410). */
50
+ /** The conversation is over (its session's own end, or a refusal with no finer reason). */
35
51
  'ended'
52
+ /** The conversation asked for does not exist for this person (deleted, or never theirs). */
53
+ | 'not_found'
54
+ /** Past the time the conversation is kept. */
55
+ | 'expired'
56
+ /** The conversation belongs to another business, mode or agent than the key's. */
57
+ | 'mismatch'
58
+ /** The conversation's flow is completed; a new one starts when the person asks. */
59
+ | 'completed'
60
+ /** The person may not act for this business or conversation. */
61
+ | 'forbidden'
62
+ /** The server holds a newer screen context than the one told (an update, never a session). */
63
+ | 'conflict'
64
+ /** The backend could not tell whether the session opened: unavailable, never retried, its `openId` kept for support. */
65
+ | 'unknown_open'
36
66
  /** The person's sign-in was refused by the tenant's API. */
37
67
  | 'auth'
38
68
  /** The agent is switched off for this tenant, mode or person. */
@@ -83,11 +113,17 @@ interface AgentStatusRead {
83
113
  interface AgentBackend {
84
114
  /** A session from the tenant's API (it creates it at Vinaverse with its service key), or an `AgentRefusal`. */
85
115
  openSession(request: AgentSessionRequest): Promise<ConversationSession>;
86
- /** A fresh client secret for the same session (before its end). */
87
- refreshSecret?(sessionId: string): Promise<string>;
116
+ /**
117
+ * A fresh client secret for the same session (before its end): a `ClientSecret` `{ value, expires_at }`, or its
118
+ * value. A refusal (`session_closed`: the session is over) goes on with the conversation in a new session.
119
+ */
120
+ refreshSecret?(sessionId: string): Promise<ClientSecret | string>;
88
121
  /**
89
122
  * The person's place changed while the chat is open: the tenant's backend tells Vinaverse
90
- * (`PUT /v1/sessions/{id}/context`). Absent, or refused, the kit offers a question about the new place instead.
123
+ * (`PUT /v1/sessions/{id}/context`). A place cleared (`tell({ place: null })`) comes as `{ screen: 'other' }`.
124
+ * `revision` only grows for a conversation, across its sessions. A conflict (`decodeBackendFailure`'s
125
+ * `context_conflict`, or a refusal with reason `conflict`, with the server's `currentRevision`) is sent once more
126
+ * above it, then said as `moved`. Absent, or refused otherwise, the kit offers a question about the new place.
91
127
  */
92
128
  updateContext?(sessionId: string, place: AgentPlace, revision: number): Promise<void>;
93
129
  /** Whether the agent is offered, and what it can do. Absent: available, nothing extra. */
@@ -100,13 +136,16 @@ interface AgentBackend {
100
136
  }): Promise<void>;
101
137
  /** Sign-out: end the person's open sessions at the tenant's API. */
102
138
  endSessions?(): Promise<void>;
139
+ /** History's delete: the tenant's API deletes the person's conversation (authenticated, theirs only). */
140
+ deleteConversation?(conversationId: string): Promise<void>;
103
141
  }
104
142
  type AgentRating = 'up' | 'down';
105
143
  type AgentFailure = 'ended' | 'unreachable' | 'unavailable';
106
- type AgentEventType = 'session_opened' | 'session_resumed' | 'session_retried' | 'session_failed' | 'conversation_ended' | 'context_updated' | 'rated' | 'chip_tapped' | 'card_action' | 'target_opened' | 'link_opened' | 'launcher_shown' | 'launcher_opened' | 'launcher_greeted' | 'greeting_closed';
144
+ type AgentEventType = 'session_opened' | 'session_resumed' | 'session_retried' | 'session_failed' | 'conversation_ended' | 'context_updated' | 'rated' | 'chip_tapped' | 'card_action' | 'target_opened' | 'link_opened' | 'launcher_shown' | 'launcher_opened' | 'launcher_greeted' | 'greeting_closed' | 'message_sent' | 'reply_completed' | 'card_shown';
107
145
  /** For the app's analytics: what happened, for which conversation, with nothing the person said. */
108
146
  interface AgentEvent {
109
147
  readonly type: AgentEventType;
148
+ /** The conversation, as a hash of its key (`hashKey`): stable, and never the person's id. */
110
149
  readonly key?: string;
111
150
  readonly sessionId?: string;
112
151
  readonly failure?: AgentFailure;
@@ -116,8 +155,8 @@ interface AgentEvent {
116
155
  readonly rating?: AgentRating;
117
156
  readonly cardId?: string;
118
157
  readonly kind?: string;
119
- /** Where the person opened from (`entryPoint`, the screen). */
120
- readonly from?: string;
158
+ /** Where the person opened from (`launcher`, `ask`, `deep_link`, the app's own words). */
159
+ readonly from?: AgentEntryPoint;
121
160
  }
122
161
  /** What a UI draws around the store: why there is no session, whether the person moved, the thumbs given. */
123
162
  interface AgentConversationState {
@@ -129,6 +168,13 @@ interface AgentConversationState {
129
168
  readonly moved: boolean;
130
169
  readonly ratings: ReadonlyMap<string, AgentRating>;
131
170
  readonly sessionId: string | null;
171
+ /**
172
+ * The conversation the key holds: the one it continues before a session opens, then its session's. Null for a new
173
+ * one not yet opened. History's delete compares against it (`deleteConversation` does).
174
+ */
175
+ readonly conversationId: string | null;
176
+ /** The open the backend could not settle (`unknown_open`), for support; null otherwise. */
177
+ readonly openId: string | null;
132
178
  }
133
179
  interface AgentConversation {
134
180
  /** The key as a string, for the app's own maps and the events. */
@@ -142,18 +188,26 @@ interface AgentConversation {
142
188
  */
143
189
  show(open?: {
144
190
  readonly place?: AgentPlace | null;
145
- readonly entryPoint?: string | null;
191
+ readonly entryPoint?: AgentEntryPoint | null;
146
192
  }): () => void;
147
- /** The place and the way in changed while a view is attached: told live where the backend can, else on the next session. */
193
+ /**
194
+ * The place and the way in changed while a view is attached: told live where the backend can, else on the next
195
+ * session. `undefined` keeps what was told; `null` clears it.
196
+ */
148
197
  tell(open: {
149
198
  readonly place?: AgentPlace | null;
150
- readonly entryPoint?: string | null;
199
+ readonly entryPoint?: AgentEntryPoint | null;
151
200
  }): void;
152
201
  getState(): AgentConversationState;
153
202
  subscribe(listener: () => void): () => void;
154
203
  /** Asks for a session again, after the backend could not be reached. */
155
204
  retry(): void;
156
- /** A thumbs on a whole reply; a change of mind only is sent again. */
205
+ /**
206
+ * Whether a reply can be rated: one this runtime saw given live. A reply restored from the device or a resumed
207
+ * transcript cannot be, until the API rates a turn by its conversation; the kits draw no thumbs for it.
208
+ */
209
+ canRate(turnId: string): boolean;
210
+ /** A thumbs on a whole reply, against the session it was given in; a change of mind only is sent again. */
157
211
  rate(turnId: string, rating: AgentRating): void;
158
212
  /**
159
213
  * Sends the person's `text` once in this conversation's life (a chip tapped twice, a retried tap). It is shown as
@@ -172,6 +226,11 @@ interface AgentClock {
172
226
  }
173
227
  interface AgentRuntimeOptions {
174
228
  readonly backend: AgentBackend;
229
+ /**
230
+ * The agent this runtime talks to (its id at Vinaverse). It is part of every key, so two agents sharing the device's
231
+ * storage (`conversation.persistence`) never share a conversation. Default: none (one agent per storage namespace).
232
+ */
233
+ readonly agent?: string;
175
234
  /** The store's own options (locale, time zone, deep-link prefixes, transport, persistence, closeInBackground…). */
176
235
  readonly conversation?: Omit<ConversationOptions, 'getSession' | 'refreshSecret' | 'clock'>;
177
236
  /** Default: 500, 1500, 3500 ms, for errors with `retryable: true`; applied to `openSession` and `refreshSecret`. */
@@ -189,8 +248,28 @@ interface AgentRuntimeOptions {
189
248
  */
190
249
  readonly insecureAddresses?: AgentInsecureAddresses;
191
250
  readonly onEvent?: (event: AgentEvent) => void;
251
+ /**
252
+ * Where the launcher remembers it greeted a person (the app's preferences store). Given here, the launcher reads and
253
+ * writes it through the runtime (`greetings`), a sign-out fences it, and `erase()` removes it.
254
+ */
255
+ readonly greetingStorage?: ConversationPersistence;
256
+ /**
257
+ * Keys an earlier version of the app or the kit wrote that no index lists, for a storage with neither `keys` nor
258
+ * `removePrefix`: removed by every sign-out from `conversation.persistence`, and by `erase()` from `greetingStorage`
259
+ * too. The migration that makes their removal certain on such a storage.
260
+ */
261
+ readonly legacyKeys?: readonly string[];
192
262
  /** Where the runtime's warnings go (the app's logger, crash reporter); default `console.warn`. */
193
- readonly onWarning?: (message: string) => void;
263
+ readonly onWarning?: (message: string, warning: AgentWarning) => void;
264
+ }
265
+ /**
266
+ * What a warning is about, for an app's logger to count without reading the message: `insecure_addresses` (a leg let
267
+ * off the wss:// and https:// check outside development), `callback_threw` (the app's `onEvent` or `onActivity` threw;
268
+ * `callback` names it), `events_dropped` (the oldest analytics events, while the salt is read).
269
+ */
270
+ interface AgentWarning {
271
+ readonly code: 'insecure_addresses' | 'callback_threw' | 'events_dropped';
272
+ readonly callback?: 'onEvent' | 'onActivity';
194
273
  }
195
274
  /** The legs whose session address may be plaintext: `realtime` (the WebSocket), `turns` (HTTP). */
196
275
  interface AgentInsecureAddresses {
@@ -207,15 +286,43 @@ interface AgentRuntime {
207
286
  peek(key: AgentKey): AgentConversation;
208
287
  /** A new conversation for the key from the person's next opening; the old one is let go and forgotten. */
209
288
  startNew(key: AgentKey): void;
210
- /** Closes the key's conversation now and drops every reference to it; the next opening continues it in a new session. */
289
+ /**
290
+ * Closes the key's conversation now; the next opening continues it in a new session. What the device kept of it and
291
+ * the conversation it continues stay: `forget` removes them.
292
+ */
211
293
  dispose(key: AgentKey): void;
294
+ /**
295
+ * Closes the key's conversation and forgets it on this device: the conversation it continued and the transcript kept
296
+ * by `persistence`. History's "delete" calls it after the tenant's backend deleted the conversation.
297
+ */
298
+ forget(key: AgentKey): void;
299
+ /** Ends every conversation no view shows that is past its session's end. The kits call it when the app comes back. */
300
+ sweep(): void;
301
+ /**
302
+ * History's delete: the backend deletes the conversation, then the device forgets it, only where the key still
303
+ * continues that conversation (an older history entry never forgets the key's newer one).
304
+ */
305
+ deleteConversation(key: AgentKey, conversationId: string): Promise<void>;
306
+ /**
307
+ * The person's erasure on this device: a sign-out, plus the launcher's greeting records (`greetingStorage`) and the
308
+ * analytics salt, every key this version and alpha.5 or alpha.6 wrote. Keys no index lists (an earlier version's, a
309
+ * write lost with its process) are found only when the storage has `keys` or `removePrefix`.
310
+ */
311
+ erase(): Promise<void>;
312
+ /** The launcher's greeting storage, under the sign-out fence; null without `greetingStorage`. */
313
+ readonly greetings: ConversationPersistence | null;
212
314
  /**
213
315
  * The key goes on with this conversation (history's "Continue"): pinned for the key and opened through the backend's
214
316
  * resume. One that is gone (404, 410: the backend's `AgentRefusal('ended')`) is said as `ended`, never replaced by a
215
317
  * new one silently.
216
318
  */
217
319
  continue(key: AgentKey, conversationId: string): AgentConversation;
218
- /** Every conversation closed and forgotten (what the device kept too), and the backend told to end the sessions. */
320
+ /**
321
+ * Every conversation closed and forgotten, and everything this runtime kept on the device (every transcript it ever
322
+ * wrote under `conversation.persistenceKey`, even of conversations already closed), the status and the name; then
323
+ * the backend is told to end the sessions at the same time. A late answer from before (a session, a status, a write)
324
+ * changes nothing. Kept on purpose: the launcher's greeting records and the analytics salt (`erase` takes them).
325
+ */
219
326
  signOut(): Promise<void>;
220
327
  /** The agent's status, read from the backend at most every `statusStaleMs`. */
221
328
  status(): Promise<AgentStatus>;
@@ -227,8 +334,27 @@ interface AgentRuntime {
227
334
  /** An event from a kit or the app, passed to `onEvent`. */
228
335
  emit(event: AgentEvent): void;
229
336
  }
230
- /** The key as a string: one conversation per person, mode, business and thread. */
231
- declare function agentKey(key: AgentKey): string;
337
+ /**
338
+ * Where the launcher remembers that it greeted a person in a mode, the key its storage sees. Encoded so a guest
339
+ * (`null`) is never a person whose id is "guest".
340
+ */
341
+ declare function greetedKey(subject: string | null, mode: string): string;
342
+ /**
343
+ * A conversation's key as analytics sees it: SHA-256 of the install's salt and the key, 128 bits in hex. Stable per
344
+ * install (the runtime keeps the salt in the app's storage until `erase()`), never the person's id, and not
345
+ * recomputable from a guessed id without the salt.
346
+ */
347
+ declare function hashKey(key: string, salt: string): string;
348
+ /**
349
+ * The key as a string: one conversation per agent, person, mode, business and thread. Encoded as a JSON array, so no
350
+ * value can pass for another (a guest is `null`, never a subject named "guest"; a `|` in an id is just a character).
351
+ */
352
+ declare function agentKey(key: AgentKey, agent?: string | null): string;
232
353
  declare function createAgentRuntime(options: AgentRuntimeOptions): AgentRuntime;
354
+ /**
355
+ * An `AgentRefusal`, also one made by another copy of this package (the mock in `@vinaverse/client/testing`, the
356
+ * backend helpers in `@vinaverse/client/backend`, a second install): recognised by its name and kind, not its class.
357
+ */
358
+ declare function isRefusal(error: unknown): error is AgentRefusal;
233
359
 
234
- export { type AgentBackend as A, type AgentClock as a, type AgentConversation as b, type AgentConversationState as c, type AgentEvent as d, type AgentEventType as e, type AgentFailure as f, type AgentFailureReason as g, type AgentInsecureAddresses as h, type AgentKey as i, type AgentPlace as j, type AgentRating as k, AgentRefusal as l, type AgentRetryPolicy as m, type AgentRuntime as n, type AgentRuntimeOptions as o, type AgentSessionRequest as p, type AgentStatus as q, type AgentStatusRead as r, agentKey as s, createAgentRuntime as t };
360
+ export { type AgentBackend as A, type AgentClock as a, type AgentConversation as b, type AgentConversationState as c, type AgentEntryPoint as d, type AgentEvent as e, type AgentEventType as f, type AgentFailure as g, type AgentFailureReason as h, type AgentInsecureAddresses as i, type AgentKey as j, type AgentPlace as k, type AgentRating as l, AgentRefusal as m, type AgentRetryPolicy as n, type AgentRuntime as o, type AgentRuntimeOptions as p, type AgentSessionRequest as q, type AgentStatus as r, type AgentStatusRead as s, type AgentWarning as t, agentKey as u, createAgentRuntime as v, greetedKey as w, hashKey as x, isRefusal as y };
@@ -1,5 +1,5 @@
1
- import { S as SessionRequest, b as ConversationSession, C as ConversationStore, a as ConversationOptions } from './store-C7FtS5AY.cjs';
2
- import { S as Schemas } from './session-8W3fRNeb.cjs';
1
+ import { S as SessionRequest, b as ConversationSession, C as ConversationStore, f as ConversationPersistence, a as ConversationOptions } from './store-DZka9T2-.js';
2
+ import { S as Schemas, k as ClientSecret } from './session-CPz4pcPu.js';
3
3
 
4
4
  /** Whose conversation: a person (null for a guest), the app's side, the business acted for, and the thread. */
5
5
  interface AgentKey {
@@ -22,17 +22,47 @@ interface AgentPlace {
22
22
  } | null;
23
23
  readonly filters?: ScreenContext['filters'];
24
24
  }
25
+ /** How the person came in: the kit's parts say `launcher` and `ask`; an app adds `deep_link` or its own words. */
26
+ type AgentEntryPoint = 'launcher' | 'ask' | 'deep_link' | (string & Record<never, never>);
25
27
  /** What the runtime asks the tenant's backend for: a session, for this key, told this place. */
26
28
  interface AgentSessionRequest extends SessionRequest {
27
29
  readonly key: AgentKey;
30
+ /**
31
+ * This session creation's id: the same through its retries, so the backend uses it as the gateway's idempotency key
32
+ * and never opens twice; a new one for every creation (a first open, a resume, continue-here, a rollover). A creation
33
+ * that could not reach the backend keeps it, and its whole request, for the next try, the person's "try again" too,
34
+ * for 24 hours. An `unknown_open` answer is final: not retried under it.
35
+ */
36
+ readonly openId: string;
37
+ /** The runtime's `agent`, for a backend that serves more than one. */
38
+ readonly agent: string | null;
28
39
  readonly place: AgentPlace | null;
29
- /** How the person came in (`button`, `inbox`, …): the app's own words, passed through. */
30
- readonly entryPoint: string | null;
40
+ /**
41
+ * The place's revision (`ScreenContext.revision`): higher than any the session was told before, and lower than any
42
+ * `updateContext` after it, so the API keeps the newest place across a session's rollover.
43
+ */
44
+ readonly placeRevision: number;
45
+ /** How the person came in (`launcher`, `ask`, `deep_link`, the app's own words), passed through. */
46
+ readonly entryPoint: AgentEntryPoint | null;
31
47
  }
32
48
  /** Why there is no session, typed, so an app never needs sentinel errors of its own. */
33
49
  type AgentFailureReason =
34
- /** The conversation asked for is over: not theirs, deleted (404), or past the time it is kept (410). */
50
+ /** The conversation is over (its session's own end, or a refusal with no finer reason). */
35
51
  'ended'
52
+ /** The conversation asked for does not exist for this person (deleted, or never theirs). */
53
+ | 'not_found'
54
+ /** Past the time the conversation is kept. */
55
+ | 'expired'
56
+ /** The conversation belongs to another business, mode or agent than the key's. */
57
+ | 'mismatch'
58
+ /** The conversation's flow is completed; a new one starts when the person asks. */
59
+ | 'completed'
60
+ /** The person may not act for this business or conversation. */
61
+ | 'forbidden'
62
+ /** The server holds a newer screen context than the one told (an update, never a session). */
63
+ | 'conflict'
64
+ /** The backend could not tell whether the session opened: unavailable, never retried, its `openId` kept for support. */
65
+ | 'unknown_open'
36
66
  /** The person's sign-in was refused by the tenant's API. */
37
67
  | 'auth'
38
68
  /** The agent is switched off for this tenant, mode or person. */
@@ -83,11 +113,17 @@ interface AgentStatusRead {
83
113
  interface AgentBackend {
84
114
  /** A session from the tenant's API (it creates it at Vinaverse with its service key), or an `AgentRefusal`. */
85
115
  openSession(request: AgentSessionRequest): Promise<ConversationSession>;
86
- /** A fresh client secret for the same session (before its end). */
87
- refreshSecret?(sessionId: string): Promise<string>;
116
+ /**
117
+ * A fresh client secret for the same session (before its end): a `ClientSecret` `{ value, expires_at }`, or its
118
+ * value. A refusal (`session_closed`: the session is over) goes on with the conversation in a new session.
119
+ */
120
+ refreshSecret?(sessionId: string): Promise<ClientSecret | string>;
88
121
  /**
89
122
  * The person's place changed while the chat is open: the tenant's backend tells Vinaverse
90
- * (`PUT /v1/sessions/{id}/context`). Absent, or refused, the kit offers a question about the new place instead.
123
+ * (`PUT /v1/sessions/{id}/context`). A place cleared (`tell({ place: null })`) comes as `{ screen: 'other' }`.
124
+ * `revision` only grows for a conversation, across its sessions. A conflict (`decodeBackendFailure`'s
125
+ * `context_conflict`, or a refusal with reason `conflict`, with the server's `currentRevision`) is sent once more
126
+ * above it, then said as `moved`. Absent, or refused otherwise, the kit offers a question about the new place.
91
127
  */
92
128
  updateContext?(sessionId: string, place: AgentPlace, revision: number): Promise<void>;
93
129
  /** Whether the agent is offered, and what it can do. Absent: available, nothing extra. */
@@ -100,13 +136,16 @@ interface AgentBackend {
100
136
  }): Promise<void>;
101
137
  /** Sign-out: end the person's open sessions at the tenant's API. */
102
138
  endSessions?(): Promise<void>;
139
+ /** History's delete: the tenant's API deletes the person's conversation (authenticated, theirs only). */
140
+ deleteConversation?(conversationId: string): Promise<void>;
103
141
  }
104
142
  type AgentRating = 'up' | 'down';
105
143
  type AgentFailure = 'ended' | 'unreachable' | 'unavailable';
106
- type AgentEventType = 'session_opened' | 'session_resumed' | 'session_retried' | 'session_failed' | 'conversation_ended' | 'context_updated' | 'rated' | 'chip_tapped' | 'card_action' | 'target_opened' | 'link_opened' | 'launcher_shown' | 'launcher_opened' | 'launcher_greeted' | 'greeting_closed';
144
+ type AgentEventType = 'session_opened' | 'session_resumed' | 'session_retried' | 'session_failed' | 'conversation_ended' | 'context_updated' | 'rated' | 'chip_tapped' | 'card_action' | 'target_opened' | 'link_opened' | 'launcher_shown' | 'launcher_opened' | 'launcher_greeted' | 'greeting_closed' | 'message_sent' | 'reply_completed' | 'card_shown';
107
145
  /** For the app's analytics: what happened, for which conversation, with nothing the person said. */
108
146
  interface AgentEvent {
109
147
  readonly type: AgentEventType;
148
+ /** The conversation, as a hash of its key (`hashKey`): stable, and never the person's id. */
110
149
  readonly key?: string;
111
150
  readonly sessionId?: string;
112
151
  readonly failure?: AgentFailure;
@@ -116,8 +155,8 @@ interface AgentEvent {
116
155
  readonly rating?: AgentRating;
117
156
  readonly cardId?: string;
118
157
  readonly kind?: string;
119
- /** Where the person opened from (`entryPoint`, the screen). */
120
- readonly from?: string;
158
+ /** Where the person opened from (`launcher`, `ask`, `deep_link`, the app's own words). */
159
+ readonly from?: AgentEntryPoint;
121
160
  }
122
161
  /** What a UI draws around the store: why there is no session, whether the person moved, the thumbs given. */
123
162
  interface AgentConversationState {
@@ -129,6 +168,13 @@ interface AgentConversationState {
129
168
  readonly moved: boolean;
130
169
  readonly ratings: ReadonlyMap<string, AgentRating>;
131
170
  readonly sessionId: string | null;
171
+ /**
172
+ * The conversation the key holds: the one it continues before a session opens, then its session's. Null for a new
173
+ * one not yet opened. History's delete compares against it (`deleteConversation` does).
174
+ */
175
+ readonly conversationId: string | null;
176
+ /** The open the backend could not settle (`unknown_open`), for support; null otherwise. */
177
+ readonly openId: string | null;
132
178
  }
133
179
  interface AgentConversation {
134
180
  /** The key as a string, for the app's own maps and the events. */
@@ -142,18 +188,26 @@ interface AgentConversation {
142
188
  */
143
189
  show(open?: {
144
190
  readonly place?: AgentPlace | null;
145
- readonly entryPoint?: string | null;
191
+ readonly entryPoint?: AgentEntryPoint | null;
146
192
  }): () => void;
147
- /** The place and the way in changed while a view is attached: told live where the backend can, else on the next session. */
193
+ /**
194
+ * The place and the way in changed while a view is attached: told live where the backend can, else on the next
195
+ * session. `undefined` keeps what was told; `null` clears it.
196
+ */
148
197
  tell(open: {
149
198
  readonly place?: AgentPlace | null;
150
- readonly entryPoint?: string | null;
199
+ readonly entryPoint?: AgentEntryPoint | null;
151
200
  }): void;
152
201
  getState(): AgentConversationState;
153
202
  subscribe(listener: () => void): () => void;
154
203
  /** Asks for a session again, after the backend could not be reached. */
155
204
  retry(): void;
156
- /** A thumbs on a whole reply; a change of mind only is sent again. */
205
+ /**
206
+ * Whether a reply can be rated: one this runtime saw given live. A reply restored from the device or a resumed
207
+ * transcript cannot be, until the API rates a turn by its conversation; the kits draw no thumbs for it.
208
+ */
209
+ canRate(turnId: string): boolean;
210
+ /** A thumbs on a whole reply, against the session it was given in; a change of mind only is sent again. */
157
211
  rate(turnId: string, rating: AgentRating): void;
158
212
  /**
159
213
  * Sends the person's `text` once in this conversation's life (a chip tapped twice, a retried tap). It is shown as
@@ -172,6 +226,11 @@ interface AgentClock {
172
226
  }
173
227
  interface AgentRuntimeOptions {
174
228
  readonly backend: AgentBackend;
229
+ /**
230
+ * The agent this runtime talks to (its id at Vinaverse). It is part of every key, so two agents sharing the device's
231
+ * storage (`conversation.persistence`) never share a conversation. Default: none (one agent per storage namespace).
232
+ */
233
+ readonly agent?: string;
175
234
  /** The store's own options (locale, time zone, deep-link prefixes, transport, persistence, closeInBackground…). */
176
235
  readonly conversation?: Omit<ConversationOptions, 'getSession' | 'refreshSecret' | 'clock'>;
177
236
  /** Default: 500, 1500, 3500 ms, for errors with `retryable: true`; applied to `openSession` and `refreshSecret`. */
@@ -189,8 +248,28 @@ interface AgentRuntimeOptions {
189
248
  */
190
249
  readonly insecureAddresses?: AgentInsecureAddresses;
191
250
  readonly onEvent?: (event: AgentEvent) => void;
251
+ /**
252
+ * Where the launcher remembers it greeted a person (the app's preferences store). Given here, the launcher reads and
253
+ * writes it through the runtime (`greetings`), a sign-out fences it, and `erase()` removes it.
254
+ */
255
+ readonly greetingStorage?: ConversationPersistence;
256
+ /**
257
+ * Keys an earlier version of the app or the kit wrote that no index lists, for a storage with neither `keys` nor
258
+ * `removePrefix`: removed by every sign-out from `conversation.persistence`, and by `erase()` from `greetingStorage`
259
+ * too. The migration that makes their removal certain on such a storage.
260
+ */
261
+ readonly legacyKeys?: readonly string[];
192
262
  /** Where the runtime's warnings go (the app's logger, crash reporter); default `console.warn`. */
193
- readonly onWarning?: (message: string) => void;
263
+ readonly onWarning?: (message: string, warning: AgentWarning) => void;
264
+ }
265
+ /**
266
+ * What a warning is about, for an app's logger to count without reading the message: `insecure_addresses` (a leg let
267
+ * off the wss:// and https:// check outside development), `callback_threw` (the app's `onEvent` or `onActivity` threw;
268
+ * `callback` names it), `events_dropped` (the oldest analytics events, while the salt is read).
269
+ */
270
+ interface AgentWarning {
271
+ readonly code: 'insecure_addresses' | 'callback_threw' | 'events_dropped';
272
+ readonly callback?: 'onEvent' | 'onActivity';
194
273
  }
195
274
  /** The legs whose session address may be plaintext: `realtime` (the WebSocket), `turns` (HTTP). */
196
275
  interface AgentInsecureAddresses {
@@ -207,15 +286,43 @@ interface AgentRuntime {
207
286
  peek(key: AgentKey): AgentConversation;
208
287
  /** A new conversation for the key from the person's next opening; the old one is let go and forgotten. */
209
288
  startNew(key: AgentKey): void;
210
- /** Closes the key's conversation now and drops every reference to it; the next opening continues it in a new session. */
289
+ /**
290
+ * Closes the key's conversation now; the next opening continues it in a new session. What the device kept of it and
291
+ * the conversation it continues stay: `forget` removes them.
292
+ */
211
293
  dispose(key: AgentKey): void;
294
+ /**
295
+ * Closes the key's conversation and forgets it on this device: the conversation it continued and the transcript kept
296
+ * by `persistence`. History's "delete" calls it after the tenant's backend deleted the conversation.
297
+ */
298
+ forget(key: AgentKey): void;
299
+ /** Ends every conversation no view shows that is past its session's end. The kits call it when the app comes back. */
300
+ sweep(): void;
301
+ /**
302
+ * History's delete: the backend deletes the conversation, then the device forgets it, only where the key still
303
+ * continues that conversation (an older history entry never forgets the key's newer one).
304
+ */
305
+ deleteConversation(key: AgentKey, conversationId: string): Promise<void>;
306
+ /**
307
+ * The person's erasure on this device: a sign-out, plus the launcher's greeting records (`greetingStorage`) and the
308
+ * analytics salt, every key this version and alpha.5 or alpha.6 wrote. Keys no index lists (an earlier version's, a
309
+ * write lost with its process) are found only when the storage has `keys` or `removePrefix`.
310
+ */
311
+ erase(): Promise<void>;
312
+ /** The launcher's greeting storage, under the sign-out fence; null without `greetingStorage`. */
313
+ readonly greetings: ConversationPersistence | null;
212
314
  /**
213
315
  * The key goes on with this conversation (history's "Continue"): pinned for the key and opened through the backend's
214
316
  * resume. One that is gone (404, 410: the backend's `AgentRefusal('ended')`) is said as `ended`, never replaced by a
215
317
  * new one silently.
216
318
  */
217
319
  continue(key: AgentKey, conversationId: string): AgentConversation;
218
- /** Every conversation closed and forgotten (what the device kept too), and the backend told to end the sessions. */
320
+ /**
321
+ * Every conversation closed and forgotten, and everything this runtime kept on the device (every transcript it ever
322
+ * wrote under `conversation.persistenceKey`, even of conversations already closed), the status and the name; then
323
+ * the backend is told to end the sessions at the same time. A late answer from before (a session, a status, a write)
324
+ * changes nothing. Kept on purpose: the launcher's greeting records and the analytics salt (`erase` takes them).
325
+ */
219
326
  signOut(): Promise<void>;
220
327
  /** The agent's status, read from the backend at most every `statusStaleMs`. */
221
328
  status(): Promise<AgentStatus>;
@@ -227,8 +334,27 @@ interface AgentRuntime {
227
334
  /** An event from a kit or the app, passed to `onEvent`. */
228
335
  emit(event: AgentEvent): void;
229
336
  }
230
- /** The key as a string: one conversation per person, mode, business and thread. */
231
- declare function agentKey(key: AgentKey): string;
337
+ /**
338
+ * Where the launcher remembers that it greeted a person in a mode, the key its storage sees. Encoded so a guest
339
+ * (`null`) is never a person whose id is "guest".
340
+ */
341
+ declare function greetedKey(subject: string | null, mode: string): string;
342
+ /**
343
+ * A conversation's key as analytics sees it: SHA-256 of the install's salt and the key, 128 bits in hex. Stable per
344
+ * install (the runtime keeps the salt in the app's storage until `erase()`), never the person's id, and not
345
+ * recomputable from a guessed id without the salt.
346
+ */
347
+ declare function hashKey(key: string, salt: string): string;
348
+ /**
349
+ * The key as a string: one conversation per agent, person, mode, business and thread. Encoded as a JSON array, so no
350
+ * value can pass for another (a guest is `null`, never a subject named "guest"; a `|` in an id is just a character).
351
+ */
352
+ declare function agentKey(key: AgentKey, agent?: string | null): string;
232
353
  declare function createAgentRuntime(options: AgentRuntimeOptions): AgentRuntime;
354
+ /**
355
+ * An `AgentRefusal`, also one made by another copy of this package (the mock in `@vinaverse/client/testing`, the
356
+ * backend helpers in `@vinaverse/client/backend`, a second install): recognised by its name and kind, not its class.
357
+ */
358
+ declare function isRefusal(error: unknown): error is AgentRefusal;
233
359
 
234
- export { type AgentBackend as A, type AgentClock as a, type AgentConversation as b, type AgentConversationState as c, type AgentEvent as d, type AgentEventType as e, type AgentFailure as f, type AgentFailureReason as g, type AgentInsecureAddresses as h, type AgentKey as i, type AgentPlace as j, type AgentRating as k, AgentRefusal as l, type AgentRetryPolicy as m, type AgentRuntime as n, type AgentRuntimeOptions as o, type AgentSessionRequest as p, type AgentStatus as q, type AgentStatusRead as r, agentKey as s, createAgentRuntime as t };
360
+ export { type AgentBackend as A, type AgentClock as a, type AgentConversation as b, type AgentConversationState as c, type AgentEntryPoint as d, type AgentEvent as e, type AgentEventType as f, type AgentFailure as g, type AgentFailureReason as h, type AgentInsecureAddresses as i, type AgentKey as j, type AgentPlace as k, type AgentRating as l, AgentRefusal as m, type AgentRetryPolicy as n, type AgentRuntime as o, type AgentRuntimeOptions as p, type AgentSessionRequest as q, type AgentStatus as r, type AgentStatusRead as s, type AgentWarning as t, agentKey as u, createAgentRuntime as v, greetedKey as w, hashKey as x, isRefusal as y };